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