blob: da648e398e1df0f5ea8da666d07b8f2efe716686 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
25/* for normal terminals */
26 int sg_term; /* "term=" highlighting attributes */
27 char_u *sg_start; /* terminal string for start highl */
28 char_u *sg_stop; /* terminal string for stop highl */
29 int sg_term_attr; /* Screen attr for term mode */
30/* for color terminals */
31 int sg_cterm; /* "cterm=" highlighting attr */
32 int sg_cterm_bold; /* bold attr was set for light color */
33 int sg_cterm_fg; /* terminal fg color number + 1 */
34 int sg_cterm_bg; /* terminal bg color number + 1 */
35 int sg_cterm_attr; /* Screen attr for color term mode */
36#ifdef FEAT_GUI
37/* for when using the GUI */
Bram Moolenaar071d4272004-06-13 20:20:40 +000038 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaare2cc9702005-03-15 22:43:58 +000040 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000041 GuiFont sg_font; /* GUI font handle */
42#ifdef FEAT_XFONTSET
43 GuiFontset sg_fontset; /* GUI fontset handle */
44#endif
45 char_u *sg_font_name; /* GUI font or fontset name */
46 int sg_gui_attr; /* Screen attr for GUI mode */
47#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020048#if defined(FEAT_GUI) || defined(FEAT_EVAL)
49/* Store the sp color name for the GUI or synIDattr() */
50 int sg_gui; /* "gui=" highlighting attributes */
51 char_u *sg_gui_fg_name;/* GUI foreground color name */
52 char_u *sg_gui_bg_name;/* GUI background color name */
53 char_u *sg_gui_sp_name;/* GUI special color name */
54#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000055 int sg_link; /* link to this highlight group ID */
56 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000057#ifdef FEAT_EVAL
58 scid_T sg_scriptID; /* script in which the group was last set */
59#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000060};
61
62#define SG_TERM 1 /* term has been set */
63#define SG_CTERM 2 /* cterm has been set */
64#define SG_GUI 4 /* gui has been set */
65#define SG_LINK 8 /* link has been set */
66
67static garray_T highlight_ga; /* highlight groups for 'highlight' option */
68
69#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
70
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020071#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
72
Bram Moolenaar071d4272004-06-13 20:20:40 +000073#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000074/* Flags to indicate an additional string for highlight name completion. */
75static int include_none = 0; /* when 1 include "None" */
76static int include_default = 0; /* when 1 include "default" */
77static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000078#endif
79
80/*
81 * The "term", "cterm" and "gui" arguments can be any combination of the
82 * following names, separated by commas (but no spaces!).
83 */
84static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000085 {"bold", "standout", "underline", "undercurl",
86 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000087static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000089
90static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
91static void syn_unadd_group __ARGS((void));
92static void set_hl_attr __ARGS((int idx));
93static void highlight_list_one __ARGS((int id));
94static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
95static int syn_add_group __ARGS((char_u *name));
96static int syn_list_header __ARGS((int did_header, int outlen, int id));
97static int hl_has_settings __ARGS((int idx, int check_link));
98static void highlight_clear __ARGS((int idx));
99
100#ifdef FEAT_GUI
101static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
102static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
103static guicolor_T color_name2handle __ARGS((char_u *name));
104static GuiFont font_name2handle __ARGS((char_u *name));
105# ifdef FEAT_XFONTSET
106static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
107# endif
108static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
109#endif
110
111/*
112 * An attribute number is the index in attr_table plus ATTR_OFF.
113 */
114#define ATTR_OFF (HL_ALL + 1)
115
116#if defined(FEAT_SYN_HL) || defined(PROTO)
117
118#define SYN_NAMELEN 50 /* maximum length of a syntax name */
119
120/* different types of offsets that are possible */
121#define SPO_MS_OFF 0 /* match start offset */
122#define SPO_ME_OFF 1 /* match end offset */
123#define SPO_HS_OFF 2 /* highl. start offset */
124#define SPO_HE_OFF 3 /* highl. end offset */
125#define SPO_RS_OFF 4 /* region start offset */
126#define SPO_RE_OFF 5 /* region end offset */
127#define SPO_LC_OFF 6 /* leading context offset */
128#define SPO_COUNT 7
129
130static char *(spo_name_tab[SPO_COUNT]) =
131 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
132
133/*
134 * The patterns that are being searched for are stored in a syn_pattern.
135 * A match item consists of one pattern.
136 * A start/end item consists of n start patterns and m end patterns.
137 * A start/skip/end item consists of n start patterns, one skip pattern and m
138 * end patterns.
139 * For the latter two, the patterns are always consecutive: start-skip-end.
140 *
141 * A character offset can be given for the matched text (_m_start and _m_end)
142 * and for the actually highlighted text (_h_start and _h_end).
143 */
144typedef struct syn_pattern
145{
146 char sp_type; /* see SPTYPE_ defines below */
147 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200148 int sp_flags; /* see HL_ defines below */
149#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200150 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200151#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000152 struct sp_syn sp_syn; /* struct passed to in_id_list() */
153 short sp_syn_match_id; /* highlight group ID of pattern */
154 char_u *sp_pattern; /* regexp to match, pattern */
155 regprog_T *sp_prog; /* regexp to match, program */
156 int sp_ic; /* ignore-case flag for sp_prog */
157 short sp_off_flags; /* see below */
158 int sp_offsets[SPO_COUNT]; /* offsets */
159 short *sp_cont_list; /* cont. group IDs, if non-zero */
160 short *sp_next_list; /* next group IDs, if non-zero */
161 int sp_sync_idx; /* sync item index (syncing only) */
162 int sp_line_id; /* ID of last line where tried */
163 int sp_startcol; /* next match in sp_line_id line */
164} synpat_T;
165
166/* The sp_off_flags are computed like this:
167 * offset from the start of the matched text: (1 << SPO_XX_OFF)
168 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
169 * When both are present, only one is used.
170 */
171
172#define SPTYPE_MATCH 1 /* match keyword with this group ID */
173#define SPTYPE_START 2 /* match a regexp, start of item */
174#define SPTYPE_END 3 /* match a regexp, end of item */
175#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
176
Bram Moolenaar071d4272004-06-13 20:20:40 +0000177
178#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
179
180#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
181
182/*
183 * Flags for b_syn_sync_flags:
184 */
185#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
186#define SF_MATCH 0x02 /* sync by matching a pattern */
187
188#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
189
Bram Moolenaar071d4272004-06-13 20:20:40 +0000190#define MAXKEYWLEN 80 /* maximum length of a keyword */
191
192/*
193 * The attributes of the syntax item that has been recognized.
194 */
195static int current_attr = 0; /* attr of current syntax word */
196#ifdef FEAT_EVAL
197static int current_id = 0; /* ID of current char for syn_get_id() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000198static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000199#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200200#ifdef FEAT_CONCEAL
201static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200202static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200203static int current_sub_char = 0;
204#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000205
Bram Moolenaar217ad922005-03-20 22:37:15 +0000206typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000207{
208 char_u *scl_name; /* syntax cluster name */
209 char_u *scl_name_u; /* uppercase of scl_name */
210 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000211} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000212
213/*
214 * Methods of combining two clusters
215 */
216#define CLUSTER_REPLACE 1 /* replace first list with second */
217#define CLUSTER_ADD 2 /* add second list to first */
218#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
219
Bram Moolenaar217ad922005-03-20 22:37:15 +0000220#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000221
222/*
223 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200224 * 0 - 19999 normal syntax groups
225 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
226 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
227 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
228 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000229 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200230#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200231#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
232#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
233#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
234
Bram Moolenaar42431a72011-04-01 14:44:59 +0200235#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
236#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000237
238/*
239 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
240 * expand_filename(). Most of the other syntax commands don't need it, so
241 * instead of passing it to them, we stow it here.
242 */
243static char_u **syn_cmdlinep;
244
245/*
246 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200247 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000248 * rules in each ":syn include"'d file.
249 */
250static int current_syn_inc_tag = 0;
251static int running_syn_inc_tag = 0;
252
253/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000254 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
255 * This avoids adding a pointer to the hashtable item.
256 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
257 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
258 * HI2KE() converts a hashitem pointer to a var pointer.
259 */
260static keyentry_T dumkey;
261#define KE2HIKEY(kp) ((kp)->keyword)
262#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
263#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
264
265/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000266 * To reduce the time spent in keepend(), remember at which level in the state
267 * stack the first item with "keepend" is present. When "-1", there is no
268 * "keepend" on the stack.
269 */
270static int keepend_level = -1;
271
272/*
273 * For the current state we need to remember more than just the idx.
274 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
275 * (The end positions have the column number of the next char)
276 */
277typedef struct state_item
278{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000279 int si_idx; /* index of syntax pattern or
280 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000281 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000282 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000283 int si_m_lnum; /* lnum of the match */
284 int si_m_startcol; /* starting column of the match */
285 lpos_T si_m_endpos; /* just after end posn of the match */
286 lpos_T si_h_startpos; /* start position of the highlighting */
287 lpos_T si_h_endpos; /* end position of the highlighting */
288 lpos_T si_eoe_pos; /* end position of end pattern */
289 int si_end_idx; /* group ID for end pattern or zero */
290 int si_ends; /* if match ends before si_m_endpos */
291 int si_attr; /* attributes in this state */
292 long si_flags; /* HL_HAS_EOL flag in this state, and
293 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200294#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200295 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200296 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200297#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000298 short *si_cont_list; /* list of contained groups */
299 short *si_next_list; /* nextgroup IDs after this item ends */
300 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
301 * pattern */
302} stateitem_T;
303
304#define KEYWORD_IDX -1 /* value of si_idx for keywords */
305#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
306 but contained groups */
307
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200308#ifdef FEAT_CONCEAL
309static int next_seqnr = 0; /* value to use for si_seqnr */
310#endif
311
Bram Moolenaar071d4272004-06-13 20:20:40 +0000312/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000313 * Struct to reduce the number of arguments to get_syn_options(), it's used
314 * very often.
315 */
316typedef struct
317{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000318 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000319 int keyword; /* TRUE for ":syn keyword" */
320 int *sync_idx; /* syntax item for "grouphere" argument, NULL
321 if not allowed */
322 char has_cont_list; /* TRUE if "cont_list" can be used */
323 short *cont_list; /* group IDs for "contains" argument */
324 short *cont_in_list; /* group IDs for "containedin" argument */
325 short *next_list; /* group IDs for "nextgroup" argument */
326} syn_opt_arg_T;
327
328/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000329 * The next possible match in the current line for any pattern is remembered,
330 * to avoid having to try for a match in each column.
331 * If next_match_idx == -1, not tried (in this line) yet.
332 * If next_match_col == MAXCOL, no match found in this line.
333 * (All end positions have the column of the char after the end)
334 */
335static int next_match_col; /* column for start of next match */
336static lpos_T next_match_m_endpos; /* position for end of next match */
337static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
338static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
339static int next_match_idx; /* index of matched item */
340static long next_match_flags; /* flags for next match */
341static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
342static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
343static int next_match_end_idx; /* ID of group for end pattn or zero */
344static reg_extmatch_T *next_match_extmatch = NULL;
345
346/*
347 * A state stack is an array of integers or stateitem_T, stored in a
348 * garray_T. A state stack is invalid if it's itemsize entry is zero.
349 */
350#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
351#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
352
353/*
354 * The current state (within the line) of the recognition engine.
355 * When current_state.ga_itemsize is 0 the current state is invalid.
356 */
357static win_T *syn_win; /* current window for highlighting */
358static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200359static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000360static linenr_T current_lnum = 0; /* lnum of current state */
361static colnr_T current_col = 0; /* column of current state */
362static int current_state_stored = 0; /* TRUE if stored current state
363 * after setting current_finished */
364static int current_finished = 0; /* current line has been finished */
365static garray_T current_state /* current stack of state_items */
366 = {0, 0, 0, 0, NULL};
367static short *current_next_list = NULL; /* when non-zero, nextgroup list */
368static int current_next_flags = 0; /* flags for current_next_list */
369static int current_line_id = 0; /* unique number for current line */
370
371#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
372
373static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
374static int syn_match_linecont __ARGS((linenr_T lnum));
375static void syn_start_line __ARGS((void));
376static void syn_update_ends __ARGS((int startofline));
377static void syn_stack_alloc __ARGS((void));
378static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200379static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000380static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000381static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000382static void load_current_state __ARGS((synstate_T *from));
383static void invalidate_current_state __ARGS((void));
384static int syn_stack_equal __ARGS((synstate_T *sp));
385static void validate_current_state __ARGS((void));
386static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000387static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000388static int did_match_already __ARGS((int idx, garray_T *gap));
389static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
390static void check_state_ends __ARGS((void));
391static void update_si_attr __ARGS((int idx));
392static void check_keepend __ARGS((void));
393static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
394static short *copy_id_list __ARGS((short *list));
395static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
396static int push_current_state __ARGS((int idx));
397static void pop_current_state __ARGS((void));
398
Bram Moolenaar860cae12010-06-05 23:22:07 +0200399static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000400static void find_endpos __ARGS((int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext));
401static void clear_syn_state __ARGS((synstate_T *p));
402static void clear_current_state __ARGS((void));
403
404static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
405static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
406static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
407static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
408static char_u *syn_getcurline __ARGS((void));
409static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200410static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000411static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000412static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000413static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200414static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
415static void syn_clear_pattern __ARGS((synblock_T *block, int i));
416static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000417static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200418static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000419static void syn_clear_one __ARGS((int id, int syncing));
420static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
421static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
422static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
423static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
424static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
425static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
426static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
427static void syn_lines_msg __ARGS((void));
428static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200429static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000430static void syn_list_one __ARGS((int id, int syncing, int link_only));
431static void syn_list_cluster __ARGS((int id));
432static void put_id_list __ARGS((char_u *name, short *list, int attr));
433static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000434static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
435static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
436static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200437static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000438static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200439static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000440static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
441static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
442static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
443static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
444#ifdef __BORLANDC__
445static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
446#else
447static int syn_compare_stub __ARGS((const void *v1, const void *v2));
448#endif
449static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
450static int syn_scl_name2id __ARGS((char_u *name));
451static int syn_scl_namen2id __ARGS((char_u *linep, int len));
452static int syn_check_cluster __ARGS((char_u *pp, int len));
453static int syn_add_cluster __ARGS((char_u *name));
454static void init_syn_patterns __ARGS((void));
455static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
456static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
457static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
458static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
459static void syn_incl_toplevel __ARGS((int id, int *flagsp));
460
461/*
462 * Start the syntax recognition for a line. This function is normally called
463 * from the screen updating, once for each displayed line.
464 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
465 * it. Careful: curbuf and curwin are likely to point to another buffer and
466 * window.
467 */
468 void
469syntax_start(wp, lnum)
470 win_T *wp;
471 linenr_T lnum;
472{
473 synstate_T *p;
474 synstate_T *last_valid = NULL;
475 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000476 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000477 linenr_T parsed_lnum;
478 linenr_T first_stored;
479 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000480 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200482#ifdef FEAT_CONCEAL
483 current_sub_char = NUL;
484#endif
485
Bram Moolenaar071d4272004-06-13 20:20:40 +0000486 /*
487 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000488 * Also do this when a change was made, the current state may be invalid
489 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000490 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200491 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000492 {
493 invalidate_current_state();
494 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200495 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000496 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000497 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000498 syn_win = wp;
499
500 /*
501 * Allocate syntax stack when needed.
502 */
503 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200504 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000505 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200506 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000507
508 /*
509 * If the state of the end of the previous line is useful, store it.
510 */
511 if (VALID_STATE(&current_state)
512 && current_lnum < lnum
513 && current_lnum < syn_buf->b_ml.ml_line_count)
514 {
515 (void)syn_finish_line(FALSE);
516 if (!current_state_stored)
517 {
518 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000519 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000520 }
521
522 /*
523 * If the current_lnum is now the same as "lnum", keep the current
524 * state (this happens very often!). Otherwise invalidate
525 * current_state and figure it out below.
526 */
527 if (current_lnum != lnum)
528 invalidate_current_state();
529 }
530 else
531 invalidate_current_state();
532
533 /*
534 * Try to synchronize from a saved state in b_sst_array[].
535 * Only do this if lnum is not before and not to far beyond a saved state.
536 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200537 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000538 {
539 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200540 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000541 {
542 if (p->sst_lnum > lnum)
543 break;
544 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
545 {
546 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200547 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000548 last_min_valid = p;
549 }
550 }
551 if (last_min_valid != NULL)
552 load_current_state(last_min_valid);
553 }
554
555 /*
556 * If "lnum" is before or far beyond a line with a saved state, need to
557 * re-synchronize.
558 */
559 if (INVALID_STATE(&current_state))
560 {
561 syn_sync(wp, lnum, last_valid);
Bram Moolenaar860cae12010-06-05 23:22:07 +0200562 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000563 }
564 else
565 first_stored = current_lnum;
566
567 /*
568 * Advance from the sync point or saved state until the current line.
569 * Save some entries for syncing with later on.
570 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200571 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000572 dist = 999999;
573 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200574 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000575 while (current_lnum < lnum)
576 {
577 syn_start_line();
578 (void)syn_finish_line(FALSE);
579 ++current_lnum;
580
581 /* If we parsed at least "minlines" lines or started at a valid
582 * state, the current state is considered valid. */
583 if (current_lnum >= first_stored)
584 {
585 /* Check if the saved state entry is for the current line and is
586 * equal to the current state. If so, then validate all saved
587 * states that depended on a change before the parsed line. */
588 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000589 prev = syn_stack_find_entry(current_lnum - 1);
590 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200591 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000592 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000593 sp = prev;
594 while (sp != NULL && sp->sst_lnum < current_lnum)
595 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000596 if (sp != NULL
597 && sp->sst_lnum == current_lnum
598 && syn_stack_equal(sp))
599 {
600 parsed_lnum = current_lnum;
601 prev = sp;
602 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
603 {
604 if (sp->sst_lnum <= lnum)
605 /* valid state before desired line, use this one */
606 prev = sp;
607 else if (sp->sst_change_lnum == 0)
608 /* past saved states depending on change, break here. */
609 break;
610 sp->sst_change_lnum = 0;
611 sp = sp->sst_next;
612 }
613 load_current_state(prev);
614 }
615 /* Store the state at this line when it's the first one, the line
616 * where we start parsing, or some distance from the previously
617 * saved state. But only when parsed at least 'minlines'. */
618 else if (prev == NULL
619 || current_lnum == lnum
620 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000621 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622 }
623
624 /* This can take a long time: break when CTRL-C pressed. The current
625 * state will be wrong then. */
626 line_breakcheck();
627 if (got_int)
628 {
629 current_lnum = lnum;
630 break;
631 }
632 }
633
634 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000635}
636
637/*
638 * We cannot simply discard growarrays full of state_items or buf_states; we
639 * have to manually release their extmatch pointers first.
640 */
641 static void
642clear_syn_state(p)
643 synstate_T *p;
644{
645 int i;
646 garray_T *gap;
647
648 if (p->sst_stacksize > SST_FIX_STATES)
649 {
650 gap = &(p->sst_union.sst_ga);
651 for (i = 0; i < gap->ga_len; i++)
652 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
653 ga_clear(gap);
654 }
655 else
656 {
657 for (i = 0; i < p->sst_stacksize; i++)
658 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
659 }
660}
661
662/*
663 * Cleanup the current_state stack.
664 */
665 static void
666clear_current_state()
667{
668 int i;
669 stateitem_T *sip;
670
671 sip = (stateitem_T *)(current_state.ga_data);
672 for (i = 0; i < current_state.ga_len; i++)
673 unref_extmatch(sip[i].si_extmatch);
674 ga_clear(&current_state);
675}
676
677/*
678 * Try to find a synchronisation point for line "lnum".
679 *
680 * This sets current_lnum and the current state. One of three methods is
681 * used:
682 * 1. Search backwards for the end of a C-comment.
683 * 2. Search backwards for given sync patterns.
684 * 3. Simply start on a given number of lines above "lnum".
685 */
686 static void
687syn_sync(wp, start_lnum, last_valid)
688 win_T *wp;
689 linenr_T start_lnum;
690 synstate_T *last_valid;
691{
692 buf_T *curbuf_save;
693 win_T *curwin_save;
694 pos_T cursor_save;
695 int idx;
696 linenr_T lnum;
697 linenr_T end_lnum;
698 linenr_T break_lnum;
699 int had_sync_point;
700 stateitem_T *cur_si;
701 synpat_T *spp;
702 char_u *line;
703 int found_flags = 0;
704 int found_match_idx = 0;
705 linenr_T found_current_lnum = 0;
706 int found_current_col= 0;
707 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000708 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000709
710 /*
711 * Clear any current state that might be hanging around.
712 */
713 invalidate_current_state();
714
715 /*
716 * Start at least "minlines" back. Default starting point for parsing is
717 * there.
718 * Start further back, to avoid that scrolling backwards will result in
719 * resyncing for every line. Now it resyncs only one out of N lines,
720 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
721 * Watch out for overflow when minlines is MAXLNUM.
722 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200723 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000724 start_lnum = 1;
725 else
726 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200727 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000728 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200729 else if (syn_block->b_syn_sync_minlines < 10)
730 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000731 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200732 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
733 if (syn_block->b_syn_sync_maxlines != 0
734 && lnum > syn_block->b_syn_sync_maxlines)
735 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000736 if (lnum >= start_lnum)
737 start_lnum = 1;
738 else
739 start_lnum -= lnum;
740 }
741 current_lnum = start_lnum;
742
743 /*
744 * 1. Search backwards for the end of a C-style comment.
745 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200746 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000747 {
748 /* Need to make syn_buf the current buffer for a moment, to be able to
749 * use find_start_comment(). */
750 curwin_save = curwin;
751 curwin = wp;
752 curbuf_save = curbuf;
753 curbuf = syn_buf;
754
755 /*
756 * Skip lines that end in a backslash.
757 */
758 for ( ; start_lnum > 1; --start_lnum)
759 {
760 line = ml_get(start_lnum - 1);
761 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
762 break;
763 }
764 current_lnum = start_lnum;
765
766 /* set cursor to start of search */
767 cursor_save = wp->w_cursor;
768 wp->w_cursor.lnum = start_lnum;
769 wp->w_cursor.col = 0;
770
771 /*
772 * If the line is inside a comment, need to find the syntax item that
773 * defines the comment.
774 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
775 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200776 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000777 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200778 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
779 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
780 == syn_block->b_syn_sync_id
781 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000782 {
783 validate_current_state();
784 if (push_current_state(idx) == OK)
785 update_si_attr(current_state.ga_len - 1);
786 break;
787 }
788 }
789
790 /* restore cursor and buffer */
791 wp->w_cursor = cursor_save;
792 curwin = curwin_save;
793 curbuf = curbuf_save;
794 }
795
796 /*
797 * 2. Search backwards for given sync patterns.
798 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200799 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000800 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200801 if (syn_block->b_syn_sync_maxlines != 0
802 && start_lnum > syn_block->b_syn_sync_maxlines)
803 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000804 else
805 break_lnum = 0;
806
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000807 found_m_endpos.lnum = 0;
808 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000809 end_lnum = start_lnum;
810 lnum = start_lnum;
811 while (--lnum > break_lnum)
812 {
813 /* This can take a long time: break when CTRL-C pressed. */
814 line_breakcheck();
815 if (got_int)
816 {
817 invalidate_current_state();
818 current_lnum = start_lnum;
819 break;
820 }
821
822 /* Check if we have run into a valid saved state stack now. */
823 if (last_valid != NULL && lnum == last_valid->sst_lnum)
824 {
825 load_current_state(last_valid);
826 break;
827 }
828
829 /*
830 * Check if the previous line has the line-continuation pattern.
831 */
832 if (lnum > 1 && syn_match_linecont(lnum - 1))
833 continue;
834
835 /*
836 * Start with nothing on the state stack
837 */
838 validate_current_state();
839
840 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
841 {
842 syn_start_line();
843 for (;;)
844 {
845 had_sync_point = syn_finish_line(TRUE);
846 /*
847 * When a sync point has been found, remember where, and
848 * continue to look for another one, further on in the line.
849 */
850 if (had_sync_point && current_state.ga_len)
851 {
852 cur_si = &CUR_STATE(current_state.ga_len - 1);
853 if (cur_si->si_m_endpos.lnum > start_lnum)
854 {
855 /* ignore match that goes to after where started */
856 current_lnum = end_lnum;
857 break;
858 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000859 if (cur_si->si_idx < 0)
860 {
861 /* Cannot happen? */
862 found_flags = 0;
863 found_match_idx = KEYWORD_IDX;
864 }
865 else
866 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200867 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000868 found_flags = spp->sp_flags;
869 found_match_idx = spp->sp_sync_idx;
870 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000871 found_current_lnum = current_lnum;
872 found_current_col = current_col;
873 found_m_endpos = cur_si->si_m_endpos;
874 /*
875 * Continue after the match (be aware of a zero-length
876 * match).
877 */
878 if (found_m_endpos.lnum > current_lnum)
879 {
880 current_lnum = found_m_endpos.lnum;
881 current_col = found_m_endpos.col;
882 if (current_lnum >= end_lnum)
883 break;
884 }
885 else if (found_m_endpos.col > current_col)
886 current_col = found_m_endpos.col;
887 else
888 ++current_col;
889
890 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000891 * an item that ends here, need to do that now. Be
892 * careful not to go past the NUL. */
893 prev_current_col = current_col;
894 if (syn_getcurline()[current_col] != NUL)
895 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000896 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000897 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000898 }
899 else
900 break;
901 }
902 }
903
904 /*
905 * If a sync point was encountered, break here.
906 */
907 if (found_flags)
908 {
909 /*
910 * Put the item that was specified by the sync point on the
911 * state stack. If there was no item specified, make the
912 * state stack empty.
913 */
914 clear_current_state();
915 if (found_match_idx >= 0
916 && push_current_state(found_match_idx) == OK)
917 update_si_attr(current_state.ga_len - 1);
918
919 /*
920 * When using "grouphere", continue from the sync point
921 * match, until the end of the line. Parsing starts at
922 * the next line.
923 * For "groupthere" the parsing starts at start_lnum.
924 */
925 if (found_flags & HL_SYNC_HERE)
926 {
927 if (current_state.ga_len)
928 {
929 cur_si = &CUR_STATE(current_state.ga_len - 1);
930 cur_si->si_h_startpos.lnum = found_current_lnum;
931 cur_si->si_h_startpos.col = found_current_col;
932 update_si_end(cur_si, (int)current_col, TRUE);
933 check_keepend();
934 }
935 current_col = found_m_endpos.col;
936 current_lnum = found_m_endpos.lnum;
937 (void)syn_finish_line(FALSE);
938 ++current_lnum;
939 }
940 else
941 current_lnum = start_lnum;
942
943 break;
944 }
945
946 end_lnum = lnum;
947 invalidate_current_state();
948 }
949
950 /* Ran into start of the file or exceeded maximum number of lines */
951 if (lnum <= break_lnum)
952 {
953 invalidate_current_state();
954 current_lnum = break_lnum + 1;
955 }
956 }
957
958 validate_current_state();
959}
960
961/*
962 * Return TRUE if the line-continuation pattern matches in line "lnum".
963 */
964 static int
965syn_match_linecont(lnum)
966 linenr_T lnum;
967{
968 regmmatch_T regmatch;
969
Bram Moolenaar860cae12010-06-05 23:22:07 +0200970 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000971 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200972 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
973 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000974 return syn_regexec(&regmatch, lnum, (colnr_T)0);
975 }
976 return FALSE;
977}
978
979/*
980 * Prepare the current state for the start of a line.
981 */
982 static void
983syn_start_line()
984{
985 current_finished = FALSE;
986 current_col = 0;
987
988 /*
989 * Need to update the end of a start/skip/end that continues from the
990 * previous line and regions that have "keepend".
991 */
992 if (current_state.ga_len > 0)
993 syn_update_ends(TRUE);
994
995 next_match_idx = -1;
996 ++current_line_id;
997}
998
999/*
1000 * Check for items in the stack that need their end updated.
1001 * When "startofline" is TRUE the last item is always updated.
1002 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1003 */
1004 static void
1005syn_update_ends(startofline)
1006 int startofline;
1007{
1008 stateitem_T *cur_si;
1009 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001010 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001011
1012 if (startofline)
1013 {
1014 /* Check for a match carried over from a previous line with a
1015 * contained region. The match ends as soon as the region ends. */
1016 for (i = 0; i < current_state.ga_len; ++i)
1017 {
1018 cur_si = &CUR_STATE(i);
1019 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001020 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001021 == SPTYPE_MATCH
1022 && cur_si->si_m_endpos.lnum < current_lnum)
1023 {
1024 cur_si->si_flags |= HL_MATCHCONT;
1025 cur_si->si_m_endpos.lnum = 0;
1026 cur_si->si_m_endpos.col = 0;
1027 cur_si->si_h_endpos = cur_si->si_m_endpos;
1028 cur_si->si_ends = TRUE;
1029 }
1030 }
1031 }
1032
1033 /*
1034 * Need to update the end of a start/skip/end that continues from the
1035 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001036 * influence contained items. If we've just removed "extend"
1037 * (startofline == 0) then we should update ends of normal regions
1038 * contained inside "keepend" because "extend" could have extended
1039 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001040 * Then check for items ending in column 0.
1041 */
1042 i = current_state.ga_len - 1;
1043 if (keepend_level >= 0)
1044 for ( ; i > keepend_level; --i)
1045 if (CUR_STATE(i).si_flags & HL_EXTEND)
1046 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001047
1048 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001049 for ( ; i < current_state.ga_len; ++i)
1050 {
1051 cur_si = &CUR_STATE(i);
1052 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001053 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001054 || (i == current_state.ga_len - 1 && startofline))
1055 {
1056 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1057 cur_si->si_h_startpos.lnum = current_lnum;
1058
1059 if (!(cur_si->si_flags & HL_MATCHCONT))
1060 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001061
1062 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1063 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001064 }
1065 }
1066 check_keepend();
1067 check_state_ends();
1068}
1069
1070/****************************************
1071 * Handling of the state stack cache.
1072 */
1073
1074/*
1075 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1076 *
1077 * To speed up syntax highlighting, the state stack for the start of some
1078 * lines is cached. These entries can be used to start parsing at that point.
1079 *
1080 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1081 * valid entries. b_sst_first points to the first one, then follow sst_next.
1082 * The entries are sorted on line number. The first entry is often for line 2
1083 * (line 1 always starts with an empty stack).
1084 * There is also a list for free entries. This construction is used to avoid
1085 * having to allocate and free memory blocks too often.
1086 *
1087 * When making changes to the buffer, this is logged in b_mod_*. When calling
1088 * update_screen() to update the display, it will call
1089 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1090 * entries. The entries which are inside the changed area are removed,
1091 * because they must be recomputed. Entries below the changed have their line
1092 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1093 * set to indicate that a check must be made if the changed lines would change
1094 * the cached entry.
1095 *
1096 * When later displaying lines, an entry is stored for each line. Displayed
1097 * lines are likely to be displayed again, in which case the state at the
1098 * start of the line is needed.
1099 * For not displayed lines, an entry is stored for every so many lines. These
1100 * entries will be used e.g., when scrolling backwards. The distance between
1101 * entries depends on the number of lines in the buffer. For small buffers
1102 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1103 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1104 */
1105
Bram Moolenaar860cae12010-06-05 23:22:07 +02001106 static void
1107syn_stack_free_block(block)
1108 synblock_T *block;
1109{
1110 synstate_T *p;
1111
1112 if (block->b_sst_array != NULL)
1113 {
1114 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1115 clear_syn_state(p);
1116 vim_free(block->b_sst_array);
1117 block->b_sst_array = NULL;
1118 block->b_sst_len = 0;
1119 }
1120}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001121/*
1122 * Free b_sst_array[] for buffer "buf".
1123 * Used when syntax items changed to force resyncing everywhere.
1124 */
1125 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001126syn_stack_free_all(block)
1127 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001128{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001129 win_T *wp;
1130
Bram Moolenaar860cae12010-06-05 23:22:07 +02001131 syn_stack_free_block(block);
1132
1133
Bram Moolenaar071d4272004-06-13 20:20:40 +00001134#ifdef FEAT_FOLDING
1135 /* When using "syntax" fold method, must update all folds. */
1136 FOR_ALL_WINDOWS(wp)
1137 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001138 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001139 foldUpdateAll(wp);
1140 }
1141#endif
1142}
1143
1144/*
1145 * Allocate the syntax state stack for syn_buf when needed.
1146 * If the number of entries in b_sst_array[] is much too big or a bit too
1147 * small, reallocate it.
1148 * Also used to allocate b_sst_array[] for the first time.
1149 */
1150 static void
1151syn_stack_alloc()
1152{
1153 long len;
1154 synstate_T *to, *from;
1155 synstate_T *sstp;
1156
1157 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1158 if (len < SST_MIN_ENTRIES)
1159 len = SST_MIN_ENTRIES;
1160 else if (len > SST_MAX_ENTRIES)
1161 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001162 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001163 {
1164 /* Allocate 50% too much, to avoid reallocating too often. */
1165 len = syn_buf->b_ml.ml_line_count;
1166 len = (len + len / 2) / SST_DIST + Rows * 2;
1167 if (len < SST_MIN_ENTRIES)
1168 len = SST_MIN_ENTRIES;
1169 else if (len > SST_MAX_ENTRIES)
1170 len = SST_MAX_ENTRIES;
1171
Bram Moolenaar860cae12010-06-05 23:22:07 +02001172 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001173 {
1174 /* When shrinking the array, cleanup the existing stack.
1175 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001176 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001177 && syn_stack_cleanup())
1178 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001179 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1180 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001181 }
1182
1183 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1184 if (sstp == NULL) /* out of memory! */
1185 return;
1186
1187 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001188 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001189 {
1190 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001191 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192 from = from->sst_next)
1193 {
1194 ++to;
1195 *to = *from;
1196 to->sst_next = to + 1;
1197 }
1198 }
1199 if (to != sstp - 1)
1200 {
1201 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001202 syn_block->b_sst_first = sstp;
1203 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 }
1205 else
1206 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001207 syn_block->b_sst_first = NULL;
1208 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209 }
1210
1211 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001212 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001213 while (++to < sstp + len)
1214 to->sst_next = to + 1;
1215 (sstp + len - 1)->sst_next = NULL;
1216
Bram Moolenaar860cae12010-06-05 23:22:07 +02001217 vim_free(syn_block->b_sst_array);
1218 syn_block->b_sst_array = sstp;
1219 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001220 }
1221}
1222
1223/*
1224 * Check for changes in a buffer to affect stored syntax states. Uses the
1225 * b_mod_* fields.
1226 * Called from update_screen(), before screen is being updated, once for each
1227 * displayed buffer.
1228 */
1229 void
1230syn_stack_apply_changes(buf)
1231 buf_T *buf;
1232{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001233 win_T *wp;
1234
1235 syn_stack_apply_changes_block(&buf->b_s, buf);
1236
1237 FOR_ALL_WINDOWS(wp)
1238 {
1239 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1240 syn_stack_apply_changes_block(wp->w_s, buf);
1241 }
1242}
1243
1244 static void
1245syn_stack_apply_changes_block(block, buf)
1246 synblock_T *block;
1247 buf_T *buf;
1248{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 synstate_T *p, *prev, *np;
1250 linenr_T n;
1251
Bram Moolenaar860cae12010-06-05 23:22:07 +02001252 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001253 return;
1254
1255 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001256 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001257 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001258 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001259 {
1260 n = p->sst_lnum + buf->b_mod_xlines;
1261 if (n <= buf->b_mod_bot)
1262 {
1263 /* this state is inside the changed area, remove it */
1264 np = p->sst_next;
1265 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001266 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001267 else
1268 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001269 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001270 p = np;
1271 continue;
1272 }
1273 /* This state is below the changed area. Remember the line
1274 * that needs to be parsed before this entry can be made valid
1275 * again. */
1276 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1277 {
1278 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1279 p->sst_change_lnum += buf->b_mod_xlines;
1280 else
1281 p->sst_change_lnum = buf->b_mod_top;
1282 }
1283 if (p->sst_change_lnum == 0
1284 || p->sst_change_lnum < buf->b_mod_bot)
1285 p->sst_change_lnum = buf->b_mod_bot;
1286
1287 p->sst_lnum = n;
1288 }
1289 prev = p;
1290 p = p->sst_next;
1291 }
1292}
1293
1294/*
1295 * Reduce the number of entries in the state stack for syn_buf.
1296 * Returns TRUE if at least one entry was freed.
1297 */
1298 static int
1299syn_stack_cleanup()
1300{
1301 synstate_T *p, *prev;
1302 disptick_T tick;
1303 int above;
1304 int dist;
1305 int retval = FALSE;
1306
Bram Moolenaar860cae12010-06-05 23:22:07 +02001307 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001308 return retval;
1309
1310 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001311 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001312 dist = 999999;
1313 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001314 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001315
1316 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001317 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001318 * be removed. Set "above" when the "tick" for the oldest entry is above
1319 * "b_sst_lasttick" (the display tick wraps around).
1320 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001321 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001322 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001323 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001324 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1325 {
1326 if (prev->sst_lnum + dist > p->sst_lnum)
1327 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001328 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001329 {
1330 if (!above || p->sst_tick < tick)
1331 tick = p->sst_tick;
1332 above = TRUE;
1333 }
1334 else if (!above && p->sst_tick < tick)
1335 tick = p->sst_tick;
1336 }
1337 }
1338
1339 /*
1340 * Go through the list to make the entries for the oldest tick at an
1341 * interval of several lines.
1342 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001343 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1345 {
1346 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1347 {
1348 /* Move this entry from used list to free list */
1349 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001350 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351 p = prev;
1352 retval = TRUE;
1353 }
1354 }
1355 return retval;
1356}
1357
1358/*
1359 * Free the allocated memory for a syn_state item.
1360 * Move the entry into the free list.
1361 */
1362 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001363syn_stack_free_entry(block, p)
1364 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001365 synstate_T *p;
1366{
1367 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001368 p->sst_next = block->b_sst_firstfree;
1369 block->b_sst_firstfree = p;
1370 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001371}
1372
1373/*
1374 * Find an entry in the list of state stacks at or before "lnum".
1375 * Returns NULL when there is no entry or the first entry is after "lnum".
1376 */
1377 static synstate_T *
1378syn_stack_find_entry(lnum)
1379 linenr_T lnum;
1380{
1381 synstate_T *p, *prev;
1382
1383 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001384 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001385 {
1386 if (p->sst_lnum == lnum)
1387 return p;
1388 if (p->sst_lnum > lnum)
1389 break;
1390 }
1391 return prev;
1392}
1393
1394/*
1395 * Try saving the current state in b_sst_array[].
1396 * The current state must be valid for the start of the current_lnum line!
1397 */
1398 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001399store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400{
1401 int i;
1402 synstate_T *p;
1403 bufstate_T *bp;
1404 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001405 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001406
1407 /*
1408 * If the current state contains a start or end pattern that continues
1409 * from the previous line, we can't use it. Don't store it then.
1410 */
1411 for (i = current_state.ga_len - 1; i >= 0; --i)
1412 {
1413 cur_si = &CUR_STATE(i);
1414 if (cur_si->si_h_startpos.lnum >= current_lnum
1415 || cur_si->si_m_endpos.lnum >= current_lnum
1416 || cur_si->si_h_endpos.lnum >= current_lnum
1417 || (cur_si->si_end_idx
1418 && cur_si->si_eoe_pos.lnum >= current_lnum))
1419 break;
1420 }
1421 if (i >= 0)
1422 {
1423 if (sp != NULL)
1424 {
1425 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001426 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001427 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001428 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001429 else
1430 {
1431 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001432 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001433 if (p->sst_next == sp)
1434 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001435 if (p != NULL) /* just in case */
1436 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001437 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001438 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001439 sp = NULL;
1440 }
1441 }
1442 else if (sp == NULL || sp->sst_lnum != current_lnum)
1443 {
1444 /*
1445 * Add a new entry
1446 */
1447 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001448 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001449 {
1450 (void)syn_stack_cleanup();
1451 /* "sp" may have been moved to the freelist now */
1452 sp = syn_stack_find_entry(current_lnum);
1453 }
1454 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001455 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456 sp = NULL;
1457 else
1458 {
1459 /* Take the first item from the free list and put it in the used
1460 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001461 p = syn_block->b_sst_firstfree;
1462 syn_block->b_sst_firstfree = p->sst_next;
1463 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001464 if (sp == NULL)
1465 {
1466 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001467 p->sst_next = syn_block->b_sst_first;
1468 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469 }
1470 else
1471 {
1472 /* insert in list after *sp */
1473 p->sst_next = sp->sst_next;
1474 sp->sst_next = p;
1475 }
1476 sp = p;
1477 sp->sst_stacksize = 0;
1478 sp->sst_lnum = current_lnum;
1479 }
1480 }
1481 if (sp != NULL)
1482 {
1483 /* When overwriting an existing state stack, clear it first */
1484 clear_syn_state(sp);
1485 sp->sst_stacksize = current_state.ga_len;
1486 if (current_state.ga_len > SST_FIX_STATES)
1487 {
1488 /* Need to clear it, might be something remaining from when the
1489 * length was less than SST_FIX_STATES. */
1490 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1491 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1492 sp->sst_stacksize = 0;
1493 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001494 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001495 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1496 }
1497 else
1498 bp = sp->sst_union.sst_stack;
1499 for (i = 0; i < sp->sst_stacksize; ++i)
1500 {
1501 bp[i].bs_idx = CUR_STATE(i).si_idx;
1502 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001503#ifdef FEAT_CONCEAL
1504 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1505 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1506#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001507 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1508 }
1509 sp->sst_next_flags = current_next_flags;
1510 sp->sst_next_list = current_next_list;
1511 sp->sst_tick = display_tick;
1512 sp->sst_change_lnum = 0;
1513 }
1514 current_state_stored = TRUE;
1515 return sp;
1516}
1517
1518/*
1519 * Copy a state stack from "from" in b_sst_array[] to current_state;
1520 */
1521 static void
1522load_current_state(from)
1523 synstate_T *from;
1524{
1525 int i;
1526 bufstate_T *bp;
1527
1528 clear_current_state();
1529 validate_current_state();
1530 keepend_level = -1;
1531 if (from->sst_stacksize
1532 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1533 {
1534 if (from->sst_stacksize > SST_FIX_STATES)
1535 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1536 else
1537 bp = from->sst_union.sst_stack;
1538 for (i = 0; i < from->sst_stacksize; ++i)
1539 {
1540 CUR_STATE(i).si_idx = bp[i].bs_idx;
1541 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001542#ifdef FEAT_CONCEAL
1543 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1544 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1545#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001546 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1547 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1548 keepend_level = i;
1549 CUR_STATE(i).si_ends = FALSE;
1550 CUR_STATE(i).si_m_lnum = 0;
1551 if (CUR_STATE(i).si_idx >= 0)
1552 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001553 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001554 else
1555 CUR_STATE(i).si_next_list = NULL;
1556 update_si_attr(i);
1557 }
1558 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001559 }
1560 current_next_list = from->sst_next_list;
1561 current_next_flags = from->sst_next_flags;
1562 current_lnum = from->sst_lnum;
1563}
1564
1565/*
1566 * Compare saved state stack "*sp" with the current state.
1567 * Return TRUE when they are equal.
1568 */
1569 static int
1570syn_stack_equal(sp)
1571 synstate_T *sp;
1572{
1573 int i, j;
1574 bufstate_T *bp;
1575 reg_extmatch_T *six, *bsx;
1576
1577 /* First a quick check if the stacks have the same size end nextlist. */
1578 if (sp->sst_stacksize == current_state.ga_len
1579 && sp->sst_next_list == current_next_list)
1580 {
1581 /* Need to compare all states on both stacks. */
1582 if (sp->sst_stacksize > SST_FIX_STATES)
1583 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1584 else
1585 bp = sp->sst_union.sst_stack;
1586
1587 for (i = current_state.ga_len; --i >= 0; )
1588 {
1589 /* If the item has another index the state is different. */
1590 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1591 break;
1592 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1593 {
1594 /* When the extmatch pointers are different, the strings in
1595 * them can still be the same. Check if the extmatch
1596 * references are equal. */
1597 bsx = bp[i].bs_extmatch;
1598 six = CUR_STATE(i).si_extmatch;
1599 /* If one of the extmatch pointers is NULL the states are
1600 * different. */
1601 if (bsx == NULL || six == NULL)
1602 break;
1603 for (j = 0; j < NSUBEXP; ++j)
1604 {
1605 /* Check each referenced match string. They must all be
1606 * equal. */
1607 if (bsx->matches[j] != six->matches[j])
1608 {
1609 /* If the pointer is different it can still be the
1610 * same text. Compare the strings, ignore case when
1611 * the start item has the sp_ic flag set. */
1612 if (bsx->matches[j] == NULL
1613 || six->matches[j] == NULL)
1614 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001615 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001616 ? MB_STRICMP(bsx->matches[j],
1617 six->matches[j]) != 0
1618 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1619 break;
1620 }
1621 }
1622 if (j != NSUBEXP)
1623 break;
1624 }
1625 }
1626 if (i < 0)
1627 return TRUE;
1628 }
1629 return FALSE;
1630}
1631
1632/*
1633 * We stop parsing syntax above line "lnum". If the stored state at or below
1634 * this line depended on a change before it, it now depends on the line below
1635 * the last parsed line.
1636 * The window looks like this:
1637 * line which changed
1638 * displayed line
1639 * displayed line
1640 * lnum -> line below window
1641 */
1642 void
1643syntax_end_parsing(lnum)
1644 linenr_T lnum;
1645{
1646 synstate_T *sp;
1647
1648 sp = syn_stack_find_entry(lnum);
1649 if (sp != NULL && sp->sst_lnum < lnum)
1650 sp = sp->sst_next;
1651
1652 if (sp != NULL && sp->sst_change_lnum != 0)
1653 sp->sst_change_lnum = lnum;
1654}
1655
1656/*
1657 * End of handling of the state stack.
1658 ****************************************/
1659
1660 static void
1661invalidate_current_state()
1662{
1663 clear_current_state();
1664 current_state.ga_itemsize = 0; /* mark current_state invalid */
1665 current_next_list = NULL;
1666 keepend_level = -1;
1667}
1668
1669 static void
1670validate_current_state()
1671{
1672 current_state.ga_itemsize = sizeof(stateitem_T);
1673 current_state.ga_growsize = 3;
1674}
1675
1676/*
1677 * Return TRUE if the syntax at start of lnum changed since last time.
1678 * This will only be called just after get_syntax_attr() for the previous
1679 * line, to check if the next line needs to be redrawn too.
1680 */
1681 int
1682syntax_check_changed(lnum)
1683 linenr_T lnum;
1684{
1685 int retval = TRUE;
1686 synstate_T *sp;
1687
Bram Moolenaar071d4272004-06-13 20:20:40 +00001688 /*
1689 * Check the state stack when:
1690 * - lnum is just below the previously syntaxed line.
1691 * - lnum is not before the lines with saved states.
1692 * - lnum is not past the lines with saved states.
1693 * - lnum is at or before the last changed line.
1694 */
1695 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1696 {
1697 sp = syn_stack_find_entry(lnum);
1698 if (sp != NULL && sp->sst_lnum == lnum)
1699 {
1700 /*
1701 * finish the previous line (needed when not all of the line was
1702 * drawn)
1703 */
1704 (void)syn_finish_line(FALSE);
1705
1706 /*
1707 * Compare the current state with the previously saved state of
1708 * the line.
1709 */
1710 if (syn_stack_equal(sp))
1711 retval = FALSE;
1712
1713 /*
1714 * Store the current state in b_sst_array[] for later use.
1715 */
1716 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001717 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001718 }
1719 }
1720
Bram Moolenaar071d4272004-06-13 20:20:40 +00001721 return retval;
1722}
1723
1724/*
1725 * Finish the current line.
1726 * This doesn't return any attributes, it only gets the state at the end of
1727 * the line. It can start anywhere in the line, as long as the current state
1728 * is valid.
1729 */
1730 static int
1731syn_finish_line(syncing)
1732 int syncing; /* called for syncing */
1733{
1734 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001735 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001736
1737 if (!current_finished)
1738 {
1739 while (!current_finished)
1740 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001741 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001742 /*
1743 * When syncing, and found some item, need to check the item.
1744 */
1745 if (syncing && current_state.ga_len)
1746 {
1747 /*
1748 * Check for match with sync item.
1749 */
1750 cur_si = &CUR_STATE(current_state.ga_len - 1);
1751 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001752 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1754 return TRUE;
1755
1756 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001757 * that ends here, need to do that now. Be careful not to go
1758 * past the NUL. */
1759 prev_current_col = current_col;
1760 if (syn_getcurline()[current_col] != NUL)
1761 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001762 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001763 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 }
1765 ++current_col;
1766 }
1767 }
1768 return FALSE;
1769}
1770
1771/*
1772 * Return highlight attributes for next character.
1773 * Must first call syntax_start() once for the line.
1774 * "col" is normally 0 for the first use in a line, and increments by one each
1775 * time. It's allowed to skip characters and to stop before the end of the
1776 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001777 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1778 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001779 */
1780 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001781get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001783 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001784 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785{
1786 int attr = 0;
1787
Bram Moolenaar349955a2007-08-14 21:07:36 +00001788 if (can_spell != NULL)
1789 /* Default: Only do spelling when there is no @Spell cluster or when
1790 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001791 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1792 ? (syn_block->b_spell_cluster_id == 0)
1793 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001794
Bram Moolenaar071d4272004-06-13 20:20:40 +00001795 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001796 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001797 return 0;
1798
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001799 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001800 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001801 {
1802 clear_current_state();
1803#ifdef FEAT_EVAL
1804 current_id = 0;
1805 current_trans_id = 0;
1806#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001807#ifdef FEAT_CONCEAL
1808 current_flags = 0;
1809#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001810 return 0;
1811 }
1812
Bram Moolenaar071d4272004-06-13 20:20:40 +00001813 /* Make sure current_state is valid */
1814 if (INVALID_STATE(&current_state))
1815 validate_current_state();
1816
1817 /*
1818 * Skip from the current column to "col", get the attributes for "col".
1819 */
1820 while (current_col <= col)
1821 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001822 attr = syn_current_attr(FALSE, TRUE, can_spell,
1823 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001824 ++current_col;
1825 }
1826
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 return attr;
1828}
1829
1830/*
1831 * Get syntax attributes for current_lnum, current_col.
1832 */
1833 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001834syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001835 int syncing; /* When 1: called for syncing */
1836 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001837 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001838 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001839{
1840 int syn_id;
1841 lpos_T endpos; /* was: char_u *endp; */
1842 lpos_T hl_startpos; /* was: int hl_startcol; */
1843 lpos_T hl_endpos;
1844 lpos_T eos_pos; /* end-of-start match (start region) */
1845 lpos_T eoe_pos; /* end-of-end pattern */
1846 int end_idx; /* group ID for end pattern */
1847 int idx;
1848 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001849 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001850 int startcol;
1851 int endcol;
1852 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001853 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001854 short *next_list;
1855 int found_match; /* found usable match */
1856 static int try_next_column = FALSE; /* must try in next col */
1857 int do_keywords;
1858 regmmatch_T regmatch;
1859 lpos_T pos;
1860 int lc_col;
1861 reg_extmatch_T *cur_extmatch = NULL;
1862 char_u *line; /* current line. NOTE: becomes invalid after
1863 looking for a pattern match! */
1864
1865 /* variables for zero-width matches that have a "nextgroup" argument */
1866 int keep_next_list;
1867 int zero_width_next_list = FALSE;
1868 garray_T zero_width_next_ga;
1869
1870 /*
1871 * No character, no attributes! Past end of line?
1872 * Do try matching with an empty line (could be the start of a region).
1873 */
1874 line = syn_getcurline();
1875 if (line[current_col] == NUL && current_col != 0)
1876 {
1877 /*
1878 * If we found a match after the last column, use it.
1879 */
1880 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1881 && next_match_col != MAXCOL)
1882 (void)push_next_match(NULL);
1883
1884 current_finished = TRUE;
1885 current_state_stored = FALSE;
1886 return 0;
1887 }
1888
1889 /* if the current or next character is NUL, we will finish the line now */
1890 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1891 {
1892 current_finished = TRUE;
1893 current_state_stored = FALSE;
1894 }
1895
1896 /*
1897 * When in the previous column there was a match but it could not be used
1898 * (empty match or already matched in this column) need to try again in
1899 * the next column.
1900 */
1901 if (try_next_column)
1902 {
1903 next_match_idx = -1;
1904 try_next_column = FALSE;
1905 }
1906
1907 /* Only check for keywords when not syncing and there are some. */
1908 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001909 && (syn_block->b_keywtab.ht_used > 0
1910 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001911
1912 /* Init the list of zero-width matches with a nextlist. This is used to
1913 * avoid matching the same item in the same position twice. */
1914 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1915
1916 /*
1917 * Repeat matching keywords and patterns, to find contained items at the
1918 * same column. This stops when there are no extra matches at the current
1919 * column.
1920 */
1921 do
1922 {
1923 found_match = FALSE;
1924 keep_next_list = FALSE;
1925 syn_id = 0;
1926
1927 /*
1928 * 1. Check for a current state.
1929 * Only when there is no current state, or if the current state may
1930 * contain other things, we need to check for keywords and patterns.
1931 * Always need to check for contained items if some item has the
1932 * "containedin" argument (takes extra time!).
1933 */
1934 if (current_state.ga_len)
1935 cur_si = &CUR_STATE(current_state.ga_len - 1);
1936 else
1937 cur_si = NULL;
1938
Bram Moolenaar860cae12010-06-05 23:22:07 +02001939 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001940 || cur_si->si_cont_list != NULL)
1941 {
1942 /*
1943 * 2. Check for keywords, if on a keyword char after a non-keyword
1944 * char. Don't do this when syncing.
1945 */
1946 if (do_keywords)
1947 {
1948 line = syn_getcurline();
1949 if (vim_iswordc_buf(line + current_col, syn_buf)
1950 && (current_col == 0
1951 || !vim_iswordc_buf(line + current_col - 1
1952#ifdef FEAT_MBYTE
1953 - (has_mbyte
1954 ? (*mb_head_off)(line, line + current_col - 1)
1955 : 0)
1956#endif
1957 , syn_buf)))
1958 {
1959 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001960 &endcol, &flags, &next_list, cur_si,
1961 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001962 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001963 {
1964 if (push_current_state(KEYWORD_IDX) == OK)
1965 {
1966 cur_si = &CUR_STATE(current_state.ga_len - 1);
1967 cur_si->si_m_startcol = current_col;
1968 cur_si->si_h_startpos.lnum = current_lnum;
1969 cur_si->si_h_startpos.col = 0; /* starts right away */
1970 cur_si->si_m_endpos.lnum = current_lnum;
1971 cur_si->si_m_endpos.col = endcol;
1972 cur_si->si_h_endpos.lnum = current_lnum;
1973 cur_si->si_h_endpos.col = endcol;
1974 cur_si->si_ends = TRUE;
1975 cur_si->si_end_idx = 0;
1976 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001977#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02001978 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001979 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001980 if (current_state.ga_len > 1)
1981 cur_si->si_flags |=
1982 CUR_STATE(current_state.ga_len - 2).si_flags
1983 & HL_CONCEAL;
1984#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001985 cur_si->si_id = syn_id;
1986 cur_si->si_trans_id = syn_id;
1987 if (flags & HL_TRANSP)
1988 {
1989 if (current_state.ga_len < 2)
1990 {
1991 cur_si->si_attr = 0;
1992 cur_si->si_trans_id = 0;
1993 }
1994 else
1995 {
1996 cur_si->si_attr = CUR_STATE(
1997 current_state.ga_len - 2).si_attr;
1998 cur_si->si_trans_id = CUR_STATE(
1999 current_state.ga_len - 2).si_trans_id;
2000 }
2001 }
2002 else
2003 cur_si->si_attr = syn_id2attr(syn_id);
2004 cur_si->si_cont_list = NULL;
2005 cur_si->si_next_list = next_list;
2006 check_keepend();
2007 }
2008 else
2009 vim_free(next_list);
2010 }
2011 }
2012 }
2013
2014 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002015 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002016 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002017 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002018 {
2019 /*
2020 * If we didn't check for a match yet, or we are past it, check
2021 * for any match with a pattern.
2022 */
2023 if (next_match_idx < 0 || next_match_col < (int)current_col)
2024 {
2025 /*
2026 * Check all relevant patterns for a match at this
2027 * position. This is complicated, because matching with a
2028 * pattern takes quite a bit of time, thus we want to
2029 * avoid doing it when it's not needed.
2030 */
2031 next_match_idx = 0; /* no match in this line yet */
2032 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002033 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002034 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002035 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002036 if ( spp->sp_syncing == syncing
2037 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2038 && (spp->sp_type == SPTYPE_MATCH
2039 || spp->sp_type == SPTYPE_START)
2040 && (current_next_list != NULL
2041 ? in_id_list(NULL, current_next_list,
2042 &spp->sp_syn, 0)
2043 : (cur_si == NULL
2044 ? !(spp->sp_flags & HL_CONTAINED)
2045 : in_id_list(cur_si,
2046 cur_si->si_cont_list, &spp->sp_syn,
2047 spp->sp_flags & HL_CONTAINED))))
2048 {
2049 /* If we already tried matching in this line, and
2050 * there isn't a match before next_match_col, skip
2051 * this item. */
2052 if (spp->sp_line_id == current_line_id
2053 && spp->sp_startcol >= next_match_col)
2054 continue;
2055 spp->sp_line_id = current_line_id;
2056
2057 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2058 if (lc_col < 0)
2059 lc_col = 0;
2060
2061 regmatch.rmm_ic = spp->sp_ic;
2062 regmatch.regprog = spp->sp_prog;
2063 if (!syn_regexec(&regmatch, current_lnum,
2064 (colnr_T)lc_col))
2065 {
2066 /* no match in this line, try another one */
2067 spp->sp_startcol = MAXCOL;
2068 continue;
2069 }
2070
2071 /*
2072 * Compute the first column of the match.
2073 */
2074 syn_add_start_off(&pos, &regmatch,
2075 spp, SPO_MS_OFF, -1);
2076 if (pos.lnum > current_lnum)
2077 {
2078 /* must have used end of match in a next line,
2079 * we can't handle that */
2080 spp->sp_startcol = MAXCOL;
2081 continue;
2082 }
2083 startcol = pos.col;
2084
2085 /* remember the next column where this pattern
2086 * matches in the current line */
2087 spp->sp_startcol = startcol;
2088
2089 /*
2090 * If a previously found match starts at a lower
2091 * column number, don't use this one.
2092 */
2093 if (startcol >= next_match_col)
2094 continue;
2095
2096 /*
2097 * If we matched this pattern at this position
2098 * before, skip it. Must retry in the next
2099 * column, because it may match from there.
2100 */
2101 if (did_match_already(idx, &zero_width_next_ga))
2102 {
2103 try_next_column = TRUE;
2104 continue;
2105 }
2106
2107 endpos.lnum = regmatch.endpos[0].lnum;
2108 endpos.col = regmatch.endpos[0].col;
2109
2110 /* Compute the highlight start. */
2111 syn_add_start_off(&hl_startpos, &regmatch,
2112 spp, SPO_HS_OFF, -1);
2113
2114 /* Compute the region start. */
2115 /* Default is to use the end of the match. */
2116 syn_add_end_off(&eos_pos, &regmatch,
2117 spp, SPO_RS_OFF, 0);
2118
2119 /*
2120 * Grab the external submatches before they get
2121 * overwritten. Reference count doesn't change.
2122 */
2123 unref_extmatch(cur_extmatch);
2124 cur_extmatch = re_extmatch_out;
2125 re_extmatch_out = NULL;
2126
2127 flags = 0;
2128 eoe_pos.lnum = 0; /* avoid warning */
2129 eoe_pos.col = 0;
2130 end_idx = 0;
2131 hl_endpos.lnum = 0;
2132
2133 /*
2134 * For a "oneline" the end must be found in the
2135 * same line too. Search for it after the end of
2136 * the match with the start pattern. Set the
2137 * resulting end positions at the same time.
2138 */
2139 if (spp->sp_type == SPTYPE_START
2140 && (spp->sp_flags & HL_ONELINE))
2141 {
2142 lpos_T startpos;
2143
2144 startpos = endpos;
2145 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2146 &flags, &eoe_pos, &end_idx, cur_extmatch);
2147 if (endpos.lnum == 0)
2148 continue; /* not found */
2149 }
2150
2151 /*
2152 * For a "match" the size must be > 0 after the
2153 * end offset needs has been added. Except when
2154 * syncing.
2155 */
2156 else if (spp->sp_type == SPTYPE_MATCH)
2157 {
2158 syn_add_end_off(&hl_endpos, &regmatch, spp,
2159 SPO_HE_OFF, 0);
2160 syn_add_end_off(&endpos, &regmatch, spp,
2161 SPO_ME_OFF, 0);
2162 if (endpos.lnum == current_lnum
2163 && (int)endpos.col + syncing < startcol)
2164 {
2165 /*
2166 * If an empty string is matched, may need
2167 * to try matching again at next column.
2168 */
2169 if (regmatch.startpos[0].col
2170 == regmatch.endpos[0].col)
2171 try_next_column = TRUE;
2172 continue;
2173 }
2174 }
2175
2176 /*
2177 * keep the best match so far in next_match_*
2178 */
2179 /* Highlighting must start after startpos and end
2180 * before endpos. */
2181 if (hl_startpos.lnum == current_lnum
2182 && (int)hl_startpos.col < startcol)
2183 hl_startpos.col = startcol;
2184 limit_pos_zero(&hl_endpos, &endpos);
2185
2186 next_match_idx = idx;
2187 next_match_col = startcol;
2188 next_match_m_endpos = endpos;
2189 next_match_h_endpos = hl_endpos;
2190 next_match_h_startpos = hl_startpos;
2191 next_match_flags = flags;
2192 next_match_eos_pos = eos_pos;
2193 next_match_eoe_pos = eoe_pos;
2194 next_match_end_idx = end_idx;
2195 unref_extmatch(next_match_extmatch);
2196 next_match_extmatch = cur_extmatch;
2197 cur_extmatch = NULL;
2198 }
2199 }
2200 }
2201
2202 /*
2203 * If we found a match at the current column, use it.
2204 */
2205 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2206 {
2207 synpat_T *lspp;
2208
2209 /* When a zero-width item matched which has a nextgroup,
2210 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002211 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002212 if (next_match_m_endpos.lnum == current_lnum
2213 && next_match_m_endpos.col == current_col
2214 && lspp->sp_next_list != NULL)
2215 {
2216 current_next_list = lspp->sp_next_list;
2217 current_next_flags = lspp->sp_flags;
2218 keep_next_list = TRUE;
2219 zero_width_next_list = TRUE;
2220
2221 /* Add the index to a list, so that we can check
2222 * later that we don't match it again (and cause an
2223 * endless loop). */
2224 if (ga_grow(&zero_width_next_ga, 1) == OK)
2225 {
2226 ((int *)(zero_width_next_ga.ga_data))
2227 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002228 }
2229 next_match_idx = -1;
2230 }
2231 else
2232 cur_si = push_next_match(cur_si);
2233 found_match = TRUE;
2234 }
2235 }
2236 }
2237
2238 /*
2239 * Handle searching for nextgroup match.
2240 */
2241 if (current_next_list != NULL && !keep_next_list)
2242 {
2243 /*
2244 * If a nextgroup was not found, continue looking for one if:
2245 * - this is an empty line and the "skipempty" option was given
2246 * - we are on white space and the "skipwhite" option was given
2247 */
2248 if (!found_match)
2249 {
2250 line = syn_getcurline();
2251 if (((current_next_flags & HL_SKIPWHITE)
2252 && vim_iswhite(line[current_col]))
2253 || ((current_next_flags & HL_SKIPEMPTY)
2254 && *line == NUL))
2255 break;
2256 }
2257
2258 /*
2259 * If a nextgroup was found: Use it, and continue looking for
2260 * contained matches.
2261 * If a nextgroup was not found: Continue looking for a normal
2262 * match.
2263 * When did set current_next_list for a zero-width item and no
2264 * match was found don't loop (would get stuck).
2265 */
2266 current_next_list = NULL;
2267 next_match_idx = -1;
2268 if (!zero_width_next_list)
2269 found_match = TRUE;
2270 }
2271
2272 } while (found_match);
2273
2274 /*
2275 * Use attributes from the current state, if within its highlighting.
2276 * If not, use attributes from the current-but-one state, etc.
2277 */
2278 current_attr = 0;
2279#ifdef FEAT_EVAL
2280 current_id = 0;
2281 current_trans_id = 0;
2282#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002283#ifdef FEAT_CONCEAL
2284 current_flags = 0;
2285#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002286 if (cur_si != NULL)
2287 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002288#ifndef FEAT_EVAL
2289 int current_trans_id = 0;
2290#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002291 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2292 {
2293 sip = &CUR_STATE(idx);
2294 if ((current_lnum > sip->si_h_startpos.lnum
2295 || (current_lnum == sip->si_h_startpos.lnum
2296 && current_col >= sip->si_h_startpos.col))
2297 && (sip->si_h_endpos.lnum == 0
2298 || current_lnum < sip->si_h_endpos.lnum
2299 || (current_lnum == sip->si_h_endpos.lnum
2300 && current_col < sip->si_h_endpos.col)))
2301 {
2302 current_attr = sip->si_attr;
2303#ifdef FEAT_EVAL
2304 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002305#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002306 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002307#ifdef FEAT_CONCEAL
2308 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002309 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002310 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002311#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002312 break;
2313 }
2314 }
2315
Bram Moolenaar217ad922005-03-20 22:37:15 +00002316 if (can_spell != NULL)
2317 {
2318 struct sp_syn sps;
2319
2320 /*
2321 * set "can_spell" to TRUE if spell checking is supposed to be
2322 * done in the current item.
2323 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002324 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002325 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002326 /* There is no @Spell cluster: Do spelling for items without
2327 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002328 if (syn_block->b_nospell_cluster_id == 0
2329 || current_trans_id == 0)
2330 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002331 else
2332 {
2333 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002334 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002335 sps.cont_in_list = NULL;
2336 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2337 }
2338 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002339 else
2340 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002341 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002342 * the @Spell cluster. But not when @NoSpell is also there.
2343 * At the toplevel only spell check when ":syn spell toplevel"
2344 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002345 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002346 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002347 else
2348 {
2349 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002350 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002351 sps.cont_in_list = NULL;
2352 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2353
Bram Moolenaar860cae12010-06-05 23:22:07 +02002354 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002355 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002356 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002357 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2358 *can_spell = FALSE;
2359 }
2360 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002361 }
2362 }
2363
2364
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 /*
2366 * Check for end of current state (and the states before it) at the
2367 * next column. Don't do this for syncing, because we would miss a
2368 * single character match.
2369 * First check if the current state ends at the current column. It
2370 * may be for an empty match and a containing item might end in the
2371 * current column.
2372 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002373 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002374 {
2375 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002376 if (current_state.ga_len > 0
2377 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002378 {
2379 ++current_col;
2380 check_state_ends();
2381 --current_col;
2382 }
2383 }
2384 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002385 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002386 /* Default: Only do spelling when there is no @Spell cluster or when
2387 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002388 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2389 ? (syn_block->b_spell_cluster_id == 0)
2390 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002391
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002392 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002393 if (current_next_list != NULL
2394 && syn_getcurline()[current_col + 1] == NUL
2395 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2396 current_next_list = NULL;
2397
2398 if (zero_width_next_ga.ga_len > 0)
2399 ga_clear(&zero_width_next_ga);
2400
2401 /* No longer need external matches. But keep next_match_extmatch. */
2402 unref_extmatch(re_extmatch_out);
2403 re_extmatch_out = NULL;
2404 unref_extmatch(cur_extmatch);
2405
2406 return current_attr;
2407}
2408
2409
2410/*
2411 * Check if we already matched pattern "idx" at the current column.
2412 */
2413 static int
2414did_match_already(idx, gap)
2415 int idx;
2416 garray_T *gap;
2417{
2418 int i;
2419
2420 for (i = current_state.ga_len; --i >= 0; )
2421 if (CUR_STATE(i).si_m_startcol == (int)current_col
2422 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2423 && CUR_STATE(i).si_idx == idx)
2424 return TRUE;
2425
2426 /* Zero-width matches with a nextgroup argument are not put on the syntax
2427 * stack, and can only be matched once anyway. */
2428 for (i = gap->ga_len; --i >= 0; )
2429 if (((int *)(gap->ga_data))[i] == idx)
2430 return TRUE;
2431
2432 return FALSE;
2433}
2434
2435/*
2436 * Push the next match onto the stack.
2437 */
2438 static stateitem_T *
2439push_next_match(cur_si)
2440 stateitem_T *cur_si;
2441{
2442 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002443#ifdef FEAT_CONCEAL
2444 int save_flags;
2445#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002446
Bram Moolenaar860cae12010-06-05 23:22:07 +02002447 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002448
2449 /*
2450 * Push the item in current_state stack;
2451 */
2452 if (push_current_state(next_match_idx) == OK)
2453 {
2454 /*
2455 * If it's a start-skip-end type that crosses lines, figure out how
2456 * much it continues in this line. Otherwise just fill in the length.
2457 */
2458 cur_si = &CUR_STATE(current_state.ga_len - 1);
2459 cur_si->si_h_startpos = next_match_h_startpos;
2460 cur_si->si_m_startcol = current_col;
2461 cur_si->si_m_lnum = current_lnum;
2462 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002463#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002464 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002465 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002466 if (current_state.ga_len > 1)
2467 cur_si->si_flags |=
2468 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2469#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002470 cur_si->si_next_list = spp->sp_next_list;
2471 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2472 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2473 {
2474 /* Try to find the end pattern in the current line */
2475 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2476 check_keepend();
2477 }
2478 else
2479 {
2480 cur_si->si_m_endpos = next_match_m_endpos;
2481 cur_si->si_h_endpos = next_match_h_endpos;
2482 cur_si->si_ends = TRUE;
2483 cur_si->si_flags |= next_match_flags;
2484 cur_si->si_eoe_pos = next_match_eoe_pos;
2485 cur_si->si_end_idx = next_match_end_idx;
2486 }
2487 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2488 keepend_level = current_state.ga_len - 1;
2489 check_keepend();
2490 update_si_attr(current_state.ga_len - 1);
2491
Bram Moolenaar860cae12010-06-05 23:22:07 +02002492#ifdef FEAT_CONCEAL
2493 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2494#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002495 /*
2496 * If the start pattern has another highlight group, push another item
2497 * on the stack for the start pattern.
2498 */
2499 if ( spp->sp_type == SPTYPE_START
2500 && spp->sp_syn_match_id != 0
2501 && push_current_state(next_match_idx) == OK)
2502 {
2503 cur_si = &CUR_STATE(current_state.ga_len - 1);
2504 cur_si->si_h_startpos = next_match_h_startpos;
2505 cur_si->si_m_startcol = current_col;
2506 cur_si->si_m_lnum = current_lnum;
2507 cur_si->si_m_endpos = next_match_eos_pos;
2508 cur_si->si_h_endpos = next_match_eos_pos;
2509 cur_si->si_ends = TRUE;
2510 cur_si->si_end_idx = 0;
2511 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002512#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002513 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002514 cur_si->si_flags |= save_flags;
2515 if (cur_si->si_flags & HL_CONCEALENDS)
2516 cur_si->si_flags |= HL_CONCEAL;
2517#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002518 cur_si->si_next_list = NULL;
2519 check_keepend();
2520 update_si_attr(current_state.ga_len - 1);
2521 }
2522 }
2523
2524 next_match_idx = -1; /* try other match next time */
2525
2526 return cur_si;
2527}
2528
2529/*
2530 * Check for end of current state (and the states before it).
2531 */
2532 static void
2533check_state_ends()
2534{
2535 stateitem_T *cur_si;
2536 int had_extend = FALSE;
2537
2538 cur_si = &CUR_STATE(current_state.ga_len - 1);
2539 for (;;)
2540 {
2541 if (cur_si->si_ends
2542 && (cur_si->si_m_endpos.lnum < current_lnum
2543 || (cur_si->si_m_endpos.lnum == current_lnum
2544 && cur_si->si_m_endpos.col <= current_col)))
2545 {
2546 /*
2547 * If there is an end pattern group ID, highlight the end pattern
2548 * now. No need to pop the current item from the stack.
2549 * Only do this if the end pattern continues beyond the current
2550 * position.
2551 */
2552 if (cur_si->si_end_idx
2553 && (cur_si->si_eoe_pos.lnum > current_lnum
2554 || (cur_si->si_eoe_pos.lnum == current_lnum
2555 && cur_si->si_eoe_pos.col > current_col)))
2556 {
2557 cur_si->si_idx = cur_si->si_end_idx;
2558 cur_si->si_end_idx = 0;
2559 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2560 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2561 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002562#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002563 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002564 if (cur_si->si_flags & HL_CONCEALENDS)
2565 cur_si->si_flags |= HL_CONCEAL;
2566#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002567 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002568
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002569 /* nextgroup= should not match in the end pattern */
2570 current_next_list = NULL;
2571
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002572 /* what matches next may be different now, clear it */
2573 next_match_idx = 0;
2574 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575 break;
2576 }
2577 else
2578 {
2579 /* handle next_list, unless at end of line and no "skipnl" or
2580 * "skipempty" */
2581 current_next_list = cur_si->si_next_list;
2582 current_next_flags = cur_si->si_flags;
2583 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2584 && syn_getcurline()[current_col] == NUL)
2585 current_next_list = NULL;
2586
2587 /* When the ended item has "extend", another item with
2588 * "keepend" now needs to check for its end. */
2589 if (cur_si->si_flags & HL_EXTEND)
2590 had_extend = TRUE;
2591
2592 pop_current_state();
2593
2594 if (current_state.ga_len == 0)
2595 break;
2596
Bram Moolenaar81993f42008-01-11 20:27:45 +00002597 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598 {
2599 syn_update_ends(FALSE);
2600 if (current_state.ga_len == 0)
2601 break;
2602 }
2603
2604 cur_si = &CUR_STATE(current_state.ga_len - 1);
2605
2606 /*
2607 * Only for a region the search for the end continues after
2608 * the end of the contained item. If the contained match
2609 * included the end-of-line, break here, the region continues.
2610 * Don't do this when:
2611 * - "keepend" is used for the contained item
2612 * - not at the end of the line (could be end="x$"me=e-1).
2613 * - "excludenl" is used (HL_HAS_EOL won't be set)
2614 */
2615 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002616 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002617 == SPTYPE_START
2618 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2619 {
2620 update_si_end(cur_si, (int)current_col, TRUE);
2621 check_keepend();
2622 if ((current_next_flags & HL_HAS_EOL)
2623 && keepend_level < 0
2624 && syn_getcurline()[current_col] == NUL)
2625 break;
2626 }
2627 }
2628 }
2629 else
2630 break;
2631 }
2632}
2633
2634/*
2635 * Update an entry in the current_state stack for a match or region. This
2636 * fills in si_attr, si_next_list and si_cont_list.
2637 */
2638 static void
2639update_si_attr(idx)
2640 int idx;
2641{
2642 stateitem_T *sip = &CUR_STATE(idx);
2643 synpat_T *spp;
2644
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002645 /* This should not happen... */
2646 if (sip->si_idx < 0)
2647 return;
2648
Bram Moolenaar860cae12010-06-05 23:22:07 +02002649 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002650 if (sip->si_flags & HL_MATCH)
2651 sip->si_id = spp->sp_syn_match_id;
2652 else
2653 sip->si_id = spp->sp_syn.id;
2654 sip->si_attr = syn_id2attr(sip->si_id);
2655 sip->si_trans_id = sip->si_id;
2656 if (sip->si_flags & HL_MATCH)
2657 sip->si_cont_list = NULL;
2658 else
2659 sip->si_cont_list = spp->sp_cont_list;
2660
2661 /*
2662 * For transparent items, take attr from outer item.
2663 * Also take cont_list, if there is none.
2664 * Don't do this for the matchgroup of a start or end pattern.
2665 */
2666 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2667 {
2668 if (idx == 0)
2669 {
2670 sip->si_attr = 0;
2671 sip->si_trans_id = 0;
2672 if (sip->si_cont_list == NULL)
2673 sip->si_cont_list = ID_LIST_ALL;
2674 }
2675 else
2676 {
2677 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2678 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002679 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2680 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002681 if (sip->si_cont_list == NULL)
2682 {
2683 sip->si_flags |= HL_TRANS_CONT;
2684 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2685 }
2686 }
2687 }
2688}
2689
2690/*
2691 * Check the current stack for patterns with "keepend" flag.
2692 * Propagate the match-end to contained items, until a "skipend" item is found.
2693 */
2694 static void
2695check_keepend()
2696{
2697 int i;
2698 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002699 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002700 stateitem_T *sip;
2701
2702 /*
2703 * This check can consume a lot of time; only do it from the level where
2704 * there really is a keepend.
2705 */
2706 if (keepend_level < 0)
2707 return;
2708
2709 /*
2710 * Find the last index of an "extend" item. "keepend" items before that
2711 * won't do anything. If there is no "extend" item "i" will be
2712 * "keepend_level" and all "keepend" items will work normally.
2713 */
2714 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2715 if (CUR_STATE(i).si_flags & HL_EXTEND)
2716 break;
2717
2718 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002719 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002720 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002721 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002722 for ( ; i < current_state.ga_len; ++i)
2723 {
2724 sip = &CUR_STATE(i);
2725 if (maxpos.lnum != 0)
2726 {
2727 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002728 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002729 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2730 sip->si_ends = TRUE;
2731 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002732 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2733 {
2734 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002735 || maxpos.lnum > sip->si_m_endpos.lnum
2736 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002737 && maxpos.col > sip->si_m_endpos.col))
2738 maxpos = sip->si_m_endpos;
2739 if (maxpos_h.lnum == 0
2740 || maxpos_h.lnum > sip->si_h_endpos.lnum
2741 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2742 && maxpos_h.col > sip->si_h_endpos.col))
2743 maxpos_h = sip->si_h_endpos;
2744 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002745 }
2746}
2747
2748/*
2749 * Update an entry in the current_state stack for a start-skip-end pattern.
2750 * This finds the end of the current item, if it's in the current line.
2751 *
2752 * Return the flags for the matched END.
2753 */
2754 static void
2755update_si_end(sip, startcol, force)
2756 stateitem_T *sip;
2757 int startcol; /* where to start searching for the end */
2758 int force; /* when TRUE overrule a previous end */
2759{
2760 lpos_T startpos;
2761 lpos_T endpos;
2762 lpos_T hl_endpos;
2763 lpos_T end_endpos;
2764 int end_idx;
2765
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002766 /* return quickly for a keyword */
2767 if (sip->si_idx < 0)
2768 return;
2769
Bram Moolenaar071d4272004-06-13 20:20:40 +00002770 /* Don't update when it's already done. Can be a match of an end pattern
2771 * that started in a previous line. Watch out: can also be a "keepend"
2772 * from a containing item. */
2773 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2774 return;
2775
2776 /*
2777 * We need to find the end of the region. It may continue in the next
2778 * line.
2779 */
2780 end_idx = 0;
2781 startpos.lnum = current_lnum;
2782 startpos.col = startcol;
2783 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2784 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2785
2786 if (endpos.lnum == 0)
2787 {
2788 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002789 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002790 {
2791 /* a "oneline" never continues in the next line */
2792 sip->si_ends = TRUE;
2793 sip->si_m_endpos.lnum = current_lnum;
2794 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2795 }
2796 else
2797 {
2798 /* continues in the next line */
2799 sip->si_ends = FALSE;
2800 sip->si_m_endpos.lnum = 0;
2801 }
2802 sip->si_h_endpos = sip->si_m_endpos;
2803 }
2804 else
2805 {
2806 /* match within this line */
2807 sip->si_m_endpos = endpos;
2808 sip->si_h_endpos = hl_endpos;
2809 sip->si_eoe_pos = end_endpos;
2810 sip->si_ends = TRUE;
2811 sip->si_end_idx = end_idx;
2812 }
2813}
2814
2815/*
2816 * Add a new state to the current state stack.
2817 * It is cleared and the index set to "idx".
2818 * Return FAIL if it's not possible (out of memory).
2819 */
2820 static int
2821push_current_state(idx)
2822 int idx;
2823{
2824 if (ga_grow(&current_state, 1) == FAIL)
2825 return FAIL;
2826 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2827 CUR_STATE(current_state.ga_len).si_idx = idx;
2828 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002829 return OK;
2830}
2831
2832/*
2833 * Remove a state from the current_state stack.
2834 */
2835 static void
2836pop_current_state()
2837{
2838 if (current_state.ga_len)
2839 {
2840 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2841 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002842 }
2843 /* after the end of a pattern, try matching a keyword or pattern */
2844 next_match_idx = -1;
2845
2846 /* if first state with "keepend" is popped, reset keepend_level */
2847 if (keepend_level >= current_state.ga_len)
2848 keepend_level = -1;
2849}
2850
2851/*
2852 * Find the end of a start/skip/end syntax region after "startpos".
2853 * Only checks one line.
2854 * Also handles a match item that continued from a previous line.
2855 * If not found, the syntax item continues in the next line. m_endpos->lnum
2856 * will be 0.
2857 * If found, the end of the region and the end of the highlighting is
2858 * computed.
2859 */
2860 static void
2861find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2862 end_idx, start_ext)
2863 int idx; /* index of the pattern */
2864 lpos_T *startpos; /* where to start looking for an END match */
2865 lpos_T *m_endpos; /* return: end of match */
2866 lpos_T *hl_endpos; /* return: end of highlighting */
2867 long *flagsp; /* return: flags of matching END */
2868 lpos_T *end_endpos; /* return: end of end pattern match */
2869 int *end_idx; /* return: group ID for end pat. match, or 0 */
2870 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2871{
2872 colnr_T matchcol;
2873 synpat_T *spp, *spp_skip;
2874 int start_idx;
2875 int best_idx;
2876 regmmatch_T regmatch;
2877 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2878 lpos_T pos;
2879 char_u *line;
2880 int had_match = FALSE;
2881
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002882 /* just in case we are invoked for a keyword */
2883 if (idx < 0)
2884 return;
2885
Bram Moolenaar071d4272004-06-13 20:20:40 +00002886 /*
2887 * Check for being called with a START pattern.
2888 * Can happen with a match that continues to the next line, because it
2889 * contained a region.
2890 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002891 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002892 if (spp->sp_type != SPTYPE_START)
2893 {
2894 *hl_endpos = *startpos;
2895 return;
2896 }
2897
2898 /*
2899 * Find the SKIP or first END pattern after the last START pattern.
2900 */
2901 for (;;)
2902 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002903 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002904 if (spp->sp_type != SPTYPE_START)
2905 break;
2906 ++idx;
2907 }
2908
2909 /*
2910 * Lookup the SKIP pattern (if present)
2911 */
2912 if (spp->sp_type == SPTYPE_SKIP)
2913 {
2914 spp_skip = spp;
2915 ++idx;
2916 }
2917 else
2918 spp_skip = NULL;
2919
2920 /* Setup external matches for syn_regexec(). */
2921 unref_extmatch(re_extmatch_in);
2922 re_extmatch_in = ref_extmatch(start_ext);
2923
2924 matchcol = startpos->col; /* start looking for a match at sstart */
2925 start_idx = idx; /* remember the first END pattern. */
2926 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2927 for (;;)
2928 {
2929 /*
2930 * Find end pattern that matches first after "matchcol".
2931 */
2932 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002933 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002934 {
2935 int lc_col = matchcol;
2936
Bram Moolenaar860cae12010-06-05 23:22:07 +02002937 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002938 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2939 break;
2940 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2941 if (lc_col < 0)
2942 lc_col = 0;
2943
2944 regmatch.rmm_ic = spp->sp_ic;
2945 regmatch.regprog = spp->sp_prog;
2946 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2947 {
2948 if (best_idx == -1 || regmatch.startpos[0].col
2949 < best_regmatch.startpos[0].col)
2950 {
2951 best_idx = idx;
2952 best_regmatch.startpos[0] = regmatch.startpos[0];
2953 best_regmatch.endpos[0] = regmatch.endpos[0];
2954 }
2955 }
2956 }
2957
2958 /*
2959 * If all end patterns have been tried, and there is no match, the
2960 * item continues until end-of-line.
2961 */
2962 if (best_idx == -1)
2963 break;
2964
2965 /*
2966 * If the skip pattern matches before the end pattern,
2967 * continue searching after the skip pattern.
2968 */
2969 if (spp_skip != NULL)
2970 {
2971 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2972
2973 if (lc_col < 0)
2974 lc_col = 0;
2975 regmatch.rmm_ic = spp_skip->sp_ic;
2976 regmatch.regprog = spp_skip->sp_prog;
2977 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2978 && regmatch.startpos[0].col
2979 <= best_regmatch.startpos[0].col)
2980 {
2981 /* Add offset to skip pattern match */
2982 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2983
2984 /* If the skip pattern goes on to the next line, there is no
2985 * match with an end pattern in this line. */
2986 if (pos.lnum > startpos->lnum)
2987 break;
2988
2989 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2990
2991 /* take care of an empty match or negative offset */
2992 if (pos.col <= matchcol)
2993 ++matchcol;
2994 else if (pos.col <= regmatch.endpos[0].col)
2995 matchcol = pos.col;
2996 else
2997 /* Be careful not to jump over the NUL at the end-of-line */
2998 for (matchcol = regmatch.endpos[0].col;
2999 line[matchcol] != NUL && matchcol < pos.col;
3000 ++matchcol)
3001 ;
3002
3003 /* if the skip pattern includes end-of-line, break here */
3004 if (line[matchcol] == NUL)
3005 break;
3006
3007 continue; /* start with first end pattern again */
3008 }
3009 }
3010
3011 /*
3012 * Match from start pattern to end pattern.
3013 * Correct for match and highlight offset of end pattern.
3014 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003015 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003016 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3017 /* can't end before the start */
3018 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3019 m_endpos->col = startpos->col;
3020
3021 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3022 /* can't end before the start */
3023 if (end_endpos->lnum == startpos->lnum
3024 && end_endpos->col < startpos->col)
3025 end_endpos->col = startpos->col;
3026 /* can't end after the match */
3027 limit_pos(end_endpos, m_endpos);
3028
3029 /*
3030 * If the end group is highlighted differently, adjust the pointers.
3031 */
3032 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3033 {
3034 *end_idx = best_idx;
3035 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3036 {
3037 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3038 hl_endpos->col = best_regmatch.endpos[0].col;
3039 }
3040 else
3041 {
3042 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3043 hl_endpos->col = best_regmatch.startpos[0].col;
3044 }
3045 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3046
3047 /* can't end before the start */
3048 if (hl_endpos->lnum == startpos->lnum
3049 && hl_endpos->col < startpos->col)
3050 hl_endpos->col = startpos->col;
3051 limit_pos(hl_endpos, m_endpos);
3052
3053 /* now the match ends where the highlighting ends, it is turned
3054 * into the matchgroup for the end */
3055 *m_endpos = *hl_endpos;
3056 }
3057 else
3058 {
3059 *end_idx = 0;
3060 *hl_endpos = *end_endpos;
3061 }
3062
3063 *flagsp = spp->sp_flags;
3064
3065 had_match = TRUE;
3066 break;
3067 }
3068
3069 /* no match for an END pattern in this line */
3070 if (!had_match)
3071 m_endpos->lnum = 0;
3072
3073 /* Remove external matches. */
3074 unref_extmatch(re_extmatch_in);
3075 re_extmatch_in = NULL;
3076}
3077
3078/*
3079 * Limit "pos" not to be after "limit".
3080 */
3081 static void
3082limit_pos(pos, limit)
3083 lpos_T *pos;
3084 lpos_T *limit;
3085{
3086 if (pos->lnum > limit->lnum)
3087 *pos = *limit;
3088 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3089 pos->col = limit->col;
3090}
3091
3092/*
3093 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3094 */
3095 static void
3096limit_pos_zero(pos, limit)
3097 lpos_T *pos;
3098 lpos_T *limit;
3099{
3100 if (pos->lnum == 0)
3101 *pos = *limit;
3102 else
3103 limit_pos(pos, limit);
3104}
3105
3106/*
3107 * Add offset to matched text for end of match or highlight.
3108 */
3109 static void
3110syn_add_end_off(result, regmatch, spp, idx, extra)
3111 lpos_T *result; /* returned position */
3112 regmmatch_T *regmatch; /* start/end of match */
3113 synpat_T *spp; /* matched pattern */
3114 int idx; /* index of offset */
3115 int extra; /* extra chars for offset to start */
3116{
3117 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003118 int off;
3119 char_u *base;
3120 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003121
3122 if (spp->sp_off_flags & (1 << idx))
3123 {
3124 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003125 col = regmatch->startpos[0].col;
3126 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003127 }
3128 else
3129 {
3130 result->lnum = regmatch->endpos[0].lnum;
3131 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003132 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003133 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003134 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3135 * is a matchgroup. Watch out for match with last NL in the buffer. */
3136 if (result->lnum > syn_buf->b_ml.ml_line_count)
3137 col = 0;
3138 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003139 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003140 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3141 p = base + col;
3142 if (off > 0)
3143 {
3144 while (off-- > 0 && *p != NUL)
3145 mb_ptr_adv(p);
3146 }
3147 else if (off < 0)
3148 {
3149 while (off++ < 0 && base < p)
3150 mb_ptr_back(base, p);
3151 }
3152 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003153 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003154 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003155}
3156
3157/*
3158 * Add offset to matched text for start of match or highlight.
3159 * Avoid resulting column to become negative.
3160 */
3161 static void
3162syn_add_start_off(result, regmatch, spp, idx, extra)
3163 lpos_T *result; /* returned position */
3164 regmmatch_T *regmatch; /* start/end of match */
3165 synpat_T *spp;
3166 int idx;
3167 int extra; /* extra chars for offset to end */
3168{
3169 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003170 int off;
3171 char_u *base;
3172 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003173
3174 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3175 {
3176 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003177 col = regmatch->endpos[0].col;
3178 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003179 }
3180 else
3181 {
3182 result->lnum = regmatch->startpos[0].lnum;
3183 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003184 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003185 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003186 if (result->lnum > syn_buf->b_ml.ml_line_count)
3187 {
3188 /* a "\n" at the end of the pattern may take us below the last line */
3189 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003190 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003191 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003192 if (off != 0)
3193 {
3194 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3195 p = base + col;
3196 if (off > 0)
3197 {
3198 while (off-- && *p != NUL)
3199 mb_ptr_adv(p);
3200 }
3201 else if (off < 0)
3202 {
3203 while (off++ && base < p)
3204 mb_ptr_back(base, p);
3205 }
3206 col = (int)(p - base);
3207 }
3208 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003209}
3210
3211/*
3212 * Get current line in syntax buffer.
3213 */
3214 static char_u *
3215syn_getcurline()
3216{
3217 return ml_get_buf(syn_buf, current_lnum, FALSE);
3218}
3219
3220/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003221 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003222 * Returns TRUE when there is a match.
3223 */
3224 static int
3225syn_regexec(rmp, lnum, col)
3226 regmmatch_T *rmp;
3227 linenr_T lnum;
3228 colnr_T col;
3229{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003230 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar91a4e822008-01-19 14:59:58 +00003231 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL) > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232 {
3233 rmp->startpos[0].lnum += lnum;
3234 rmp->endpos[0].lnum += lnum;
3235 return TRUE;
3236 }
3237 return FALSE;
3238}
3239
3240/*
3241 * Check one position in a line for a matching keyword.
3242 * The caller must check if a keyword can start at startcol.
3243 * Return it's ID if found, 0 otherwise.
3244 */
3245 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003246check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003247 char_u *line;
3248 int startcol; /* position in line to check for keyword */
3249 int *endcolp; /* return: character after found keyword */
3250 long *flagsp; /* return: flags of matching keyword */
3251 short **next_listp; /* return: next_list of matching keyword */
3252 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003253 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003255 keyentry_T *kp;
3256 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003257 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003258 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003259 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003260 hashtab_T *ht;
3261 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262
3263 /* Find first character after the keyword. First character was already
3264 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003265 kwp = line + startcol;
3266 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003267 do
3268 {
3269#ifdef FEAT_MBYTE
3270 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003271 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003272 else
3273#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003274 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003275 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003276 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277
Bram Moolenaardad6b692005-01-25 22:14:34 +00003278 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003279 return 0;
3280
3281 /*
3282 * Must make a copy of the keyword, so we can add a NUL and make it
3283 * lowercase.
3284 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003285 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003286
3287 /*
3288 * Try twice:
3289 * 1. matching case
3290 * 2. ignoring case
3291 */
3292 for (round = 1; round <= 2; ++round)
3293 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003294 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003295 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003296 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003297 if (round == 2) /* ignore case */
3298 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003299
3300 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003301 * Find keywords that match. There can be several with different
3302 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003303 * When current_next_list is non-zero accept only that group, otherwise:
3304 * Accept a not-contained keyword at toplevel.
3305 * Accept a keyword at other levels only if it is in the contains list.
3306 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003307 hi = hash_find(ht, keyword);
3308 if (!HASHITEM_EMPTY(hi))
3309 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003310 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003311 if (current_next_list != 0
3312 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3313 : (cur_si == NULL
3314 ? !(kp->flags & HL_CONTAINED)
3315 : in_id_list(cur_si, cur_si->si_cont_list,
3316 &kp->k_syn, kp->flags & HL_CONTAINED)))
3317 {
3318 *endcolp = startcol + kwlen;
3319 *flagsp = kp->flags;
3320 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003321#ifdef FEAT_CONCEAL
3322 *ccharp = kp->k_char;
3323#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003324 return kp->k_syn.id;
3325 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003326 }
3327 }
3328 return 0;
3329}
3330
3331/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003332 * Handle ":syntax conceal" command.
3333 */
3334 static void
3335syn_cmd_conceal(eap, syncing)
3336 exarg_T *eap UNUSED;
3337 int syncing UNUSED;
3338{
3339#ifdef FEAT_CONCEAL
3340 char_u *arg = eap->arg;
3341 char_u *next;
3342
3343 eap->nextcmd = find_nextcmd(arg);
3344 if (eap->skip)
3345 return;
3346
3347 next = skiptowhite(arg);
3348 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3349 curwin->w_s->b_syn_conceal = TRUE;
3350 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3351 curwin->w_s->b_syn_conceal = FALSE;
3352 else
3353 EMSG2(_("E390: Illegal argument: %s"), arg);
3354#endif
3355}
3356
3357/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003358 * Handle ":syntax case" command.
3359 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003360 static void
3361syn_cmd_case(eap, syncing)
3362 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003363 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364{
3365 char_u *arg = eap->arg;
3366 char_u *next;
3367
3368 eap->nextcmd = find_nextcmd(arg);
3369 if (eap->skip)
3370 return;
3371
3372 next = skiptowhite(arg);
3373 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003374 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003375 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003376 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003377 else
3378 EMSG2(_("E390: Illegal argument: %s"), arg);
3379}
3380
3381/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003382 * Handle ":syntax spell" command.
3383 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003384 static void
3385syn_cmd_spell(eap, syncing)
3386 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003387 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003388{
3389 char_u *arg = eap->arg;
3390 char_u *next;
3391
3392 eap->nextcmd = find_nextcmd(arg);
3393 if (eap->skip)
3394 return;
3395
3396 next = skiptowhite(arg);
3397 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003398 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003399 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003400 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003401 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003402 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003403 else
3404 EMSG2(_("E390: Illegal argument: %s"), arg);
3405}
3406
3407/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003408 * Clear all syntax info for one buffer.
3409 */
3410 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003411syntax_clear(block)
3412 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413{
3414 int i;
3415
Bram Moolenaar860cae12010-06-05 23:22:07 +02003416 block->b_syn_error = FALSE; /* clear previous error */
3417 block->b_syn_ic = FALSE; /* Use case, by default */
3418 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3419 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420
3421 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003422 clear_keywtab(&block->b_keywtab);
3423 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003424
3425 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003426 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3427 syn_clear_pattern(block, i);
3428 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003429
3430 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003431 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3432 syn_clear_cluster(block, i);
3433 ga_clear(&block->b_syn_clusters);
3434 block->b_spell_cluster_id = 0;
3435 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003436
Bram Moolenaar860cae12010-06-05 23:22:07 +02003437 block->b_syn_sync_flags = 0;
3438 block->b_syn_sync_minlines = 0;
3439 block->b_syn_sync_maxlines = 0;
3440 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003441
Bram Moolenaar860cae12010-06-05 23:22:07 +02003442 vim_free(block->b_syn_linecont_prog);
3443 block->b_syn_linecont_prog = NULL;
3444 vim_free(block->b_syn_linecont_pat);
3445 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003446#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003447 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003448#endif
3449
3450 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003451 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003452 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003453
3454 /* Reset the counter for ":syn include" */
3455 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003456}
3457
3458/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003459 * Get rid of ownsyntax for window "wp".
3460 */
3461 void
3462reset_synblock(wp)
3463 win_T *wp;
3464{
3465 if (wp->w_s != &wp->w_buffer->b_s)
3466 {
3467 syntax_clear(wp->w_s);
3468 vim_free(wp->w_s);
3469 wp->w_s = &wp->w_buffer->b_s;
3470 }
3471}
3472
3473/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003474 * Clear syncing info for one buffer.
3475 */
3476 static void
3477syntax_sync_clear()
3478{
3479 int i;
3480
3481 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003482 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3483 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3484 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003485
Bram Moolenaar860cae12010-06-05 23:22:07 +02003486 curwin->w_s->b_syn_sync_flags = 0;
3487 curwin->w_s->b_syn_sync_minlines = 0;
3488 curwin->w_s->b_syn_sync_maxlines = 0;
3489 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490
Bram Moolenaar860cae12010-06-05 23:22:07 +02003491 vim_free(curwin->w_s->b_syn_linecont_prog);
3492 curwin->w_s->b_syn_linecont_prog = NULL;
3493 vim_free(curwin->w_s->b_syn_linecont_pat);
3494 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003495
Bram Moolenaar860cae12010-06-05 23:22:07 +02003496 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497}
3498
3499/*
3500 * Remove one pattern from the buffer's pattern list.
3501 */
3502 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003503syn_remove_pattern(block, idx)
3504 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505 int idx;
3506{
3507 synpat_T *spp;
3508
Bram Moolenaar860cae12010-06-05 23:22:07 +02003509 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510#ifdef FEAT_FOLDING
3511 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003512 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003514 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003515 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003516 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3517 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003518}
3519
3520/*
3521 * Clear and free one syntax pattern. When clearing all, must be called from
3522 * last to first!
3523 */
3524 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003525syn_clear_pattern(block, i)
3526 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003527 int i;
3528{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003529 vim_free(SYN_ITEMS(block)[i].sp_pattern);
3530 vim_free(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003531 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003532 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003534 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3535 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3536 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003537 }
3538}
3539
3540/*
3541 * Clear and free one syntax cluster.
3542 */
3543 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003544syn_clear_cluster(block, i)
3545 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003546 int i;
3547{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003548 vim_free(SYN_CLSTR(block)[i].scl_name);
3549 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3550 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551}
3552
3553/*
3554 * Handle ":syntax clear" command.
3555 */
3556 static void
3557syn_cmd_clear(eap, syncing)
3558 exarg_T *eap;
3559 int syncing;
3560{
3561 char_u *arg = eap->arg;
3562 char_u *arg_end;
3563 int id;
3564
3565 eap->nextcmd = find_nextcmd(arg);
3566 if (eap->skip)
3567 return;
3568
3569 /*
3570 * We have to disable this within ":syn include @group filename",
3571 * because otherwise @group would get deleted.
3572 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3573 * clear".
3574 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003575 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003576 return;
3577
3578 if (ends_excmd(*arg))
3579 {
3580 /*
3581 * No argument: Clear all syntax items.
3582 */
3583 if (syncing)
3584 syntax_sync_clear();
3585 else
3586 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003587 syntax_clear(curwin->w_s);
3588 if (curwin->w_s == &curwin->w_buffer->b_s)
3589 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003590 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003591 }
3592 }
3593 else
3594 {
3595 /*
3596 * Clear the group IDs that are in the argument.
3597 */
3598 while (!ends_excmd(*arg))
3599 {
3600 arg_end = skiptowhite(arg);
3601 if (*arg == '@')
3602 {
3603 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3604 if (id == 0)
3605 {
3606 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3607 break;
3608 }
3609 else
3610 {
3611 /*
3612 * We can't physically delete a cluster without changing
3613 * the IDs of other clusters, so we do the next best thing
3614 * and make it empty.
3615 */
3616 short scl_id = id - SYNID_CLUSTER;
3617
Bram Moolenaar860cae12010-06-05 23:22:07 +02003618 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3619 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620 }
3621 }
3622 else
3623 {
3624 id = syn_namen2id(arg, (int)(arg_end - arg));
3625 if (id == 0)
3626 {
3627 EMSG2(_(e_nogroup), arg);
3628 break;
3629 }
3630 else
3631 syn_clear_one(id, syncing);
3632 }
3633 arg = skipwhite(arg_end);
3634 }
3635 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003636 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003637 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003638}
3639
3640/*
3641 * Clear one syntax group for the current buffer.
3642 */
3643 static void
3644syn_clear_one(id, syncing)
3645 int id;
3646 int syncing;
3647{
3648 synpat_T *spp;
3649 int idx;
3650
3651 /* Clear keywords only when not ":syn sync clear group-name" */
3652 if (!syncing)
3653 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003654 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3655 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003656 }
3657
3658 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003659 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003660 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003661 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003662 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3663 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003664 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665 }
3666}
3667
3668/*
3669 * Handle ":syntax on" command.
3670 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003671 static void
3672syn_cmd_on(eap, syncing)
3673 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003674 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003675{
3676 syn_cmd_onoff(eap, "syntax");
3677}
3678
3679/*
3680 * Handle ":syntax enable" command.
3681 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003682 static void
3683syn_cmd_enable(eap, syncing)
3684 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003685 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003686{
3687 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3688 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003689 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003690}
3691
3692/*
3693 * Handle ":syntax reset" command.
3694 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003695 static void
3696syn_cmd_reset(eap, syncing)
3697 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003698 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003699{
3700 eap->nextcmd = check_nextcmd(eap->arg);
3701 if (!eap->skip)
3702 {
3703 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3704 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003705 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003706 }
3707}
3708
3709/*
3710 * Handle ":syntax manual" command.
3711 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 static void
3713syn_cmd_manual(eap, syncing)
3714 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003715 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716{
3717 syn_cmd_onoff(eap, "manual");
3718}
3719
3720/*
3721 * Handle ":syntax off" command.
3722 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003723 static void
3724syn_cmd_off(eap, syncing)
3725 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003726 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727{
3728 syn_cmd_onoff(eap, "nosyntax");
3729}
3730
3731 static void
3732syn_cmd_onoff(eap, name)
3733 exarg_T *eap;
3734 char *name;
3735{
3736 char_u buf[100];
3737
3738 eap->nextcmd = check_nextcmd(eap->arg);
3739 if (!eap->skip)
3740 {
3741 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003742 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003743 do_cmdline_cmd(buf);
3744 }
3745}
3746
3747/*
3748 * Handle ":syntax [list]" command: list current syntax words.
3749 */
3750 static void
3751syn_cmd_list(eap, syncing)
3752 exarg_T *eap;
3753 int syncing; /* when TRUE: list syncing items */
3754{
3755 char_u *arg = eap->arg;
3756 int id;
3757 char_u *arg_end;
3758
3759 eap->nextcmd = find_nextcmd(arg);
3760 if (eap->skip)
3761 return;
3762
Bram Moolenaar860cae12010-06-05 23:22:07 +02003763 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003764 {
3765 MSG(_("No Syntax items defined for this buffer"));
3766 return;
3767 }
3768
3769 if (syncing)
3770 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003771 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772 {
3773 MSG_PUTS(_("syncing on C-style comments"));
3774 syn_lines_msg();
3775 syn_match_msg();
3776 return;
3777 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003778 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003780 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003781 MSG_PUTS(_("no syncing"));
3782 else
3783 {
3784 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003785 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786 MSG_PUTS(_(" lines before top line"));
3787 syn_match_msg();
3788 }
3789 return;
3790 }
3791 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003792 if (curwin->w_s->b_syn_sync_minlines > 0
3793 || curwin->w_s->b_syn_sync_maxlines > 0
3794 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003795 {
3796 MSG_PUTS(_("\nsyncing on items"));
3797 syn_lines_msg();
3798 syn_match_msg();
3799 }
3800 }
3801 else
3802 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3803 if (ends_excmd(*arg))
3804 {
3805 /*
3806 * No argument: List all group IDs and all syntax clusters.
3807 */
3808 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3809 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003810 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003811 syn_list_cluster(id);
3812 }
3813 else
3814 {
3815 /*
3816 * List the group IDs and syntax clusters that are in the argument.
3817 */
3818 while (!ends_excmd(*arg) && !got_int)
3819 {
3820 arg_end = skiptowhite(arg);
3821 if (*arg == '@')
3822 {
3823 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3824 if (id == 0)
3825 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3826 else
3827 syn_list_cluster(id - SYNID_CLUSTER);
3828 }
3829 else
3830 {
3831 id = syn_namen2id(arg, (int)(arg_end - arg));
3832 if (id == 0)
3833 EMSG2(_(e_nogroup), arg);
3834 else
3835 syn_list_one(id, syncing, TRUE);
3836 }
3837 arg = skipwhite(arg_end);
3838 }
3839 }
3840 eap->nextcmd = check_nextcmd(arg);
3841}
3842
3843 static void
3844syn_lines_msg()
3845{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003846 if (curwin->w_s->b_syn_sync_maxlines > 0
3847 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 {
3849 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003850 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 {
3852 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003853 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3854 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 MSG_PUTS(", ");
3856 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003857 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003858 {
3859 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003860 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003861 }
3862 MSG_PUTS(_(" lines before top line"));
3863 }
3864}
3865
3866 static void
3867syn_match_msg()
3868{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003869 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870 {
3871 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003872 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003873 MSG_PUTS(_(" line breaks"));
3874 }
3875}
3876
3877static int last_matchgroup;
3878
3879struct name_list
3880{
3881 int flag;
3882 char *name;
3883};
3884
3885static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3886
3887/*
3888 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3889 */
3890 static void
3891syn_list_one(id, syncing, link_only)
3892 int id;
3893 int syncing; /* when TRUE: list syncing items */
3894 int link_only; /* when TRUE; list link-only too */
3895{
3896 int attr;
3897 int idx;
3898 int did_header = FALSE;
3899 synpat_T *spp;
3900 static struct name_list namelist1[] =
3901 {
3902 {HL_DISPLAY, "display"},
3903 {HL_CONTAINED, "contained"},
3904 {HL_ONELINE, "oneline"},
3905 {HL_KEEPEND, "keepend"},
3906 {HL_EXTEND, "extend"},
3907 {HL_EXCLUDENL, "excludenl"},
3908 {HL_TRANSP, "transparent"},
3909 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003910#ifdef FEAT_CONCEAL
3911 {HL_CONCEAL, "conceal"},
3912 {HL_CONCEALENDS, "concealends"},
3913#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 {0, NULL}
3915 };
3916 static struct name_list namelist2[] =
3917 {
3918 {HL_SKIPWHITE, "skipwhite"},
3919 {HL_SKIPNL, "skipnl"},
3920 {HL_SKIPEMPTY, "skipempty"},
3921 {0, NULL}
3922 };
3923
3924 attr = hl_attr(HLF_D); /* highlight like directories */
3925
3926 /* list the keywords for "id" */
3927 if (!syncing)
3928 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003929 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3930 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003931 did_header, attr);
3932 }
3933
3934 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003935 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003936 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003937 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003938 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3939 continue;
3940
3941 (void)syn_list_header(did_header, 999, id);
3942 did_header = TRUE;
3943 last_matchgroup = 0;
3944 if (spp->sp_type == SPTYPE_MATCH)
3945 {
3946 put_pattern("match", ' ', spp, attr);
3947 msg_putchar(' ');
3948 }
3949 else if (spp->sp_type == SPTYPE_START)
3950 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003951 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
3952 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3953 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
3954 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
3955 while (idx < curwin->w_s->b_syn_patterns.ga_len
3956 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
3957 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003958 --idx;
3959 msg_putchar(' ');
3960 }
3961 syn_list_flags(namelist1, spp->sp_flags, attr);
3962
3963 if (spp->sp_cont_list != NULL)
3964 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3965
3966 if (spp->sp_syn.cont_in_list != NULL)
3967 put_id_list((char_u *)"containedin",
3968 spp->sp_syn.cont_in_list, attr);
3969
3970 if (spp->sp_next_list != NULL)
3971 {
3972 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3973 syn_list_flags(namelist2, spp->sp_flags, attr);
3974 }
3975 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3976 {
3977 if (spp->sp_flags & HL_SYNC_HERE)
3978 msg_puts_attr((char_u *)"grouphere", attr);
3979 else
3980 msg_puts_attr((char_u *)"groupthere", attr);
3981 msg_putchar(' ');
3982 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003983 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3985 else
3986 MSG_PUTS("NONE");
3987 msg_putchar(' ');
3988 }
3989 }
3990
3991 /* list the link, if there is one */
3992 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3993 {
3994 (void)syn_list_header(did_header, 999, id);
3995 msg_puts_attr((char_u *)"links to", attr);
3996 msg_putchar(' ');
3997 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3998 }
3999}
4000
4001 static void
4002syn_list_flags(nl, flags, attr)
4003 struct name_list *nl;
4004 int flags;
4005 int attr;
4006{
4007 int i;
4008
4009 for (i = 0; nl[i].flag != 0; ++i)
4010 if (flags & nl[i].flag)
4011 {
4012 msg_puts_attr((char_u *)nl[i].name, attr);
4013 msg_putchar(' ');
4014 }
4015}
4016
4017/*
4018 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4019 */
4020 static void
4021syn_list_cluster(id)
4022 int id;
4023{
4024 int endcol = 15;
4025
4026 /* slight hack: roughly duplicate the guts of syn_list_header() */
4027 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004028 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029
4030 if (msg_col >= endcol) /* output at least one space */
4031 endcol = msg_col + 1;
4032 if (Columns <= endcol) /* avoid hang for tiny window */
4033 endcol = Columns - 1;
4034
4035 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004036 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004037 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004038 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039 hl_attr(HLF_D));
4040 }
4041 else
4042 {
4043 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4044 msg_puts((char_u *)"=NONE");
4045 }
4046}
4047
4048 static void
4049put_id_list(name, list, attr)
4050 char_u *name;
4051 short *list;
4052 int attr;
4053{
4054 short *p;
4055
4056 msg_puts_attr(name, attr);
4057 msg_putchar('=');
4058 for (p = list; *p; ++p)
4059 {
4060 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4061 {
4062 if (p[1])
4063 MSG_PUTS("ALLBUT");
4064 else
4065 MSG_PUTS("ALL");
4066 }
4067 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4068 {
4069 MSG_PUTS("TOP");
4070 }
4071 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4072 {
4073 MSG_PUTS("CONTAINED");
4074 }
4075 else if (*p >= SYNID_CLUSTER)
4076 {
4077 short scl_id = *p - SYNID_CLUSTER;
4078
4079 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004080 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004081 }
4082 else
4083 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4084 if (p[1])
4085 msg_putchar(',');
4086 }
4087 msg_putchar(' ');
4088}
4089
4090 static void
4091put_pattern(s, c, spp, attr)
4092 char *s;
4093 int c;
4094 synpat_T *spp;
4095 int attr;
4096{
4097 long n;
4098 int mask;
4099 int first;
4100 static char *sepchars = "/+=-#@\"|'^&";
4101 int i;
4102
4103 /* May have to write "matchgroup=group" */
4104 if (last_matchgroup != spp->sp_syn_match_id)
4105 {
4106 last_matchgroup = spp->sp_syn_match_id;
4107 msg_puts_attr((char_u *)"matchgroup", attr);
4108 msg_putchar('=');
4109 if (last_matchgroup == 0)
4110 msg_outtrans((char_u *)"NONE");
4111 else
4112 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4113 msg_putchar(' ');
4114 }
4115
4116 /* output the name of the pattern and an '=' or ' ' */
4117 msg_puts_attr((char_u *)s, attr);
4118 msg_putchar(c);
4119
4120 /* output the pattern, in between a char that is not in the pattern */
4121 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4122 if (sepchars[++i] == NUL)
4123 {
4124 i = 0; /* no good char found, just use the first one */
4125 break;
4126 }
4127 msg_putchar(sepchars[i]);
4128 msg_outtrans(spp->sp_pattern);
4129 msg_putchar(sepchars[i]);
4130
4131 /* output any pattern options */
4132 first = TRUE;
4133 for (i = 0; i < SPO_COUNT; ++i)
4134 {
4135 mask = (1 << i);
4136 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4137 {
4138 if (!first)
4139 msg_putchar(','); /* separate with commas */
4140 msg_puts((char_u *)spo_name_tab[i]);
4141 n = spp->sp_offsets[i];
4142 if (i != SPO_LC_OFF)
4143 {
4144 if (spp->sp_off_flags & mask)
4145 msg_putchar('s');
4146 else
4147 msg_putchar('e');
4148 if (n > 0)
4149 msg_putchar('+');
4150 }
4151 if (n || i == SPO_LC_OFF)
4152 msg_outnum(n);
4153 first = FALSE;
4154 }
4155 }
4156 msg_putchar(' ');
4157}
4158
4159/*
4160 * List or clear the keywords for one syntax group.
4161 * Return TRUE if the header has been printed.
4162 */
4163 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004164syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004165 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004166 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004167 int did_header; /* header has already been printed */
4168 int attr;
4169{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004171 hashitem_T *hi;
4172 keyentry_T *kp;
4173 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004174 int prev_contained = 0;
4175 short *prev_next_list = NULL;
4176 short *prev_cont_in_list = NULL;
4177 int prev_skipnl = 0;
4178 int prev_skipwhite = 0;
4179 int prev_skipempty = 0;
4180
Bram Moolenaar071d4272004-06-13 20:20:40 +00004181 /*
4182 * Unfortunately, this list of keywords is not sorted on alphabet but on
4183 * hash value...
4184 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004185 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004186 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004188 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004190 --todo;
4191 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004193 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004194 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004195 if (prev_contained != (kp->flags & HL_CONTAINED)
4196 || prev_skipnl != (kp->flags & HL_SKIPNL)
4197 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4198 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4199 || prev_cont_in_list != kp->k_syn.cont_in_list
4200 || prev_next_list != kp->next_list)
4201 outlen = 9999;
4202 else
4203 outlen = (int)STRLEN(kp->keyword);
4204 /* output "contained" and "nextgroup" on each line */
4205 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004206 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004207 prev_contained = 0;
4208 prev_next_list = NULL;
4209 prev_cont_in_list = NULL;
4210 prev_skipnl = 0;
4211 prev_skipwhite = 0;
4212 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004214 did_header = TRUE;
4215 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004216 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004217 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004218 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004219 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004220 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004221 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004223 put_id_list((char_u *)"containedin",
4224 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004226 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004227 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004228 if (kp->next_list != prev_next_list)
4229 {
4230 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4231 msg_putchar(' ');
4232 prev_next_list = kp->next_list;
4233 if (kp->flags & HL_SKIPNL)
4234 {
4235 msg_puts_attr((char_u *)"skipnl", attr);
4236 msg_putchar(' ');
4237 prev_skipnl = (kp->flags & HL_SKIPNL);
4238 }
4239 if (kp->flags & HL_SKIPWHITE)
4240 {
4241 msg_puts_attr((char_u *)"skipwhite", attr);
4242 msg_putchar(' ');
4243 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4244 }
4245 if (kp->flags & HL_SKIPEMPTY)
4246 {
4247 msg_puts_attr((char_u *)"skipempty", attr);
4248 msg_putchar(' ');
4249 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4250 }
4251 }
4252 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004253 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004254 }
4255 }
4256 }
4257
4258 return did_header;
4259}
4260
4261 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004262syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004263 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004264 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004265{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004266 hashitem_T *hi;
4267 keyentry_T *kp;
4268 keyentry_T *kp_prev;
4269 keyentry_T *kp_next;
4270 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271
Bram Moolenaardad6b692005-01-25 22:14:34 +00004272 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004273 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004274 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004275 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004276 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004277 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004278 --todo;
4279 kp_prev = NULL;
4280 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004282 if (kp->k_syn.id == id)
4283 {
4284 kp_next = kp->ke_next;
4285 if (kp_prev == NULL)
4286 {
4287 if (kp_next == NULL)
4288 hash_remove(ht, hi);
4289 else
4290 hi->hi_key = KE2HIKEY(kp_next);
4291 }
4292 else
4293 kp_prev->ke_next = kp_next;
4294 vim_free(kp->next_list);
4295 vim_free(kp->k_syn.cont_in_list);
4296 vim_free(kp);
4297 kp = kp_next;
4298 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004299 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 {
4301 kp_prev = kp;
4302 kp = kp->ke_next;
4303 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304 }
4305 }
4306 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004307 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308}
4309
4310/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004311 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004312 */
4313 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004314clear_keywtab(ht)
4315 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004316{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004317 hashitem_T *hi;
4318 int todo;
4319 keyentry_T *kp;
4320 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004321
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004322 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004323 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004324 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004325 if (!HASHITEM_EMPTY(hi))
4326 {
4327 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004329 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 kp_next = kp->ke_next;
4331 vim_free(kp->next_list);
4332 vim_free(kp->k_syn.cont_in_list);
4333 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004334 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004335 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004337 hash_clear(ht);
4338 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004339}
4340
4341/*
4342 * Add a keyword to the list of keywords.
4343 */
4344 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004345add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004346 char_u *name; /* name of keyword */
4347 int id; /* group ID for this keyword */
4348 int flags; /* flags for this keyword */
4349 short *cont_in_list; /* containedin for this keyword */
4350 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004351 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004352{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004353 keyentry_T *kp;
4354 hashtab_T *ht;
4355 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004356 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004357 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004358 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004359
Bram Moolenaar860cae12010-06-05 23:22:07 +02004360 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004361 name_ic = str_foldcase(name, (int)STRLEN(name),
4362 name_folded, MAXKEYWLEN + 1);
4363 else
4364 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004365 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4366 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004368 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004369 kp->k_syn.id = id;
4370 kp->k_syn.inc_tag = current_syn_inc_tag;
4371 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004372 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004373 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004374 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004375 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004376 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377
Bram Moolenaar860cae12010-06-05 23:22:07 +02004378 if (curwin->w_s->b_syn_ic)
4379 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004380 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004381 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004382
Bram Moolenaardad6b692005-01-25 22:14:34 +00004383 hash = hash_hash(kp->keyword);
4384 hi = hash_lookup(ht, kp->keyword, hash);
4385 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004387 /* new keyword, add to hashtable */
4388 kp->ke_next = NULL;
4389 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004390 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004391 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004392 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004393 /* keyword already exists, prepend to list */
4394 kp->ke_next = HI2KE(hi);
4395 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004396 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004397}
4398
4399/*
4400 * Get the start and end of the group name argument.
4401 * Return a pointer to the first argument.
4402 * Return NULL if the end of the command was found instead of further args.
4403 */
4404 static char_u *
4405get_group_name(arg, name_end)
4406 char_u *arg; /* start of the argument */
4407 char_u **name_end; /* pointer to end of the name */
4408{
4409 char_u *rest;
4410
4411 *name_end = skiptowhite(arg);
4412 rest = skipwhite(*name_end);
4413
4414 /*
4415 * Check if there are enough arguments. The first argument may be a
4416 * pattern, where '|' is allowed, so only check for NUL.
4417 */
4418 if (ends_excmd(*arg) || *rest == NUL)
4419 return NULL;
4420 return rest;
4421}
4422
4423/*
4424 * Check for syntax command option arguments.
4425 * This can be called at any place in the list of arguments, and just picks
4426 * out the arguments that are known. Can be called several times in a row to
4427 * collect all options in between other arguments.
4428 * Return a pointer to the next argument (which isn't an option).
4429 * Return NULL for any error;
4430 */
4431 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004432get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004433 char_u *arg; /* next argument to be checked */
4434 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004435 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437 char_u *gname_start, *gname;
4438 int syn_id;
4439 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004440 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 int i;
4442 int fidx;
4443 static struct flag
4444 {
4445 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004446 int argtype;
4447 int flags;
4448 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4449 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4450 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4451 {"eExXtTeEnNdD", 0, HL_EXTEND},
4452 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4453 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4454 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4455 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4456 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4457 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4458 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4459 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4460 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004461 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4462 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4463 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004464 {"cCoOnNtTaAiInNsS", 1, 0},
4465 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4466 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004468 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469
4470 if (arg == NULL) /* already detected error */
4471 return NULL;
4472
Bram Moolenaar860cae12010-06-05 23:22:07 +02004473#ifdef FEAT_CONCEAL
4474 if (curwin->w_s->b_syn_conceal)
4475 opt->flags |= HL_CONCEAL;
4476#endif
4477
Bram Moolenaar071d4272004-06-13 20:20:40 +00004478 for (;;)
4479 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004480 /*
4481 * This is used very often when a large number of keywords is defined.
4482 * Need to skip quickly when no option name is found.
4483 * Also avoid tolower(), it's slow.
4484 */
4485 if (strchr(first_letters, *arg) == NULL)
4486 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487
4488 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4489 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004490 p = flagtab[fidx].name;
4491 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4492 if (arg[len] != p[i] && arg[len] != p[i + 1])
4493 break;
4494 if (p[i] == NUL && (vim_iswhite(arg[len])
4495 || (flagtab[fidx].argtype > 0
4496 ? arg[len] == '='
4497 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004498 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004499 if (opt->keyword
4500 && (flagtab[fidx].flags == HL_DISPLAY
4501 || flagtab[fidx].flags == HL_FOLD
4502 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004503 /* treat "display", "fold" and "extend" as a keyword */
4504 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004505 break;
4506 }
4507 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004508 if (fidx < 0) /* no match found */
4509 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004511 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004512 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004513 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514 {
4515 EMSG(_("E395: contains argument not accepted here"));
4516 return NULL;
4517 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004518 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519 return NULL;
4520 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004521 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004523 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524 return NULL;
4525 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004526 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004527 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004528 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004529 return NULL;
4530 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004531 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4532 {
4533#ifdef FEAT_MBYTE
4534 /* cchar=? */
4535 if (has_mbyte)
4536 {
4537# ifdef FEAT_CONCEAL
4538 *conceal_char = mb_ptr2char(arg + 6);
4539# endif
4540 arg += mb_ptr2len(arg + 6) - 1;
4541 }
4542 else
4543#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004544 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004545#ifdef FEAT_CONCEAL
4546 *conceal_char = arg[6];
4547#else
4548 ;
4549#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004550 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004551#ifdef FEAT_CONCEAL
4552 if (!vim_isprintc_strict(*conceal_char))
4553 {
4554 EMSG(_("E844: invalid cchar value"));
4555 return NULL;
4556 }
4557#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004558 arg = skipwhite(arg + 7);
4559 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004560 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004561 {
4562 opt->flags |= flagtab[fidx].flags;
4563 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004565 if (flagtab[fidx].flags == HL_SYNC_HERE
4566 || flagtab[fidx].flags == HL_SYNC_THERE)
4567 {
4568 if (opt->sync_idx == NULL)
4569 {
4570 EMSG(_("E393: group[t]here not accepted here"));
4571 return NULL;
4572 }
4573 gname_start = arg;
4574 arg = skiptowhite(arg);
4575 if (gname_start == arg)
4576 return NULL;
4577 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4578 if (gname == NULL)
4579 return NULL;
4580 if (STRCMP(gname, "NONE") == 0)
4581 *opt->sync_idx = NONE_IDX;
4582 else
4583 {
4584 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004585 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4586 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4587 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004588 {
4589 *opt->sync_idx = i;
4590 break;
4591 }
4592 if (i < 0)
4593 {
4594 EMSG2(_("E394: Didn't find region item for %s"), gname);
4595 vim_free(gname);
4596 return NULL;
4597 }
4598 }
4599
4600 vim_free(gname);
4601 arg = skipwhite(arg);
4602 }
4603#ifdef FEAT_FOLDING
4604 else if (flagtab[fidx].flags == HL_FOLD
4605 && foldmethodIsSyntax(curwin))
4606 /* Need to update folds later. */
4607 foldUpdateAll(curwin);
4608#endif
4609 }
4610 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004611
4612 return arg;
4613}
4614
4615/*
4616 * Adjustments to syntax item when declared in a ":syn include"'d file.
4617 * Set the contained flag, and if the item is not already contained, add it
4618 * to the specified top-level group, if any.
4619 */
4620 static void
4621syn_incl_toplevel(id, flagsp)
4622 int id;
4623 int *flagsp;
4624{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004625 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004626 return;
4627 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004628 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004629 {
4630 /* We have to alloc this, because syn_combine_list() will free it. */
4631 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004632 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633
4634 if (grp_list != NULL)
4635 {
4636 grp_list[0] = id;
4637 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004638 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004639 CLUSTER_ADD);
4640 }
4641 }
4642}
4643
4644/*
4645 * Handle ":syntax include [@{group-name}] filename" command.
4646 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004647 static void
4648syn_cmd_include(eap, syncing)
4649 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004650 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004651{
4652 char_u *arg = eap->arg;
4653 int sgl_id = 1;
4654 char_u *group_name_end;
4655 char_u *rest;
4656 char_u *errormsg = NULL;
4657 int prev_toplvl_grp;
4658 int prev_syn_inc_tag;
4659 int source = FALSE;
4660
4661 eap->nextcmd = find_nextcmd(arg);
4662 if (eap->skip)
4663 return;
4664
4665 if (arg[0] == '@')
4666 {
4667 ++arg;
4668 rest = get_group_name(arg, &group_name_end);
4669 if (rest == NULL)
4670 {
4671 EMSG((char_u *)_("E397: Filename required"));
4672 return;
4673 }
4674 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004675 if (sgl_id == 0)
4676 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004677 /* separate_nextcmd() and expand_filename() depend on this */
4678 eap->arg = rest;
4679 }
4680
4681 /*
4682 * Everything that's left, up to the next command, should be the
4683 * filename to include.
4684 */
4685 eap->argt |= (XFILE | NOSPC);
4686 separate_nextcmd(eap);
4687 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4688 {
4689 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4690 * file. Need to expand the file name first. In other cases
4691 * ":runtime!" is used. */
4692 source = TRUE;
4693 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4694 {
4695 if (errormsg != NULL)
4696 EMSG(errormsg);
4697 return;
4698 }
4699 }
4700
4701 /*
4702 * Save and restore the existing top-level grouplist id and ":syn
4703 * include" tag around the actual inclusion.
4704 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004705 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4706 {
4707 EMSG((char_u *)_("E847: Too many syntax includes"));
4708 return;
4709 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004710 prev_syn_inc_tag = current_syn_inc_tag;
4711 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004712 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4713 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004714 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4715 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004716 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004717 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718 current_syn_inc_tag = prev_syn_inc_tag;
4719}
4720
4721/*
4722 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4723 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004724 static void
4725syn_cmd_keyword(eap, syncing)
4726 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004727 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004728{
4729 char_u *arg = eap->arg;
4730 char_u *group_name_end;
4731 int syn_id;
4732 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004733 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004734 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004735 char_u *kw;
4736 syn_opt_arg_T syn_opt_arg;
4737 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004738 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004739
4740 rest = get_group_name(arg, &group_name_end);
4741
4742 if (rest != NULL)
4743 {
4744 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004745 if (syn_id != 0)
4746 /* allocate a buffer, for removing backslashes in the keyword */
4747 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004748 if (keyword_copy != NULL)
4749 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004750 syn_opt_arg.flags = 0;
4751 syn_opt_arg.keyword = TRUE;
4752 syn_opt_arg.sync_idx = NULL;
4753 syn_opt_arg.has_cont_list = FALSE;
4754 syn_opt_arg.cont_in_list = NULL;
4755 syn_opt_arg.next_list = NULL;
4756
Bram Moolenaar071d4272004-06-13 20:20:40 +00004757 /*
4758 * The options given apply to ALL keywords, so all options must be
4759 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004760 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004761 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004762 cnt = 0;
4763 p = keyword_copy;
4764 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004765 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004766 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004767 if (rest == NULL || ends_excmd(*rest))
4768 break;
4769 /* Copy the keyword, removing backslashes, and add a NUL. */
4770 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004771 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004772 if (*rest == '\\' && rest[1] != NUL)
4773 ++rest;
4774 *p++ = *rest++;
4775 }
4776 *p++ = NUL;
4777 ++cnt;
4778 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004779
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004780 if (!eap->skip)
4781 {
4782 /* Adjust flags for use of ":syn include". */
4783 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4784
4785 /*
4786 * 2: Add an entry for each keyword.
4787 */
4788 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4789 {
4790 for (p = vim_strchr(kw, '['); ; )
4791 {
4792 if (p != NULL)
4793 *p = NUL;
4794 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004795 syn_opt_arg.cont_in_list,
4796 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004797 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004798 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004799 if (p[1] == NUL)
4800 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004801 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004802 kw = p + 2; /* skip over the NUL */
4803 break;
4804 }
4805 if (p[1] == ']')
4806 {
4807 kw = p + 1; /* skip over the "]" */
4808 break;
4809 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004810#ifdef FEAT_MBYTE
4811 if (has_mbyte)
4812 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004813 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004814
4815 mch_memmove(p, p + 1, l);
4816 p += l;
4817 }
4818 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004819#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004820 {
4821 p[0] = p[1];
4822 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004823 }
4824 }
4825 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004826 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004827
Bram Moolenaar071d4272004-06-13 20:20:40 +00004828 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004829 vim_free(syn_opt_arg.cont_in_list);
4830 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004831 }
4832 }
4833
4834 if (rest != NULL)
4835 eap->nextcmd = check_nextcmd(rest);
4836 else
4837 EMSG2(_(e_invarg2), arg);
4838
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004839 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004840 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004841}
4842
4843/*
4844 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4845 *
4846 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4847 */
4848 static void
4849syn_cmd_match(eap, syncing)
4850 exarg_T *eap;
4851 int syncing; /* TRUE for ":syntax sync match .. " */
4852{
4853 char_u *arg = eap->arg;
4854 char_u *group_name_end;
4855 char_u *rest;
4856 synpat_T item; /* the item found in the line */
4857 int syn_id;
4858 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004859 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004860 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004861 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004862
4863 /* Isolate the group name, check for validity */
4864 rest = get_group_name(arg, &group_name_end);
4865
4866 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004867 syn_opt_arg.flags = 0;
4868 syn_opt_arg.keyword = FALSE;
4869 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4870 syn_opt_arg.has_cont_list = TRUE;
4871 syn_opt_arg.cont_list = NULL;
4872 syn_opt_arg.cont_in_list = NULL;
4873 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004874 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004875
4876 /* get the pattern. */
4877 init_syn_patterns();
4878 vim_memset(&item, 0, sizeof(item));
4879 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004880 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4881 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882
4883 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004884 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004885
4886 if (rest != NULL) /* all arguments are valid */
4887 {
4888 /*
4889 * Check for trailing command and illegal trailing arguments.
4890 */
4891 eap->nextcmd = check_nextcmd(rest);
4892 if (!ends_excmd(*rest) || eap->skip)
4893 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004894 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004895 && (syn_id = syn_check_group(arg,
4896 (int)(group_name_end - arg))) != 0)
4897 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004898 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004899 /*
4900 * Store the pattern in the syn_items list
4901 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004902 idx = curwin->w_s->b_syn_patterns.ga_len;
4903 SYN_ITEMS(curwin->w_s)[idx] = item;
4904 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4905 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4906 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4907 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4908 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4909 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4910 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4911 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004912 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004913#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004914 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004915#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004916 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004917 curwin->w_s->b_syn_containedin = TRUE;
4918 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4919 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004920
4921 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004922 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004923 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004924#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004925 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004926 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004927#endif
4928
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004929 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004930 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931 return; /* don't free the progs and patterns now */
4932 }
4933 }
4934
4935 /*
4936 * Something failed, free the allocated memory.
4937 */
4938 vim_free(item.sp_prog);
4939 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004940 vim_free(syn_opt_arg.cont_list);
4941 vim_free(syn_opt_arg.cont_in_list);
4942 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004943
4944 if (rest == NULL)
4945 EMSG2(_(e_invarg2), arg);
4946}
4947
4948/*
4949 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4950 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4951 */
4952 static void
4953syn_cmd_region(eap, syncing)
4954 exarg_T *eap;
4955 int syncing; /* TRUE for ":syntax sync region .." */
4956{
4957 char_u *arg = eap->arg;
4958 char_u *group_name_end;
4959 char_u *rest; /* next arg, NULL on error */
4960 char_u *key_end;
4961 char_u *key = NULL;
4962 char_u *p;
4963 int item;
4964#define ITEM_START 0
4965#define ITEM_SKIP 1
4966#define ITEM_END 2
4967#define ITEM_MATCHGROUP 3
4968 struct pat_ptr
4969 {
4970 synpat_T *pp_synp; /* pointer to syn_pattern */
4971 int pp_matchgroup_id; /* matchgroup ID */
4972 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4973 } *(pat_ptrs[3]);
4974 /* patterns found in the line */
4975 struct pat_ptr *ppp;
4976 struct pat_ptr *ppp_next;
4977 int pat_count = 0; /* nr of syn_patterns found */
4978 int syn_id;
4979 int matchgroup_id = 0;
4980 int not_enough = FALSE; /* not enough arguments */
4981 int illegal = FALSE; /* illegal arguments */
4982 int success = FALSE;
4983 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004984 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004985 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004986
4987 /* Isolate the group name, check for validity */
4988 rest = get_group_name(arg, &group_name_end);
4989
4990 pat_ptrs[0] = NULL;
4991 pat_ptrs[1] = NULL;
4992 pat_ptrs[2] = NULL;
4993
4994 init_syn_patterns();
4995
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004996 syn_opt_arg.flags = 0;
4997 syn_opt_arg.keyword = FALSE;
4998 syn_opt_arg.sync_idx = NULL;
4999 syn_opt_arg.has_cont_list = TRUE;
5000 syn_opt_arg.cont_list = NULL;
5001 syn_opt_arg.cont_in_list = NULL;
5002 syn_opt_arg.next_list = NULL;
5003
Bram Moolenaar071d4272004-06-13 20:20:40 +00005004 /*
5005 * get the options, patterns and matchgroup.
5006 */
5007 while (rest != NULL && !ends_excmd(*rest))
5008 {
5009 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005010 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005011 if (rest == NULL || ends_excmd(*rest))
5012 break;
5013
5014 /* must be a pattern or matchgroup then */
5015 key_end = rest;
5016 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5017 ++key_end;
5018 vim_free(key);
5019 key = vim_strnsave_up(rest, (int)(key_end - rest));
5020 if (key == NULL) /* out of memory */
5021 {
5022 rest = NULL;
5023 break;
5024 }
5025 if (STRCMP(key, "MATCHGROUP") == 0)
5026 item = ITEM_MATCHGROUP;
5027 else if (STRCMP(key, "START") == 0)
5028 item = ITEM_START;
5029 else if (STRCMP(key, "END") == 0)
5030 item = ITEM_END;
5031 else if (STRCMP(key, "SKIP") == 0)
5032 {
5033 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5034 {
5035 illegal = TRUE;
5036 break;
5037 }
5038 item = ITEM_SKIP;
5039 }
5040 else
5041 break;
5042 rest = skipwhite(key_end);
5043 if (*rest != '=')
5044 {
5045 rest = NULL;
5046 EMSG2(_("E398: Missing '=': %s"), arg);
5047 break;
5048 }
5049 rest = skipwhite(rest + 1);
5050 if (*rest == NUL)
5051 {
5052 not_enough = TRUE;
5053 break;
5054 }
5055
5056 if (item == ITEM_MATCHGROUP)
5057 {
5058 p = skiptowhite(rest);
5059 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5060 matchgroup_id = 0;
5061 else
5062 {
5063 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5064 if (matchgroup_id == 0)
5065 {
5066 illegal = TRUE;
5067 break;
5068 }
5069 }
5070 rest = skipwhite(p);
5071 }
5072 else
5073 {
5074 /*
5075 * Allocate room for a syn_pattern, and link it in the list of
5076 * syn_patterns for this item, at the start (because the list is
5077 * used from end to start).
5078 */
5079 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5080 if (ppp == NULL)
5081 {
5082 rest = NULL;
5083 break;
5084 }
5085 ppp->pp_next = pat_ptrs[item];
5086 pat_ptrs[item] = ppp;
5087 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5088 if (ppp->pp_synp == NULL)
5089 {
5090 rest = NULL;
5091 break;
5092 }
5093
5094 /*
5095 * Get the syntax pattern and the following offset(s).
5096 */
5097 /* Enable the appropriate \z specials. */
5098 if (item == ITEM_START)
5099 reg_do_extmatch = REX_SET;
5100 else if (item == ITEM_SKIP || item == ITEM_END)
5101 reg_do_extmatch = REX_USE;
5102 rest = get_syn_pattern(rest, ppp->pp_synp);
5103 reg_do_extmatch = 0;
5104 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005105 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005106 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5107 ppp->pp_matchgroup_id = matchgroup_id;
5108 ++pat_count;
5109 }
5110 }
5111 vim_free(key);
5112 if (illegal || not_enough)
5113 rest = NULL;
5114
5115 /*
5116 * Must have a "start" and "end" pattern.
5117 */
5118 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5119 pat_ptrs[ITEM_END] == NULL))
5120 {
5121 not_enough = TRUE;
5122 rest = NULL;
5123 }
5124
5125 if (rest != NULL)
5126 {
5127 /*
5128 * Check for trailing garbage or command.
5129 * If OK, add the item.
5130 */
5131 eap->nextcmd = check_nextcmd(rest);
5132 if (!ends_excmd(*rest) || eap->skip)
5133 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005134 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005135 && (syn_id = syn_check_group(arg,
5136 (int)(group_name_end - arg))) != 0)
5137 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005138 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005139 /*
5140 * Store the start/skip/end in the syn_items list
5141 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005142 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005143 for (item = ITEM_START; item <= ITEM_END; ++item)
5144 {
5145 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5146 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005147 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5148 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5149 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005150 (item == ITEM_START) ? SPTYPE_START :
5151 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005152 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5153 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005154 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5155 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005156 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005157 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005158#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005159 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005160#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005161 if (item == ITEM_START)
5162 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005163 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005164 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005165 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005166 syn_opt_arg.cont_in_list;
5167 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005168 curwin->w_s->b_syn_containedin = TRUE;
5169 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005170 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005171 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005172 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005173 ++idx;
5174#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005175 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005176 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005177#endif
5178 }
5179 }
5180
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005181 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005182 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005183 success = TRUE; /* don't free the progs and patterns now */
5184 }
5185 }
5186
5187 /*
5188 * Free the allocated memory.
5189 */
5190 for (item = ITEM_START; item <= ITEM_END; ++item)
5191 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5192 {
5193 if (!success)
5194 {
5195 vim_free(ppp->pp_synp->sp_prog);
5196 vim_free(ppp->pp_synp->sp_pattern);
5197 }
5198 vim_free(ppp->pp_synp);
5199 ppp_next = ppp->pp_next;
5200 vim_free(ppp);
5201 }
5202
5203 if (!success)
5204 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005205 vim_free(syn_opt_arg.cont_list);
5206 vim_free(syn_opt_arg.cont_in_list);
5207 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005208 if (not_enough)
5209 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5210 else if (illegal || rest == NULL)
5211 EMSG2(_(e_invarg2), arg);
5212 }
5213}
5214
5215/*
5216 * A simple syntax group ID comparison function suitable for use in qsort()
5217 */
5218 static int
5219#ifdef __BORLANDC__
5220_RTLENTRYF
5221#endif
5222syn_compare_stub(v1, v2)
5223 const void *v1;
5224 const void *v2;
5225{
5226 const short *s1 = v1;
5227 const short *s2 = v2;
5228
5229 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5230}
5231
5232/*
5233 * Combines lists of syntax clusters.
5234 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5235 */
5236 static void
5237syn_combine_list(clstr1, clstr2, list_op)
5238 short **clstr1;
5239 short **clstr2;
5240 int list_op;
5241{
5242 int count1 = 0;
5243 int count2 = 0;
5244 short *g1;
5245 short *g2;
5246 short *clstr = NULL;
5247 int count;
5248 int round;
5249
5250 /*
5251 * Handle degenerate cases.
5252 */
5253 if (*clstr2 == NULL)
5254 return;
5255 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5256 {
5257 if (list_op == CLUSTER_REPLACE)
5258 vim_free(*clstr1);
5259 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5260 *clstr1 = *clstr2;
5261 else
5262 vim_free(*clstr2);
5263 return;
5264 }
5265
5266 for (g1 = *clstr1; *g1; g1++)
5267 ++count1;
5268 for (g2 = *clstr2; *g2; g2++)
5269 ++count2;
5270
5271 /*
5272 * For speed purposes, sort both lists.
5273 */
5274 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5275 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5276
5277 /*
5278 * We proceed in two passes; in round 1, we count the elements to place
5279 * in the new list, and in round 2, we allocate and populate the new
5280 * list. For speed, we use a mergesort-like method, adding the smaller
5281 * of the current elements in each list to the new list.
5282 */
5283 for (round = 1; round <= 2; round++)
5284 {
5285 g1 = *clstr1;
5286 g2 = *clstr2;
5287 count = 0;
5288
5289 /*
5290 * First, loop through the lists until one of them is empty.
5291 */
5292 while (*g1 && *g2)
5293 {
5294 /*
5295 * We always want to add from the first list.
5296 */
5297 if (*g1 < *g2)
5298 {
5299 if (round == 2)
5300 clstr[count] = *g1;
5301 count++;
5302 g1++;
5303 continue;
5304 }
5305 /*
5306 * We only want to add from the second list if we're adding the
5307 * lists.
5308 */
5309 if (list_op == CLUSTER_ADD)
5310 {
5311 if (round == 2)
5312 clstr[count] = *g2;
5313 count++;
5314 }
5315 if (*g1 == *g2)
5316 g1++;
5317 g2++;
5318 }
5319
5320 /*
5321 * Now add the leftovers from whichever list didn't get finished
5322 * first. As before, we only want to add from the second list if
5323 * we're adding the lists.
5324 */
5325 for (; *g1; g1++, count++)
5326 if (round == 2)
5327 clstr[count] = *g1;
5328 if (list_op == CLUSTER_ADD)
5329 for (; *g2; g2++, count++)
5330 if (round == 2)
5331 clstr[count] = *g2;
5332
5333 if (round == 1)
5334 {
5335 /*
5336 * If the group ended up empty, we don't need to allocate any
5337 * space for it.
5338 */
5339 if (count == 0)
5340 {
5341 clstr = NULL;
5342 break;
5343 }
5344 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5345 if (clstr == NULL)
5346 break;
5347 clstr[count] = 0;
5348 }
5349 }
5350
5351 /*
5352 * Finally, put the new list in place.
5353 */
5354 vim_free(*clstr1);
5355 vim_free(*clstr2);
5356 *clstr1 = clstr;
5357}
5358
5359/*
5360 * Lookup a syntax cluster name and return it's ID.
5361 * If it is not found, 0 is returned.
5362 */
5363 static int
5364syn_scl_name2id(name)
5365 char_u *name;
5366{
5367 int i;
5368 char_u *name_u;
5369
5370 /* Avoid using stricmp() too much, it's slow on some systems */
5371 name_u = vim_strsave_up(name);
5372 if (name_u == NULL)
5373 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005374 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5375 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5376 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005377 break;
5378 vim_free(name_u);
5379 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5380}
5381
5382/*
5383 * Like syn_scl_name2id(), but take a pointer + length argument.
5384 */
5385 static int
5386syn_scl_namen2id(linep, len)
5387 char_u *linep;
5388 int len;
5389{
5390 char_u *name;
5391 int id = 0;
5392
5393 name = vim_strnsave(linep, len);
5394 if (name != NULL)
5395 {
5396 id = syn_scl_name2id(name);
5397 vim_free(name);
5398 }
5399 return id;
5400}
5401
5402/*
5403 * Find syntax cluster name in the table and return it's ID.
5404 * The argument is a pointer to the name and the length of the name.
5405 * If it doesn't exist yet, a new entry is created.
5406 * Return 0 for failure.
5407 */
5408 static int
5409syn_check_cluster(pp, len)
5410 char_u *pp;
5411 int len;
5412{
5413 int id;
5414 char_u *name;
5415
5416 name = vim_strnsave(pp, len);
5417 if (name == NULL)
5418 return 0;
5419
5420 id = syn_scl_name2id(name);
5421 if (id == 0) /* doesn't exist yet */
5422 id = syn_add_cluster(name);
5423 else
5424 vim_free(name);
5425 return id;
5426}
5427
5428/*
5429 * Add new syntax cluster and return it's ID.
5430 * "name" must be an allocated string, it will be consumed.
5431 * Return 0 for failure.
5432 */
5433 static int
5434syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005435 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005436{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005437 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005438
5439 /*
5440 * First call for this growarray: init growing array.
5441 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005442 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005443 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005444 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5445 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005446 }
5447
Bram Moolenaar42431a72011-04-01 14:44:59 +02005448 len = curwin->w_s->b_syn_clusters.ga_len;
5449 if (len >= MAX_CLUSTER_ID)
5450 {
5451 EMSG((char_u *)_("E848: Too many syntax clusters"));
5452 vim_free(name);
5453 return 0;
5454 }
5455
Bram Moolenaar071d4272004-06-13 20:20:40 +00005456 /*
5457 * Make room for at least one other cluster entry.
5458 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005459 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005460 {
5461 vim_free(name);
5462 return 0;
5463 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005464
Bram Moolenaar860cae12010-06-05 23:22:07 +02005465 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5466 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5467 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5468 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5469 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005470
Bram Moolenaar217ad922005-03-20 22:37:15 +00005471 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005472 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005473 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005474 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005475
Bram Moolenaar071d4272004-06-13 20:20:40 +00005476 return len + SYNID_CLUSTER;
5477}
5478
5479/*
5480 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5481 * [add={groupname},..] [remove={groupname},..]".
5482 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005483 static void
5484syn_cmd_cluster(eap, syncing)
5485 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005486 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005487{
5488 char_u *arg = eap->arg;
5489 char_u *group_name_end;
5490 char_u *rest;
5491 int scl_id;
5492 short *clstr_list;
5493 int got_clstr = FALSE;
5494 int opt_len;
5495 int list_op;
5496
5497 eap->nextcmd = find_nextcmd(arg);
5498 if (eap->skip)
5499 return;
5500
5501 rest = get_group_name(arg, &group_name_end);
5502
5503 if (rest != NULL)
5504 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005505 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5506 if (scl_id == 0)
5507 return;
5508 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005509
5510 for (;;)
5511 {
5512 if (STRNICMP(rest, "add", 3) == 0
5513 && (vim_iswhite(rest[3]) || rest[3] == '='))
5514 {
5515 opt_len = 3;
5516 list_op = CLUSTER_ADD;
5517 }
5518 else if (STRNICMP(rest, "remove", 6) == 0
5519 && (vim_iswhite(rest[6]) || rest[6] == '='))
5520 {
5521 opt_len = 6;
5522 list_op = CLUSTER_SUBTRACT;
5523 }
5524 else if (STRNICMP(rest, "contains", 8) == 0
5525 && (vim_iswhite(rest[8]) || rest[8] == '='))
5526 {
5527 opt_len = 8;
5528 list_op = CLUSTER_REPLACE;
5529 }
5530 else
5531 break;
5532
5533 clstr_list = NULL;
5534 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5535 {
5536 EMSG2(_(e_invarg2), rest);
5537 break;
5538 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005539 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005540 &clstr_list, list_op);
5541 got_clstr = TRUE;
5542 }
5543
5544 if (got_clstr)
5545 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005546 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005547 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005548 }
5549 }
5550
5551 if (!got_clstr)
5552 EMSG(_("E400: No cluster specified"));
5553 if (rest == NULL || !ends_excmd(*rest))
5554 EMSG2(_(e_invarg2), arg);
5555}
5556
5557/*
5558 * On first call for current buffer: Init growing array.
5559 */
5560 static void
5561init_syn_patterns()
5562{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005563 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5564 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565}
5566
5567/*
5568 * Get one pattern for a ":syntax match" or ":syntax region" command.
5569 * Stores the pattern and program in a synpat_T.
5570 * Returns a pointer to the next argument, or NULL in case of an error.
5571 */
5572 static char_u *
5573get_syn_pattern(arg, ci)
5574 char_u *arg;
5575 synpat_T *ci;
5576{
5577 char_u *end;
5578 int *p;
5579 int idx;
5580 char_u *cpo_save;
5581
5582 /* need at least three chars */
5583 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5584 return NULL;
5585
5586 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5587 if (*end != *arg) /* end delimiter not found */
5588 {
5589 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5590 return NULL;
5591 }
5592 /* store the pattern and compiled regexp program */
5593 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5594 return NULL;
5595
5596 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5597 cpo_save = p_cpo;
5598 p_cpo = (char_u *)"";
5599 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5600 p_cpo = cpo_save;
5601
5602 if (ci->sp_prog == NULL)
5603 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005604 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005605
5606 /*
5607 * Check for a match, highlight or region offset.
5608 */
5609 ++end;
5610 do
5611 {
5612 for (idx = SPO_COUNT; --idx >= 0; )
5613 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5614 break;
5615 if (idx >= 0)
5616 {
5617 p = &(ci->sp_offsets[idx]);
5618 if (idx != SPO_LC_OFF)
5619 switch (end[3])
5620 {
5621 case 's': break;
5622 case 'b': break;
5623 case 'e': idx += SPO_COUNT; break;
5624 default: idx = -1; break;
5625 }
5626 if (idx >= 0)
5627 {
5628 ci->sp_off_flags |= (1 << idx);
5629 if (idx == SPO_LC_OFF) /* lc=99 */
5630 {
5631 end += 3;
5632 *p = getdigits(&end);
5633
5634 /* "lc=" offset automatically sets "ms=" offset */
5635 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5636 {
5637 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5638 ci->sp_offsets[SPO_MS_OFF] = *p;
5639 }
5640 }
5641 else /* yy=x+99 */
5642 {
5643 end += 4;
5644 if (*end == '+')
5645 {
5646 ++end;
5647 *p = getdigits(&end); /* positive offset */
5648 }
5649 else if (*end == '-')
5650 {
5651 ++end;
5652 *p = -getdigits(&end); /* negative offset */
5653 }
5654 }
5655 if (*end != ',')
5656 break;
5657 ++end;
5658 }
5659 }
5660 } while (idx >= 0);
5661
5662 if (!ends_excmd(*end) && !vim_iswhite(*end))
5663 {
5664 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5665 return NULL;
5666 }
5667 return skipwhite(end);
5668}
5669
5670/*
5671 * Handle ":syntax sync .." command.
5672 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005673 static void
5674syn_cmd_sync(eap, syncing)
5675 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005676 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005677{
5678 char_u *arg_start = eap->arg;
5679 char_u *arg_end;
5680 char_u *key = NULL;
5681 char_u *next_arg;
5682 int illegal = FALSE;
5683 int finished = FALSE;
5684 long n;
5685 char_u *cpo_save;
5686
5687 if (ends_excmd(*arg_start))
5688 {
5689 syn_cmd_list(eap, TRUE);
5690 return;
5691 }
5692
5693 while (!ends_excmd(*arg_start))
5694 {
5695 arg_end = skiptowhite(arg_start);
5696 next_arg = skipwhite(arg_end);
5697 vim_free(key);
5698 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5699 if (STRCMP(key, "CCOMMENT") == 0)
5700 {
5701 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005702 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005703 if (!ends_excmd(*next_arg))
5704 {
5705 arg_end = skiptowhite(next_arg);
5706 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005707 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005708 (int)(arg_end - next_arg));
5709 next_arg = skipwhite(arg_end);
5710 }
5711 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005712 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005713 }
5714 else if ( STRNCMP(key, "LINES", 5) == 0
5715 || STRNCMP(key, "MINLINES", 8) == 0
5716 || STRNCMP(key, "MAXLINES", 8) == 0
5717 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5718 {
5719 if (key[4] == 'S')
5720 arg_end = key + 6;
5721 else if (key[0] == 'L')
5722 arg_end = key + 11;
5723 else
5724 arg_end = key + 9;
5725 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5726 {
5727 illegal = TRUE;
5728 break;
5729 }
5730 n = getdigits(&arg_end);
5731 if (!eap->skip)
5732 {
5733 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005734 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005735 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005736 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005737 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005738 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005739 }
5740 }
5741 else if (STRCMP(key, "FROMSTART") == 0)
5742 {
5743 if (!eap->skip)
5744 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005745 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5746 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005747 }
5748 }
5749 else if (STRCMP(key, "LINECONT") == 0)
5750 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005751 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005752 {
5753 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5754 finished = TRUE;
5755 break;
5756 }
5757 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5758 if (*arg_end != *next_arg) /* end delimiter not found */
5759 {
5760 illegal = TRUE;
5761 break;
5762 }
5763
5764 if (!eap->skip)
5765 {
5766 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005767 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005768 (int)(arg_end - next_arg - 1))) == NULL)
5769 {
5770 finished = TRUE;
5771 break;
5772 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005773 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005774
5775 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5776 cpo_save = p_cpo;
5777 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005778 curwin->w_s->b_syn_linecont_prog =
5779 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005780 p_cpo = cpo_save;
5781
Bram Moolenaar860cae12010-06-05 23:22:07 +02005782 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005783 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005784 vim_free(curwin->w_s->b_syn_linecont_pat);
5785 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005786 finished = TRUE;
5787 break;
5788 }
5789 }
5790 next_arg = skipwhite(arg_end + 1);
5791 }
5792 else
5793 {
5794 eap->arg = next_arg;
5795 if (STRCMP(key, "MATCH") == 0)
5796 syn_cmd_match(eap, TRUE);
5797 else if (STRCMP(key, "REGION") == 0)
5798 syn_cmd_region(eap, TRUE);
5799 else if (STRCMP(key, "CLEAR") == 0)
5800 syn_cmd_clear(eap, TRUE);
5801 else
5802 illegal = TRUE;
5803 finished = TRUE;
5804 break;
5805 }
5806 arg_start = next_arg;
5807 }
5808 vim_free(key);
5809 if (illegal)
5810 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5811 else if (!finished)
5812 {
5813 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005814 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005815 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005816 }
5817}
5818
5819/*
5820 * Convert a line of highlight group names into a list of group ID numbers.
5821 * "arg" should point to the "contains" or "nextgroup" keyword.
5822 * "arg" is advanced to after the last group name.
5823 * Careful: the argument is modified (NULs added).
5824 * returns FAIL for some error, OK for success.
5825 */
5826 static int
5827get_id_list(arg, keylen, list)
5828 char_u **arg;
5829 int keylen; /* length of keyword */
5830 short **list; /* where to store the resulting list, if not
5831 NULL, the list is silently skipped! */
5832{
5833 char_u *p = NULL;
5834 char_u *end;
5835 int round;
5836 int count;
5837 int total_count = 0;
5838 short *retval = NULL;
5839 char_u *name;
5840 regmatch_T regmatch;
5841 int id;
5842 int i;
5843 int failed = FALSE;
5844
5845 /*
5846 * We parse the list twice:
5847 * round == 1: count the number of items, allocate the array.
5848 * round == 2: fill the array with the items.
5849 * In round 1 new groups may be added, causing the number of items to
5850 * grow when a regexp is used. In that case round 1 is done once again.
5851 */
5852 for (round = 1; round <= 2; ++round)
5853 {
5854 /*
5855 * skip "contains"
5856 */
5857 p = skipwhite(*arg + keylen);
5858 if (*p != '=')
5859 {
5860 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5861 break;
5862 }
5863 p = skipwhite(p + 1);
5864 if (ends_excmd(*p))
5865 {
5866 EMSG2(_("E406: Empty argument: %s"), *arg);
5867 break;
5868 }
5869
5870 /*
5871 * parse the arguments after "contains"
5872 */
5873 count = 0;
5874 while (!ends_excmd(*p))
5875 {
5876 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5877 ;
5878 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5879 if (name == NULL)
5880 {
5881 failed = TRUE;
5882 break;
5883 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005884 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005885 if ( STRCMP(name + 1, "ALLBUT") == 0
5886 || STRCMP(name + 1, "ALL") == 0
5887 || STRCMP(name + 1, "TOP") == 0
5888 || STRCMP(name + 1, "CONTAINED") == 0)
5889 {
5890 if (TOUPPER_ASC(**arg) != 'C')
5891 {
5892 EMSG2(_("E407: %s not allowed here"), name + 1);
5893 failed = TRUE;
5894 vim_free(name);
5895 break;
5896 }
5897 if (count != 0)
5898 {
5899 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5900 failed = TRUE;
5901 vim_free(name);
5902 break;
5903 }
5904 if (name[1] == 'A')
5905 id = SYNID_ALLBUT;
5906 else if (name[1] == 'T')
5907 id = SYNID_TOP;
5908 else
5909 id = SYNID_CONTAINED;
5910 id += current_syn_inc_tag;
5911 }
5912 else if (name[1] == '@')
5913 {
5914 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5915 }
5916 else
5917 {
5918 /*
5919 * Handle full group name.
5920 */
5921 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5922 id = syn_check_group(name + 1, (int)(end - p));
5923 else
5924 {
5925 /*
5926 * Handle match of regexp with group names.
5927 */
5928 *name = '^';
5929 STRCAT(name, "$");
5930 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5931 if (regmatch.regprog == NULL)
5932 {
5933 failed = TRUE;
5934 vim_free(name);
5935 break;
5936 }
5937
5938 regmatch.rm_ic = TRUE;
5939 id = 0;
5940 for (i = highlight_ga.ga_len; --i >= 0; )
5941 {
5942 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5943 (colnr_T)0))
5944 {
5945 if (round == 2)
5946 {
5947 /* Got more items than expected; can happen
5948 * when adding items that match:
5949 * "contains=a.*b,axb".
5950 * Go back to first round */
5951 if (count >= total_count)
5952 {
5953 vim_free(retval);
5954 round = 1;
5955 }
5956 else
5957 retval[count] = i + 1;
5958 }
5959 ++count;
5960 id = -1; /* remember that we found one */
5961 }
5962 }
5963 vim_free(regmatch.regprog);
5964 }
5965 }
5966 vim_free(name);
5967 if (id == 0)
5968 {
5969 EMSG2(_("E409: Unknown group name: %s"), p);
5970 failed = TRUE;
5971 break;
5972 }
5973 if (id > 0)
5974 {
5975 if (round == 2)
5976 {
5977 /* Got more items than expected, go back to first round */
5978 if (count >= total_count)
5979 {
5980 vim_free(retval);
5981 round = 1;
5982 }
5983 else
5984 retval[count] = id;
5985 }
5986 ++count;
5987 }
5988 p = skipwhite(end);
5989 if (*p != ',')
5990 break;
5991 p = skipwhite(p + 1); /* skip comma in between arguments */
5992 }
5993 if (failed)
5994 break;
5995 if (round == 1)
5996 {
5997 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5998 if (retval == NULL)
5999 break;
6000 retval[count] = 0; /* zero means end of the list */
6001 total_count = count;
6002 }
6003 }
6004
6005 *arg = p;
6006 if (failed || retval == NULL)
6007 {
6008 vim_free(retval);
6009 return FAIL;
6010 }
6011
6012 if (*list == NULL)
6013 *list = retval;
6014 else
6015 vim_free(retval); /* list already found, don't overwrite it */
6016
6017 return OK;
6018}
6019
6020/*
6021 * Make a copy of an ID list.
6022 */
6023 static short *
6024copy_id_list(list)
6025 short *list;
6026{
6027 int len;
6028 int count;
6029 short *retval;
6030
6031 if (list == NULL)
6032 return NULL;
6033
6034 for (count = 0; list[count]; ++count)
6035 ;
6036 len = (count + 1) * sizeof(short);
6037 retval = (short *)alloc((unsigned)len);
6038 if (retval != NULL)
6039 mch_memmove(retval, list, (size_t)len);
6040
6041 return retval;
6042}
6043
6044/*
6045 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6046 * "cur_si" can be NULL if not checking the "containedin" list.
6047 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6048 * the current item.
6049 * This function is called very often, keep it fast!!
6050 */
6051 static int
6052in_id_list(cur_si, list, ssp, contained)
6053 stateitem_T *cur_si; /* current item or NULL */
6054 short *list; /* id list */
6055 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6056 int contained; /* group id is contained */
6057{
6058 int retval;
6059 short *scl_list;
6060 short item;
6061 short id = ssp->id;
6062 static int depth = 0;
6063 int r;
6064
6065 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006066 if (cur_si != NULL && ssp->cont_in_list != NULL
6067 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006068 {
6069 /* Ignore transparent items without a contains argument. Double check
6070 * that we don't go back past the first one. */
6071 while ((cur_si->si_flags & HL_TRANS_CONT)
6072 && cur_si > (stateitem_T *)(current_state.ga_data))
6073 --cur_si;
6074 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6075 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006076 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6077 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006078 return TRUE;
6079 }
6080
6081 if (list == NULL)
6082 return FALSE;
6083
6084 /*
6085 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6086 * inside anything. Only allow not-contained groups.
6087 */
6088 if (list == ID_LIST_ALL)
6089 return !contained;
6090
6091 /*
6092 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6093 * contains list. We also require that "id" is at the same ":syn include"
6094 * level as the list.
6095 */
6096 item = *list;
6097 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6098 {
6099 if (item < SYNID_TOP)
6100 {
6101 /* ALL or ALLBUT: accept all groups in the same file */
6102 if (item - SYNID_ALLBUT != ssp->inc_tag)
6103 return FALSE;
6104 }
6105 else if (item < SYNID_CONTAINED)
6106 {
6107 /* TOP: accept all not-contained groups in the same file */
6108 if (item - SYNID_TOP != ssp->inc_tag || contained)
6109 return FALSE;
6110 }
6111 else
6112 {
6113 /* CONTAINED: accept all contained groups in the same file */
6114 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6115 return FALSE;
6116 }
6117 item = *++list;
6118 retval = FALSE;
6119 }
6120 else
6121 retval = TRUE;
6122
6123 /*
6124 * Return "retval" if id is in the contains list.
6125 */
6126 while (item != 0)
6127 {
6128 if (item == id)
6129 return retval;
6130 if (item >= SYNID_CLUSTER)
6131 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006132 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006133 /* restrict recursiveness to 30 to avoid an endless loop for a
6134 * cluster that includes itself (indirectly) */
6135 if (scl_list != NULL && depth < 30)
6136 {
6137 ++depth;
6138 r = in_id_list(NULL, scl_list, ssp, contained);
6139 --depth;
6140 if (r)
6141 return retval;
6142 }
6143 }
6144 item = *++list;
6145 }
6146 return !retval;
6147}
6148
6149struct subcommand
6150{
6151 char *name; /* subcommand name */
6152 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6153};
6154
6155static struct subcommand subcommands[] =
6156{
6157 {"case", syn_cmd_case},
6158 {"clear", syn_cmd_clear},
6159 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006160 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006161 {"enable", syn_cmd_enable},
6162 {"include", syn_cmd_include},
6163 {"keyword", syn_cmd_keyword},
6164 {"list", syn_cmd_list},
6165 {"manual", syn_cmd_manual},
6166 {"match", syn_cmd_match},
6167 {"on", syn_cmd_on},
6168 {"off", syn_cmd_off},
6169 {"region", syn_cmd_region},
6170 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006171 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006172 {"sync", syn_cmd_sync},
6173 {"", syn_cmd_list},
6174 {NULL, NULL}
6175};
6176
6177/*
6178 * ":syntax".
6179 * This searches the subcommands[] table for the subcommand name, and calls a
6180 * syntax_subcommand() function to do the rest.
6181 */
6182 void
6183ex_syntax(eap)
6184 exarg_T *eap;
6185{
6186 char_u *arg = eap->arg;
6187 char_u *subcmd_end;
6188 char_u *subcmd_name;
6189 int i;
6190
6191 syn_cmdlinep = eap->cmdlinep;
6192
6193 /* isolate subcommand name */
6194 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6195 ;
6196 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6197 if (subcmd_name != NULL)
6198 {
6199 if (eap->skip) /* skip error messages for all subcommands */
6200 ++emsg_skip;
6201 for (i = 0; ; ++i)
6202 {
6203 if (subcommands[i].name == NULL)
6204 {
6205 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6206 break;
6207 }
6208 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6209 {
6210 eap->arg = skipwhite(subcmd_end);
6211 (subcommands[i].func)(eap, FALSE);
6212 break;
6213 }
6214 }
6215 vim_free(subcmd_name);
6216 if (eap->skip)
6217 --emsg_skip;
6218 }
6219}
6220
Bram Moolenaar860cae12010-06-05 23:22:07 +02006221 void
6222ex_ownsyntax(eap)
6223 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006224{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006225 char_u *old_value;
6226 char_u *new_value;
6227
Bram Moolenaar860cae12010-06-05 23:22:07 +02006228 if (curwin->w_s == &curwin->w_buffer->b_s)
6229 {
6230 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6231 memset(curwin->w_s, 0, sizeof(synblock_T));
6232#ifdef FEAT_SPELL
6233 curwin->w_p_spell = FALSE; /* No spell checking */
6234 clear_string_option(&curwin->w_s->b_p_spc);
6235 clear_string_option(&curwin->w_s->b_p_spf);
6236 vim_free(curwin->w_s->b_cap_prog);
6237 curwin->w_s->b_cap_prog = NULL;
6238 clear_string_option(&curwin->w_s->b_p_spl);
6239#endif
6240 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006241
6242 /* save value of b:current_syntax */
6243 old_value = get_var_value((char_u *)"b:current_syntax");
6244 if (old_value != NULL)
6245 old_value = vim_strsave(old_value);
6246
6247 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6248 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006249 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006250
6251 /* move value of b:current_syntax to w:current_syntax */
6252 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006253 if (new_value != NULL)
6254 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006255
6256 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006257 if (old_value == NULL)
6258 do_unlet((char_u *)"b:current_syntax", TRUE);
6259 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006260 {
6261 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6262 vim_free(old_value);
6263 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006264}
6265
6266 int
6267syntax_present(win)
6268 win_T *win;
6269{
6270 return (win->w_s->b_syn_patterns.ga_len != 0
6271 || win->w_s->b_syn_clusters.ga_len != 0
6272 || win->w_s->b_keywtab.ht_used > 0
6273 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006274}
6275
6276#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6277
6278static enum
6279{
6280 EXP_SUBCMD, /* expand ":syn" sub-commands */
6281 EXP_CASE /* expand ":syn case" arguments */
6282} expand_what;
6283
Bram Moolenaar4f688582007-07-24 12:34:30 +00006284/*
6285 * Reset include_link, include_default, include_none to 0.
6286 * Called when we are done expanding.
6287 */
6288 void
6289reset_expand_highlight()
6290{
6291 include_link = include_default = include_none = 0;
6292}
6293
6294/*
6295 * Handle command line completion for :match and :echohl command: Add "None"
6296 * as highlight group.
6297 */
6298 void
6299set_context_in_echohl_cmd(xp, arg)
6300 expand_T *xp;
6301 char_u *arg;
6302{
6303 xp->xp_context = EXPAND_HIGHLIGHT;
6304 xp->xp_pattern = arg;
6305 include_none = 1;
6306}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006307
6308/*
6309 * Handle command line completion for :syntax command.
6310 */
6311 void
6312set_context_in_syntax_cmd(xp, arg)
6313 expand_T *xp;
6314 char_u *arg;
6315{
6316 char_u *p;
6317
6318 /* Default: expand subcommands */
6319 xp->xp_context = EXPAND_SYNTAX;
6320 expand_what = EXP_SUBCMD;
6321 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006322 include_link = 0;
6323 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006324
6325 /* (part of) subcommand already typed */
6326 if (*arg != NUL)
6327 {
6328 p = skiptowhite(arg);
6329 if (*p != NUL) /* past first word */
6330 {
6331 xp->xp_pattern = skipwhite(p);
6332 if (*skiptowhite(xp->xp_pattern) != NUL)
6333 xp->xp_context = EXPAND_NOTHING;
6334 else if (STRNICMP(arg, "case", p - arg) == 0)
6335 expand_what = EXP_CASE;
6336 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6337 || STRNICMP(arg, "region", p - arg) == 0
6338 || STRNICMP(arg, "match", p - arg) == 0
6339 || STRNICMP(arg, "list", p - arg) == 0)
6340 xp->xp_context = EXPAND_HIGHLIGHT;
6341 else
6342 xp->xp_context = EXPAND_NOTHING;
6343 }
6344 }
6345}
6346
6347static char *(case_args[]) = {"match", "ignore", NULL};
6348
6349/*
6350 * Function given to ExpandGeneric() to obtain the list syntax names for
6351 * expansion.
6352 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006353 char_u *
6354get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006355 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006356 int idx;
6357{
6358 if (expand_what == EXP_SUBCMD)
6359 return (char_u *)subcommands[idx].name;
6360 return (char_u *)case_args[idx];
6361}
6362
6363#endif /* FEAT_CMDL_COMPL */
6364
Bram Moolenaar071d4272004-06-13 20:20:40 +00006365/*
6366 * Function called for expression evaluation: get syntax ID at file position.
6367 */
6368 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006369syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006370 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006371 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006372 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006373 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006374 int *spellp; /* return: can do spell checking */
6375 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006376{
6377 /* When the position is not after the current position and in the same
6378 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006379 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006380 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006381 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006382 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006383
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006384 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006385
6386 return (trans ? current_trans_id : current_id);
6387}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006388
Bram Moolenaar860cae12010-06-05 23:22:07 +02006389#if defined(FEAT_CONCEAL) || defined(PROTO)
6390/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006391 * Get extra information about the syntax item. Must be called right after
6392 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006393 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006394 * Returns the current flags.
6395 */
6396 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006397get_syntax_info(seqnrp)
6398 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006399{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006400 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006401 return current_flags;
6402}
6403
6404/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006405 * Return conceal substitution character
6406 */
6407 int
6408syn_get_sub_char()
6409{
6410 return current_sub_char;
6411}
6412#endif
6413
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006414#if defined(FEAT_EVAL) || defined(PROTO)
6415/*
6416 * Return the syntax ID at position "i" in the current stack.
6417 * The caller must have called syn_get_id() before to fill the stack.
6418 * Returns -1 when "i" is out of range.
6419 */
6420 int
6421syn_get_stack_item(i)
6422 int i;
6423{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006424 if (i >= current_state.ga_len)
6425 {
6426 /* Need to invalidate the state, because we didn't properly finish it
6427 * for the last character, "keep_state" was TRUE. */
6428 invalidate_current_state();
6429 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006430 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006431 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006432 return CUR_STATE(i).si_id;
6433}
6434#endif
6435
Bram Moolenaar071d4272004-06-13 20:20:40 +00006436#if defined(FEAT_FOLDING) || defined(PROTO)
6437/*
6438 * Function called to get folding level for line "lnum" in window "wp".
6439 */
6440 int
6441syn_get_foldlevel(wp, lnum)
6442 win_T *wp;
6443 long lnum;
6444{
6445 int level = 0;
6446 int i;
6447
6448 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006449 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450 {
6451 syntax_start(wp, lnum);
6452
6453 for (i = 0; i < current_state.ga_len; ++i)
6454 if (CUR_STATE(i).si_flags & HL_FOLD)
6455 ++level;
6456 }
6457 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006458 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006459 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006460 if (level < 0)
6461 level = 0;
6462 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006463 return level;
6464}
6465#endif
6466
6467#endif /* FEAT_SYN_HL */
6468
Bram Moolenaar071d4272004-06-13 20:20:40 +00006469/**************************************
6470 * Highlighting stuff *
6471 **************************************/
6472
6473/*
6474 * The default highlight groups. These are compiled-in for fast startup and
6475 * they still work when the runtime files can't be found.
6476 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006477 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6478 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006479 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006480#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006481# define CENT(a, b) b
6482#else
6483# define CENT(a, b) a
6484#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006485static char *(highlight_init_both[]) =
6486 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006487 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6488 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6489 CENT("IncSearch term=reverse cterm=reverse",
6490 "IncSearch term=reverse cterm=reverse gui=reverse"),
6491 CENT("ModeMsg term=bold cterm=bold",
6492 "ModeMsg term=bold cterm=bold gui=bold"),
6493 CENT("NonText term=bold ctermfg=Blue",
6494 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6495 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6496 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6497 CENT("StatusLineNC term=reverse cterm=reverse",
6498 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006499#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006500 CENT("VertSplit term=reverse cterm=reverse",
6501 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006502#endif
6503#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006504 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6505 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006506#endif
6507#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006508 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6509 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006510#endif
6511#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006512 CENT("PmenuThumb cterm=reverse",
6513 "PmenuThumb cterm=reverse gui=reverse"),
6514 CENT("PmenuSbar ctermbg=Grey",
6515 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006516#endif
6517#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006518 CENT("TabLineSel term=bold cterm=bold",
6519 "TabLineSel term=bold cterm=bold gui=bold"),
6520 CENT("TabLineFill term=reverse cterm=reverse",
6521 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006522#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006523#ifdef FEAT_GUI
6524 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006525 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006526#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006527 NULL
6528 };
6529
6530static char *(highlight_init_light[]) =
6531 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006532 CENT("Directory term=bold ctermfg=DarkBlue",
6533 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6534 CENT("LineNr term=underline ctermfg=Brown",
6535 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6536 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6537 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6538 CENT("Question term=standout ctermfg=DarkGreen",
6539 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6540 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6541 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006542#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006543 CENT("SpellBad term=reverse ctermbg=LightRed",
6544 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6545 CENT("SpellCap term=reverse ctermbg=LightBlue",
6546 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6547 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6548 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6549 CENT("SpellLocal term=underline ctermbg=Cyan",
6550 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006551#endif
6552#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006553 CENT("Pmenu ctermbg=LightMagenta",
6554 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6555 CENT("PmenuSel ctermbg=LightGrey",
6556 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006557#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006558 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6559 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6560 CENT("Title term=bold ctermfg=DarkMagenta",
6561 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6562 CENT("WarningMsg term=standout ctermfg=DarkRed",
6563 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006564#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006565 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6566 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006567#endif
6568#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006569 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6570 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6571 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6572 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006573#endif
6574#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006575 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6576 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006577#endif
6578#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006579 CENT("Visual term=reverse",
6580 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006581#endif
6582#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006583 CENT("DiffAdd term=bold ctermbg=LightBlue",
6584 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6585 CENT("DiffChange term=bold ctermbg=LightMagenta",
6586 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6587 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6588 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006589#endif
6590#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006591 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6592 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006593#endif
6594#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006595 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006596 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006597 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006598 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006599 CENT("ColorColumn term=reverse ctermbg=LightRed",
6600 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006601#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006602#ifdef FEAT_CONCEAL
6603 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6604 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6605#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006606#ifdef FEAT_AUTOCMD
6607 CENT("MatchParen term=reverse ctermbg=Cyan",
6608 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6609#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006610#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006611 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006612#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006613 NULL
6614 };
6615
6616static char *(highlight_init_dark[]) =
6617 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006618 CENT("Directory term=bold ctermfg=LightCyan",
6619 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6620 CENT("LineNr term=underline ctermfg=Yellow",
6621 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6622 CENT("MoreMsg term=bold ctermfg=LightGreen",
6623 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6624 CENT("Question term=standout ctermfg=LightGreen",
6625 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6626 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6627 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6628 CENT("SpecialKey term=bold ctermfg=LightBlue",
6629 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006630#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006631 CENT("SpellBad term=reverse ctermbg=Red",
6632 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6633 CENT("SpellCap term=reverse ctermbg=Blue",
6634 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6635 CENT("SpellRare term=reverse ctermbg=Magenta",
6636 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6637 CENT("SpellLocal term=underline ctermbg=Cyan",
6638 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006639#endif
6640#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006641 CENT("Pmenu ctermbg=Magenta",
6642 "Pmenu ctermbg=Magenta guibg=Magenta"),
6643 CENT("PmenuSel ctermbg=DarkGrey",
6644 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006645#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006646 CENT("Title term=bold ctermfg=LightMagenta",
6647 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6648 CENT("WarningMsg term=standout ctermfg=LightRed",
6649 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006650#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006651 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6652 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006653#endif
6654#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006655 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6656 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6657 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6658 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006659#endif
6660#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006661 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6662 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006663#endif
6664#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006665 CENT("Visual term=reverse",
6666 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006667#endif
6668#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006669 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6670 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6671 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6672 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6673 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6674 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006675#endif
6676#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006677 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6678 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006679#endif
6680#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006681 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006682 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006683 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006684 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006685 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6686 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006687#endif
6688#ifdef FEAT_AUTOCMD
6689 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6690 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006691#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006692#ifdef FEAT_CONCEAL
6693 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6694 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6695#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006696#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006697 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006698#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006699 NULL
6700 };
6701
6702 void
6703init_highlight(both, reset)
6704 int both; /* include groups where 'bg' doesn't matter */
6705 int reset; /* clear group first */
6706{
6707 int i;
6708 char **pp;
6709 static int had_both = FALSE;
6710#ifdef FEAT_EVAL
6711 char_u *p;
6712
6713 /*
6714 * Try finding the color scheme file. Used when a color file was loaded
6715 * and 'background' or 't_Co' is changed.
6716 */
6717 p = get_var_value((char_u *)"g:colors_name");
6718 if (p != NULL && load_colors(p) == OK)
6719 return;
6720#endif
6721
6722 /*
6723 * Didn't use a color file, use the compiled-in colors.
6724 */
6725 if (both)
6726 {
6727 had_both = TRUE;
6728 pp = highlight_init_both;
6729 for (i = 0; pp[i] != NULL; ++i)
6730 do_highlight((char_u *)pp[i], reset, TRUE);
6731 }
6732 else if (!had_both)
6733 /* Don't do anything before the call with both == TRUE from main().
6734 * Not everything has been setup then, and that call will overrule
6735 * everything anyway. */
6736 return;
6737
6738 if (*p_bg == 'l')
6739 pp = highlight_init_light;
6740 else
6741 pp = highlight_init_dark;
6742 for (i = 0; pp[i] != NULL; ++i)
6743 do_highlight((char_u *)pp[i], reset, TRUE);
6744
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006745 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006746 * depend on the number of colors available.
6747 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006748 * to avoid Statement highlighted text disappears.
6749 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006750 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006751 do_highlight((char_u *)(*p_bg == 'l'
6752 ? "Visual cterm=NONE ctermbg=LightGrey"
6753 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006754 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006755 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006756 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6757 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006758 if (*p_bg == 'l')
6759 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6760 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006761
Bram Moolenaar071d4272004-06-13 20:20:40 +00006762#ifdef FEAT_SYN_HL
6763 /*
6764 * If syntax highlighting is enabled load the highlighting for it.
6765 */
6766 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006767 {
6768 static int recursive = 0;
6769
6770 if (recursive >= 5)
6771 EMSG(_("E679: recursive loop loading syncolor.vim"));
6772 else
6773 {
6774 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006775 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006776 --recursive;
6777 }
6778 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006779#endif
6780}
6781
6782/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006783 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006784 * Return OK for success, FAIL for failure.
6785 */
6786 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006787load_colors(name)
6788 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006789{
6790 char_u *buf;
6791 int retval = FAIL;
6792 static int recursive = FALSE;
6793
6794 /* When being called recursively, this is probably because setting
6795 * 'background' caused the highlighting to be reloaded. This means it is
6796 * working, thus we should return OK. */
6797 if (recursive)
6798 return OK;
6799
6800 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006801 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006802 if (buf != NULL)
6803 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006804 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006805 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006806 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006807#ifdef FEAT_AUTOCMD
6808 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6809#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006810 }
6811 recursive = FALSE;
6812
6813 return retval;
6814}
6815
6816/*
6817 * Handle the ":highlight .." command.
6818 * When using ":hi clear" this is called recursively for each group with
6819 * "forceit" and "init" both TRUE.
6820 */
6821 void
6822do_highlight(line, forceit, init)
6823 char_u *line;
6824 int forceit;
6825 int init; /* TRUE when called for initializing */
6826{
6827 char_u *name_end;
6828 char_u *p;
6829 char_u *linep;
6830 char_u *key_start;
6831 char_u *arg_start;
6832 char_u *key = NULL, *arg = NULL;
6833 long i;
6834 int off;
6835 int len;
6836 int attr;
6837 int id;
6838 int idx;
6839 int dodefault = FALSE;
6840 int doclear = FALSE;
6841 int dolink = FALSE;
6842 int error = FALSE;
6843 int color;
6844 int is_normal_group = FALSE; /* "Normal" group */
6845#ifdef FEAT_GUI_X11
6846 int is_menu_group = FALSE; /* "Menu" group */
6847 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6848 int is_tooltip_group = FALSE; /* "Tooltip" group */
6849 int do_colors = FALSE; /* need to update colors? */
6850#else
6851# define is_menu_group 0
6852# define is_tooltip_group 0
6853#endif
6854
6855 /*
6856 * If no argument, list current highlighting.
6857 */
6858 if (ends_excmd(*line))
6859 {
6860 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6861 /* TODO: only call when the group has attributes set */
6862 highlight_list_one((int)i);
6863 return;
6864 }
6865
6866 /*
6867 * Isolate the name.
6868 */
6869 name_end = skiptowhite(line);
6870 linep = skipwhite(name_end);
6871
6872 /*
6873 * Check for "default" argument.
6874 */
6875 if (STRNCMP(line, "default", name_end - line) == 0)
6876 {
6877 dodefault = TRUE;
6878 line = linep;
6879 name_end = skiptowhite(line);
6880 linep = skipwhite(name_end);
6881 }
6882
6883 /*
6884 * Check for "clear" or "link" argument.
6885 */
6886 if (STRNCMP(line, "clear", name_end - line) == 0)
6887 doclear = TRUE;
6888 if (STRNCMP(line, "link", name_end - line) == 0)
6889 dolink = TRUE;
6890
6891 /*
6892 * ":highlight {group-name}": list highlighting for one group.
6893 */
6894 if (!doclear && !dolink && ends_excmd(*linep))
6895 {
6896 id = syn_namen2id(line, (int)(name_end - line));
6897 if (id == 0)
6898 EMSG2(_("E411: highlight group not found: %s"), line);
6899 else
6900 highlight_list_one(id);
6901 return;
6902 }
6903
6904 /*
6905 * Handle ":highlight link {from} {to}" command.
6906 */
6907 if (dolink)
6908 {
6909 char_u *from_start = linep;
6910 char_u *from_end;
6911 char_u *to_start;
6912 char_u *to_end;
6913 int from_id;
6914 int to_id;
6915
6916 from_end = skiptowhite(from_start);
6917 to_start = skipwhite(from_end);
6918 to_end = skiptowhite(to_start);
6919
6920 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6921 {
6922 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6923 from_start);
6924 return;
6925 }
6926
6927 if (!ends_excmd(*skipwhite(to_end)))
6928 {
6929 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6930 return;
6931 }
6932
6933 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6934 if (STRNCMP(to_start, "NONE", 4) == 0)
6935 to_id = 0;
6936 else
6937 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6938
6939 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6940 {
6941 /*
6942 * Don't allow a link when there already is some highlighting
6943 * for the group, unless '!' is used
6944 */
6945 if (to_id > 0 && !forceit && !init
6946 && hl_has_settings(from_id - 1, dodefault))
6947 {
6948 if (sourcing_name == NULL && !dodefault)
6949 EMSG(_("E414: group has settings, highlight link ignored"));
6950 }
6951 else
6952 {
6953 if (!init)
6954 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6955 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006956#ifdef FEAT_EVAL
6957 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6958#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006959 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006960 }
6961 }
6962
6963 /* Only call highlight_changed() once, after sourcing a syntax file */
6964 need_highlight_changed = TRUE;
6965
6966 return;
6967 }
6968
6969 if (doclear)
6970 {
6971 /*
6972 * ":highlight clear [group]" command.
6973 */
6974 line = linep;
6975 if (ends_excmd(*line))
6976 {
6977#ifdef FEAT_GUI
6978 /* First, we do not destroy the old values, but allocate the new
6979 * ones and update the display. THEN we destroy the old values.
6980 * If we destroy the old values first, then the old values
6981 * (such as GuiFont's or GuiFontset's) will still be displayed but
6982 * invalid because they were free'd.
6983 */
6984 if (gui.in_use)
6985 {
6986# ifdef FEAT_BEVAL_TIP
6987 gui_init_tooltip_font();
6988# endif
6989# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6990 gui_init_menu_font();
6991# endif
6992 }
6993# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6994 gui_mch_def_colors();
6995# endif
6996# ifdef FEAT_GUI_X11
6997# ifdef FEAT_MENU
6998
6999 /* This only needs to be done when there is no Menu highlight
7000 * group defined by default, which IS currently the case.
7001 */
7002 gui_mch_new_menu_colors();
7003# endif
7004 if (gui.in_use)
7005 {
7006 gui_new_scrollbar_colors();
7007# ifdef FEAT_BEVAL
7008 gui_mch_new_tooltip_colors();
7009# endif
7010# ifdef FEAT_MENU
7011 gui_mch_new_menu_font();
7012# endif
7013 }
7014# endif
7015
7016 /* Ok, we're done allocating the new default graphics items.
7017 * The screen should already be refreshed at this point.
7018 * It is now Ok to clear out the old data.
7019 */
7020#endif
7021#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007022 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007023#endif
7024 restore_cterm_colors();
7025
7026 /*
7027 * Clear all default highlight groups and load the defaults.
7028 */
7029 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7030 highlight_clear(idx);
7031 init_highlight(TRUE, TRUE);
7032#ifdef FEAT_GUI
7033 if (gui.in_use)
7034 highlight_gui_started();
7035#endif
7036 highlight_changed();
7037 redraw_later_clear();
7038 return;
7039 }
7040 name_end = skiptowhite(line);
7041 linep = skipwhite(name_end);
7042 }
7043
7044 /*
7045 * Find the group name in the table. If it does not exist yet, add it.
7046 */
7047 id = syn_check_group(line, (int)(name_end - line));
7048 if (id == 0) /* failed (out of memory) */
7049 return;
7050 idx = id - 1; /* index is ID minus one */
7051
7052 /* Return if "default" was used and the group already has settings. */
7053 if (dodefault && hl_has_settings(idx, TRUE))
7054 return;
7055
7056 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7057 is_normal_group = TRUE;
7058#ifdef FEAT_GUI_X11
7059 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7060 is_menu_group = TRUE;
7061 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7062 is_scrollbar_group = TRUE;
7063 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7064 is_tooltip_group = TRUE;
7065#endif
7066
7067 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7068 if (doclear || (forceit && init))
7069 {
7070 highlight_clear(idx);
7071 if (!doclear)
7072 HL_TABLE()[idx].sg_set = 0;
7073 }
7074
7075 if (!doclear)
7076 while (!ends_excmd(*linep))
7077 {
7078 key_start = linep;
7079 if (*linep == '=')
7080 {
7081 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7082 error = TRUE;
7083 break;
7084 }
7085
7086 /*
7087 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7088 * "guibg").
7089 */
7090 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7091 ++linep;
7092 vim_free(key);
7093 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7094 if (key == NULL)
7095 {
7096 error = TRUE;
7097 break;
7098 }
7099 linep = skipwhite(linep);
7100
7101 if (STRCMP(key, "NONE") == 0)
7102 {
7103 if (!init || HL_TABLE()[idx].sg_set == 0)
7104 {
7105 if (!init)
7106 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7107 highlight_clear(idx);
7108 }
7109 continue;
7110 }
7111
7112 /*
7113 * Check for the equal sign.
7114 */
7115 if (*linep != '=')
7116 {
7117 EMSG2(_("E416: missing equal sign: %s"), key_start);
7118 error = TRUE;
7119 break;
7120 }
7121 ++linep;
7122
7123 /*
7124 * Isolate the argument.
7125 */
7126 linep = skipwhite(linep);
7127 if (*linep == '\'') /* guifg='color name' */
7128 {
7129 arg_start = ++linep;
7130 linep = vim_strchr(linep, '\'');
7131 if (linep == NULL)
7132 {
7133 EMSG2(_(e_invarg2), key_start);
7134 error = TRUE;
7135 break;
7136 }
7137 }
7138 else
7139 {
7140 arg_start = linep;
7141 linep = skiptowhite(linep);
7142 }
7143 if (linep == arg_start)
7144 {
7145 EMSG2(_("E417: missing argument: %s"), key_start);
7146 error = TRUE;
7147 break;
7148 }
7149 vim_free(arg);
7150 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7151 if (arg == NULL)
7152 {
7153 error = TRUE;
7154 break;
7155 }
7156 if (*linep == '\'')
7157 ++linep;
7158
7159 /*
7160 * Store the argument.
7161 */
7162 if ( STRCMP(key, "TERM") == 0
7163 || STRCMP(key, "CTERM") == 0
7164 || STRCMP(key, "GUI") == 0)
7165 {
7166 attr = 0;
7167 off = 0;
7168 while (arg[off] != NUL)
7169 {
7170 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7171 {
7172 len = (int)STRLEN(hl_name_table[i]);
7173 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7174 {
7175 attr |= hl_attr_table[i];
7176 off += len;
7177 break;
7178 }
7179 }
7180 if (i < 0)
7181 {
7182 EMSG2(_("E418: Illegal value: %s"), arg);
7183 error = TRUE;
7184 break;
7185 }
7186 if (arg[off] == ',') /* another one follows */
7187 ++off;
7188 }
7189 if (error)
7190 break;
7191 if (*key == 'T')
7192 {
7193 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7194 {
7195 if (!init)
7196 HL_TABLE()[idx].sg_set |= SG_TERM;
7197 HL_TABLE()[idx].sg_term = attr;
7198 }
7199 }
7200 else if (*key == 'C')
7201 {
7202 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7203 {
7204 if (!init)
7205 HL_TABLE()[idx].sg_set |= SG_CTERM;
7206 HL_TABLE()[idx].sg_cterm = attr;
7207 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7208 }
7209 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007210#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007211 else
7212 {
7213 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7214 {
7215 if (!init)
7216 HL_TABLE()[idx].sg_set |= SG_GUI;
7217 HL_TABLE()[idx].sg_gui = attr;
7218 }
7219 }
7220#endif
7221 }
7222 else if (STRCMP(key, "FONT") == 0)
7223 {
7224 /* in non-GUI fonts are simply ignored */
7225#ifdef FEAT_GUI
7226 if (!gui.shell_created)
7227 {
7228 /* GUI not started yet, always accept the name. */
7229 vim_free(HL_TABLE()[idx].sg_font_name);
7230 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7231 }
7232 else
7233 {
7234 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7235# ifdef FEAT_XFONTSET
7236 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7237# endif
7238 /* First, save the current font/fontset.
7239 * Then try to allocate the font/fontset.
7240 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7241 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7242 */
7243
7244 HL_TABLE()[idx].sg_font = NOFONT;
7245# ifdef FEAT_XFONTSET
7246 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7247# endif
7248 hl_do_font(idx, arg, is_normal_group, is_menu_group,
7249 is_tooltip_group);
7250
7251# ifdef FEAT_XFONTSET
7252 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7253 {
7254 /* New fontset was accepted. Free the old one, if there was
7255 * one.
7256 */
7257 gui_mch_free_fontset(temp_sg_fontset);
7258 vim_free(HL_TABLE()[idx].sg_font_name);
7259 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7260 }
7261 else
7262 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7263# endif
7264 if (HL_TABLE()[idx].sg_font != NOFONT)
7265 {
7266 /* New font was accepted. Free the old one, if there was
7267 * one.
7268 */
7269 gui_mch_free_font(temp_sg_font);
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_font = temp_sg_font;
7275 }
7276#endif
7277 }
7278 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7279 {
7280 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7281 {
7282 if (!init)
7283 HL_TABLE()[idx].sg_set |= SG_CTERM;
7284
7285 /* When setting the foreground color, and previously the "bold"
7286 * flag was set for a light color, reset it now */
7287 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7288 {
7289 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7290 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7291 }
7292
7293 if (VIM_ISDIGIT(*arg))
7294 color = atoi((char *)arg);
7295 else if (STRICMP(arg, "fg") == 0)
7296 {
7297 if (cterm_normal_fg_color)
7298 color = cterm_normal_fg_color - 1;
7299 else
7300 {
7301 EMSG(_("E419: FG color unknown"));
7302 error = TRUE;
7303 break;
7304 }
7305 }
7306 else if (STRICMP(arg, "bg") == 0)
7307 {
7308 if (cterm_normal_bg_color > 0)
7309 color = cterm_normal_bg_color - 1;
7310 else
7311 {
7312 EMSG(_("E420: BG color unknown"));
7313 error = TRUE;
7314 break;
7315 }
7316 }
7317 else
7318 {
7319 static char *(color_names[28]) = {
7320 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7321 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7322 "Gray", "Grey",
7323 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7324 "Blue", "LightBlue", "Green", "LightGreen",
7325 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7326 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7327 static int color_numbers_16[28] = {0, 1, 2, 3,
7328 4, 5, 6, 6,
7329 7, 7,
7330 7, 7, 8, 8,
7331 9, 9, 10, 10,
7332 11, 11, 12, 12, 13,
7333 13, 14, 14, 15, -1};
7334 /* for xterm with 88 colors... */
7335 static int color_numbers_88[28] = {0, 4, 2, 6,
7336 1, 5, 32, 72,
7337 84, 84,
7338 7, 7, 82, 82,
7339 12, 43, 10, 61,
7340 14, 63, 9, 74, 13,
7341 75, 11, 78, 15, -1};
7342 /* for xterm with 256 colors... */
7343 static int color_numbers_256[28] = {0, 4, 2, 6,
7344 1, 5, 130, 130,
7345 248, 248,
7346 7, 7, 242, 242,
7347 12, 81, 10, 121,
7348 14, 159, 9, 224, 13,
7349 225, 11, 229, 15, -1};
7350 /* for terminals with less than 16 colors... */
7351 static int color_numbers_8[28] = {0, 4, 2, 6,
7352 1, 5, 3, 3,
7353 7, 7,
7354 7, 7, 0+8, 0+8,
7355 4+8, 4+8, 2+8, 2+8,
7356 6+8, 6+8, 1+8, 1+8, 5+8,
7357 5+8, 3+8, 3+8, 7+8, -1};
7358#if defined(__QNXNTO__)
7359 static int *color_numbers_8_qansi = color_numbers_8;
7360 /* On qnx, the 8 & 16 color arrays are the same */
7361 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7362 color_numbers_8_qansi = color_numbers_16;
7363#endif
7364
7365 /* reduce calls to STRICMP a bit, it can be slow */
7366 off = TOUPPER_ASC(*arg);
7367 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7368 if (off == color_names[i][0]
7369 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7370 break;
7371 if (i < 0)
7372 {
7373 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7374 error = TRUE;
7375 break;
7376 }
7377
7378 /* Use the _16 table to check if its a valid color name. */
7379 color = color_numbers_16[i];
7380 if (color >= 0)
7381 {
7382 if (t_colors == 8)
7383 {
7384 /* t_Co is 8: use the 8 colors table */
7385#if defined(__QNXNTO__)
7386 color = color_numbers_8_qansi[i];
7387#else
7388 color = color_numbers_8[i];
7389#endif
7390 if (key[5] == 'F')
7391 {
7392 /* set/reset bold attribute to get light foreground
7393 * colors (on some terminals, e.g. "linux") */
7394 if (color & 8)
7395 {
7396 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7397 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7398 }
7399 else
7400 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7401 }
7402 color &= 7; /* truncate to 8 colors */
7403 }
7404 else if (t_colors == 16 || t_colors == 88
7405 || t_colors == 256)
7406 {
7407 /*
7408 * Guess: if the termcap entry ends in 'm', it is
7409 * probably an xterm-like terminal. Use the changed
7410 * order for colors.
7411 */
7412 if (*T_CAF != NUL)
7413 p = T_CAF;
7414 else
7415 p = T_CSF;
7416 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7417 switch (t_colors)
7418 {
7419 case 16:
7420 color = color_numbers_8[i];
7421 break;
7422 case 88:
7423 color = color_numbers_88[i];
7424 break;
7425 case 256:
7426 color = color_numbers_256[i];
7427 break;
7428 }
7429 }
7430 }
7431 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007432 /* Add one to the argument, to avoid zero. Zero is used for
7433 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007434 if (key[5] == 'F')
7435 {
7436 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7437 if (is_normal_group)
7438 {
7439 cterm_normal_fg_color = color + 1;
7440 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7441#ifdef FEAT_GUI
7442 /* Don't do this if the GUI is used. */
7443 if (!gui.in_use && !gui.starting)
7444#endif
7445 {
7446 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007447 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007448 term_fg_color(color);
7449 }
7450 }
7451 }
7452 else
7453 {
7454 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7455 if (is_normal_group)
7456 {
7457 cterm_normal_bg_color = color + 1;
7458#ifdef FEAT_GUI
7459 /* Don't mess with 'background' if the GUI is used. */
7460 if (!gui.in_use && !gui.starting)
7461#endif
7462 {
7463 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007464 if (color >= 0)
7465 {
7466 if (termcap_active)
7467 term_bg_color(color);
7468 if (t_colors < 16)
7469 i = (color == 0 || color == 4);
7470 else
7471 i = (color < 7 || color == 8);
7472 /* Set the 'background' option if the value is
7473 * wrong. */
7474 if (i != (*p_bg == 'd'))
7475 set_option_value((char_u *)"bg", 0L,
7476 i ? (char_u *)"dark"
7477 : (char_u *)"light", 0);
7478 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007479 }
7480 }
7481 }
7482 }
7483 }
7484 else if (STRCMP(key, "GUIFG") == 0)
7485 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007486#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007487 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007488 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007489 if (!init)
7490 HL_TABLE()[idx].sg_set |= SG_GUI;
7491
Bram Moolenaar61623362010-07-14 22:04:22 +02007492# ifdef FEAT_GUI
7493 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007494 i = color_name2handle(arg);
7495 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7496 {
7497 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007498# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007499 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7500 if (STRCMP(arg, "NONE"))
7501 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7502 else
7503 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007504# ifdef FEAT_GUI
7505# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007506 if (is_menu_group)
7507 gui.menu_fg_pixel = i;
7508 if (is_scrollbar_group)
7509 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007510# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007511 if (is_tooltip_group)
7512 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007513# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007514 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007515# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007516 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007517# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007518 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007519#endif
7520 }
7521 else if (STRCMP(key, "GUIBG") == 0)
7522 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007523#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007524 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007525 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007526 if (!init)
7527 HL_TABLE()[idx].sg_set |= SG_GUI;
7528
Bram Moolenaar61623362010-07-14 22:04:22 +02007529# ifdef FEAT_GUI
7530 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007531 i = color_name2handle(arg);
7532 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7533 {
7534 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007535# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007536 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7537 if (STRCMP(arg, "NONE") != 0)
7538 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7539 else
7540 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007541# ifdef FEAT_GUI
7542# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007543 if (is_menu_group)
7544 gui.menu_bg_pixel = i;
7545 if (is_scrollbar_group)
7546 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007547# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007548 if (is_tooltip_group)
7549 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007550# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007551 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007552# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007553 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007554# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007555 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007556#endif
7557 }
7558 else if (STRCMP(key, "GUISP") == 0)
7559 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007560#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007561 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7562 {
7563 if (!init)
7564 HL_TABLE()[idx].sg_set |= SG_GUI;
7565
Bram Moolenaar61623362010-07-14 22:04:22 +02007566# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007567 i = color_name2handle(arg);
7568 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7569 {
7570 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007571# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007572 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7573 if (STRCMP(arg, "NONE") != 0)
7574 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7575 else
7576 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007577# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007578 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007579# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007580 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007581#endif
7582 }
7583 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7584 {
7585 char_u buf[100];
7586 char_u *tname;
7587
7588 if (!init)
7589 HL_TABLE()[idx].sg_set |= SG_TERM;
7590
7591 /*
7592 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007593 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007594 */
7595 if (STRNCMP(arg, "t_", 2) == 0)
7596 {
7597 off = 0;
7598 buf[0] = 0;
7599 while (arg[off] != NUL)
7600 {
7601 /* Isolate one termcap name */
7602 for (len = 0; arg[off + len] &&
7603 arg[off + len] != ','; ++len)
7604 ;
7605 tname = vim_strnsave(arg + off, len);
7606 if (tname == NULL) /* out of memory */
7607 {
7608 error = TRUE;
7609 break;
7610 }
7611 /* lookup the escape sequence for the item */
7612 p = get_term_code(tname);
7613 vim_free(tname);
7614 if (p == NULL) /* ignore non-existing things */
7615 p = (char_u *)"";
7616
7617 /* Append it to the already found stuff */
7618 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7619 {
7620 EMSG2(_("E422: terminal code too long: %s"), arg);
7621 error = TRUE;
7622 break;
7623 }
7624 STRCAT(buf, p);
7625
7626 /* Advance to the next item */
7627 off += len;
7628 if (arg[off] == ',') /* another one follows */
7629 ++off;
7630 }
7631 }
7632 else
7633 {
7634 /*
7635 * Copy characters from arg[] to buf[], translating <> codes.
7636 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007637 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007638 {
7639 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007640 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007641 off += len;
7642 else /* copy as normal char */
7643 buf[off++] = *p++;
7644 }
7645 buf[off] = NUL;
7646 }
7647 if (error)
7648 break;
7649
7650 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7651 p = NULL;
7652 else
7653 p = vim_strsave(buf);
7654 if (key[2] == 'A')
7655 {
7656 vim_free(HL_TABLE()[idx].sg_start);
7657 HL_TABLE()[idx].sg_start = p;
7658 }
7659 else
7660 {
7661 vim_free(HL_TABLE()[idx].sg_stop);
7662 HL_TABLE()[idx].sg_stop = p;
7663 }
7664 }
7665 else
7666 {
7667 EMSG2(_("E423: Illegal argument: %s"), key_start);
7668 error = TRUE;
7669 break;
7670 }
7671
7672 /*
7673 * When highlighting has been given for a group, don't link it.
7674 */
7675 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7676 HL_TABLE()[idx].sg_link = 0;
7677
7678 /*
7679 * Continue with next argument.
7680 */
7681 linep = skipwhite(linep);
7682 }
7683
7684 /*
7685 * If there is an error, and it's a new entry, remove it from the table.
7686 */
7687 if (error && idx == highlight_ga.ga_len)
7688 syn_unadd_group();
7689 else
7690 {
7691 if (is_normal_group)
7692 {
7693 HL_TABLE()[idx].sg_term_attr = 0;
7694 HL_TABLE()[idx].sg_cterm_attr = 0;
7695#ifdef FEAT_GUI
7696 HL_TABLE()[idx].sg_gui_attr = 0;
7697 /*
7698 * Need to update all groups, because they might be using "bg"
7699 * and/or "fg", which have been changed now.
7700 */
7701 if (gui.in_use)
7702 highlight_gui_started();
7703#endif
7704 }
7705#ifdef FEAT_GUI_X11
7706# ifdef FEAT_MENU
7707 else if (is_menu_group)
7708 {
7709 if (gui.in_use && do_colors)
7710 gui_mch_new_menu_colors();
7711 }
7712# endif
7713 else if (is_scrollbar_group)
7714 {
7715 if (gui.in_use && do_colors)
7716 gui_new_scrollbar_colors();
7717 }
7718# ifdef FEAT_BEVAL
7719 else if (is_tooltip_group)
7720 {
7721 if (gui.in_use && do_colors)
7722 gui_mch_new_tooltip_colors();
7723 }
7724# endif
7725#endif
7726 else
7727 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007728#ifdef FEAT_EVAL
7729 HL_TABLE()[idx].sg_scriptID = current_SID;
7730#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007731 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007732 }
7733 vim_free(key);
7734 vim_free(arg);
7735
7736 /* Only call highlight_changed() once, after sourcing a syntax file */
7737 need_highlight_changed = TRUE;
7738}
7739
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007740#if defined(EXITFREE) || defined(PROTO)
7741 void
7742free_highlight()
7743{
7744 int i;
7745
7746 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007747 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007748 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007749 vim_free(HL_TABLE()[i].sg_name);
7750 vim_free(HL_TABLE()[i].sg_name_u);
7751 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007752 ga_clear(&highlight_ga);
7753}
7754#endif
7755
Bram Moolenaar071d4272004-06-13 20:20:40 +00007756/*
7757 * Reset the cterm colors to what they were before Vim was started, if
7758 * possible. Otherwise reset them to zero.
7759 */
7760 void
7761restore_cterm_colors()
7762{
7763#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7764 /* Since t_me has been set, this probably means that the user
7765 * wants to use this as default colors. Need to reset default
7766 * background/foreground colors. */
7767 mch_set_normal_colors();
7768#else
7769 cterm_normal_fg_color = 0;
7770 cterm_normal_fg_bold = 0;
7771 cterm_normal_bg_color = 0;
7772#endif
7773}
7774
7775/*
7776 * Return TRUE if highlight group "idx" has any settings.
7777 * When "check_link" is TRUE also check for an existing link.
7778 */
7779 static int
7780hl_has_settings(idx, check_link)
7781 int idx;
7782 int check_link;
7783{
7784 return ( HL_TABLE()[idx].sg_term_attr != 0
7785 || HL_TABLE()[idx].sg_cterm_attr != 0
7786#ifdef FEAT_GUI
7787 || HL_TABLE()[idx].sg_gui_attr != 0
7788#endif
7789 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7790}
7791
7792/*
7793 * Clear highlighting for one group.
7794 */
7795 static void
7796highlight_clear(idx)
7797 int idx;
7798{
7799 HL_TABLE()[idx].sg_term = 0;
7800 vim_free(HL_TABLE()[idx].sg_start);
7801 HL_TABLE()[idx].sg_start = NULL;
7802 vim_free(HL_TABLE()[idx].sg_stop);
7803 HL_TABLE()[idx].sg_stop = NULL;
7804 HL_TABLE()[idx].sg_term_attr = 0;
7805 HL_TABLE()[idx].sg_cterm = 0;
7806 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7807 HL_TABLE()[idx].sg_cterm_fg = 0;
7808 HL_TABLE()[idx].sg_cterm_bg = 0;
7809 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02007810#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007811 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007812 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7813 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007814 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7815 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007816 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7817 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007818#endif
7819#ifdef FEAT_GUI
7820 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7821 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7822 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007823 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7824 HL_TABLE()[idx].sg_font = NOFONT;
7825# ifdef FEAT_XFONTSET
7826 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7827 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7828# endif
7829 vim_free(HL_TABLE()[idx].sg_font_name);
7830 HL_TABLE()[idx].sg_font_name = NULL;
7831 HL_TABLE()[idx].sg_gui_attr = 0;
7832#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007833#ifdef FEAT_EVAL
7834 /* Clear the script ID only when there is no link, since that is not
7835 * cleared. */
7836 if (HL_TABLE()[idx].sg_link == 0)
7837 HL_TABLE()[idx].sg_scriptID = 0;
7838#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007839}
7840
7841#if defined(FEAT_GUI) || defined(PROTO)
7842/*
7843 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00007844 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00007845 * "Tooltip" colors.
7846 */
7847 void
7848set_normal_colors()
7849{
7850 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007851 &gui.norm_pixel, &gui.back_pixel,
7852 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007853 {
7854 gui_mch_new_colors();
7855 must_redraw = CLEAR;
7856 }
7857#ifdef FEAT_GUI_X11
7858 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007859 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7860 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007861 {
7862# ifdef FEAT_MENU
7863 gui_mch_new_menu_colors();
7864# endif
7865 must_redraw = CLEAR;
7866 }
7867# ifdef FEAT_BEVAL
7868 if (set_group_colors((char_u *)"Tooltip",
7869 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7870 FALSE, FALSE, TRUE))
7871 {
7872# ifdef FEAT_TOOLBAR
7873 gui_mch_new_tooltip_colors();
7874# endif
7875 must_redraw = CLEAR;
7876 }
7877#endif
7878 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007879 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7880 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007881 {
7882 gui_new_scrollbar_colors();
7883 must_redraw = CLEAR;
7884 }
7885#endif
7886}
7887
7888/*
7889 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7890 */
7891 static int
7892set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7893 char_u *name;
7894 guicolor_T *fgp;
7895 guicolor_T *bgp;
7896 int do_menu;
7897 int use_norm;
7898 int do_tooltip;
7899{
7900 int idx;
7901
7902 idx = syn_name2id(name) - 1;
7903 if (idx >= 0)
7904 {
7905 gui_do_one_color(idx, do_menu, do_tooltip);
7906
7907 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7908 *fgp = HL_TABLE()[idx].sg_gui_fg;
7909 else if (use_norm)
7910 *fgp = gui.def_norm_pixel;
7911 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7912 *bgp = HL_TABLE()[idx].sg_gui_bg;
7913 else if (use_norm)
7914 *bgp = gui.def_back_pixel;
7915 return TRUE;
7916 }
7917 return FALSE;
7918}
7919
7920/*
7921 * Get the font of the "Normal" group.
7922 * Returns "" when it's not found or not set.
7923 */
7924 char_u *
7925hl_get_font_name()
7926{
7927 int id;
7928 char_u *s;
7929
7930 id = syn_name2id((char_u *)"Normal");
7931 if (id > 0)
7932 {
7933 s = HL_TABLE()[id - 1].sg_font_name;
7934 if (s != NULL)
7935 return s;
7936 }
7937 return (char_u *)"";
7938}
7939
7940/*
7941 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7942 * actually chosen to be used.
7943 */
7944 void
7945hl_set_font_name(font_name)
7946 char_u *font_name;
7947{
7948 int id;
7949
7950 id = syn_name2id((char_u *)"Normal");
7951 if (id > 0)
7952 {
7953 vim_free(HL_TABLE()[id - 1].sg_font_name);
7954 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7955 }
7956}
7957
7958/*
7959 * Set background color for "Normal" group. Called by gui_set_bg_color()
7960 * when the color is known.
7961 */
7962 void
7963hl_set_bg_color_name(name)
7964 char_u *name; /* must have been allocated */
7965{
7966 int id;
7967
7968 if (name != NULL)
7969 {
7970 id = syn_name2id((char_u *)"Normal");
7971 if (id > 0)
7972 {
7973 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7974 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7975 }
7976 }
7977}
7978
7979/*
7980 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7981 * when the color is known.
7982 */
7983 void
7984hl_set_fg_color_name(name)
7985 char_u *name; /* must have been allocated */
7986{
7987 int id;
7988
7989 if (name != NULL)
7990 {
7991 id = syn_name2id((char_u *)"Normal");
7992 if (id > 0)
7993 {
7994 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7995 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7996 }
7997 }
7998}
7999
8000/*
8001 * Return the handle for a color name.
8002 * Returns INVALCOLOR when failed.
8003 */
8004 static guicolor_T
8005color_name2handle(name)
8006 char_u *name;
8007{
8008 if (STRCMP(name, "NONE") == 0)
8009 return INVALCOLOR;
8010
8011 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8012 return gui.norm_pixel;
8013 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8014 return gui.back_pixel;
8015
8016 return gui_get_color(name);
8017}
8018
8019/*
8020 * Return the handle for a font name.
8021 * Returns NOFONT when failed.
8022 */
8023 static GuiFont
8024font_name2handle(name)
8025 char_u *name;
8026{
8027 if (STRCMP(name, "NONE") == 0)
8028 return NOFONT;
8029
8030 return gui_mch_get_font(name, TRUE);
8031}
8032
8033# ifdef FEAT_XFONTSET
8034/*
8035 * Return the handle for a fontset name.
8036 * Returns NOFONTSET when failed.
8037 */
8038 static GuiFontset
8039fontset_name2handle(name, fixed_width)
8040 char_u *name;
8041 int fixed_width;
8042{
8043 if (STRCMP(name, "NONE") == 0)
8044 return NOFONTSET;
8045
8046 return gui_mch_get_fontset(name, TRUE, fixed_width);
8047}
8048# endif
8049
8050/*
8051 * Get the font or fontset for one highlight group.
8052 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008053 static void
8054hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
8055 int idx;
8056 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008057 int do_normal; /* set normal font */
8058 int do_menu UNUSED; /* set menu font */
8059 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008060{
8061# ifdef FEAT_XFONTSET
8062 /* If 'guifontset' is not empty, first try using the name as a
8063 * fontset. If that doesn't work, use it as a font name. */
8064 if (*p_guifontset != NUL
8065# ifdef FONTSET_ALWAYS
8066 || do_menu
8067# endif
8068# ifdef FEAT_BEVAL_TIP
8069 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8070 || do_tooltip
8071# endif
8072 )
8073 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8074# ifdef FONTSET_ALWAYS
8075 || do_menu
8076# endif
8077# ifdef FEAT_BEVAL_TIP
8078 || do_tooltip
8079# endif
8080 );
8081 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8082 {
8083 /* If it worked and it's the Normal group, use it as the
8084 * normal fontset. Same for the Menu group. */
8085 if (do_normal)
8086 gui_init_font(arg, TRUE);
8087# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8088 if (do_menu)
8089 {
8090# ifdef FONTSET_ALWAYS
8091 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8092# else
8093 /* YIKES! This is a bug waiting to crash the program */
8094 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8095# endif
8096 gui_mch_new_menu_font();
8097 }
8098# ifdef FEAT_BEVAL
8099 if (do_tooltip)
8100 {
8101 /* The Athena widget set cannot currently handle switching between
8102 * displaying a single font and a fontset.
8103 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008104 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008105 * XFontStruct is used.
8106 */
8107 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8108 gui_mch_new_tooltip_font();
8109 }
8110# endif
8111# endif
8112 }
8113 else
8114# endif
8115 {
8116 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8117 /* If it worked and it's the Normal group, use it as the
8118 * normal font. Same for the Menu group. */
8119 if (HL_TABLE()[idx].sg_font != NOFONT)
8120 {
8121 if (do_normal)
8122 gui_init_font(arg, FALSE);
8123#ifndef FONTSET_ALWAYS
8124# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8125 if (do_menu)
8126 {
8127 gui.menu_font = HL_TABLE()[idx].sg_font;
8128 gui_mch_new_menu_font();
8129 }
8130# endif
8131#endif
8132 }
8133 }
8134}
8135
8136#endif /* FEAT_GUI */
8137
8138/*
8139 * Table with the specifications for an attribute number.
8140 * Note that this table is used by ALL buffers. This is required because the
8141 * GUI can redraw at any time for any buffer.
8142 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008143static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008144
8145#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8146
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008147static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008148
8149#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8150
8151#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008152static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008153
8154#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8155#endif
8156
8157/*
8158 * Return the attr number for a set of colors and font.
8159 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8160 * if the combination is new.
8161 * Return 0 for error (no more room).
8162 */
8163 static int
8164get_attr_entry(table, aep)
8165 garray_T *table;
8166 attrentry_T *aep;
8167{
8168 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008169 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008170 static int recursive = FALSE;
8171
8172 /*
8173 * Init the table, in case it wasn't done yet.
8174 */
8175 table->ga_itemsize = sizeof(attrentry_T);
8176 table->ga_growsize = 7;
8177
8178 /*
8179 * Try to find an entry with the same specifications.
8180 */
8181 for (i = 0; i < table->ga_len; ++i)
8182 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008183 taep = &(((attrentry_T *)table->ga_data)[i]);
8184 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008185 && (
8186#ifdef FEAT_GUI
8187 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008188 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8189 && aep->ae_u.gui.bg_color
8190 == taep->ae_u.gui.bg_color
8191 && aep->ae_u.gui.sp_color
8192 == taep->ae_u.gui.sp_color
8193 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008194# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008195 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008196# endif
8197 ))
8198 ||
8199#endif
8200 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008201 && (aep->ae_u.term.start == NULL)
8202 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008203 && (aep->ae_u.term.start == NULL
8204 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008205 taep->ae_u.term.start) == 0)
8206 && (aep->ae_u.term.stop == NULL)
8207 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008208 && (aep->ae_u.term.stop == NULL
8209 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008210 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008211 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008212 && aep->ae_u.cterm.fg_color
8213 == taep->ae_u.cterm.fg_color
8214 && aep->ae_u.cterm.bg_color
8215 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008216 ))
8217
8218 return i + ATTR_OFF;
8219 }
8220
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008221 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008222 {
8223 /*
8224 * Running out of attribute entries! remove all attributes, and
8225 * compute new ones for all groups.
8226 * When called recursively, we are really out of numbers.
8227 */
8228 if (recursive)
8229 {
8230 EMSG(_("E424: Too many different highlighting attributes in use"));
8231 return 0;
8232 }
8233 recursive = TRUE;
8234
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008235 clear_hl_tables();
8236
Bram Moolenaar071d4272004-06-13 20:20:40 +00008237 must_redraw = CLEAR;
8238
8239 for (i = 0; i < highlight_ga.ga_len; ++i)
8240 set_hl_attr(i);
8241
8242 recursive = FALSE;
8243 }
8244
8245 /*
8246 * This is a new combination of colors and font, add an entry.
8247 */
8248 if (ga_grow(table, 1) == FAIL)
8249 return 0;
8250
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008251 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8252 vim_memset(taep, 0, sizeof(attrentry_T));
8253 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008254#ifdef FEAT_GUI
8255 if (table == &gui_attr_table)
8256 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008257 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8258 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8259 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8260 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008261# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008262 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008263# endif
8264 }
8265#endif
8266 if (table == &term_attr_table)
8267 {
8268 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008269 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008270 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008271 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008272 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008273 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008274 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008275 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008276 }
8277 else if (table == &cterm_attr_table)
8278 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008279 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8280 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008281 }
8282 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008283 return (table->ga_len - 1 + ATTR_OFF);
8284}
8285
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008286/*
8287 * Clear all highlight tables.
8288 */
8289 void
8290clear_hl_tables()
8291{
8292 int i;
8293 attrentry_T *taep;
8294
8295#ifdef FEAT_GUI
8296 ga_clear(&gui_attr_table);
8297#endif
8298 for (i = 0; i < term_attr_table.ga_len; ++i)
8299 {
8300 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8301 vim_free(taep->ae_u.term.start);
8302 vim_free(taep->ae_u.term.stop);
8303 }
8304 ga_clear(&term_attr_table);
8305 ga_clear(&cterm_attr_table);
8306}
8307
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008308#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008309/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008310 * Combine special attributes (e.g., for spelling) with other attributes
8311 * (e.g., for syntax highlighting).
8312 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008313 * This creates a new group when required.
8314 * Since we expect there to be few spelling mistakes we don't cache the
8315 * result.
8316 * Return the resulting attributes.
8317 */
8318 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008319hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008320 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008321 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008322{
8323 attrentry_T *char_aep = NULL;
8324 attrentry_T *spell_aep;
8325 attrentry_T new_en;
8326
8327 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008328 return prim_attr;
8329 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8330 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008331#ifdef FEAT_GUI
8332 if (gui.in_use)
8333 {
8334 if (char_attr > HL_ALL)
8335 char_aep = syn_gui_attr2entry(char_attr);
8336 if (char_aep != NULL)
8337 new_en = *char_aep;
8338 else
8339 {
8340 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008341 new_en.ae_u.gui.fg_color = INVALCOLOR;
8342 new_en.ae_u.gui.bg_color = INVALCOLOR;
8343 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008344 if (char_attr <= HL_ALL)
8345 new_en.ae_attr = char_attr;
8346 }
8347
Bram Moolenaar30abd282005-06-22 22:35:10 +00008348 if (prim_attr <= HL_ALL)
8349 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008350 else
8351 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008352 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008353 if (spell_aep != NULL)
8354 {
8355 new_en.ae_attr |= spell_aep->ae_attr;
8356 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8357 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8358 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8359 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8360 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8361 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8362 if (spell_aep->ae_u.gui.font != NOFONT)
8363 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8364# ifdef FEAT_XFONTSET
8365 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8366 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8367# endif
8368 }
8369 }
8370 return get_attr_entry(&gui_attr_table, &new_en);
8371 }
8372#endif
8373
8374 if (t_colors > 1)
8375 {
8376 if (char_attr > HL_ALL)
8377 char_aep = syn_cterm_attr2entry(char_attr);
8378 if (char_aep != NULL)
8379 new_en = *char_aep;
8380 else
8381 {
8382 vim_memset(&new_en, 0, sizeof(new_en));
8383 if (char_attr <= HL_ALL)
8384 new_en.ae_attr = char_attr;
8385 }
8386
Bram Moolenaar30abd282005-06-22 22:35:10 +00008387 if (prim_attr <= HL_ALL)
8388 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008389 else
8390 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008391 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008392 if (spell_aep != NULL)
8393 {
8394 new_en.ae_attr |= spell_aep->ae_attr;
8395 if (spell_aep->ae_u.cterm.fg_color > 0)
8396 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8397 if (spell_aep->ae_u.cterm.bg_color > 0)
8398 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8399 }
8400 }
8401 return get_attr_entry(&cterm_attr_table, &new_en);
8402 }
8403
8404 if (char_attr > HL_ALL)
8405 char_aep = syn_term_attr2entry(char_attr);
8406 if (char_aep != NULL)
8407 new_en = *char_aep;
8408 else
8409 {
8410 vim_memset(&new_en, 0, sizeof(new_en));
8411 if (char_attr <= HL_ALL)
8412 new_en.ae_attr = char_attr;
8413 }
8414
Bram Moolenaar30abd282005-06-22 22:35:10 +00008415 if (prim_attr <= HL_ALL)
8416 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008417 else
8418 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008419 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008420 if (spell_aep != NULL)
8421 {
8422 new_en.ae_attr |= spell_aep->ae_attr;
8423 if (spell_aep->ae_u.term.start != NULL)
8424 {
8425 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8426 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8427 }
8428 }
8429 }
8430 return get_attr_entry(&term_attr_table, &new_en);
8431}
8432#endif
8433
Bram Moolenaar071d4272004-06-13 20:20:40 +00008434#ifdef FEAT_GUI
8435
8436 attrentry_T *
8437syn_gui_attr2entry(attr)
8438 int attr;
8439{
8440 attr -= ATTR_OFF;
8441 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8442 return NULL;
8443 return &(GUI_ATTR_ENTRY(attr));
8444}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008445#endif /* FEAT_GUI */
8446
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008447/*
8448 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8449 * Only to be used when "attr" > HL_ALL.
8450 */
8451 int
8452syn_attr2attr(attr)
8453 int attr;
8454{
8455 attrentry_T *aep;
8456
8457#ifdef FEAT_GUI
8458 if (gui.in_use)
8459 aep = syn_gui_attr2entry(attr);
8460 else
8461#endif
8462 if (t_colors > 1)
8463 aep = syn_cterm_attr2entry(attr);
8464 else
8465 aep = syn_term_attr2entry(attr);
8466
8467 if (aep == NULL) /* highlighting not set */
8468 return 0;
8469 return aep->ae_attr;
8470}
8471
8472
Bram Moolenaar071d4272004-06-13 20:20:40 +00008473 attrentry_T *
8474syn_term_attr2entry(attr)
8475 int attr;
8476{
8477 attr -= ATTR_OFF;
8478 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8479 return NULL;
8480 return &(TERM_ATTR_ENTRY(attr));
8481}
8482
8483 attrentry_T *
8484syn_cterm_attr2entry(attr)
8485 int attr;
8486{
8487 attr -= ATTR_OFF;
8488 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8489 return NULL;
8490 return &(CTERM_ATTR_ENTRY(attr));
8491}
8492
8493#define LIST_ATTR 1
8494#define LIST_STRING 2
8495#define LIST_INT 3
8496
8497 static void
8498highlight_list_one(id)
8499 int id;
8500{
8501 struct hl_group *sgp;
8502 int didh = FALSE;
8503
8504 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8505
8506 didh = highlight_list_arg(id, didh, LIST_ATTR,
8507 sgp->sg_term, NULL, "term");
8508 didh = highlight_list_arg(id, didh, LIST_STRING,
8509 0, sgp->sg_start, "start");
8510 didh = highlight_list_arg(id, didh, LIST_STRING,
8511 0, sgp->sg_stop, "stop");
8512
8513 didh = highlight_list_arg(id, didh, LIST_ATTR,
8514 sgp->sg_cterm, NULL, "cterm");
8515 didh = highlight_list_arg(id, didh, LIST_INT,
8516 sgp->sg_cterm_fg, NULL, "ctermfg");
8517 didh = highlight_list_arg(id, didh, LIST_INT,
8518 sgp->sg_cterm_bg, NULL, "ctermbg");
8519
Bram Moolenaar61623362010-07-14 22:04:22 +02008520#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008521 didh = highlight_list_arg(id, didh, LIST_ATTR,
8522 sgp->sg_gui, NULL, "gui");
8523 didh = highlight_list_arg(id, didh, LIST_STRING,
8524 0, sgp->sg_gui_fg_name, "guifg");
8525 didh = highlight_list_arg(id, didh, LIST_STRING,
8526 0, sgp->sg_gui_bg_name, "guibg");
8527 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008528 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008529#endif
8530#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008531 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008532 0, sgp->sg_font_name, "font");
8533#endif
8534
Bram Moolenaar661b1822005-07-28 22:36:45 +00008535 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008536 {
8537 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008538 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008539 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8540 msg_putchar(' ');
8541 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8542 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008543
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008544 if (!didh)
8545 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008546#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008547 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008548 last_set_msg(sgp->sg_scriptID);
8549#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008550}
8551
8552 static int
8553highlight_list_arg(id, didh, type, iarg, sarg, name)
8554 int id;
8555 int didh;
8556 int type;
8557 int iarg;
8558 char_u *sarg;
8559 char *name;
8560{
8561 char_u buf[100];
8562 char_u *ts;
8563 int i;
8564
Bram Moolenaar661b1822005-07-28 22:36:45 +00008565 if (got_int)
8566 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008567 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8568 {
8569 ts = buf;
8570 if (type == LIST_INT)
8571 sprintf((char *)buf, "%d", iarg - 1);
8572 else if (type == LIST_STRING)
8573 ts = sarg;
8574 else /* type == LIST_ATTR */
8575 {
8576 buf[0] = NUL;
8577 for (i = 0; hl_attr_table[i] != 0; ++i)
8578 {
8579 if (iarg & hl_attr_table[i])
8580 {
8581 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008582 vim_strcat(buf, (char_u *)",", 100);
8583 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008584 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8585 }
8586 }
8587 }
8588
8589 (void)syn_list_header(didh,
8590 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8591 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008592 if (!got_int)
8593 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008594 if (*name != NUL)
8595 {
8596 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8597 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8598 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008599 msg_outtrans(ts);
8600 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008601 }
8602 return didh;
8603}
8604
8605#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8606/*
8607 * Return "1" if highlight group "id" has attribute "flag".
8608 * Return NULL otherwise.
8609 */
8610 char_u *
8611highlight_has_attr(id, flag, modec)
8612 int id;
8613 int flag;
8614 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8615{
8616 int attr;
8617
8618 if (id <= 0 || id > highlight_ga.ga_len)
8619 return NULL;
8620
Bram Moolenaar61623362010-07-14 22:04:22 +02008621#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008622 if (modec == 'g')
8623 attr = HL_TABLE()[id - 1].sg_gui;
8624 else
8625#endif
8626 if (modec == 'c')
8627 attr = HL_TABLE()[id - 1].sg_cterm;
8628 else
8629 attr = HL_TABLE()[id - 1].sg_term;
8630
8631 if (attr & flag)
8632 return (char_u *)"1";
8633 return NULL;
8634}
8635#endif
8636
8637#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8638/*
8639 * Return color name of highlight group "id".
8640 */
8641 char_u *
8642highlight_color(id, what, modec)
8643 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008644 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008645 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8646{
8647 static char_u name[20];
8648 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008649 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008650 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008651 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008652
8653 if (id <= 0 || id > highlight_ga.ga_len)
8654 return NULL;
8655
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008656 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008657 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008658 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008659 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008660 font = TRUE;
8661 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008662 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008663 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8664 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008665 if (modec == 'g')
8666 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008667# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008668 /* return font name */
8669 if (font)
8670 return HL_TABLE()[id - 1].sg_font_name;
8671
Bram Moolenaar071d4272004-06-13 20:20:40 +00008672 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008673 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008674 {
8675 guicolor_T color;
8676 long_u rgb;
8677 static char_u buf[10];
8678
8679 if (fg)
8680 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008681 else if (sp)
8682 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008683 else
8684 color = HL_TABLE()[id - 1].sg_gui_bg;
8685 if (color == INVALCOLOR)
8686 return NULL;
8687 rgb = gui_mch_get_rgb(color);
8688 sprintf((char *)buf, "#%02x%02x%02x",
8689 (unsigned)(rgb >> 16),
8690 (unsigned)(rgb >> 8) & 255,
8691 (unsigned)rgb & 255);
8692 return buf;
8693 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008694#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008695 if (fg)
8696 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008697 if (sp)
8698 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008699 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8700 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008701 if (font || sp)
8702 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008703 if (modec == 'c')
8704 {
8705 if (fg)
8706 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8707 else
8708 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8709 sprintf((char *)name, "%d", n);
8710 return name;
8711 }
8712 /* term doesn't have color */
8713 return NULL;
8714}
8715#endif
8716
8717#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8718 || defined(PROTO)
8719/*
8720 * Return color name of highlight group "id" as RGB value.
8721 */
8722 long_u
8723highlight_gui_color_rgb(id, fg)
8724 int id;
8725 int fg; /* TRUE = fg, FALSE = bg */
8726{
8727 guicolor_T color;
8728
8729 if (id <= 0 || id > highlight_ga.ga_len)
8730 return 0L;
8731
8732 if (fg)
8733 color = HL_TABLE()[id - 1].sg_gui_fg;
8734 else
8735 color = HL_TABLE()[id - 1].sg_gui_bg;
8736
8737 if (color == INVALCOLOR)
8738 return 0L;
8739
8740 return gui_mch_get_rgb(color);
8741}
8742#endif
8743
8744/*
8745 * Output the syntax list header.
8746 * Return TRUE when started a new line.
8747 */
8748 static int
8749syn_list_header(did_header, outlen, id)
8750 int did_header; /* did header already */
8751 int outlen; /* length of string that comes */
8752 int id; /* highlight group id */
8753{
8754 int endcol = 19;
8755 int newline = TRUE;
8756
8757 if (!did_header)
8758 {
8759 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008760 if (got_int)
8761 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008762 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8763 endcol = 15;
8764 }
8765 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008766 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008767 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008768 if (got_int)
8769 return TRUE;
8770 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008771 else
8772 {
8773 if (msg_col >= endcol) /* wrap around is like starting a new line */
8774 newline = FALSE;
8775 }
8776
8777 if (msg_col >= endcol) /* output at least one space */
8778 endcol = msg_col + 1;
8779 if (Columns <= endcol) /* avoid hang for tiny window */
8780 endcol = Columns - 1;
8781
8782 msg_advance(endcol);
8783
8784 /* Show "xxx" with the attributes. */
8785 if (!did_header)
8786 {
8787 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8788 msg_putchar(' ');
8789 }
8790
8791 return newline;
8792}
8793
8794/*
8795 * Set the attribute numbers for a highlight group.
8796 * Called after one of the attributes has changed.
8797 */
8798 static void
8799set_hl_attr(idx)
8800 int idx; /* index in array */
8801{
8802 attrentry_T at_en;
8803 struct hl_group *sgp = HL_TABLE() + idx;
8804
8805 /* The "Normal" group doesn't need an attribute number */
8806 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8807 return;
8808
8809#ifdef FEAT_GUI
8810 /*
8811 * For the GUI mode: If there are other than "normal" highlighting
8812 * attributes, need to allocate an attr number.
8813 */
8814 if (sgp->sg_gui_fg == INVALCOLOR
8815 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008816 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008817 && sgp->sg_font == NOFONT
8818# ifdef FEAT_XFONTSET
8819 && sgp->sg_fontset == NOFONTSET
8820# endif
8821 )
8822 {
8823 sgp->sg_gui_attr = sgp->sg_gui;
8824 }
8825 else
8826 {
8827 at_en.ae_attr = sgp->sg_gui;
8828 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8829 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008830 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008831 at_en.ae_u.gui.font = sgp->sg_font;
8832# ifdef FEAT_XFONTSET
8833 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8834# endif
8835 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8836 }
8837#endif
8838 /*
8839 * For the term mode: If there are other than "normal" highlighting
8840 * attributes, need to allocate an attr number.
8841 */
8842 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8843 sgp->sg_term_attr = sgp->sg_term;
8844 else
8845 {
8846 at_en.ae_attr = sgp->sg_term;
8847 at_en.ae_u.term.start = sgp->sg_start;
8848 at_en.ae_u.term.stop = sgp->sg_stop;
8849 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8850 }
8851
8852 /*
8853 * For the color term mode: If there are other than "normal"
8854 * highlighting attributes, need to allocate an attr number.
8855 */
8856 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8857 sgp->sg_cterm_attr = sgp->sg_cterm;
8858 else
8859 {
8860 at_en.ae_attr = sgp->sg_cterm;
8861 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8862 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8863 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8864 }
8865}
8866
8867/*
8868 * Lookup a highlight group name and return it's ID.
8869 * If it is not found, 0 is returned.
8870 */
8871 int
8872syn_name2id(name)
8873 char_u *name;
8874{
8875 int i;
8876 char_u name_u[200];
8877
8878 /* Avoid using stricmp() too much, it's slow on some systems */
8879 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8880 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008881 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008882 vim_strup(name_u);
8883 for (i = highlight_ga.ga_len; --i >= 0; )
8884 if (HL_TABLE()[i].sg_name_u != NULL
8885 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8886 break;
8887 return i + 1;
8888}
8889
8890#if defined(FEAT_EVAL) || defined(PROTO)
8891/*
8892 * Return TRUE if highlight group "name" exists.
8893 */
8894 int
8895highlight_exists(name)
8896 char_u *name;
8897{
8898 return (syn_name2id(name) > 0);
8899}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008900
8901# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8902/*
8903 * Return the name of highlight group "id".
8904 * When not a valid ID return an empty string.
8905 */
8906 char_u *
8907syn_id2name(id)
8908 int id;
8909{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00008910 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008911 return (char_u *)"";
8912 return HL_TABLE()[id - 1].sg_name;
8913}
8914# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008915#endif
8916
8917/*
8918 * Like syn_name2id(), but take a pointer + length argument.
8919 */
8920 int
8921syn_namen2id(linep, len)
8922 char_u *linep;
8923 int len;
8924{
8925 char_u *name;
8926 int id = 0;
8927
8928 name = vim_strnsave(linep, len);
8929 if (name != NULL)
8930 {
8931 id = syn_name2id(name);
8932 vim_free(name);
8933 }
8934 return id;
8935}
8936
8937/*
8938 * Find highlight group name in the table and return it's ID.
8939 * The argument is a pointer to the name and the length of the name.
8940 * If it doesn't exist yet, a new entry is created.
8941 * Return 0 for failure.
8942 */
8943 int
8944syn_check_group(pp, len)
8945 char_u *pp;
8946 int len;
8947{
8948 int id;
8949 char_u *name;
8950
8951 name = vim_strnsave(pp, len);
8952 if (name == NULL)
8953 return 0;
8954
8955 id = syn_name2id(name);
8956 if (id == 0) /* doesn't exist yet */
8957 id = syn_add_group(name);
8958 else
8959 vim_free(name);
8960 return id;
8961}
8962
8963/*
8964 * Add new highlight group and return it's ID.
8965 * "name" must be an allocated string, it will be consumed.
8966 * Return 0 for failure.
8967 */
8968 static int
8969syn_add_group(name)
8970 char_u *name;
8971{
8972 char_u *p;
8973
8974 /* Check that the name is ASCII letters, digits and underscore. */
8975 for (p = name; *p != NUL; ++p)
8976 {
8977 if (!vim_isprintc(*p))
8978 {
8979 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008980 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008981 return 0;
8982 }
8983 else if (!ASCII_ISALNUM(*p) && *p != '_')
8984 {
8985 /* This is an error, but since there previously was no check only
8986 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008987 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008988 MSG(_("W18: Invalid character in group name"));
8989 break;
8990 }
8991 }
8992
8993 /*
8994 * First call for this growarray: init growing array.
8995 */
8996 if (highlight_ga.ga_data == NULL)
8997 {
8998 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8999 highlight_ga.ga_growsize = 10;
9000 }
9001
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009002 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009003 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009004 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009005 vim_free(name);
9006 return 0;
9007 }
9008
Bram Moolenaar071d4272004-06-13 20:20:40 +00009009 /*
9010 * Make room for at least one other syntax_highlight entry.
9011 */
9012 if (ga_grow(&highlight_ga, 1) == FAIL)
9013 {
9014 vim_free(name);
9015 return 0;
9016 }
9017
9018 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9019 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9020 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9021#ifdef FEAT_GUI
9022 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9023 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009024 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009025#endif
9026 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009027
9028 return highlight_ga.ga_len; /* ID is index plus one */
9029}
9030
9031/*
9032 * When, just after calling syn_add_group(), an error is discovered, this
9033 * function deletes the new name.
9034 */
9035 static void
9036syn_unadd_group()
9037{
9038 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009039 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9040 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9041}
9042
9043/*
9044 * Translate a group ID to highlight attributes.
9045 */
9046 int
9047syn_id2attr(hl_id)
9048 int hl_id;
9049{
9050 int attr;
9051 struct hl_group *sgp;
9052
9053 hl_id = syn_get_final_id(hl_id);
9054 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9055
9056#ifdef FEAT_GUI
9057 /*
9058 * Only use GUI attr when the GUI is being used.
9059 */
9060 if (gui.in_use)
9061 attr = sgp->sg_gui_attr;
9062 else
9063#endif
9064 if (t_colors > 1)
9065 attr = sgp->sg_cterm_attr;
9066 else
9067 attr = sgp->sg_term_attr;
9068
9069 return attr;
9070}
9071
9072#ifdef FEAT_GUI
9073/*
9074 * Get the GUI colors and attributes for a group ID.
9075 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9076 */
9077 int
9078syn_id2colors(hl_id, fgp, bgp)
9079 int hl_id;
9080 guicolor_T *fgp;
9081 guicolor_T *bgp;
9082{
9083 struct hl_group *sgp;
9084
9085 hl_id = syn_get_final_id(hl_id);
9086 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9087
9088 *fgp = sgp->sg_gui_fg;
9089 *bgp = sgp->sg_gui_bg;
9090 return sgp->sg_gui;
9091}
9092#endif
9093
9094/*
9095 * Translate a group ID to the final group ID (following links).
9096 */
9097 int
9098syn_get_final_id(hl_id)
9099 int hl_id;
9100{
9101 int count;
9102 struct hl_group *sgp;
9103
9104 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9105 return 0; /* Can be called from eval!! */
9106
9107 /*
9108 * Follow links until there is no more.
9109 * Look out for loops! Break after 100 links.
9110 */
9111 for (count = 100; --count >= 0; )
9112 {
9113 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9114 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9115 break;
9116 hl_id = sgp->sg_link;
9117 }
9118
9119 return hl_id;
9120}
9121
9122#ifdef FEAT_GUI
9123/*
9124 * Call this function just after the GUI has started.
9125 * It finds the font and color handles for the highlighting groups.
9126 */
9127 void
9128highlight_gui_started()
9129{
9130 int idx;
9131
9132 /* First get the colors from the "Normal" and "Menu" group, if set */
9133 set_normal_colors();
9134
9135 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9136 gui_do_one_color(idx, FALSE, FALSE);
9137
9138 highlight_changed();
9139}
9140
9141 static void
9142gui_do_one_color(idx, do_menu, do_tooltip)
9143 int idx;
9144 int do_menu; /* TRUE: might set the menu font */
9145 int do_tooltip; /* TRUE: might set the tooltip font */
9146{
9147 int didit = FALSE;
9148
9149 if (HL_TABLE()[idx].sg_font_name != NULL)
9150 {
9151 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
9152 do_tooltip);
9153 didit = TRUE;
9154 }
9155 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9156 {
9157 HL_TABLE()[idx].sg_gui_fg =
9158 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9159 didit = TRUE;
9160 }
9161 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9162 {
9163 HL_TABLE()[idx].sg_gui_bg =
9164 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9165 didit = TRUE;
9166 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009167 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9168 {
9169 HL_TABLE()[idx].sg_gui_sp =
9170 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9171 didit = TRUE;
9172 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009173 if (didit) /* need to get a new attr number */
9174 set_hl_attr(idx);
9175}
9176
9177#endif
9178
9179/*
9180 * Translate the 'highlight' option into attributes in highlight_attr[] and
9181 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9182 * corresponding highlights to use on top of HLF_SNC is computed.
9183 * Called only when the 'highlight' option has been changed and upon first
9184 * screen redraw after any :highlight command.
9185 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9186 */
9187 int
9188highlight_changed()
9189{
9190 int hlf;
9191 int i;
9192 char_u *p;
9193 int attr;
9194 char_u *end;
9195 int id;
9196#ifdef USER_HIGHLIGHT
9197 char_u userhl[10];
9198# ifdef FEAT_STL_OPT
9199 int id_SNC = -1;
9200 int id_S = -1;
9201 int hlcnt;
9202# endif
9203#endif
9204 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9205
9206 need_highlight_changed = FALSE;
9207
9208 /*
9209 * Clear all attributes.
9210 */
9211 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9212 highlight_attr[hlf] = 0;
9213
9214 /*
9215 * First set all attributes to their default value.
9216 * Then use the attributes from the 'highlight' option.
9217 */
9218 for (i = 0; i < 2; ++i)
9219 {
9220 if (i)
9221 p = p_hl;
9222 else
9223 p = get_highlight_default();
9224 if (p == NULL) /* just in case */
9225 continue;
9226
9227 while (*p)
9228 {
9229 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9230 if (hl_flags[hlf] == *p)
9231 break;
9232 ++p;
9233 if (hlf == (int)HLF_COUNT || *p == NUL)
9234 return FAIL;
9235
9236 /*
9237 * Allow several hl_flags to be combined, like "bu" for
9238 * bold-underlined.
9239 */
9240 attr = 0;
9241 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9242 {
9243 if (vim_iswhite(*p)) /* ignore white space */
9244 continue;
9245
9246 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9247 return FAIL;
9248
9249 switch (*p)
9250 {
9251 case 'b': attr |= HL_BOLD;
9252 break;
9253 case 'i': attr |= HL_ITALIC;
9254 break;
9255 case '-':
9256 case 'n': /* no highlighting */
9257 break;
9258 case 'r': attr |= HL_INVERSE;
9259 break;
9260 case 's': attr |= HL_STANDOUT;
9261 break;
9262 case 'u': attr |= HL_UNDERLINE;
9263 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009264 case 'c': attr |= HL_UNDERCURL;
9265 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009266 case ':': ++p; /* highlight group name */
9267 if (attr || *p == NUL) /* no combinations */
9268 return FAIL;
9269 end = vim_strchr(p, ',');
9270 if (end == NULL)
9271 end = p + STRLEN(p);
9272 id = syn_check_group(p, (int)(end - p));
9273 if (id == 0)
9274 return FAIL;
9275 attr = syn_id2attr(id);
9276 p = end - 1;
9277#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9278 if (hlf == (int)HLF_SNC)
9279 id_SNC = syn_get_final_id(id);
9280 else if (hlf == (int)HLF_S)
9281 id_S = syn_get_final_id(id);
9282#endif
9283 break;
9284 default: return FAIL;
9285 }
9286 }
9287 highlight_attr[hlf] = attr;
9288
9289 p = skip_to_option_part(p); /* skip comma and spaces */
9290 }
9291 }
9292
9293#ifdef USER_HIGHLIGHT
9294 /* Setup the user highlights
9295 *
9296 * Temporarily utilize 10 more hl entries. Have to be in there
9297 * simultaneously in case of table overflows in get_attr_entry()
9298 */
9299# ifdef FEAT_STL_OPT
9300 if (ga_grow(&highlight_ga, 10) == FAIL)
9301 return FAIL;
9302 hlcnt = highlight_ga.ga_len;
9303 if (id_S == 0)
9304 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009305 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009306 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9307 id_S = hlcnt + 10;
9308 }
9309# endif
9310 for (i = 0; i < 9; i++)
9311 {
9312 sprintf((char *)userhl, "User%d", i + 1);
9313 id = syn_name2id(userhl);
9314 if (id == 0)
9315 {
9316 highlight_user[i] = 0;
9317# ifdef FEAT_STL_OPT
9318 highlight_stlnc[i] = 0;
9319# endif
9320 }
9321 else
9322 {
9323# ifdef FEAT_STL_OPT
9324 struct hl_group *hlt = HL_TABLE();
9325# endif
9326
9327 highlight_user[i] = syn_id2attr(id);
9328# ifdef FEAT_STL_OPT
9329 if (id_SNC == 0)
9330 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009331 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009332 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9333 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009334# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009335 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9336# endif
9337 }
9338 else
9339 mch_memmove(&hlt[hlcnt + i],
9340 &hlt[id_SNC - 1],
9341 sizeof(struct hl_group));
9342 hlt[hlcnt + i].sg_link = 0;
9343
9344 /* Apply difference between UserX and HLF_S to HLF_SNC */
9345 hlt[hlcnt + i].sg_term ^=
9346 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9347 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9348 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9349 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9350 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9351 hlt[hlcnt + i].sg_cterm ^=
9352 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9353 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9354 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9355 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9356 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009357# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009358 hlt[hlcnt + i].sg_gui ^=
9359 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009360# endif
9361# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009362 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9363 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9364 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9365 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009366 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9367 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009368 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9369 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9370# ifdef FEAT_XFONTSET
9371 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9372 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9373# endif
9374# endif
9375 highlight_ga.ga_len = hlcnt + i + 1;
9376 set_hl_attr(hlcnt + i); /* At long last we can apply */
9377 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9378# endif
9379 }
9380 }
9381# ifdef FEAT_STL_OPT
9382 highlight_ga.ga_len = hlcnt;
9383# endif
9384
9385#endif /* USER_HIGHLIGHT */
9386
9387 return OK;
9388}
9389
Bram Moolenaar4f688582007-07-24 12:34:30 +00009390#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009391
9392static void highlight_list __ARGS((void));
9393static void highlight_list_two __ARGS((int cnt, int attr));
9394
9395/*
9396 * Handle command line completion for :highlight command.
9397 */
9398 void
9399set_context_in_highlight_cmd(xp, arg)
9400 expand_T *xp;
9401 char_u *arg;
9402{
9403 char_u *p;
9404
9405 /* Default: expand group names */
9406 xp->xp_context = EXPAND_HIGHLIGHT;
9407 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009408 include_link = 2;
9409 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009410
9411 /* (part of) subcommand already typed */
9412 if (*arg != NUL)
9413 {
9414 p = skiptowhite(arg);
9415 if (*p != NUL) /* past "default" or group name */
9416 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009417 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009418 if (STRNCMP("default", arg, p - arg) == 0)
9419 {
9420 arg = skipwhite(p);
9421 xp->xp_pattern = arg;
9422 p = skiptowhite(arg);
9423 }
9424 if (*p != NUL) /* past group name */
9425 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009426 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009427 if (arg[1] == 'i' && arg[0] == 'N')
9428 highlight_list();
9429 if (STRNCMP("link", arg, p - arg) == 0
9430 || STRNCMP("clear", arg, p - arg) == 0)
9431 {
9432 xp->xp_pattern = skipwhite(p);
9433 p = skiptowhite(xp->xp_pattern);
9434 if (*p != NUL) /* past first group name */
9435 {
9436 xp->xp_pattern = skipwhite(p);
9437 p = skiptowhite(xp->xp_pattern);
9438 }
9439 }
9440 if (*p != NUL) /* past group name(s) */
9441 xp->xp_context = EXPAND_NOTHING;
9442 }
9443 }
9444 }
9445}
9446
9447/*
9448 * List highlighting matches in a nice way.
9449 */
9450 static void
9451highlight_list()
9452{
9453 int i;
9454
9455 for (i = 10; --i >= 0; )
9456 highlight_list_two(i, hl_attr(HLF_D));
9457 for (i = 40; --i >= 0; )
9458 highlight_list_two(99, 0);
9459}
9460
9461 static void
9462highlight_list_two(cnt, attr)
9463 int cnt;
9464 int attr;
9465{
9466 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9467 msg_clr_eos();
9468 out_flush();
9469 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9470}
9471
9472#endif /* FEAT_CMDL_COMPL */
9473
9474#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9475 || defined(FEAT_SIGNS) || defined(PROTO)
9476/*
9477 * Function given to ExpandGeneric() to obtain the list of group names.
9478 * Also used for synIDattr() function.
9479 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009480 char_u *
9481get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009482 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009483 int idx;
9484{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009485#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009486 if (idx == highlight_ga.ga_len && include_none != 0)
9487 return (char_u *)"none";
9488 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009489 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009490 if (idx == highlight_ga.ga_len + include_none + include_default
9491 && include_link != 0)
9492 return (char_u *)"link";
9493 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9494 && include_link != 0)
9495 return (char_u *)"clear";
9496#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009497 if (idx < 0 || idx >= highlight_ga.ga_len)
9498 return NULL;
9499 return HL_TABLE()[idx].sg_name;
9500}
9501#endif
9502
Bram Moolenaar4f688582007-07-24 12:34:30 +00009503#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009504/*
9505 * Free all the highlight group fonts.
9506 * Used when quitting for systems which need it.
9507 */
9508 void
9509free_highlight_fonts()
9510{
9511 int idx;
9512
9513 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9514 {
9515 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9516 HL_TABLE()[idx].sg_font = NOFONT;
9517# ifdef FEAT_XFONTSET
9518 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9519 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9520# endif
9521 }
9522
9523 gui_mch_free_font(gui.norm_font);
9524# ifdef FEAT_XFONTSET
9525 gui_mch_free_fontset(gui.fontset);
9526# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009527# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009528 gui_mch_free_font(gui.bold_font);
9529 gui_mch_free_font(gui.ital_font);
9530 gui_mch_free_font(gui.boldital_font);
9531# endif
9532}
9533#endif
9534
9535/**************************************
9536 * End of Highlighting stuff *
9537 **************************************/