blob: a6741cf17ee2efeb4b65349507d38cd4e0981b15 [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 */
38 int sg_gui; /* "gui=" highlighting attributes */
39 guicolor_T sg_gui_fg; /* GUI foreground color handle */
40 char_u *sg_gui_fg_name;/* GUI foreground color name */
41 guicolor_T sg_gui_bg; /* GUI background color handle */
42 char_u *sg_gui_bg_name;/* GUI background color name */
Bram Moolenaare2cc9702005-03-15 22:43:58 +000043 guicolor_T sg_gui_sp; /* GUI special color handle */
44 char_u *sg_gui_sp_name;/* GUI special color name */
Bram Moolenaar071d4272004-06-13 20:20:40 +000045 GuiFont sg_font; /* GUI font handle */
46#ifdef FEAT_XFONTSET
47 GuiFontset sg_fontset; /* GUI fontset handle */
48#endif
49 char_u *sg_font_name; /* GUI font or fontset name */
50 int sg_gui_attr; /* Screen attr for GUI mode */
51#endif
52 int sg_link; /* link to this highlight group ID */
53 int sg_set; /* combination of SG_* flags */
54};
55
56#define SG_TERM 1 /* term has been set */
57#define SG_CTERM 2 /* cterm has been set */
58#define SG_GUI 4 /* gui has been set */
59#define SG_LINK 8 /* link has been set */
60
61static garray_T highlight_ga; /* highlight groups for 'highlight' option */
62
63#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
64
65#ifdef FEAT_CMDL_COMPL
66static int include_default = FALSE; /* include "default" for expansion */
67static int include_link = FALSE; /* include "link" for expansion */
68#endif
69
70/*
71 * The "term", "cterm" and "gui" arguments can be any combination of the
72 * following names, separated by commas (but no spaces!).
73 */
74static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000075 {"bold", "standout", "underline", "undercurl",
76 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000077static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000078 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000079
80static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
81static void syn_unadd_group __ARGS((void));
82static void set_hl_attr __ARGS((int idx));
83static void highlight_list_one __ARGS((int id));
84static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
85static int syn_add_group __ARGS((char_u *name));
86static int syn_list_header __ARGS((int did_header, int outlen, int id));
87static int hl_has_settings __ARGS((int idx, int check_link));
88static void highlight_clear __ARGS((int idx));
89
90#ifdef FEAT_GUI
91static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
92static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
93static guicolor_T color_name2handle __ARGS((char_u *name));
94static GuiFont font_name2handle __ARGS((char_u *name));
95# ifdef FEAT_XFONTSET
96static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
97# endif
98static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
99#endif
100
101/*
102 * An attribute number is the index in attr_table plus ATTR_OFF.
103 */
104#define ATTR_OFF (HL_ALL + 1)
105
106#if defined(FEAT_SYN_HL) || defined(PROTO)
107
108#define SYN_NAMELEN 50 /* maximum length of a syntax name */
109
110/* different types of offsets that are possible */
111#define SPO_MS_OFF 0 /* match start offset */
112#define SPO_ME_OFF 1 /* match end offset */
113#define SPO_HS_OFF 2 /* highl. start offset */
114#define SPO_HE_OFF 3 /* highl. end offset */
115#define SPO_RS_OFF 4 /* region start offset */
116#define SPO_RE_OFF 5 /* region end offset */
117#define SPO_LC_OFF 6 /* leading context offset */
118#define SPO_COUNT 7
119
120static char *(spo_name_tab[SPO_COUNT]) =
121 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
122
123/*
124 * The patterns that are being searched for are stored in a syn_pattern.
125 * A match item consists of one pattern.
126 * A start/end item consists of n start patterns and m end patterns.
127 * A start/skip/end item consists of n start patterns, one skip pattern and m
128 * end patterns.
129 * For the latter two, the patterns are always consecutive: start-skip-end.
130 *
131 * A character offset can be given for the matched text (_m_start and _m_end)
132 * and for the actually highlighted text (_h_start and _h_end).
133 */
134typedef struct syn_pattern
135{
136 char sp_type; /* see SPTYPE_ defines below */
137 char sp_syncing; /* this item used for syncing */
138 short sp_flags; /* see HL_ defines below */
139 struct sp_syn sp_syn; /* struct passed to in_id_list() */
140 short sp_syn_match_id; /* highlight group ID of pattern */
141 char_u *sp_pattern; /* regexp to match, pattern */
142 regprog_T *sp_prog; /* regexp to match, program */
143 int sp_ic; /* ignore-case flag for sp_prog */
144 short sp_off_flags; /* see below */
145 int sp_offsets[SPO_COUNT]; /* offsets */
146 short *sp_cont_list; /* cont. group IDs, if non-zero */
147 short *sp_next_list; /* next group IDs, if non-zero */
148 int sp_sync_idx; /* sync item index (syncing only) */
149 int sp_line_id; /* ID of last line where tried */
150 int sp_startcol; /* next match in sp_line_id line */
151} synpat_T;
152
153/* The sp_off_flags are computed like this:
154 * offset from the start of the matched text: (1 << SPO_XX_OFF)
155 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
156 * When both are present, only one is used.
157 */
158
159#define SPTYPE_MATCH 1 /* match keyword with this group ID */
160#define SPTYPE_START 2 /* match a regexp, start of item */
161#define SPTYPE_END 3 /* match a regexp, end of item */
162#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
163
164#define HL_CONTAINED 0x01 /* not used on toplevel */
165#define HL_TRANSP 0x02 /* has no highlighting */
166#define HL_ONELINE 0x04 /* match within one line only */
167#define HL_HAS_EOL 0x08 /* end pattern that matches with $ */
168#define HL_SYNC_HERE 0x10 /* sync point after this item (syncing only) */
169#define HL_SYNC_THERE 0x20 /* sync point at current line (syncing only) */
170#define HL_MATCH 0x40 /* use match ID instead of item ID */
171#define HL_SKIPNL 0x80 /* nextgroup can skip newlines */
172#define HL_SKIPWHITE 0x100 /* nextgroup can skip white space */
173#define HL_SKIPEMPTY 0x200 /* nextgroup can skip empty lines */
174#define HL_KEEPEND 0x400 /* end match always kept */
175#define HL_EXCLUDENL 0x800 /* exclude NL from match */
176#define HL_DISPLAY 0x1000 /* only used for displaying, not syncing */
177#define HL_FOLD 0x2000 /* define fold */
178#define HL_EXTEND 0x4000 /* ignore a keepend */
179/* These don't fit in a short, thus can't be used for syntax items, only for
180 * si_flags and bs_flags. */
181#define HL_MATCHCONT 0x8000 /* match continued from previous line */
182#define HL_TRANS_CONT 0x10000L /* transparent item without contains arg */
183
184#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
185
186#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
187
188/*
189 * Flags for b_syn_sync_flags:
190 */
191#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
192#define SF_MATCH 0x02 /* sync by matching a pattern */
193
194#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
195
Bram Moolenaar071d4272004-06-13 20:20:40 +0000196#define MAXKEYWLEN 80 /* maximum length of a keyword */
197
198/*
199 * The attributes of the syntax item that has been recognized.
200 */
201static int current_attr = 0; /* attr of current syntax word */
202#ifdef FEAT_EVAL
203static int current_id = 0; /* ID of current char for syn_get_id() */
204static int current_trans_id = 0; /* idem, transparancy removed */
205#endif
206
Bram Moolenaar217ad922005-03-20 22:37:15 +0000207typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000208{
209 char_u *scl_name; /* syntax cluster name */
210 char_u *scl_name_u; /* uppercase of scl_name */
211 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000212} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000213
214/*
215 * Methods of combining two clusters
216 */
217#define CLUSTER_REPLACE 1 /* replace first list with second */
218#define CLUSTER_ADD 2 /* add second list to first */
219#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
220
Bram Moolenaar217ad922005-03-20 22:37:15 +0000221#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000222
223/*
224 * Syntax group IDs have different types:
225 * 0 - 9999 normal syntax groups
226 * 10000 - 14999 ALLBUT indicator (current_syn_inc_tag added)
227 * 15000 - 19999 TOP indicator (current_syn_inc_tag added)
228 * 20000 - 24999 CONTAINED indicator (current_syn_inc_tag added)
229 * >= 25000 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
230 */
231#define SYNID_ALLBUT 10000 /* syntax group ID for contains=ALLBUT */
232#define SYNID_TOP 15000 /* syntax group ID for contains=TOP */
233#define SYNID_CONTAINED 20000 /* syntax group ID for contains=CONTAINED */
234#define SYNID_CLUSTER 25000 /* first syntax group ID for clusters */
235
236/*
237 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
238 * expand_filename(). Most of the other syntax commands don't need it, so
239 * instead of passing it to them, we stow it here.
240 */
241static char_u **syn_cmdlinep;
242
243/*
244 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
245 * files from from leaking into ALLBUT lists, we assign a unique ID to the
246 * rules in each ":syn include"'d file.
247 */
248static int current_syn_inc_tag = 0;
249static int running_syn_inc_tag = 0;
250
251/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000252 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
253 * This avoids adding a pointer to the hashtable item.
254 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
255 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
256 * HI2KE() converts a hashitem pointer to a var pointer.
257 */
258static keyentry_T dumkey;
259#define KE2HIKEY(kp) ((kp)->keyword)
260#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
261#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
262
263/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000264 * To reduce the time spent in keepend(), remember at which level in the state
265 * stack the first item with "keepend" is present. When "-1", there is no
266 * "keepend" on the stack.
267 */
268static int keepend_level = -1;
269
270/*
271 * For the current state we need to remember more than just the idx.
272 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
273 * (The end positions have the column number of the next char)
274 */
275typedef struct state_item
276{
277 int si_idx; /* index of syntax pattern */
278 int si_id; /* highlight group ID for keywords */
279 int si_trans_id; /* idem, transparancy removed */
280 int si_m_lnum; /* lnum of the match */
281 int si_m_startcol; /* starting column of the match */
282 lpos_T si_m_endpos; /* just after end posn of the match */
283 lpos_T si_h_startpos; /* start position of the highlighting */
284 lpos_T si_h_endpos; /* end position of the highlighting */
285 lpos_T si_eoe_pos; /* end position of end pattern */
286 int si_end_idx; /* group ID for end pattern or zero */
287 int si_ends; /* if match ends before si_m_endpos */
288 int si_attr; /* attributes in this state */
289 long si_flags; /* HL_HAS_EOL flag in this state, and
290 * HL_SKIP* for si_next_list */
291 short *si_cont_list; /* list of contained groups */
292 short *si_next_list; /* nextgroup IDs after this item ends */
293 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
294 * pattern */
295} stateitem_T;
296
297#define KEYWORD_IDX -1 /* value of si_idx for keywords */
298#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
299 but contained groups */
300
301/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000302 * Struct to reduce the number of arguments to get_syn_options(), it's used
303 * very often.
304 */
305typedef struct
306{
307 int flags; /* flags for contained and transpartent */
308 int keyword; /* TRUE for ":syn keyword" */
309 int *sync_idx; /* syntax item for "grouphere" argument, NULL
310 if not allowed */
311 char has_cont_list; /* TRUE if "cont_list" can be used */
312 short *cont_list; /* group IDs for "contains" argument */
313 short *cont_in_list; /* group IDs for "containedin" argument */
314 short *next_list; /* group IDs for "nextgroup" argument */
315} syn_opt_arg_T;
316
317/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000318 * The next possible match in the current line for any pattern is remembered,
319 * to avoid having to try for a match in each column.
320 * If next_match_idx == -1, not tried (in this line) yet.
321 * If next_match_col == MAXCOL, no match found in this line.
322 * (All end positions have the column of the char after the end)
323 */
324static int next_match_col; /* column for start of next match */
325static lpos_T next_match_m_endpos; /* position for end of next match */
326static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
327static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
328static int next_match_idx; /* index of matched item */
329static long next_match_flags; /* flags for next match */
330static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
331static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
332static int next_match_end_idx; /* ID of group for end pattn or zero */
333static reg_extmatch_T *next_match_extmatch = NULL;
334
335/*
336 * A state stack is an array of integers or stateitem_T, stored in a
337 * garray_T. A state stack is invalid if it's itemsize entry is zero.
338 */
339#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
340#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
341
342/*
343 * The current state (within the line) of the recognition engine.
344 * When current_state.ga_itemsize is 0 the current state is invalid.
345 */
346static win_T *syn_win; /* current window for highlighting */
347static buf_T *syn_buf; /* current buffer for highlighting */
348static linenr_T current_lnum = 0; /* lnum of current state */
349static colnr_T current_col = 0; /* column of current state */
350static int current_state_stored = 0; /* TRUE if stored current state
351 * after setting current_finished */
352static int current_finished = 0; /* current line has been finished */
353static garray_T current_state /* current stack of state_items */
354 = {0, 0, 0, 0, NULL};
355static short *current_next_list = NULL; /* when non-zero, nextgroup list */
356static int current_next_flags = 0; /* flags for current_next_list */
357static int current_line_id = 0; /* unique number for current line */
358
359#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
360
361static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
362static int syn_match_linecont __ARGS((linenr_T lnum));
363static void syn_start_line __ARGS((void));
364static void syn_update_ends __ARGS((int startofline));
365static void syn_stack_alloc __ARGS((void));
366static int syn_stack_cleanup __ARGS((void));
367static void syn_stack_free_entry __ARGS((buf_T *buf, synstate_T *p));
368static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
369static synstate_T *store_current_state __ARGS((synstate_T *sp));
370static void load_current_state __ARGS((synstate_T *from));
371static void invalidate_current_state __ARGS((void));
372static int syn_stack_equal __ARGS((synstate_T *sp));
373static void validate_current_state __ARGS((void));
374static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar217ad922005-03-20 22:37:15 +0000375static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000376static int did_match_already __ARGS((int idx, garray_T *gap));
377static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
378static void check_state_ends __ARGS((void));
379static void update_si_attr __ARGS((int idx));
380static void check_keepend __ARGS((void));
381static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
382static short *copy_id_list __ARGS((short *list));
383static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
384static int push_current_state __ARGS((int idx));
385static void pop_current_state __ARGS((void));
386
387static 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));
388static void clear_syn_state __ARGS((synstate_T *p));
389static void clear_current_state __ARGS((void));
390
391static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
392static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
393static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
394static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
395static char_u *syn_getcurline __ARGS((void));
396static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
397static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si));
398static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
399static void syntax_sync_clear __ARGS((void));
400static void syn_remove_pattern __ARGS((buf_T *buf, int idx));
401static void syn_clear_pattern __ARGS((buf_T *buf, int i));
402static void syn_clear_cluster __ARGS((buf_T *buf, int i));
403static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
404static void syn_clear_one __ARGS((int id, int syncing));
405static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
406static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
407static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
408static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
409static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
410static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
411static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
412static void syn_lines_msg __ARGS((void));
413static void syn_match_msg __ARGS((void));
414static void syn_list_one __ARGS((int id, int syncing, int link_only));
415static void syn_list_cluster __ARGS((int id));
416static void put_id_list __ARGS((char_u *name, short *list, int attr));
417static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000418static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
419static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
420static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000421static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000422static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000423static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000424static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
425static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
426static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
427static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
428#ifdef __BORLANDC__
429static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
430#else
431static int syn_compare_stub __ARGS((const void *v1, const void *v2));
432#endif
433static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
434static int syn_scl_name2id __ARGS((char_u *name));
435static int syn_scl_namen2id __ARGS((char_u *linep, int len));
436static int syn_check_cluster __ARGS((char_u *pp, int len));
437static int syn_add_cluster __ARGS((char_u *name));
438static void init_syn_patterns __ARGS((void));
439static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
440static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
441static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
442static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
443static void syn_incl_toplevel __ARGS((int id, int *flagsp));
444
445/*
446 * Start the syntax recognition for a line. This function is normally called
447 * from the screen updating, once for each displayed line.
448 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
449 * it. Careful: curbuf and curwin are likely to point to another buffer and
450 * window.
451 */
452 void
453syntax_start(wp, lnum)
454 win_T *wp;
455 linenr_T lnum;
456{
457 synstate_T *p;
458 synstate_T *last_valid = NULL;
459 synstate_T *last_min_valid = NULL;
460 synstate_T *sp, *prev;
461 linenr_T parsed_lnum;
462 linenr_T first_stored;
463 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000464 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000465
466 reg_syn = TRUE; /* let vim_regexec() know we're using syntax */
467
468 /*
469 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000470 * Also do this when a change was made, the current state may be invalid
471 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000472 */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000473 if (syn_buf != wp->w_buffer || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474 {
475 invalidate_current_state();
476 syn_buf = wp->w_buffer;
477 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000478 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000479 syn_win = wp;
480
481 /*
482 * Allocate syntax stack when needed.
483 */
484 syn_stack_alloc();
485 if (syn_buf->b_sst_array == NULL)
486 goto theend; /* out of memory */
487 syn_buf->b_sst_lasttick = display_tick;
488
489 /*
490 * If the state of the end of the previous line is useful, store it.
491 */
492 if (VALID_STATE(&current_state)
493 && current_lnum < lnum
494 && current_lnum < syn_buf->b_ml.ml_line_count)
495 {
496 (void)syn_finish_line(FALSE);
497 if (!current_state_stored)
498 {
499 ++current_lnum;
500 (void)store_current_state(NULL);
501 }
502
503 /*
504 * If the current_lnum is now the same as "lnum", keep the current
505 * state (this happens very often!). Otherwise invalidate
506 * current_state and figure it out below.
507 */
508 if (current_lnum != lnum)
509 invalidate_current_state();
510 }
511 else
512 invalidate_current_state();
513
514 /*
515 * Try to synchronize from a saved state in b_sst_array[].
516 * Only do this if lnum is not before and not to far beyond a saved state.
517 */
518 if (INVALID_STATE(&current_state) && syn_buf->b_sst_array != NULL)
519 {
520 /* Find last valid saved state before start_lnum. */
521 for (p = syn_buf->b_sst_first; p != NULL; p = p->sst_next)
522 {
523 if (p->sst_lnum > lnum)
524 break;
525 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
526 {
527 last_valid = p;
528 if (p->sst_lnum >= lnum - syn_buf->b_syn_sync_minlines)
529 last_min_valid = p;
530 }
531 }
532 if (last_min_valid != NULL)
533 load_current_state(last_min_valid);
534 }
535
536 /*
537 * If "lnum" is before or far beyond a line with a saved state, need to
538 * re-synchronize.
539 */
540 if (INVALID_STATE(&current_state))
541 {
542 syn_sync(wp, lnum, last_valid);
543 first_stored = current_lnum + syn_buf->b_syn_sync_minlines;
544 }
545 else
546 first_stored = current_lnum;
547
548 /*
549 * Advance from the sync point or saved state until the current line.
550 * Save some entries for syncing with later on.
551 */
552 dist = syn_buf->b_ml.ml_line_count / (syn_buf->b_sst_len - Rows) + 1;
553 prev = syn_stack_find_entry(current_lnum);
554 while (current_lnum < lnum)
555 {
556 syn_start_line();
557 (void)syn_finish_line(FALSE);
558 ++current_lnum;
559
560 /* If we parsed at least "minlines" lines or started at a valid
561 * state, the current state is considered valid. */
562 if (current_lnum >= first_stored)
563 {
564 /* Check if the saved state entry is for the current line and is
565 * equal to the current state. If so, then validate all saved
566 * states that depended on a change before the parsed line. */
567 if (prev == NULL)
568 sp = syn_buf->b_sst_first;
569 else
570 sp = prev->sst_next;
571 if (sp != NULL
572 && sp->sst_lnum == current_lnum
573 && syn_stack_equal(sp))
574 {
575 parsed_lnum = current_lnum;
576 prev = sp;
577 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
578 {
579 if (sp->sst_lnum <= lnum)
580 /* valid state before desired line, use this one */
581 prev = sp;
582 else if (sp->sst_change_lnum == 0)
583 /* past saved states depending on change, break here. */
584 break;
585 sp->sst_change_lnum = 0;
586 sp = sp->sst_next;
587 }
588 load_current_state(prev);
589 }
590 /* Store the state at this line when it's the first one, the line
591 * where we start parsing, or some distance from the previously
592 * saved state. But only when parsed at least 'minlines'. */
593 else if (prev == NULL
594 || current_lnum == lnum
595 || current_lnum >= prev->sst_lnum + dist)
596 prev = store_current_state(prev);
597 }
598
599 /* This can take a long time: break when CTRL-C pressed. The current
600 * state will be wrong then. */
601 line_breakcheck();
602 if (got_int)
603 {
604 current_lnum = lnum;
605 break;
606 }
607 }
608
609 syn_start_line();
610
611theend:
612 reg_syn = FALSE;
613}
614
615/*
616 * We cannot simply discard growarrays full of state_items or buf_states; we
617 * have to manually release their extmatch pointers first.
618 */
619 static void
620clear_syn_state(p)
621 synstate_T *p;
622{
623 int i;
624 garray_T *gap;
625
626 if (p->sst_stacksize > SST_FIX_STATES)
627 {
628 gap = &(p->sst_union.sst_ga);
629 for (i = 0; i < gap->ga_len; i++)
630 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
631 ga_clear(gap);
632 }
633 else
634 {
635 for (i = 0; i < p->sst_stacksize; i++)
636 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
637 }
638}
639
640/*
641 * Cleanup the current_state stack.
642 */
643 static void
644clear_current_state()
645{
646 int i;
647 stateitem_T *sip;
648
649 sip = (stateitem_T *)(current_state.ga_data);
650 for (i = 0; i < current_state.ga_len; i++)
651 unref_extmatch(sip[i].si_extmatch);
652 ga_clear(&current_state);
653}
654
655/*
656 * Try to find a synchronisation point for line "lnum".
657 *
658 * This sets current_lnum and the current state. One of three methods is
659 * used:
660 * 1. Search backwards for the end of a C-comment.
661 * 2. Search backwards for given sync patterns.
662 * 3. Simply start on a given number of lines above "lnum".
663 */
664 static void
665syn_sync(wp, start_lnum, last_valid)
666 win_T *wp;
667 linenr_T start_lnum;
668 synstate_T *last_valid;
669{
670 buf_T *curbuf_save;
671 win_T *curwin_save;
672 pos_T cursor_save;
673 int idx;
674 linenr_T lnum;
675 linenr_T end_lnum;
676 linenr_T break_lnum;
677 int had_sync_point;
678 stateitem_T *cur_si;
679 synpat_T *spp;
680 char_u *line;
681 int found_flags = 0;
682 int found_match_idx = 0;
683 linenr_T found_current_lnum = 0;
684 int found_current_col= 0;
685 lpos_T found_m_endpos;
686
687 /*
688 * Clear any current state that might be hanging around.
689 */
690 invalidate_current_state();
691
692 /*
693 * Start at least "minlines" back. Default starting point for parsing is
694 * there.
695 * Start further back, to avoid that scrolling backwards will result in
696 * resyncing for every line. Now it resyncs only one out of N lines,
697 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
698 * Watch out for overflow when minlines is MAXLNUM.
699 */
700 if (syn_buf->b_syn_sync_minlines > start_lnum)
701 start_lnum = 1;
702 else
703 {
704 if (syn_buf->b_syn_sync_minlines == 1)
705 lnum = 1;
706 else if (syn_buf->b_syn_sync_minlines < 10)
707 lnum = syn_buf->b_syn_sync_minlines * 2;
708 else
709 lnum = syn_buf->b_syn_sync_minlines * 3 / 2;
710 if (syn_buf->b_syn_sync_maxlines != 0
711 && lnum > syn_buf->b_syn_sync_maxlines)
712 lnum = syn_buf->b_syn_sync_maxlines;
713 if (lnum >= start_lnum)
714 start_lnum = 1;
715 else
716 start_lnum -= lnum;
717 }
718 current_lnum = start_lnum;
719
720 /*
721 * 1. Search backwards for the end of a C-style comment.
722 */
723 if (syn_buf->b_syn_sync_flags & SF_CCOMMENT)
724 {
725 /* Need to make syn_buf the current buffer for a moment, to be able to
726 * use find_start_comment(). */
727 curwin_save = curwin;
728 curwin = wp;
729 curbuf_save = curbuf;
730 curbuf = syn_buf;
731
732 /*
733 * Skip lines that end in a backslash.
734 */
735 for ( ; start_lnum > 1; --start_lnum)
736 {
737 line = ml_get(start_lnum - 1);
738 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
739 break;
740 }
741 current_lnum = start_lnum;
742
743 /* set cursor to start of search */
744 cursor_save = wp->w_cursor;
745 wp->w_cursor.lnum = start_lnum;
746 wp->w_cursor.col = 0;
747
748 /*
749 * If the line is inside a comment, need to find the syntax item that
750 * defines the comment.
751 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
752 */
753 if (find_start_comment((int)syn_buf->b_syn_sync_maxlines) != NULL)
754 {
755 for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
756 if (SYN_ITEMS(syn_buf)[idx].sp_syn.id == syn_buf->b_syn_sync_id
757 && SYN_ITEMS(syn_buf)[idx].sp_type == SPTYPE_START)
758 {
759 validate_current_state();
760 if (push_current_state(idx) == OK)
761 update_si_attr(current_state.ga_len - 1);
762 break;
763 }
764 }
765
766 /* restore cursor and buffer */
767 wp->w_cursor = cursor_save;
768 curwin = curwin_save;
769 curbuf = curbuf_save;
770 }
771
772 /*
773 * 2. Search backwards for given sync patterns.
774 */
775 else if (syn_buf->b_syn_sync_flags & SF_MATCH)
776 {
777 if (syn_buf->b_syn_sync_maxlines != 0
778 && start_lnum > syn_buf->b_syn_sync_maxlines)
779 break_lnum = start_lnum - syn_buf->b_syn_sync_maxlines;
780 else
781 break_lnum = 0;
782
783 end_lnum = start_lnum;
784 lnum = start_lnum;
785 while (--lnum > break_lnum)
786 {
787 /* This can take a long time: break when CTRL-C pressed. */
788 line_breakcheck();
789 if (got_int)
790 {
791 invalidate_current_state();
792 current_lnum = start_lnum;
793 break;
794 }
795
796 /* Check if we have run into a valid saved state stack now. */
797 if (last_valid != NULL && lnum == last_valid->sst_lnum)
798 {
799 load_current_state(last_valid);
800 break;
801 }
802
803 /*
804 * Check if the previous line has the line-continuation pattern.
805 */
806 if (lnum > 1 && syn_match_linecont(lnum - 1))
807 continue;
808
809 /*
810 * Start with nothing on the state stack
811 */
812 validate_current_state();
813
814 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
815 {
816 syn_start_line();
817 for (;;)
818 {
819 had_sync_point = syn_finish_line(TRUE);
820 /*
821 * When a sync point has been found, remember where, and
822 * continue to look for another one, further on in the line.
823 */
824 if (had_sync_point && current_state.ga_len)
825 {
826 cur_si = &CUR_STATE(current_state.ga_len - 1);
827 if (cur_si->si_m_endpos.lnum > start_lnum)
828 {
829 /* ignore match that goes to after where started */
830 current_lnum = end_lnum;
831 break;
832 }
833 spp = &(SYN_ITEMS(syn_buf)[cur_si->si_idx]);
834 found_flags = spp->sp_flags;
835 found_match_idx = spp->sp_sync_idx;
836 found_current_lnum = current_lnum;
837 found_current_col = current_col;
838 found_m_endpos = cur_si->si_m_endpos;
839 /*
840 * Continue after the match (be aware of a zero-length
841 * match).
842 */
843 if (found_m_endpos.lnum > current_lnum)
844 {
845 current_lnum = found_m_endpos.lnum;
846 current_col = found_m_endpos.col;
847 if (current_lnum >= end_lnum)
848 break;
849 }
850 else if (found_m_endpos.col > current_col)
851 current_col = found_m_endpos.col;
852 else
853 ++current_col;
854
855 /* syn_current_attr() will have skipped the check for
856 * an item that ends here, need to do that now. */
857 ++current_col;
858 check_state_ends();
859 --current_col;
860 }
861 else
862 break;
863 }
864 }
865
866 /*
867 * If a sync point was encountered, break here.
868 */
869 if (found_flags)
870 {
871 /*
872 * Put the item that was specified by the sync point on the
873 * state stack. If there was no item specified, make the
874 * state stack empty.
875 */
876 clear_current_state();
877 if (found_match_idx >= 0
878 && push_current_state(found_match_idx) == OK)
879 update_si_attr(current_state.ga_len - 1);
880
881 /*
882 * When using "grouphere", continue from the sync point
883 * match, until the end of the line. Parsing starts at
884 * the next line.
885 * For "groupthere" the parsing starts at start_lnum.
886 */
887 if (found_flags & HL_SYNC_HERE)
888 {
889 if (current_state.ga_len)
890 {
891 cur_si = &CUR_STATE(current_state.ga_len - 1);
892 cur_si->si_h_startpos.lnum = found_current_lnum;
893 cur_si->si_h_startpos.col = found_current_col;
894 update_si_end(cur_si, (int)current_col, TRUE);
895 check_keepend();
896 }
897 current_col = found_m_endpos.col;
898 current_lnum = found_m_endpos.lnum;
899 (void)syn_finish_line(FALSE);
900 ++current_lnum;
901 }
902 else
903 current_lnum = start_lnum;
904
905 break;
906 }
907
908 end_lnum = lnum;
909 invalidate_current_state();
910 }
911
912 /* Ran into start of the file or exceeded maximum number of lines */
913 if (lnum <= break_lnum)
914 {
915 invalidate_current_state();
916 current_lnum = break_lnum + 1;
917 }
918 }
919
920 validate_current_state();
921}
922
923/*
924 * Return TRUE if the line-continuation pattern matches in line "lnum".
925 */
926 static int
927syn_match_linecont(lnum)
928 linenr_T lnum;
929{
930 regmmatch_T regmatch;
931
932 if (syn_buf->b_syn_linecont_prog != NULL)
933 {
934 regmatch.rmm_ic = syn_buf->b_syn_linecont_ic;
935 regmatch.regprog = syn_buf->b_syn_linecont_prog;
936 return syn_regexec(&regmatch, lnum, (colnr_T)0);
937 }
938 return FALSE;
939}
940
941/*
942 * Prepare the current state for the start of a line.
943 */
944 static void
945syn_start_line()
946{
947 current_finished = FALSE;
948 current_col = 0;
949
950 /*
951 * Need to update the end of a start/skip/end that continues from the
952 * previous line and regions that have "keepend".
953 */
954 if (current_state.ga_len > 0)
955 syn_update_ends(TRUE);
956
957 next_match_idx = -1;
958 ++current_line_id;
959}
960
961/*
962 * Check for items in the stack that need their end updated.
963 * When "startofline" is TRUE the last item is always updated.
964 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
965 */
966 static void
967syn_update_ends(startofline)
968 int startofline;
969{
970 stateitem_T *cur_si;
971 int i;
972
973 if (startofline)
974 {
975 /* Check for a match carried over from a previous line with a
976 * contained region. The match ends as soon as the region ends. */
977 for (i = 0; i < current_state.ga_len; ++i)
978 {
979 cur_si = &CUR_STATE(i);
980 if (cur_si->si_idx >= 0
981 && (SYN_ITEMS(syn_buf)[cur_si->si_idx]).sp_type
982 == SPTYPE_MATCH
983 && cur_si->si_m_endpos.lnum < current_lnum)
984 {
985 cur_si->si_flags |= HL_MATCHCONT;
986 cur_si->si_m_endpos.lnum = 0;
987 cur_si->si_m_endpos.col = 0;
988 cur_si->si_h_endpos = cur_si->si_m_endpos;
989 cur_si->si_ends = TRUE;
990 }
991 }
992 }
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", because they may
997 * influence contained items.
998 * Then check for items ending in column 0.
999 */
1000 i = current_state.ga_len - 1;
1001 if (keepend_level >= 0)
1002 for ( ; i > keepend_level; --i)
1003 if (CUR_STATE(i).si_flags & HL_EXTEND)
1004 break;
1005 for ( ; i < current_state.ga_len; ++i)
1006 {
1007 cur_si = &CUR_STATE(i);
1008 if ((cur_si->si_flags & HL_KEEPEND)
1009 || (i == current_state.ga_len - 1 && startofline))
1010 {
1011 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1012 cur_si->si_h_startpos.lnum = current_lnum;
1013
1014 if (!(cur_si->si_flags & HL_MATCHCONT))
1015 update_si_end(cur_si, (int)current_col, !startofline);
1016 }
1017 }
1018 check_keepend();
1019 check_state_ends();
1020}
1021
1022/****************************************
1023 * Handling of the state stack cache.
1024 */
1025
1026/*
1027 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1028 *
1029 * To speed up syntax highlighting, the state stack for the start of some
1030 * lines is cached. These entries can be used to start parsing at that point.
1031 *
1032 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1033 * valid entries. b_sst_first points to the first one, then follow sst_next.
1034 * The entries are sorted on line number. The first entry is often for line 2
1035 * (line 1 always starts with an empty stack).
1036 * There is also a list for free entries. This construction is used to avoid
1037 * having to allocate and free memory blocks too often.
1038 *
1039 * When making changes to the buffer, this is logged in b_mod_*. When calling
1040 * update_screen() to update the display, it will call
1041 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1042 * entries. The entries which are inside the changed area are removed,
1043 * because they must be recomputed. Entries below the changed have their line
1044 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1045 * set to indicate that a check must be made if the changed lines would change
1046 * the cached entry.
1047 *
1048 * When later displaying lines, an entry is stored for each line. Displayed
1049 * lines are likely to be displayed again, in which case the state at the
1050 * start of the line is needed.
1051 * For not displayed lines, an entry is stored for every so many lines. These
1052 * entries will be used e.g., when scrolling backwards. The distance between
1053 * entries depends on the number of lines in the buffer. For small buffers
1054 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1055 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1056 */
1057
1058/*
1059 * Free b_sst_array[] for buffer "buf".
1060 * Used when syntax items changed to force resyncing everywhere.
1061 */
1062 void
1063syn_stack_free_all(buf)
1064 buf_T *buf;
1065{
1066 synstate_T *p;
1067 win_T *wp;
1068
1069 if (buf->b_sst_array != NULL)
1070 {
1071 for (p = buf->b_sst_first; p != NULL; p = p->sst_next)
1072 clear_syn_state(p);
1073 vim_free(buf->b_sst_array);
1074 buf->b_sst_array = NULL;
1075 buf->b_sst_len = 0;
1076 }
1077#ifdef FEAT_FOLDING
1078 /* When using "syntax" fold method, must update all folds. */
1079 FOR_ALL_WINDOWS(wp)
1080 {
1081 if (wp->w_buffer == buf && foldmethodIsSyntax(wp))
1082 foldUpdateAll(wp);
1083 }
1084#endif
1085}
1086
1087/*
1088 * Allocate the syntax state stack for syn_buf when needed.
1089 * If the number of entries in b_sst_array[] is much too big or a bit too
1090 * small, reallocate it.
1091 * Also used to allocate b_sst_array[] for the first time.
1092 */
1093 static void
1094syn_stack_alloc()
1095{
1096 long len;
1097 synstate_T *to, *from;
1098 synstate_T *sstp;
1099
1100 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1101 if (len < SST_MIN_ENTRIES)
1102 len = SST_MIN_ENTRIES;
1103 else if (len > SST_MAX_ENTRIES)
1104 len = SST_MAX_ENTRIES;
1105 if (syn_buf->b_sst_len > len * 2 || syn_buf->b_sst_len < len)
1106 {
1107 /* Allocate 50% too much, to avoid reallocating too often. */
1108 len = syn_buf->b_ml.ml_line_count;
1109 len = (len + len / 2) / SST_DIST + Rows * 2;
1110 if (len < SST_MIN_ENTRIES)
1111 len = SST_MIN_ENTRIES;
1112 else if (len > SST_MAX_ENTRIES)
1113 len = SST_MAX_ENTRIES;
1114
1115 if (syn_buf->b_sst_array != NULL)
1116 {
1117 /* When shrinking the array, cleanup the existing stack.
1118 * Make sure that all valid entries fit in the new array. */
1119 while (syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2 > len
1120 && syn_stack_cleanup())
1121 ;
1122 if (len < syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2)
1123 len = syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2;
1124 }
1125
1126 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1127 if (sstp == NULL) /* out of memory! */
1128 return;
1129
1130 to = sstp - 1;
1131 if (syn_buf->b_sst_array != NULL)
1132 {
1133 /* Move the states from the old array to the new one. */
1134 for (from = syn_buf->b_sst_first; from != NULL;
1135 from = from->sst_next)
1136 {
1137 ++to;
1138 *to = *from;
1139 to->sst_next = to + 1;
1140 }
1141 }
1142 if (to != sstp - 1)
1143 {
1144 to->sst_next = NULL;
1145 syn_buf->b_sst_first = sstp;
1146 syn_buf->b_sst_freecount = len - (int)(to - sstp) - 1;
1147 }
1148 else
1149 {
1150 syn_buf->b_sst_first = NULL;
1151 syn_buf->b_sst_freecount = len;
1152 }
1153
1154 /* Create the list of free entries. */
1155 syn_buf->b_sst_firstfree = to + 1;
1156 while (++to < sstp + len)
1157 to->sst_next = to + 1;
1158 (sstp + len - 1)->sst_next = NULL;
1159
1160 vim_free(syn_buf->b_sst_array);
1161 syn_buf->b_sst_array = sstp;
1162 syn_buf->b_sst_len = len;
1163 }
1164}
1165
1166/*
1167 * Check for changes in a buffer to affect stored syntax states. Uses the
1168 * b_mod_* fields.
1169 * Called from update_screen(), before screen is being updated, once for each
1170 * displayed buffer.
1171 */
1172 void
1173syn_stack_apply_changes(buf)
1174 buf_T *buf;
1175{
1176 synstate_T *p, *prev, *np;
1177 linenr_T n;
1178
1179 if (buf->b_sst_array == NULL) /* nothing to do */
1180 return;
1181
1182 prev = NULL;
1183 for (p = buf->b_sst_first; p != NULL; )
1184 {
1185 if (p->sst_lnum + syn_buf->b_syn_sync_linebreaks > buf->b_mod_top)
1186 {
1187 n = p->sst_lnum + buf->b_mod_xlines;
1188 if (n <= buf->b_mod_bot)
1189 {
1190 /* this state is inside the changed area, remove it */
1191 np = p->sst_next;
1192 if (prev == NULL)
1193 buf->b_sst_first = np;
1194 else
1195 prev->sst_next = np;
1196 syn_stack_free_entry(buf, p);
1197 p = np;
1198 continue;
1199 }
1200 /* This state is below the changed area. Remember the line
1201 * that needs to be parsed before this entry can be made valid
1202 * again. */
1203 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1204 {
1205 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1206 p->sst_change_lnum += buf->b_mod_xlines;
1207 else
1208 p->sst_change_lnum = buf->b_mod_top;
1209 }
1210 if (p->sst_change_lnum == 0
1211 || p->sst_change_lnum < buf->b_mod_bot)
1212 p->sst_change_lnum = buf->b_mod_bot;
1213
1214 p->sst_lnum = n;
1215 }
1216 prev = p;
1217 p = p->sst_next;
1218 }
1219}
1220
1221/*
1222 * Reduce the number of entries in the state stack for syn_buf.
1223 * Returns TRUE if at least one entry was freed.
1224 */
1225 static int
1226syn_stack_cleanup()
1227{
1228 synstate_T *p, *prev;
1229 disptick_T tick;
1230 int above;
1231 int dist;
1232 int retval = FALSE;
1233
1234 if (syn_buf->b_sst_array == NULL || syn_buf->b_sst_first == NULL)
1235 return retval;
1236
1237 /* Compute normal distance between non-displayed entries. */
1238 dist = syn_buf->b_ml.ml_line_count / (syn_buf->b_sst_len - Rows) + 1;
1239
1240 /*
1241 * Go throught the list to find the "tick" for the oldest entry that can
1242 * be removed. Set "above" when the "tick" for the oldest entry is above
1243 * "b_sst_lasttick" (the display tick wraps around).
1244 */
1245 tick = syn_buf->b_sst_lasttick;
1246 above = FALSE;
1247 prev = syn_buf->b_sst_first;
1248 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1249 {
1250 if (prev->sst_lnum + dist > p->sst_lnum)
1251 {
1252 if (p->sst_tick > syn_buf->b_sst_lasttick)
1253 {
1254 if (!above || p->sst_tick < tick)
1255 tick = p->sst_tick;
1256 above = TRUE;
1257 }
1258 else if (!above && p->sst_tick < tick)
1259 tick = p->sst_tick;
1260 }
1261 }
1262
1263 /*
1264 * Go through the list to make the entries for the oldest tick at an
1265 * interval of several lines.
1266 */
1267 prev = syn_buf->b_sst_first;
1268 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1269 {
1270 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1271 {
1272 /* Move this entry from used list to free list */
1273 prev->sst_next = p->sst_next;
1274 syn_stack_free_entry(syn_buf, p);
1275 p = prev;
1276 retval = TRUE;
1277 }
1278 }
1279 return retval;
1280}
1281
1282/*
1283 * Free the allocated memory for a syn_state item.
1284 * Move the entry into the free list.
1285 */
1286 static void
1287syn_stack_free_entry(buf, p)
1288 buf_T *buf;
1289 synstate_T *p;
1290{
1291 clear_syn_state(p);
1292 p->sst_next = buf->b_sst_firstfree;
1293 buf->b_sst_firstfree = p;
1294 ++buf->b_sst_freecount;
1295}
1296
1297/*
1298 * Find an entry in the list of state stacks at or before "lnum".
1299 * Returns NULL when there is no entry or the first entry is after "lnum".
1300 */
1301 static synstate_T *
1302syn_stack_find_entry(lnum)
1303 linenr_T lnum;
1304{
1305 synstate_T *p, *prev;
1306
1307 prev = NULL;
1308 for (p = syn_buf->b_sst_first; p != NULL; prev = p, p = p->sst_next)
1309 {
1310 if (p->sst_lnum == lnum)
1311 return p;
1312 if (p->sst_lnum > lnum)
1313 break;
1314 }
1315 return prev;
1316}
1317
1318/*
1319 * Try saving the current state in b_sst_array[].
1320 * The current state must be valid for the start of the current_lnum line!
1321 */
1322 static synstate_T *
1323store_current_state(sp)
1324 synstate_T *sp; /* at or before where state is to be saved or
1325 NULL */
1326{
1327 int i;
1328 synstate_T *p;
1329 bufstate_T *bp;
1330 stateitem_T *cur_si;
1331
1332 if (sp == NULL)
1333 sp = syn_stack_find_entry(current_lnum);
1334
1335 /*
1336 * If the current state contains a start or end pattern that continues
1337 * from the previous line, we can't use it. Don't store it then.
1338 */
1339 for (i = current_state.ga_len - 1; i >= 0; --i)
1340 {
1341 cur_si = &CUR_STATE(i);
1342 if (cur_si->si_h_startpos.lnum >= current_lnum
1343 || cur_si->si_m_endpos.lnum >= current_lnum
1344 || cur_si->si_h_endpos.lnum >= current_lnum
1345 || (cur_si->si_end_idx
1346 && cur_si->si_eoe_pos.lnum >= current_lnum))
1347 break;
1348 }
1349 if (i >= 0)
1350 {
1351 if (sp != NULL)
1352 {
1353 /* find "sp" in the list and remove it */
1354 if (syn_buf->b_sst_first == sp)
1355 /* it's the first entry */
1356 syn_buf->b_sst_first = sp->sst_next;
1357 else
1358 {
1359 /* find the entry just before this one to adjust sst_next */
1360 for (p = syn_buf->b_sst_first; p != NULL; p = p->sst_next)
1361 if (p->sst_next == sp)
1362 break;
1363 p->sst_next = sp->sst_next;
1364 }
1365 syn_stack_free_entry(syn_buf, sp);
1366 sp = NULL;
1367 }
1368 }
1369 else if (sp == NULL || sp->sst_lnum != current_lnum)
1370 {
1371 /*
1372 * Add a new entry
1373 */
1374 /* If no free items, cleanup the array first. */
1375 if (syn_buf->b_sst_freecount == 0)
1376 {
1377 (void)syn_stack_cleanup();
1378 /* "sp" may have been moved to the freelist now */
1379 sp = syn_stack_find_entry(current_lnum);
1380 }
1381 /* Still no free items? Must be a strange problem... */
1382 if (syn_buf->b_sst_freecount == 0)
1383 sp = NULL;
1384 else
1385 {
1386 /* Take the first item from the free list and put it in the used
1387 * list, after *sp */
1388 p = syn_buf->b_sst_firstfree;
1389 syn_buf->b_sst_firstfree = p->sst_next;
1390 --syn_buf->b_sst_freecount;
1391 if (sp == NULL)
1392 {
1393 /* Insert in front of the list */
1394 p->sst_next = syn_buf->b_sst_first;
1395 syn_buf->b_sst_first = p;
1396 }
1397 else
1398 {
1399 /* insert in list after *sp */
1400 p->sst_next = sp->sst_next;
1401 sp->sst_next = p;
1402 }
1403 sp = p;
1404 sp->sst_stacksize = 0;
1405 sp->sst_lnum = current_lnum;
1406 }
1407 }
1408 if (sp != NULL)
1409 {
1410 /* When overwriting an existing state stack, clear it first */
1411 clear_syn_state(sp);
1412 sp->sst_stacksize = current_state.ga_len;
1413 if (current_state.ga_len > SST_FIX_STATES)
1414 {
1415 /* Need to clear it, might be something remaining from when the
1416 * length was less than SST_FIX_STATES. */
1417 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1418 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1419 sp->sst_stacksize = 0;
1420 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001421 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001422 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1423 }
1424 else
1425 bp = sp->sst_union.sst_stack;
1426 for (i = 0; i < sp->sst_stacksize; ++i)
1427 {
1428 bp[i].bs_idx = CUR_STATE(i).si_idx;
1429 bp[i].bs_flags = CUR_STATE(i).si_flags;
1430 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1431 }
1432 sp->sst_next_flags = current_next_flags;
1433 sp->sst_next_list = current_next_list;
1434 sp->sst_tick = display_tick;
1435 sp->sst_change_lnum = 0;
1436 }
1437 current_state_stored = TRUE;
1438 return sp;
1439}
1440
1441/*
1442 * Copy a state stack from "from" in b_sst_array[] to current_state;
1443 */
1444 static void
1445load_current_state(from)
1446 synstate_T *from;
1447{
1448 int i;
1449 bufstate_T *bp;
1450
1451 clear_current_state();
1452 validate_current_state();
1453 keepend_level = -1;
1454 if (from->sst_stacksize
1455 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1456 {
1457 if (from->sst_stacksize > SST_FIX_STATES)
1458 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1459 else
1460 bp = from->sst_union.sst_stack;
1461 for (i = 0; i < from->sst_stacksize; ++i)
1462 {
1463 CUR_STATE(i).si_idx = bp[i].bs_idx;
1464 CUR_STATE(i).si_flags = bp[i].bs_flags;
1465 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1466 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1467 keepend_level = i;
1468 CUR_STATE(i).si_ends = FALSE;
1469 CUR_STATE(i).si_m_lnum = 0;
1470 if (CUR_STATE(i).si_idx >= 0)
1471 CUR_STATE(i).si_next_list =
1472 (SYN_ITEMS(syn_buf)[CUR_STATE(i).si_idx]).sp_next_list;
1473 else
1474 CUR_STATE(i).si_next_list = NULL;
1475 update_si_attr(i);
1476 }
1477 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478 }
1479 current_next_list = from->sst_next_list;
1480 current_next_flags = from->sst_next_flags;
1481 current_lnum = from->sst_lnum;
1482}
1483
1484/*
1485 * Compare saved state stack "*sp" with the current state.
1486 * Return TRUE when they are equal.
1487 */
1488 static int
1489syn_stack_equal(sp)
1490 synstate_T *sp;
1491{
1492 int i, j;
1493 bufstate_T *bp;
1494 reg_extmatch_T *six, *bsx;
1495
1496 /* First a quick check if the stacks have the same size end nextlist. */
1497 if (sp->sst_stacksize == current_state.ga_len
1498 && sp->sst_next_list == current_next_list)
1499 {
1500 /* Need to compare all states on both stacks. */
1501 if (sp->sst_stacksize > SST_FIX_STATES)
1502 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1503 else
1504 bp = sp->sst_union.sst_stack;
1505
1506 for (i = current_state.ga_len; --i >= 0; )
1507 {
1508 /* If the item has another index the state is different. */
1509 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1510 break;
1511 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1512 {
1513 /* When the extmatch pointers are different, the strings in
1514 * them can still be the same. Check if the extmatch
1515 * references are equal. */
1516 bsx = bp[i].bs_extmatch;
1517 six = CUR_STATE(i).si_extmatch;
1518 /* If one of the extmatch pointers is NULL the states are
1519 * different. */
1520 if (bsx == NULL || six == NULL)
1521 break;
1522 for (j = 0; j < NSUBEXP; ++j)
1523 {
1524 /* Check each referenced match string. They must all be
1525 * equal. */
1526 if (bsx->matches[j] != six->matches[j])
1527 {
1528 /* If the pointer is different it can still be the
1529 * same text. Compare the strings, ignore case when
1530 * the start item has the sp_ic flag set. */
1531 if (bsx->matches[j] == NULL
1532 || six->matches[j] == NULL)
1533 break;
1534 if ((SYN_ITEMS(syn_buf)[CUR_STATE(i).si_idx]).sp_ic
1535 ? MB_STRICMP(bsx->matches[j],
1536 six->matches[j]) != 0
1537 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1538 break;
1539 }
1540 }
1541 if (j != NSUBEXP)
1542 break;
1543 }
1544 }
1545 if (i < 0)
1546 return TRUE;
1547 }
1548 return FALSE;
1549}
1550
1551/*
1552 * We stop parsing syntax above line "lnum". If the stored state at or below
1553 * this line depended on a change before it, it now depends on the line below
1554 * the last parsed line.
1555 * The window looks like this:
1556 * line which changed
1557 * displayed line
1558 * displayed line
1559 * lnum -> line below window
1560 */
1561 void
1562syntax_end_parsing(lnum)
1563 linenr_T lnum;
1564{
1565 synstate_T *sp;
1566
1567 sp = syn_stack_find_entry(lnum);
1568 if (sp != NULL && sp->sst_lnum < lnum)
1569 sp = sp->sst_next;
1570
1571 if (sp != NULL && sp->sst_change_lnum != 0)
1572 sp->sst_change_lnum = lnum;
1573}
1574
1575/*
1576 * End of handling of the state stack.
1577 ****************************************/
1578
1579 static void
1580invalidate_current_state()
1581{
1582 clear_current_state();
1583 current_state.ga_itemsize = 0; /* mark current_state invalid */
1584 current_next_list = NULL;
1585 keepend_level = -1;
1586}
1587
1588 static void
1589validate_current_state()
1590{
1591 current_state.ga_itemsize = sizeof(stateitem_T);
1592 current_state.ga_growsize = 3;
1593}
1594
1595/*
1596 * Return TRUE if the syntax at start of lnum changed since last time.
1597 * This will only be called just after get_syntax_attr() for the previous
1598 * line, to check if the next line needs to be redrawn too.
1599 */
1600 int
1601syntax_check_changed(lnum)
1602 linenr_T lnum;
1603{
1604 int retval = TRUE;
1605 synstate_T *sp;
1606
1607 reg_syn = TRUE; /* let vim_regexec() know we're using syntax */
1608
1609 /*
1610 * Check the state stack when:
1611 * - lnum is just below the previously syntaxed line.
1612 * - lnum is not before the lines with saved states.
1613 * - lnum is not past the lines with saved states.
1614 * - lnum is at or before the last changed line.
1615 */
1616 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1617 {
1618 sp = syn_stack_find_entry(lnum);
1619 if (sp != NULL && sp->sst_lnum == lnum)
1620 {
1621 /*
1622 * finish the previous line (needed when not all of the line was
1623 * drawn)
1624 */
1625 (void)syn_finish_line(FALSE);
1626
1627 /*
1628 * Compare the current state with the previously saved state of
1629 * the line.
1630 */
1631 if (syn_stack_equal(sp))
1632 retval = FALSE;
1633
1634 /*
1635 * Store the current state in b_sst_array[] for later use.
1636 */
1637 ++current_lnum;
1638 (void)store_current_state(NULL);
1639 }
1640 }
1641
1642 reg_syn = FALSE;
1643
1644 return retval;
1645}
1646
1647/*
1648 * Finish the current line.
1649 * This doesn't return any attributes, it only gets the state at the end of
1650 * the line. It can start anywhere in the line, as long as the current state
1651 * is valid.
1652 */
1653 static int
1654syn_finish_line(syncing)
1655 int syncing; /* called for syncing */
1656{
1657 stateitem_T *cur_si;
1658
1659 if (!current_finished)
1660 {
1661 while (!current_finished)
1662 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00001663 (void)syn_current_attr(syncing, FALSE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001664 /*
1665 * When syncing, and found some item, need to check the item.
1666 */
1667 if (syncing && current_state.ga_len)
1668 {
1669 /*
1670 * Check for match with sync item.
1671 */
1672 cur_si = &CUR_STATE(current_state.ga_len - 1);
1673 if (cur_si->si_idx >= 0
1674 && (SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags
1675 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1676 return TRUE;
1677
1678 /* syn_current_attr() will have skipped the check for an item
1679 * that ends here, need to do that now. */
1680 ++current_col;
1681 check_state_ends();
1682 --current_col;
1683 }
1684 ++current_col;
1685 }
1686 }
1687 return FALSE;
1688}
1689
1690/*
1691 * Return highlight attributes for next character.
1692 * Must first call syntax_start() once for the line.
1693 * "col" is normally 0 for the first use in a line, and increments by one each
1694 * time. It's allowed to skip characters and to stop before the end of the
1695 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001696 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1697 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001698 */
1699 int
Bram Moolenaar217ad922005-03-20 22:37:15 +00001700get_syntax_attr(col, can_spell)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001701 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001702 int *can_spell;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703{
1704 int attr = 0;
1705
1706 /* check for out of memory situation */
1707 if (syn_buf->b_sst_array == NULL)
1708 return 0;
1709
1710 reg_syn = TRUE; /* let vim_regexec() know we're using syntax */
1711
1712 /* Make sure current_state is valid */
1713 if (INVALID_STATE(&current_state))
1714 validate_current_state();
1715
1716 /*
1717 * Skip from the current column to "col", get the attributes for "col".
1718 */
1719 while (current_col <= col)
1720 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00001721 attr = syn_current_attr(FALSE, TRUE, can_spell);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001722 ++current_col;
1723 }
1724
1725 reg_syn = FALSE;
1726 return attr;
1727}
1728
1729/*
1730 * Get syntax attributes for current_lnum, current_col.
1731 */
1732 static int
Bram Moolenaar217ad922005-03-20 22:37:15 +00001733syn_current_attr(syncing, displaying, can_spell)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001734 int syncing; /* When 1: called for syncing */
1735 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001736 int *can_spell; /* return: do spell checking */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001737{
1738 int syn_id;
1739 lpos_T endpos; /* was: char_u *endp; */
1740 lpos_T hl_startpos; /* was: int hl_startcol; */
1741 lpos_T hl_endpos;
1742 lpos_T eos_pos; /* end-of-start match (start region) */
1743 lpos_T eoe_pos; /* end-of-end pattern */
1744 int end_idx; /* group ID for end pattern */
1745 int idx;
1746 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001747 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001748 int startcol;
1749 int endcol;
1750 long flags;
1751 short *next_list;
1752 int found_match; /* found usable match */
1753 static int try_next_column = FALSE; /* must try in next col */
1754 int do_keywords;
1755 regmmatch_T regmatch;
1756 lpos_T pos;
1757 int lc_col;
1758 reg_extmatch_T *cur_extmatch = NULL;
1759 char_u *line; /* current line. NOTE: becomes invalid after
1760 looking for a pattern match! */
1761
1762 /* variables for zero-width matches that have a "nextgroup" argument */
1763 int keep_next_list;
1764 int zero_width_next_list = FALSE;
1765 garray_T zero_width_next_ga;
1766
1767 /*
1768 * No character, no attributes! Past end of line?
1769 * Do try matching with an empty line (could be the start of a region).
1770 */
1771 line = syn_getcurline();
1772 if (line[current_col] == NUL && current_col != 0)
1773 {
1774 /*
1775 * If we found a match after the last column, use it.
1776 */
1777 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1778 && next_match_col != MAXCOL)
1779 (void)push_next_match(NULL);
1780
1781 current_finished = TRUE;
1782 current_state_stored = FALSE;
1783 return 0;
1784 }
1785
1786 /* if the current or next character is NUL, we will finish the line now */
1787 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1788 {
1789 current_finished = TRUE;
1790 current_state_stored = FALSE;
1791 }
1792
1793 /*
1794 * When in the previous column there was a match but it could not be used
1795 * (empty match or already matched in this column) need to try again in
1796 * the next column.
1797 */
1798 if (try_next_column)
1799 {
1800 next_match_idx = -1;
1801 try_next_column = FALSE;
1802 }
1803
1804 /* Only check for keywords when not syncing and there are some. */
1805 do_keywords = !syncing
Bram Moolenaardad6b692005-01-25 22:14:34 +00001806 && (syn_buf->b_keywtab.ht_used > 0
1807 || syn_buf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808
1809 /* Init the list of zero-width matches with a nextlist. This is used to
1810 * avoid matching the same item in the same position twice. */
1811 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1812
1813 /*
1814 * Repeat matching keywords and patterns, to find contained items at the
1815 * same column. This stops when there are no extra matches at the current
1816 * column.
1817 */
1818 do
1819 {
1820 found_match = FALSE;
1821 keep_next_list = FALSE;
1822 syn_id = 0;
1823
1824 /*
1825 * 1. Check for a current state.
1826 * Only when there is no current state, or if the current state may
1827 * contain other things, we need to check for keywords and patterns.
1828 * Always need to check for contained items if some item has the
1829 * "containedin" argument (takes extra time!).
1830 */
1831 if (current_state.ga_len)
1832 cur_si = &CUR_STATE(current_state.ga_len - 1);
1833 else
1834 cur_si = NULL;
1835
1836 if (syn_buf->b_syn_containedin || cur_si == NULL
1837 || cur_si->si_cont_list != NULL)
1838 {
1839 /*
1840 * 2. Check for keywords, if on a keyword char after a non-keyword
1841 * char. Don't do this when syncing.
1842 */
1843 if (do_keywords)
1844 {
1845 line = syn_getcurline();
1846 if (vim_iswordc_buf(line + current_col, syn_buf)
1847 && (current_col == 0
1848 || !vim_iswordc_buf(line + current_col - 1
1849#ifdef FEAT_MBYTE
1850 - (has_mbyte
1851 ? (*mb_head_off)(line, line + current_col - 1)
1852 : 0)
1853#endif
1854 , syn_buf)))
1855 {
1856 syn_id = check_keyword_id(line, (int)current_col,
1857 &endcol, &flags, &next_list, cur_si);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001858 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 {
1860 if (push_current_state(KEYWORD_IDX) == OK)
1861 {
1862 cur_si = &CUR_STATE(current_state.ga_len - 1);
1863 cur_si->si_m_startcol = current_col;
1864 cur_si->si_h_startpos.lnum = current_lnum;
1865 cur_si->si_h_startpos.col = 0; /* starts right away */
1866 cur_si->si_m_endpos.lnum = current_lnum;
1867 cur_si->si_m_endpos.col = endcol;
1868 cur_si->si_h_endpos.lnum = current_lnum;
1869 cur_si->si_h_endpos.col = endcol;
1870 cur_si->si_ends = TRUE;
1871 cur_si->si_end_idx = 0;
1872 cur_si->si_flags = flags;
1873 cur_si->si_id = syn_id;
1874 cur_si->si_trans_id = syn_id;
1875 if (flags & HL_TRANSP)
1876 {
1877 if (current_state.ga_len < 2)
1878 {
1879 cur_si->si_attr = 0;
1880 cur_si->si_trans_id = 0;
1881 }
1882 else
1883 {
1884 cur_si->si_attr = CUR_STATE(
1885 current_state.ga_len - 2).si_attr;
1886 cur_si->si_trans_id = CUR_STATE(
1887 current_state.ga_len - 2).si_trans_id;
1888 }
1889 }
1890 else
1891 cur_si->si_attr = syn_id2attr(syn_id);
1892 cur_si->si_cont_list = NULL;
1893 cur_si->si_next_list = next_list;
1894 check_keepend();
1895 }
1896 else
1897 vim_free(next_list);
1898 }
1899 }
1900 }
1901
1902 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001903 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001904 */
1905 if (syn_id == 0 && syn_buf->b_syn_patterns.ga_len)
1906 {
1907 /*
1908 * If we didn't check for a match yet, or we are past it, check
1909 * for any match with a pattern.
1910 */
1911 if (next_match_idx < 0 || next_match_col < (int)current_col)
1912 {
1913 /*
1914 * Check all relevant patterns for a match at this
1915 * position. This is complicated, because matching with a
1916 * pattern takes quite a bit of time, thus we want to
1917 * avoid doing it when it's not needed.
1918 */
1919 next_match_idx = 0; /* no match in this line yet */
1920 next_match_col = MAXCOL;
1921 for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
1922 {
1923 spp = &(SYN_ITEMS(syn_buf)[idx]);
1924 if ( spp->sp_syncing == syncing
1925 && (displaying || !(spp->sp_flags & HL_DISPLAY))
1926 && (spp->sp_type == SPTYPE_MATCH
1927 || spp->sp_type == SPTYPE_START)
1928 && (current_next_list != NULL
1929 ? in_id_list(NULL, current_next_list,
1930 &spp->sp_syn, 0)
1931 : (cur_si == NULL
1932 ? !(spp->sp_flags & HL_CONTAINED)
1933 : in_id_list(cur_si,
1934 cur_si->si_cont_list, &spp->sp_syn,
1935 spp->sp_flags & HL_CONTAINED))))
1936 {
1937 /* If we already tried matching in this line, and
1938 * there isn't a match before next_match_col, skip
1939 * this item. */
1940 if (spp->sp_line_id == current_line_id
1941 && spp->sp_startcol >= next_match_col)
1942 continue;
1943 spp->sp_line_id = current_line_id;
1944
1945 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1946 if (lc_col < 0)
1947 lc_col = 0;
1948
1949 regmatch.rmm_ic = spp->sp_ic;
1950 regmatch.regprog = spp->sp_prog;
1951 if (!syn_regexec(&regmatch, current_lnum,
1952 (colnr_T)lc_col))
1953 {
1954 /* no match in this line, try another one */
1955 spp->sp_startcol = MAXCOL;
1956 continue;
1957 }
1958
1959 /*
1960 * Compute the first column of the match.
1961 */
1962 syn_add_start_off(&pos, &regmatch,
1963 spp, SPO_MS_OFF, -1);
1964 if (pos.lnum > current_lnum)
1965 {
1966 /* must have used end of match in a next line,
1967 * we can't handle that */
1968 spp->sp_startcol = MAXCOL;
1969 continue;
1970 }
1971 startcol = pos.col;
1972
1973 /* remember the next column where this pattern
1974 * matches in the current line */
1975 spp->sp_startcol = startcol;
1976
1977 /*
1978 * If a previously found match starts at a lower
1979 * column number, don't use this one.
1980 */
1981 if (startcol >= next_match_col)
1982 continue;
1983
1984 /*
1985 * If we matched this pattern at this position
1986 * before, skip it. Must retry in the next
1987 * column, because it may match from there.
1988 */
1989 if (did_match_already(idx, &zero_width_next_ga))
1990 {
1991 try_next_column = TRUE;
1992 continue;
1993 }
1994
1995 endpos.lnum = regmatch.endpos[0].lnum;
1996 endpos.col = regmatch.endpos[0].col;
1997
1998 /* Compute the highlight start. */
1999 syn_add_start_off(&hl_startpos, &regmatch,
2000 spp, SPO_HS_OFF, -1);
2001
2002 /* Compute the region start. */
2003 /* Default is to use the end of the match. */
2004 syn_add_end_off(&eos_pos, &regmatch,
2005 spp, SPO_RS_OFF, 0);
2006
2007 /*
2008 * Grab the external submatches before they get
2009 * overwritten. Reference count doesn't change.
2010 */
2011 unref_extmatch(cur_extmatch);
2012 cur_extmatch = re_extmatch_out;
2013 re_extmatch_out = NULL;
2014
2015 flags = 0;
2016 eoe_pos.lnum = 0; /* avoid warning */
2017 eoe_pos.col = 0;
2018 end_idx = 0;
2019 hl_endpos.lnum = 0;
2020
2021 /*
2022 * For a "oneline" the end must be found in the
2023 * same line too. Search for it after the end of
2024 * the match with the start pattern. Set the
2025 * resulting end positions at the same time.
2026 */
2027 if (spp->sp_type == SPTYPE_START
2028 && (spp->sp_flags & HL_ONELINE))
2029 {
2030 lpos_T startpos;
2031
2032 startpos = endpos;
2033 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2034 &flags, &eoe_pos, &end_idx, cur_extmatch);
2035 if (endpos.lnum == 0)
2036 continue; /* not found */
2037 }
2038
2039 /*
2040 * For a "match" the size must be > 0 after the
2041 * end offset needs has been added. Except when
2042 * syncing.
2043 */
2044 else if (spp->sp_type == SPTYPE_MATCH)
2045 {
2046 syn_add_end_off(&hl_endpos, &regmatch, spp,
2047 SPO_HE_OFF, 0);
2048 syn_add_end_off(&endpos, &regmatch, spp,
2049 SPO_ME_OFF, 0);
2050 if (endpos.lnum == current_lnum
2051 && (int)endpos.col + syncing < startcol)
2052 {
2053 /*
2054 * If an empty string is matched, may need
2055 * to try matching again at next column.
2056 */
2057 if (regmatch.startpos[0].col
2058 == regmatch.endpos[0].col)
2059 try_next_column = TRUE;
2060 continue;
2061 }
2062 }
2063
2064 /*
2065 * keep the best match so far in next_match_*
2066 */
2067 /* Highlighting must start after startpos and end
2068 * before endpos. */
2069 if (hl_startpos.lnum == current_lnum
2070 && (int)hl_startpos.col < startcol)
2071 hl_startpos.col = startcol;
2072 limit_pos_zero(&hl_endpos, &endpos);
2073
2074 next_match_idx = idx;
2075 next_match_col = startcol;
2076 next_match_m_endpos = endpos;
2077 next_match_h_endpos = hl_endpos;
2078 next_match_h_startpos = hl_startpos;
2079 next_match_flags = flags;
2080 next_match_eos_pos = eos_pos;
2081 next_match_eoe_pos = eoe_pos;
2082 next_match_end_idx = end_idx;
2083 unref_extmatch(next_match_extmatch);
2084 next_match_extmatch = cur_extmatch;
2085 cur_extmatch = NULL;
2086 }
2087 }
2088 }
2089
2090 /*
2091 * If we found a match at the current column, use it.
2092 */
2093 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2094 {
2095 synpat_T *lspp;
2096
2097 /* When a zero-width item matched which has a nextgroup,
2098 * don't push the item but set nextgroup. */
2099 lspp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2100 if (next_match_m_endpos.lnum == current_lnum
2101 && next_match_m_endpos.col == current_col
2102 && lspp->sp_next_list != NULL)
2103 {
2104 current_next_list = lspp->sp_next_list;
2105 current_next_flags = lspp->sp_flags;
2106 keep_next_list = TRUE;
2107 zero_width_next_list = TRUE;
2108
2109 /* Add the index to a list, so that we can check
2110 * later that we don't match it again (and cause an
2111 * endless loop). */
2112 if (ga_grow(&zero_width_next_ga, 1) == OK)
2113 {
2114 ((int *)(zero_width_next_ga.ga_data))
2115 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002116 }
2117 next_match_idx = -1;
2118 }
2119 else
2120 cur_si = push_next_match(cur_si);
2121 found_match = TRUE;
2122 }
2123 }
2124 }
2125
2126 /*
2127 * Handle searching for nextgroup match.
2128 */
2129 if (current_next_list != NULL && !keep_next_list)
2130 {
2131 /*
2132 * If a nextgroup was not found, continue looking for one if:
2133 * - this is an empty line and the "skipempty" option was given
2134 * - we are on white space and the "skipwhite" option was given
2135 */
2136 if (!found_match)
2137 {
2138 line = syn_getcurline();
2139 if (((current_next_flags & HL_SKIPWHITE)
2140 && vim_iswhite(line[current_col]))
2141 || ((current_next_flags & HL_SKIPEMPTY)
2142 && *line == NUL))
2143 break;
2144 }
2145
2146 /*
2147 * If a nextgroup was found: Use it, and continue looking for
2148 * contained matches.
2149 * If a nextgroup was not found: Continue looking for a normal
2150 * match.
2151 * When did set current_next_list for a zero-width item and no
2152 * match was found don't loop (would get stuck).
2153 */
2154 current_next_list = NULL;
2155 next_match_idx = -1;
2156 if (!zero_width_next_list)
2157 found_match = TRUE;
2158 }
2159
2160 } while (found_match);
2161
2162 /*
2163 * Use attributes from the current state, if within its highlighting.
2164 * If not, use attributes from the current-but-one state, etc.
2165 */
2166 current_attr = 0;
2167#ifdef FEAT_EVAL
2168 current_id = 0;
2169 current_trans_id = 0;
2170#endif
2171 if (cur_si != NULL)
2172 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002173#ifndef FEAT_EVAL
2174 int current_trans_id = 0;
2175#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002176 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2177 {
2178 sip = &CUR_STATE(idx);
2179 if ((current_lnum > sip->si_h_startpos.lnum
2180 || (current_lnum == sip->si_h_startpos.lnum
2181 && current_col >= sip->si_h_startpos.col))
2182 && (sip->si_h_endpos.lnum == 0
2183 || current_lnum < sip->si_h_endpos.lnum
2184 || (current_lnum == sip->si_h_endpos.lnum
2185 && current_col < sip->si_h_endpos.col)))
2186 {
2187 current_attr = sip->si_attr;
2188#ifdef FEAT_EVAL
2189 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002190#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002191 current_trans_id = sip->si_trans_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002192 break;
2193 }
2194 }
2195
Bram Moolenaar217ad922005-03-20 22:37:15 +00002196 if (can_spell != NULL)
2197 {
2198 struct sp_syn sps;
2199
2200 /*
2201 * set "can_spell" to TRUE if spell checking is supposed to be
2202 * done in the current item.
2203 */
2204
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002205 /* If there is no @Spell cluster: Do spelling for items without
2206 * @NoSpell. Otherwise only in items with @Spell */
Bram Moolenaar217ad922005-03-20 22:37:15 +00002207 if (syn_buf->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002208 {
2209 if (syn_buf->b_nospell_cluster_id == 0 || current_trans_id == 0)
2210 *can_spell = TRUE;
2211 else
2212 {
2213 sps.inc_tag = 0;
2214 sps.id = syn_buf->b_nospell_cluster_id;
2215 sps.cont_in_list = NULL;
2216 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2217 }
2218 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002219 else if (current_trans_id == 0)
2220 *can_spell = FALSE;
2221 else
2222 {
2223 sps.inc_tag = 0;
2224 sps.id = syn_buf->b_spell_cluster_id;
2225 sps.cont_in_list = NULL;
2226 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2227 }
2228 }
2229
2230
Bram Moolenaar071d4272004-06-13 20:20:40 +00002231 /*
2232 * Check for end of current state (and the states before it) at the
2233 * next column. Don't do this for syncing, because we would miss a
2234 * single character match.
2235 * First check if the current state ends at the current column. It
2236 * may be for an empty match and a containing item might end in the
2237 * current column.
2238 */
2239 if (!syncing)
2240 {
2241 check_state_ends();
2242 if (current_state.ga_len > 0)
2243 {
2244 ++current_col;
2245 check_state_ends();
2246 --current_col;
2247 }
2248 }
2249 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002250 else if (can_spell != NULL)
2251 /* Only do spelling when there is no @Spell cluster. */
2252 *can_spell = (syn_buf->b_spell_cluster_id == 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002253
2254 /* nextgroup ends at end of line, unless "skipnl" or "skipemtpy" present */
2255 if (current_next_list != NULL
2256 && syn_getcurline()[current_col + 1] == NUL
2257 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2258 current_next_list = NULL;
2259
2260 if (zero_width_next_ga.ga_len > 0)
2261 ga_clear(&zero_width_next_ga);
2262
2263 /* No longer need external matches. But keep next_match_extmatch. */
2264 unref_extmatch(re_extmatch_out);
2265 re_extmatch_out = NULL;
2266 unref_extmatch(cur_extmatch);
2267
2268 return current_attr;
2269}
2270
2271
2272/*
2273 * Check if we already matched pattern "idx" at the current column.
2274 */
2275 static int
2276did_match_already(idx, gap)
2277 int idx;
2278 garray_T *gap;
2279{
2280 int i;
2281
2282 for (i = current_state.ga_len; --i >= 0; )
2283 if (CUR_STATE(i).si_m_startcol == (int)current_col
2284 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2285 && CUR_STATE(i).si_idx == idx)
2286 return TRUE;
2287
2288 /* Zero-width matches with a nextgroup argument are not put on the syntax
2289 * stack, and can only be matched once anyway. */
2290 for (i = gap->ga_len; --i >= 0; )
2291 if (((int *)(gap->ga_data))[i] == idx)
2292 return TRUE;
2293
2294 return FALSE;
2295}
2296
2297/*
2298 * Push the next match onto the stack.
2299 */
2300 static stateitem_T *
2301push_next_match(cur_si)
2302 stateitem_T *cur_si;
2303{
2304 synpat_T *spp;
2305
2306 spp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2307
2308 /*
2309 * Push the item in current_state stack;
2310 */
2311 if (push_current_state(next_match_idx) == OK)
2312 {
2313 /*
2314 * If it's a start-skip-end type that crosses lines, figure out how
2315 * much it continues in this line. Otherwise just fill in the length.
2316 */
2317 cur_si = &CUR_STATE(current_state.ga_len - 1);
2318 cur_si->si_h_startpos = next_match_h_startpos;
2319 cur_si->si_m_startcol = current_col;
2320 cur_si->si_m_lnum = current_lnum;
2321 cur_si->si_flags = spp->sp_flags;
2322 cur_si->si_next_list = spp->sp_next_list;
2323 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2324 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2325 {
2326 /* Try to find the end pattern in the current line */
2327 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2328 check_keepend();
2329 }
2330 else
2331 {
2332 cur_si->si_m_endpos = next_match_m_endpos;
2333 cur_si->si_h_endpos = next_match_h_endpos;
2334 cur_si->si_ends = TRUE;
2335 cur_si->si_flags |= next_match_flags;
2336 cur_si->si_eoe_pos = next_match_eoe_pos;
2337 cur_si->si_end_idx = next_match_end_idx;
2338 }
2339 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2340 keepend_level = current_state.ga_len - 1;
2341 check_keepend();
2342 update_si_attr(current_state.ga_len - 1);
2343
2344 /*
2345 * If the start pattern has another highlight group, push another item
2346 * on the stack for the start pattern.
2347 */
2348 if ( spp->sp_type == SPTYPE_START
2349 && spp->sp_syn_match_id != 0
2350 && push_current_state(next_match_idx) == OK)
2351 {
2352 cur_si = &CUR_STATE(current_state.ga_len - 1);
2353 cur_si->si_h_startpos = next_match_h_startpos;
2354 cur_si->si_m_startcol = current_col;
2355 cur_si->si_m_lnum = current_lnum;
2356 cur_si->si_m_endpos = next_match_eos_pos;
2357 cur_si->si_h_endpos = next_match_eos_pos;
2358 cur_si->si_ends = TRUE;
2359 cur_si->si_end_idx = 0;
2360 cur_si->si_flags = HL_MATCH;
2361 cur_si->si_next_list = NULL;
2362 check_keepend();
2363 update_si_attr(current_state.ga_len - 1);
2364 }
2365 }
2366
2367 next_match_idx = -1; /* try other match next time */
2368
2369 return cur_si;
2370}
2371
2372/*
2373 * Check for end of current state (and the states before it).
2374 */
2375 static void
2376check_state_ends()
2377{
2378 stateitem_T *cur_si;
2379 int had_extend = FALSE;
2380
2381 cur_si = &CUR_STATE(current_state.ga_len - 1);
2382 for (;;)
2383 {
2384 if (cur_si->si_ends
2385 && (cur_si->si_m_endpos.lnum < current_lnum
2386 || (cur_si->si_m_endpos.lnum == current_lnum
2387 && cur_si->si_m_endpos.col <= current_col)))
2388 {
2389 /*
2390 * If there is an end pattern group ID, highlight the end pattern
2391 * now. No need to pop the current item from the stack.
2392 * Only do this if the end pattern continues beyond the current
2393 * position.
2394 */
2395 if (cur_si->si_end_idx
2396 && (cur_si->si_eoe_pos.lnum > current_lnum
2397 || (cur_si->si_eoe_pos.lnum == current_lnum
2398 && cur_si->si_eoe_pos.col > current_col)))
2399 {
2400 cur_si->si_idx = cur_si->si_end_idx;
2401 cur_si->si_end_idx = 0;
2402 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2403 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2404 cur_si->si_flags |= HL_MATCH;
2405 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002406
2407 /* what matches next may be different now, clear it */
2408 next_match_idx = 0;
2409 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002410 break;
2411 }
2412 else
2413 {
2414 /* handle next_list, unless at end of line and no "skipnl" or
2415 * "skipempty" */
2416 current_next_list = cur_si->si_next_list;
2417 current_next_flags = cur_si->si_flags;
2418 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2419 && syn_getcurline()[current_col] == NUL)
2420 current_next_list = NULL;
2421
2422 /* When the ended item has "extend", another item with
2423 * "keepend" now needs to check for its end. */
2424 if (cur_si->si_flags & HL_EXTEND)
2425 had_extend = TRUE;
2426
2427 pop_current_state();
2428
2429 if (current_state.ga_len == 0)
2430 break;
2431
2432 if (had_extend)
2433 {
2434 syn_update_ends(FALSE);
2435 if (current_state.ga_len == 0)
2436 break;
2437 }
2438
2439 cur_si = &CUR_STATE(current_state.ga_len - 1);
2440
2441 /*
2442 * Only for a region the search for the end continues after
2443 * the end of the contained item. If the contained match
2444 * included the end-of-line, break here, the region continues.
2445 * Don't do this when:
2446 * - "keepend" is used for the contained item
2447 * - not at the end of the line (could be end="x$"me=e-1).
2448 * - "excludenl" is used (HL_HAS_EOL won't be set)
2449 */
2450 if (cur_si->si_idx >= 0
2451 && SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_type
2452 == SPTYPE_START
2453 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2454 {
2455 update_si_end(cur_si, (int)current_col, TRUE);
2456 check_keepend();
2457 if ((current_next_flags & HL_HAS_EOL)
2458 && keepend_level < 0
2459 && syn_getcurline()[current_col] == NUL)
2460 break;
2461 }
2462 }
2463 }
2464 else
2465 break;
2466 }
2467}
2468
2469/*
2470 * Update an entry in the current_state stack for a match or region. This
2471 * fills in si_attr, si_next_list and si_cont_list.
2472 */
2473 static void
2474update_si_attr(idx)
2475 int idx;
2476{
2477 stateitem_T *sip = &CUR_STATE(idx);
2478 synpat_T *spp;
2479
2480 spp = &(SYN_ITEMS(syn_buf)[sip->si_idx]);
2481 if (sip->si_flags & HL_MATCH)
2482 sip->si_id = spp->sp_syn_match_id;
2483 else
2484 sip->si_id = spp->sp_syn.id;
2485 sip->si_attr = syn_id2attr(sip->si_id);
2486 sip->si_trans_id = sip->si_id;
2487 if (sip->si_flags & HL_MATCH)
2488 sip->si_cont_list = NULL;
2489 else
2490 sip->si_cont_list = spp->sp_cont_list;
2491
2492 /*
2493 * For transparent items, take attr from outer item.
2494 * Also take cont_list, if there is none.
2495 * Don't do this for the matchgroup of a start or end pattern.
2496 */
2497 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2498 {
2499 if (idx == 0)
2500 {
2501 sip->si_attr = 0;
2502 sip->si_trans_id = 0;
2503 if (sip->si_cont_list == NULL)
2504 sip->si_cont_list = ID_LIST_ALL;
2505 }
2506 else
2507 {
2508 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2509 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002510 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2511 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002512 if (sip->si_cont_list == NULL)
2513 {
2514 sip->si_flags |= HL_TRANS_CONT;
2515 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2516 }
2517 }
2518 }
2519}
2520
2521/*
2522 * Check the current stack for patterns with "keepend" flag.
2523 * Propagate the match-end to contained items, until a "skipend" item is found.
2524 */
2525 static void
2526check_keepend()
2527{
2528 int i;
2529 lpos_T maxpos;
2530 stateitem_T *sip;
2531
2532 /*
2533 * This check can consume a lot of time; only do it from the level where
2534 * there really is a keepend.
2535 */
2536 if (keepend_level < 0)
2537 return;
2538
2539 /*
2540 * Find the last index of an "extend" item. "keepend" items before that
2541 * won't do anything. If there is no "extend" item "i" will be
2542 * "keepend_level" and all "keepend" items will work normally.
2543 */
2544 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2545 if (CUR_STATE(i).si_flags & HL_EXTEND)
2546 break;
2547
2548 maxpos.lnum = 0;
2549 for ( ; i < current_state.ga_len; ++i)
2550 {
2551 sip = &CUR_STATE(i);
2552 if (maxpos.lnum != 0)
2553 {
2554 limit_pos_zero(&sip->si_m_endpos, &maxpos);
2555 limit_pos_zero(&sip->si_h_endpos, &maxpos);
2556 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2557 sip->si_ends = TRUE;
2558 }
2559 if (sip->si_ends
2560 && (sip->si_flags & HL_KEEPEND)
2561 && (maxpos.lnum == 0
2562 || maxpos.lnum > sip->si_m_endpos.lnum
2563 || (maxpos.lnum == sip->si_m_endpos.lnum
2564 && maxpos.col > sip->si_m_endpos.col)))
2565 maxpos = sip->si_m_endpos;
2566 }
2567}
2568
2569/*
2570 * Update an entry in the current_state stack for a start-skip-end pattern.
2571 * This finds the end of the current item, if it's in the current line.
2572 *
2573 * Return the flags for the matched END.
2574 */
2575 static void
2576update_si_end(sip, startcol, force)
2577 stateitem_T *sip;
2578 int startcol; /* where to start searching for the end */
2579 int force; /* when TRUE overrule a previous end */
2580{
2581 lpos_T startpos;
2582 lpos_T endpos;
2583 lpos_T hl_endpos;
2584 lpos_T end_endpos;
2585 int end_idx;
2586
2587 /* Don't update when it's already done. Can be a match of an end pattern
2588 * that started in a previous line. Watch out: can also be a "keepend"
2589 * from a containing item. */
2590 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2591 return;
2592
2593 /*
2594 * We need to find the end of the region. It may continue in the next
2595 * line.
2596 */
2597 end_idx = 0;
2598 startpos.lnum = current_lnum;
2599 startpos.col = startcol;
2600 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2601 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2602
2603 if (endpos.lnum == 0)
2604 {
2605 /* No end pattern matched. */
2606 if (SYN_ITEMS(syn_buf)[sip->si_idx].sp_flags & HL_ONELINE)
2607 {
2608 /* a "oneline" never continues in the next line */
2609 sip->si_ends = TRUE;
2610 sip->si_m_endpos.lnum = current_lnum;
2611 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2612 }
2613 else
2614 {
2615 /* continues in the next line */
2616 sip->si_ends = FALSE;
2617 sip->si_m_endpos.lnum = 0;
2618 }
2619 sip->si_h_endpos = sip->si_m_endpos;
2620 }
2621 else
2622 {
2623 /* match within this line */
2624 sip->si_m_endpos = endpos;
2625 sip->si_h_endpos = hl_endpos;
2626 sip->si_eoe_pos = end_endpos;
2627 sip->si_ends = TRUE;
2628 sip->si_end_idx = end_idx;
2629 }
2630}
2631
2632/*
2633 * Add a new state to the current state stack.
2634 * It is cleared and the index set to "idx".
2635 * Return FAIL if it's not possible (out of memory).
2636 */
2637 static int
2638push_current_state(idx)
2639 int idx;
2640{
2641 if (ga_grow(&current_state, 1) == FAIL)
2642 return FAIL;
2643 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2644 CUR_STATE(current_state.ga_len).si_idx = idx;
2645 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002646 return OK;
2647}
2648
2649/*
2650 * Remove a state from the current_state stack.
2651 */
2652 static void
2653pop_current_state()
2654{
2655 if (current_state.ga_len)
2656 {
2657 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2658 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002659 }
2660 /* after the end of a pattern, try matching a keyword or pattern */
2661 next_match_idx = -1;
2662
2663 /* if first state with "keepend" is popped, reset keepend_level */
2664 if (keepend_level >= current_state.ga_len)
2665 keepend_level = -1;
2666}
2667
2668/*
2669 * Find the end of a start/skip/end syntax region after "startpos".
2670 * Only checks one line.
2671 * Also handles a match item that continued from a previous line.
2672 * If not found, the syntax item continues in the next line. m_endpos->lnum
2673 * will be 0.
2674 * If found, the end of the region and the end of the highlighting is
2675 * computed.
2676 */
2677 static void
2678find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2679 end_idx, start_ext)
2680 int idx; /* index of the pattern */
2681 lpos_T *startpos; /* where to start looking for an END match */
2682 lpos_T *m_endpos; /* return: end of match */
2683 lpos_T *hl_endpos; /* return: end of highlighting */
2684 long *flagsp; /* return: flags of matching END */
2685 lpos_T *end_endpos; /* return: end of end pattern match */
2686 int *end_idx; /* return: group ID for end pat. match, or 0 */
2687 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2688{
2689 colnr_T matchcol;
2690 synpat_T *spp, *spp_skip;
2691 int start_idx;
2692 int best_idx;
2693 regmmatch_T regmatch;
2694 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2695 lpos_T pos;
2696 char_u *line;
2697 int had_match = FALSE;
2698
2699 /*
2700 * Check for being called with a START pattern.
2701 * Can happen with a match that continues to the next line, because it
2702 * contained a region.
2703 */
2704 spp = &(SYN_ITEMS(syn_buf)[idx]);
2705 if (spp->sp_type != SPTYPE_START)
2706 {
2707 *hl_endpos = *startpos;
2708 return;
2709 }
2710
2711 /*
2712 * Find the SKIP or first END pattern after the last START pattern.
2713 */
2714 for (;;)
2715 {
2716 spp = &(SYN_ITEMS(syn_buf)[idx]);
2717 if (spp->sp_type != SPTYPE_START)
2718 break;
2719 ++idx;
2720 }
2721
2722 /*
2723 * Lookup the SKIP pattern (if present)
2724 */
2725 if (spp->sp_type == SPTYPE_SKIP)
2726 {
2727 spp_skip = spp;
2728 ++idx;
2729 }
2730 else
2731 spp_skip = NULL;
2732
2733 /* Setup external matches for syn_regexec(). */
2734 unref_extmatch(re_extmatch_in);
2735 re_extmatch_in = ref_extmatch(start_ext);
2736
2737 matchcol = startpos->col; /* start looking for a match at sstart */
2738 start_idx = idx; /* remember the first END pattern. */
2739 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2740 for (;;)
2741 {
2742 /*
2743 * Find end pattern that matches first after "matchcol".
2744 */
2745 best_idx = -1;
2746 for (idx = start_idx; idx < syn_buf->b_syn_patterns.ga_len; ++idx)
2747 {
2748 int lc_col = matchcol;
2749
2750 spp = &(SYN_ITEMS(syn_buf)[idx]);
2751 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2752 break;
2753 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2754 if (lc_col < 0)
2755 lc_col = 0;
2756
2757 regmatch.rmm_ic = spp->sp_ic;
2758 regmatch.regprog = spp->sp_prog;
2759 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2760 {
2761 if (best_idx == -1 || regmatch.startpos[0].col
2762 < best_regmatch.startpos[0].col)
2763 {
2764 best_idx = idx;
2765 best_regmatch.startpos[0] = regmatch.startpos[0];
2766 best_regmatch.endpos[0] = regmatch.endpos[0];
2767 }
2768 }
2769 }
2770
2771 /*
2772 * If all end patterns have been tried, and there is no match, the
2773 * item continues until end-of-line.
2774 */
2775 if (best_idx == -1)
2776 break;
2777
2778 /*
2779 * If the skip pattern matches before the end pattern,
2780 * continue searching after the skip pattern.
2781 */
2782 if (spp_skip != NULL)
2783 {
2784 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2785
2786 if (lc_col < 0)
2787 lc_col = 0;
2788 regmatch.rmm_ic = spp_skip->sp_ic;
2789 regmatch.regprog = spp_skip->sp_prog;
2790 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2791 && regmatch.startpos[0].col
2792 <= best_regmatch.startpos[0].col)
2793 {
2794 /* Add offset to skip pattern match */
2795 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2796
2797 /* If the skip pattern goes on to the next line, there is no
2798 * match with an end pattern in this line. */
2799 if (pos.lnum > startpos->lnum)
2800 break;
2801
2802 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2803
2804 /* take care of an empty match or negative offset */
2805 if (pos.col <= matchcol)
2806 ++matchcol;
2807 else if (pos.col <= regmatch.endpos[0].col)
2808 matchcol = pos.col;
2809 else
2810 /* Be careful not to jump over the NUL at the end-of-line */
2811 for (matchcol = regmatch.endpos[0].col;
2812 line[matchcol] != NUL && matchcol < pos.col;
2813 ++matchcol)
2814 ;
2815
2816 /* if the skip pattern includes end-of-line, break here */
2817 if (line[matchcol] == NUL)
2818 break;
2819
2820 continue; /* start with first end pattern again */
2821 }
2822 }
2823
2824 /*
2825 * Match from start pattern to end pattern.
2826 * Correct for match and highlight offset of end pattern.
2827 */
2828 spp = &(SYN_ITEMS(syn_buf)[best_idx]);
2829 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2830 /* can't end before the start */
2831 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2832 m_endpos->col = startpos->col;
2833
2834 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2835 /* can't end before the start */
2836 if (end_endpos->lnum == startpos->lnum
2837 && end_endpos->col < startpos->col)
2838 end_endpos->col = startpos->col;
2839 /* can't end after the match */
2840 limit_pos(end_endpos, m_endpos);
2841
2842 /*
2843 * If the end group is highlighted differently, adjust the pointers.
2844 */
2845 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2846 {
2847 *end_idx = best_idx;
2848 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2849 {
2850 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2851 hl_endpos->col = best_regmatch.endpos[0].col;
2852 }
2853 else
2854 {
2855 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2856 hl_endpos->col = best_regmatch.startpos[0].col;
2857 }
2858 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2859
2860 /* can't end before the start */
2861 if (hl_endpos->lnum == startpos->lnum
2862 && hl_endpos->col < startpos->col)
2863 hl_endpos->col = startpos->col;
2864 limit_pos(hl_endpos, m_endpos);
2865
2866 /* now the match ends where the highlighting ends, it is turned
2867 * into the matchgroup for the end */
2868 *m_endpos = *hl_endpos;
2869 }
2870 else
2871 {
2872 *end_idx = 0;
2873 *hl_endpos = *end_endpos;
2874 }
2875
2876 *flagsp = spp->sp_flags;
2877
2878 had_match = TRUE;
2879 break;
2880 }
2881
2882 /* no match for an END pattern in this line */
2883 if (!had_match)
2884 m_endpos->lnum = 0;
2885
2886 /* Remove external matches. */
2887 unref_extmatch(re_extmatch_in);
2888 re_extmatch_in = NULL;
2889}
2890
2891/*
2892 * Limit "pos" not to be after "limit".
2893 */
2894 static void
2895limit_pos(pos, limit)
2896 lpos_T *pos;
2897 lpos_T *limit;
2898{
2899 if (pos->lnum > limit->lnum)
2900 *pos = *limit;
2901 else if (pos->lnum == limit->lnum && pos->col > limit->col)
2902 pos->col = limit->col;
2903}
2904
2905/*
2906 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
2907 */
2908 static void
2909limit_pos_zero(pos, limit)
2910 lpos_T *pos;
2911 lpos_T *limit;
2912{
2913 if (pos->lnum == 0)
2914 *pos = *limit;
2915 else
2916 limit_pos(pos, limit);
2917}
2918
2919/*
2920 * Add offset to matched text for end of match or highlight.
2921 */
2922 static void
2923syn_add_end_off(result, regmatch, spp, idx, extra)
2924 lpos_T *result; /* returned position */
2925 regmmatch_T *regmatch; /* start/end of match */
2926 synpat_T *spp; /* matched pattern */
2927 int idx; /* index of offset */
2928 int extra; /* extra chars for offset to start */
2929{
2930 int col;
2931
2932 if (spp->sp_off_flags & (1 << idx))
2933 {
2934 result->lnum = regmatch->startpos[0].lnum;
2935 col = regmatch->startpos[0].col + extra;
2936 }
2937 else
2938 {
2939 result->lnum = regmatch->endpos[0].lnum;
2940 col = regmatch->endpos[0].col;
2941 }
2942 col += spp->sp_offsets[idx];
2943 if (col < 0)
2944 result->col = 0;
2945 else
2946 result->col = col;
2947}
2948
2949/*
2950 * Add offset to matched text for start of match or highlight.
2951 * Avoid resulting column to become negative.
2952 */
2953 static void
2954syn_add_start_off(result, regmatch, spp, idx, extra)
2955 lpos_T *result; /* returned position */
2956 regmmatch_T *regmatch; /* start/end of match */
2957 synpat_T *spp;
2958 int idx;
2959 int extra; /* extra chars for offset to end */
2960{
2961 int col;
2962
2963 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
2964 {
2965 result->lnum = regmatch->endpos[0].lnum;
2966 col = regmatch->endpos[0].col + extra;
2967 }
2968 else
2969 {
2970 result->lnum = regmatch->startpos[0].lnum;
2971 col = regmatch->startpos[0].col;
2972 }
2973 col += spp->sp_offsets[idx];
2974 if (col < 0)
2975 result->col = 0;
2976 else
2977 result->col = col;
2978}
2979
2980/*
2981 * Get current line in syntax buffer.
2982 */
2983 static char_u *
2984syn_getcurline()
2985{
2986 return ml_get_buf(syn_buf, current_lnum, FALSE);
2987}
2988
2989/*
2990 * Call vim_regexec() to match in syn_buf.
2991 * Returns TRUE when there is a match.
2992 */
2993 static int
2994syn_regexec(rmp, lnum, col)
2995 regmmatch_T *rmp;
2996 linenr_T lnum;
2997 colnr_T col;
2998{
2999 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col) > 0)
3000 {
3001 rmp->startpos[0].lnum += lnum;
3002 rmp->endpos[0].lnum += lnum;
3003 return TRUE;
3004 }
3005 return FALSE;
3006}
3007
3008/*
3009 * Check one position in a line for a matching keyword.
3010 * The caller must check if a keyword can start at startcol.
3011 * Return it's ID if found, 0 otherwise.
3012 */
3013 static int
3014check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
3015 char_u *line;
3016 int startcol; /* position in line to check for keyword */
3017 int *endcolp; /* return: character after found keyword */
3018 long *flagsp; /* return: flags of matching keyword */
3019 short **next_listp; /* return: next_list of matching keyword */
3020 stateitem_T *cur_si; /* item at the top of the stack */
3021{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003022 keyentry_T *kp;
3023 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003024 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003025 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003026 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003027 hashtab_T *ht;
3028 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003029
3030 /* Find first character after the keyword. First character was already
3031 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003032 kwp = line + startcol;
3033 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003034 do
3035 {
3036#ifdef FEAT_MBYTE
3037 if (has_mbyte)
Bram Moolenaardad6b692005-01-25 22:14:34 +00003038 kwlen += (*mb_ptr2len_check)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003039 else
3040#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003041 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003042 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003043 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003044
Bram Moolenaardad6b692005-01-25 22:14:34 +00003045 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003046 return 0;
3047
3048 /*
3049 * Must make a copy of the keyword, so we can add a NUL and make it
3050 * lowercase.
3051 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003052 STRNCPY(keyword, kwp, kwlen);
3053 keyword[kwlen] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003054
3055 /*
3056 * Try twice:
3057 * 1. matching case
3058 * 2. ignoring case
3059 */
3060 for (round = 1; round <= 2; ++round)
3061 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003062 ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
3063 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003065 if (round == 2) /* ignore case */
3066 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067
3068 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003069 * Find keywords that match. There can be several with different
3070 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003071 * When current_next_list is non-zero accept only that group, otherwise:
3072 * Accept a not-contained keyword at toplevel.
3073 * Accept a keyword at other levels only if it is in the contains list.
3074 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003075 hi = hash_find(ht, keyword);
3076 if (!HASHITEM_EMPTY(hi))
3077 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003078 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003079 if (current_next_list != 0
3080 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3081 : (cur_si == NULL
3082 ? !(kp->flags & HL_CONTAINED)
3083 : in_id_list(cur_si, cur_si->si_cont_list,
3084 &kp->k_syn, kp->flags & HL_CONTAINED)))
3085 {
3086 *endcolp = startcol + kwlen;
3087 *flagsp = kp->flags;
3088 *next_listp = kp->next_list;
3089 return kp->k_syn.id;
3090 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003091 }
3092 }
3093 return 0;
3094}
3095
3096/*
3097 * Handle ":syntax case" command.
3098 */
3099/* ARGSUSED */
3100 static void
3101syn_cmd_case(eap, syncing)
3102 exarg_T *eap;
3103 int syncing; /* not used */
3104{
3105 char_u *arg = eap->arg;
3106 char_u *next;
3107
3108 eap->nextcmd = find_nextcmd(arg);
3109 if (eap->skip)
3110 return;
3111
3112 next = skiptowhite(arg);
3113 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3114 curbuf->b_syn_ic = FALSE;
3115 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3116 curbuf->b_syn_ic = TRUE;
3117 else
3118 EMSG2(_("E390: Illegal argument: %s"), arg);
3119}
3120
3121/*
3122 * Clear all syntax info for one buffer.
3123 */
3124 void
3125syntax_clear(buf)
3126 buf_T *buf;
3127{
3128 int i;
3129
3130 curbuf->b_syn_ic = FALSE; /* Use case, by default */
3131 curbuf->b_syn_containedin = FALSE;
3132
3133 /* free the keywords */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003134 clear_keywtab(&buf->b_keywtab);
3135 clear_keywtab(&buf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003136
3137 /* free the syntax patterns */
3138 for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3139 syn_clear_pattern(buf, i);
3140 ga_clear(&buf->b_syn_patterns);
3141
3142 /* free the syntax clusters */
3143 for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3144 syn_clear_cluster(buf, i);
3145 ga_clear(&buf->b_syn_clusters);
3146
3147 buf->b_syn_sync_flags = 0;
3148 buf->b_syn_sync_minlines = 0;
3149 buf->b_syn_sync_maxlines = 0;
3150 buf->b_syn_sync_linebreaks = 0;
3151
3152 vim_free(buf->b_syn_linecont_prog);
3153 buf->b_syn_linecont_prog = NULL;
3154 vim_free(buf->b_syn_linecont_pat);
3155 buf->b_syn_linecont_pat = NULL;
3156#ifdef FEAT_FOLDING
3157 buf->b_syn_folditems = 0;
3158#endif
3159
3160 /* free the stored states */
3161 syn_stack_free_all(buf);
3162 invalidate_current_state();
3163}
3164
3165/*
3166 * Clear syncing info for one buffer.
3167 */
3168 static void
3169syntax_sync_clear()
3170{
3171 int i;
3172
3173 /* free the syntax patterns */
3174 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3175 if (SYN_ITEMS(curbuf)[i].sp_syncing)
3176 syn_remove_pattern(curbuf, i);
3177
3178 curbuf->b_syn_sync_flags = 0;
3179 curbuf->b_syn_sync_minlines = 0;
3180 curbuf->b_syn_sync_maxlines = 0;
3181 curbuf->b_syn_sync_linebreaks = 0;
3182
3183 vim_free(curbuf->b_syn_linecont_prog);
3184 curbuf->b_syn_linecont_prog = NULL;
3185 vim_free(curbuf->b_syn_linecont_pat);
3186 curbuf->b_syn_linecont_pat = NULL;
3187
3188 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3189}
3190
3191/*
3192 * Remove one pattern from the buffer's pattern list.
3193 */
3194 static void
3195syn_remove_pattern(buf, idx)
3196 buf_T *buf;
3197 int idx;
3198{
3199 synpat_T *spp;
3200
3201 spp = &(SYN_ITEMS(buf)[idx]);
3202#ifdef FEAT_FOLDING
3203 if (spp->sp_flags & HL_FOLD)
3204 --buf->b_syn_folditems;
3205#endif
3206 syn_clear_pattern(buf, idx);
3207 mch_memmove(spp, spp + 1,
3208 sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3209 --buf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003210}
3211
3212/*
3213 * Clear and free one syntax pattern. When clearing all, must be called from
3214 * last to first!
3215 */
3216 static void
3217syn_clear_pattern(buf, i)
3218 buf_T *buf;
3219 int i;
3220{
3221 vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3222 vim_free(SYN_ITEMS(buf)[i].sp_prog);
3223 /* Only free sp_cont_list and sp_next_list of first start pattern */
3224 if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3225 {
3226 vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3227 vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3228 }
3229}
3230
3231/*
3232 * Clear and free one syntax cluster.
3233 */
3234 static void
3235syn_clear_cluster(buf, i)
3236 buf_T *buf;
3237 int i;
3238{
3239 vim_free(SYN_CLSTR(buf)[i].scl_name);
3240 vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3241 vim_free(SYN_CLSTR(buf)[i].scl_list);
3242}
3243
3244/*
3245 * Handle ":syntax clear" command.
3246 */
3247 static void
3248syn_cmd_clear(eap, syncing)
3249 exarg_T *eap;
3250 int syncing;
3251{
3252 char_u *arg = eap->arg;
3253 char_u *arg_end;
3254 int id;
3255
3256 eap->nextcmd = find_nextcmd(arg);
3257 if (eap->skip)
3258 return;
3259
3260 /*
3261 * We have to disable this within ":syn include @group filename",
3262 * because otherwise @group would get deleted.
3263 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3264 * clear".
3265 */
3266 if (curbuf->b_syn_topgrp != 0)
3267 return;
3268
3269 if (ends_excmd(*arg))
3270 {
3271 /*
3272 * No argument: Clear all syntax items.
3273 */
3274 if (syncing)
3275 syntax_sync_clear();
3276 else
3277 {
3278 syntax_clear(curbuf);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003279 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003280 }
3281 }
3282 else
3283 {
3284 /*
3285 * Clear the group IDs that are in the argument.
3286 */
3287 while (!ends_excmd(*arg))
3288 {
3289 arg_end = skiptowhite(arg);
3290 if (*arg == '@')
3291 {
3292 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3293 if (id == 0)
3294 {
3295 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3296 break;
3297 }
3298 else
3299 {
3300 /*
3301 * We can't physically delete a cluster without changing
3302 * the IDs of other clusters, so we do the next best thing
3303 * and make it empty.
3304 */
3305 short scl_id = id - SYNID_CLUSTER;
3306
3307 vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3308 SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3309 }
3310 }
3311 else
3312 {
3313 id = syn_namen2id(arg, (int)(arg_end - arg));
3314 if (id == 0)
3315 {
3316 EMSG2(_(e_nogroup), arg);
3317 break;
3318 }
3319 else
3320 syn_clear_one(id, syncing);
3321 }
3322 arg = skipwhite(arg_end);
3323 }
3324 }
3325 redraw_curbuf_later(NOT_VALID);
3326 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3327}
3328
3329/*
3330 * Clear one syntax group for the current buffer.
3331 */
3332 static void
3333syn_clear_one(id, syncing)
3334 int id;
3335 int syncing;
3336{
3337 synpat_T *spp;
3338 int idx;
3339
3340 /* Clear keywords only when not ":syn sync clear group-name" */
3341 if (!syncing)
3342 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003343 (void)syn_clear_keyword(id, &curbuf->b_keywtab);
3344 (void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003345 }
3346
3347 /* clear the patterns for "id" */
3348 for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3349 {
3350 spp = &(SYN_ITEMS(curbuf)[idx]);
3351 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3352 continue;
3353 syn_remove_pattern(curbuf, idx);
3354 }
3355}
3356
3357/*
3358 * Handle ":syntax on" command.
3359 */
3360/* ARGSUSED */
3361 static void
3362syn_cmd_on(eap, syncing)
3363 exarg_T *eap;
3364 int syncing; /* not used */
3365{
3366 syn_cmd_onoff(eap, "syntax");
3367}
3368
3369/*
3370 * Handle ":syntax enable" command.
3371 */
3372/* ARGSUSED */
3373 static void
3374syn_cmd_enable(eap, syncing)
3375 exarg_T *eap;
3376 int syncing; /* not used */
3377{
3378 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3379 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003380 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381}
3382
3383/*
3384 * Handle ":syntax reset" command.
3385 */
3386/* ARGSUSED */
3387 static void
3388syn_cmd_reset(eap, syncing)
3389 exarg_T *eap;
3390 int syncing; /* not used */
3391{
3392 eap->nextcmd = check_nextcmd(eap->arg);
3393 if (!eap->skip)
3394 {
3395 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3396 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003397 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003398 }
3399}
3400
3401/*
3402 * Handle ":syntax manual" command.
3403 */
3404/* ARGSUSED */
3405 static void
3406syn_cmd_manual(eap, syncing)
3407 exarg_T *eap;
3408 int syncing; /* not used */
3409{
3410 syn_cmd_onoff(eap, "manual");
3411}
3412
3413/*
3414 * Handle ":syntax off" command.
3415 */
3416/* ARGSUSED */
3417 static void
3418syn_cmd_off(eap, syncing)
3419 exarg_T *eap;
3420 int syncing; /* not used */
3421{
3422 syn_cmd_onoff(eap, "nosyntax");
3423}
3424
3425 static void
3426syn_cmd_onoff(eap, name)
3427 exarg_T *eap;
3428 char *name;
3429{
3430 char_u buf[100];
3431
3432 eap->nextcmd = check_nextcmd(eap->arg);
3433 if (!eap->skip)
3434 {
3435 STRCPY(buf, "so ");
3436 sprintf((char *)buf + 3, SYNTAX_FNAME, name);
3437 do_cmdline_cmd(buf);
3438 }
3439}
3440
3441/*
3442 * Handle ":syntax [list]" command: list current syntax words.
3443 */
3444 static void
3445syn_cmd_list(eap, syncing)
3446 exarg_T *eap;
3447 int syncing; /* when TRUE: list syncing items */
3448{
3449 char_u *arg = eap->arg;
3450 int id;
3451 char_u *arg_end;
3452
3453 eap->nextcmd = find_nextcmd(arg);
3454 if (eap->skip)
3455 return;
3456
3457 if (!syntax_present(curbuf))
3458 {
3459 MSG(_("No Syntax items defined for this buffer"));
3460 return;
3461 }
3462
3463 if (syncing)
3464 {
3465 if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3466 {
3467 MSG_PUTS(_("syncing on C-style comments"));
3468 syn_lines_msg();
3469 syn_match_msg();
3470 return;
3471 }
3472 else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3473 {
3474 if (curbuf->b_syn_sync_minlines == 0)
3475 MSG_PUTS(_("no syncing"));
3476 else
3477 {
3478 MSG_PUTS(_("syncing starts "));
3479 msg_outnum(curbuf->b_syn_sync_minlines);
3480 MSG_PUTS(_(" lines before top line"));
3481 syn_match_msg();
3482 }
3483 return;
3484 }
3485 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3486 if (curbuf->b_syn_sync_minlines > 0
3487 || curbuf->b_syn_sync_maxlines > 0
3488 || curbuf->b_syn_sync_linebreaks > 0)
3489 {
3490 MSG_PUTS(_("\nsyncing on items"));
3491 syn_lines_msg();
3492 syn_match_msg();
3493 }
3494 }
3495 else
3496 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3497 if (ends_excmd(*arg))
3498 {
3499 /*
3500 * No argument: List all group IDs and all syntax clusters.
3501 */
3502 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3503 syn_list_one(id, syncing, FALSE);
3504 for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3505 syn_list_cluster(id);
3506 }
3507 else
3508 {
3509 /*
3510 * List the group IDs and syntax clusters that are in the argument.
3511 */
3512 while (!ends_excmd(*arg) && !got_int)
3513 {
3514 arg_end = skiptowhite(arg);
3515 if (*arg == '@')
3516 {
3517 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3518 if (id == 0)
3519 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3520 else
3521 syn_list_cluster(id - SYNID_CLUSTER);
3522 }
3523 else
3524 {
3525 id = syn_namen2id(arg, (int)(arg_end - arg));
3526 if (id == 0)
3527 EMSG2(_(e_nogroup), arg);
3528 else
3529 syn_list_one(id, syncing, TRUE);
3530 }
3531 arg = skipwhite(arg_end);
3532 }
3533 }
3534 eap->nextcmd = check_nextcmd(arg);
3535}
3536
3537 static void
3538syn_lines_msg()
3539{
3540 if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3541 {
3542 MSG_PUTS("; ");
3543 if (curbuf->b_syn_sync_minlines > 0)
3544 {
3545 MSG_PUTS(_("minimal "));
3546 msg_outnum(curbuf->b_syn_sync_minlines);
3547 if (curbuf->b_syn_sync_maxlines)
3548 MSG_PUTS(", ");
3549 }
3550 if (curbuf->b_syn_sync_maxlines > 0)
3551 {
3552 MSG_PUTS(_("maximal "));
3553 msg_outnum(curbuf->b_syn_sync_maxlines);
3554 }
3555 MSG_PUTS(_(" lines before top line"));
3556 }
3557}
3558
3559 static void
3560syn_match_msg()
3561{
3562 if (curbuf->b_syn_sync_linebreaks > 0)
3563 {
3564 MSG_PUTS(_("; match "));
3565 msg_outnum(curbuf->b_syn_sync_linebreaks);
3566 MSG_PUTS(_(" line breaks"));
3567 }
3568}
3569
3570static int last_matchgroup;
3571
3572struct name_list
3573{
3574 int flag;
3575 char *name;
3576};
3577
3578static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3579
3580/*
3581 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3582 */
3583 static void
3584syn_list_one(id, syncing, link_only)
3585 int id;
3586 int syncing; /* when TRUE: list syncing items */
3587 int link_only; /* when TRUE; list link-only too */
3588{
3589 int attr;
3590 int idx;
3591 int did_header = FALSE;
3592 synpat_T *spp;
3593 static struct name_list namelist1[] =
3594 {
3595 {HL_DISPLAY, "display"},
3596 {HL_CONTAINED, "contained"},
3597 {HL_ONELINE, "oneline"},
3598 {HL_KEEPEND, "keepend"},
3599 {HL_EXTEND, "extend"},
3600 {HL_EXCLUDENL, "excludenl"},
3601 {HL_TRANSP, "transparent"},
3602 {HL_FOLD, "fold"},
3603 {0, NULL}
3604 };
3605 static struct name_list namelist2[] =
3606 {
3607 {HL_SKIPWHITE, "skipwhite"},
3608 {HL_SKIPNL, "skipnl"},
3609 {HL_SKIPEMPTY, "skipempty"},
3610 {0, NULL}
3611 };
3612
3613 attr = hl_attr(HLF_D); /* highlight like directories */
3614
3615 /* list the keywords for "id" */
3616 if (!syncing)
3617 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003618 did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3619 did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620 did_header, attr);
3621 }
3622
3623 /* list the patterns for "id" */
3624 for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3625 {
3626 spp = &(SYN_ITEMS(curbuf)[idx]);
3627 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3628 continue;
3629
3630 (void)syn_list_header(did_header, 999, id);
3631 did_header = TRUE;
3632 last_matchgroup = 0;
3633 if (spp->sp_type == SPTYPE_MATCH)
3634 {
3635 put_pattern("match", ' ', spp, attr);
3636 msg_putchar(' ');
3637 }
3638 else if (spp->sp_type == SPTYPE_START)
3639 {
3640 while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3641 put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3642 if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3643 put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3644 while (idx < curbuf->b_syn_patterns.ga_len
3645 && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3646 put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3647 --idx;
3648 msg_putchar(' ');
3649 }
3650 syn_list_flags(namelist1, spp->sp_flags, attr);
3651
3652 if (spp->sp_cont_list != NULL)
3653 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3654
3655 if (spp->sp_syn.cont_in_list != NULL)
3656 put_id_list((char_u *)"containedin",
3657 spp->sp_syn.cont_in_list, attr);
3658
3659 if (spp->sp_next_list != NULL)
3660 {
3661 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3662 syn_list_flags(namelist2, spp->sp_flags, attr);
3663 }
3664 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3665 {
3666 if (spp->sp_flags & HL_SYNC_HERE)
3667 msg_puts_attr((char_u *)"grouphere", attr);
3668 else
3669 msg_puts_attr((char_u *)"groupthere", attr);
3670 msg_putchar(' ');
3671 if (spp->sp_sync_idx >= 0)
3672 msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3673 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3674 else
3675 MSG_PUTS("NONE");
3676 msg_putchar(' ');
3677 }
3678 }
3679
3680 /* list the link, if there is one */
3681 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3682 {
3683 (void)syn_list_header(did_header, 999, id);
3684 msg_puts_attr((char_u *)"links to", attr);
3685 msg_putchar(' ');
3686 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3687 }
3688}
3689
3690 static void
3691syn_list_flags(nl, flags, attr)
3692 struct name_list *nl;
3693 int flags;
3694 int attr;
3695{
3696 int i;
3697
3698 for (i = 0; nl[i].flag != 0; ++i)
3699 if (flags & nl[i].flag)
3700 {
3701 msg_puts_attr((char_u *)nl[i].name, attr);
3702 msg_putchar(' ');
3703 }
3704}
3705
3706/*
3707 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3708 */
3709 static void
3710syn_list_cluster(id)
3711 int id;
3712{
3713 int endcol = 15;
3714
3715 /* slight hack: roughly duplicate the guts of syn_list_header() */
3716 msg_putchar('\n');
3717 msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3718
3719 if (msg_col >= endcol) /* output at least one space */
3720 endcol = msg_col + 1;
3721 if (Columns <= endcol) /* avoid hang for tiny window */
3722 endcol = Columns - 1;
3723
3724 msg_advance(endcol);
3725 if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3726 {
3727 put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3728 hl_attr(HLF_D));
3729 }
3730 else
3731 {
3732 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3733 msg_puts((char_u *)"=NONE");
3734 }
3735}
3736
3737 static void
3738put_id_list(name, list, attr)
3739 char_u *name;
3740 short *list;
3741 int attr;
3742{
3743 short *p;
3744
3745 msg_puts_attr(name, attr);
3746 msg_putchar('=');
3747 for (p = list; *p; ++p)
3748 {
3749 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3750 {
3751 if (p[1])
3752 MSG_PUTS("ALLBUT");
3753 else
3754 MSG_PUTS("ALL");
3755 }
3756 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3757 {
3758 MSG_PUTS("TOP");
3759 }
3760 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3761 {
3762 MSG_PUTS("CONTAINED");
3763 }
3764 else if (*p >= SYNID_CLUSTER)
3765 {
3766 short scl_id = *p - SYNID_CLUSTER;
3767
3768 msg_putchar('@');
3769 msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3770 }
3771 else
3772 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3773 if (p[1])
3774 msg_putchar(',');
3775 }
3776 msg_putchar(' ');
3777}
3778
3779 static void
3780put_pattern(s, c, spp, attr)
3781 char *s;
3782 int c;
3783 synpat_T *spp;
3784 int attr;
3785{
3786 long n;
3787 int mask;
3788 int first;
3789 static char *sepchars = "/+=-#@\"|'^&";
3790 int i;
3791
3792 /* May have to write "matchgroup=group" */
3793 if (last_matchgroup != spp->sp_syn_match_id)
3794 {
3795 last_matchgroup = spp->sp_syn_match_id;
3796 msg_puts_attr((char_u *)"matchgroup", attr);
3797 msg_putchar('=');
3798 if (last_matchgroup == 0)
3799 msg_outtrans((char_u *)"NONE");
3800 else
3801 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3802 msg_putchar(' ');
3803 }
3804
3805 /* output the name of the pattern and an '=' or ' ' */
3806 msg_puts_attr((char_u *)s, attr);
3807 msg_putchar(c);
3808
3809 /* output the pattern, in between a char that is not in the pattern */
3810 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3811 if (sepchars[++i] == NUL)
3812 {
3813 i = 0; /* no good char found, just use the first one */
3814 break;
3815 }
3816 msg_putchar(sepchars[i]);
3817 msg_outtrans(spp->sp_pattern);
3818 msg_putchar(sepchars[i]);
3819
3820 /* output any pattern options */
3821 first = TRUE;
3822 for (i = 0; i < SPO_COUNT; ++i)
3823 {
3824 mask = (1 << i);
3825 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3826 {
3827 if (!first)
3828 msg_putchar(','); /* separate with commas */
3829 msg_puts((char_u *)spo_name_tab[i]);
3830 n = spp->sp_offsets[i];
3831 if (i != SPO_LC_OFF)
3832 {
3833 if (spp->sp_off_flags & mask)
3834 msg_putchar('s');
3835 else
3836 msg_putchar('e');
3837 if (n > 0)
3838 msg_putchar('+');
3839 }
3840 if (n || i == SPO_LC_OFF)
3841 msg_outnum(n);
3842 first = FALSE;
3843 }
3844 }
3845 msg_putchar(' ');
3846}
3847
3848/*
3849 * List or clear the keywords for one syntax group.
3850 * Return TRUE if the header has been printed.
3851 */
3852 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00003853syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003854 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003855 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003856 int did_header; /* header has already been printed */
3857 int attr;
3858{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003860 hashitem_T *hi;
3861 keyentry_T *kp;
3862 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003863 int prev_contained = 0;
3864 short *prev_next_list = NULL;
3865 short *prev_cont_in_list = NULL;
3866 int prev_skipnl = 0;
3867 int prev_skipwhite = 0;
3868 int prev_skipempty = 0;
3869
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870 /*
3871 * Unfortunately, this list of keywords is not sorted on alphabet but on
3872 * hash value...
3873 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003874 todo = ht->ht_used;
3875 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003876 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003877 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003878 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003879 --todo;
3880 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003881 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003882 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003883 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003884 if (prev_contained != (kp->flags & HL_CONTAINED)
3885 || prev_skipnl != (kp->flags & HL_SKIPNL)
3886 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
3887 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
3888 || prev_cont_in_list != kp->k_syn.cont_in_list
3889 || prev_next_list != kp->next_list)
3890 outlen = 9999;
3891 else
3892 outlen = (int)STRLEN(kp->keyword);
3893 /* output "contained" and "nextgroup" on each line */
3894 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003895 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003896 prev_contained = 0;
3897 prev_next_list = NULL;
3898 prev_cont_in_list = NULL;
3899 prev_skipnl = 0;
3900 prev_skipwhite = 0;
3901 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003902 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003903 did_header = TRUE;
3904 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003905 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003906 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00003908 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003910 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003912 put_id_list((char_u *)"containedin",
3913 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00003915 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003916 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003917 if (kp->next_list != prev_next_list)
3918 {
3919 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
3920 msg_putchar(' ');
3921 prev_next_list = kp->next_list;
3922 if (kp->flags & HL_SKIPNL)
3923 {
3924 msg_puts_attr((char_u *)"skipnl", attr);
3925 msg_putchar(' ');
3926 prev_skipnl = (kp->flags & HL_SKIPNL);
3927 }
3928 if (kp->flags & HL_SKIPWHITE)
3929 {
3930 msg_puts_attr((char_u *)"skipwhite", attr);
3931 msg_putchar(' ');
3932 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
3933 }
3934 if (kp->flags & HL_SKIPEMPTY)
3935 {
3936 msg_puts_attr((char_u *)"skipempty", attr);
3937 msg_putchar(' ');
3938 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
3939 }
3940 }
3941 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003942 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003943 }
3944 }
3945 }
3946
3947 return did_header;
3948}
3949
3950 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00003951syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003953 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003955 hashitem_T *hi;
3956 keyentry_T *kp;
3957 keyentry_T *kp_prev;
3958 keyentry_T *kp_next;
3959 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960
Bram Moolenaardad6b692005-01-25 22:14:34 +00003961 hash_lock(ht);
3962 todo = ht->ht_used;
3963 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003964 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003965 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003966 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003967 --todo;
3968 kp_prev = NULL;
3969 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003971 if (kp->k_syn.id == id)
3972 {
3973 kp_next = kp->ke_next;
3974 if (kp_prev == NULL)
3975 {
3976 if (kp_next == NULL)
3977 hash_remove(ht, hi);
3978 else
3979 hi->hi_key = KE2HIKEY(kp_next);
3980 }
3981 else
3982 kp_prev->ke_next = kp_next;
3983 vim_free(kp->next_list);
3984 vim_free(kp->k_syn.cont_in_list);
3985 vim_free(kp);
3986 kp = kp_next;
3987 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003989 {
3990 kp_prev = kp;
3991 kp = kp->ke_next;
3992 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003993 }
3994 }
3995 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003996 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003997}
3998
3999/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004000 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004001 */
4002 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004003clear_keywtab(ht)
4004 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004006 hashitem_T *hi;
4007 int todo;
4008 keyentry_T *kp;
4009 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010
Bram Moolenaardad6b692005-01-25 22:14:34 +00004011 todo = ht->ht_used;
4012 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004013 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004014 if (!HASHITEM_EMPTY(hi))
4015 {
4016 --todo;
4017 kp = HI2KE(hi);
4018 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004019 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004020 kp_next = kp->ke_next;
4021 vim_free(kp->next_list);
4022 vim_free(kp->k_syn.cont_in_list);
4023 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004024 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004025 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004026 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004027 hash_clear(ht);
4028 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029}
4030
4031/*
4032 * Add a keyword to the list of keywords.
4033 */
4034 static void
4035add_keyword(name, id, flags, cont_in_list, next_list)
4036 char_u *name; /* name of keyword */
4037 int id; /* group ID for this keyword */
4038 int flags; /* flags for this keyword */
4039 short *cont_in_list; /* containedin for this keyword */
4040 short *next_list; /* nextgroup for this keyword */
4041{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004042 keyentry_T *kp;
4043 hashtab_T *ht;
4044 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004045 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004046 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004047 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004048
4049 if (curbuf->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004050 name_ic = str_foldcase(name, (int)STRLEN(name),
4051 name_folded, MAXKEYWLEN + 1);
4052 else
4053 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004054 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4055 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004057 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004058 kp->k_syn.id = id;
4059 kp->k_syn.inc_tag = current_syn_inc_tag;
4060 kp->flags = flags;
4061 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004062 if (cont_in_list != NULL)
4063 curbuf->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004064 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004065
4066 if (curbuf->b_syn_ic)
Bram Moolenaardad6b692005-01-25 22:14:34 +00004067 ht = &curbuf->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004068 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004069 ht = &curbuf->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004070
Bram Moolenaardad6b692005-01-25 22:14:34 +00004071 hash = hash_hash(kp->keyword);
4072 hi = hash_lookup(ht, kp->keyword, hash);
4073 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004074 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004075 /* new keyword, add to hashtable */
4076 kp->ke_next = NULL;
4077 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004079 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004080 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004081 /* keyword already exists, prepend to list */
4082 kp->ke_next = HI2KE(hi);
4083 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004084 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085}
4086
4087/*
4088 * Get the start and end of the group name argument.
4089 * Return a pointer to the first argument.
4090 * Return NULL if the end of the command was found instead of further args.
4091 */
4092 static char_u *
4093get_group_name(arg, name_end)
4094 char_u *arg; /* start of the argument */
4095 char_u **name_end; /* pointer to end of the name */
4096{
4097 char_u *rest;
4098
4099 *name_end = skiptowhite(arg);
4100 rest = skipwhite(*name_end);
4101
4102 /*
4103 * Check if there are enough arguments. The first argument may be a
4104 * pattern, where '|' is allowed, so only check for NUL.
4105 */
4106 if (ends_excmd(*arg) || *rest == NUL)
4107 return NULL;
4108 return rest;
4109}
4110
4111/*
4112 * Check for syntax command option arguments.
4113 * This can be called at any place in the list of arguments, and just picks
4114 * out the arguments that are known. Can be called several times in a row to
4115 * collect all options in between other arguments.
4116 * Return a pointer to the next argument (which isn't an option).
4117 * Return NULL for any error;
4118 */
4119 static char_u *
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004120get_syn_options(arg, opt)
4121 char_u *arg; /* next argument to be checked */
4122 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004123{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124 char_u *gname_start, *gname;
4125 int syn_id;
4126 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004127 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004128 int i;
4129 int fidx;
4130 static struct flag
4131 {
4132 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004133 int argtype;
4134 int flags;
4135 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4136 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4137 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4138 {"eExXtTeEnNdD", 0, HL_EXTEND},
4139 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4140 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4141 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4142 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4143 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4144 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4145 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4146 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4147 {"fFoOlLdD", 0, HL_FOLD},
4148 {"cCoOnNtTaAiInNsS", 1, 0},
4149 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4150 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004151 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004152 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153
4154 if (arg == NULL) /* already detected error */
4155 return NULL;
4156
Bram Moolenaar071d4272004-06-13 20:20:40 +00004157 for (;;)
4158 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004159 /*
4160 * This is used very often when a large number of keywords is defined.
4161 * Need to skip quickly when no option name is found.
4162 * Also avoid tolower(), it's slow.
4163 */
4164 if (strchr(first_letters, *arg) == NULL)
4165 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166
4167 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4168 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004169 p = flagtab[fidx].name;
4170 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4171 if (arg[len] != p[i] && arg[len] != p[i + 1])
4172 break;
4173 if (p[i] == NUL && (vim_iswhite(arg[len])
4174 || (flagtab[fidx].argtype > 0
4175 ? arg[len] == '='
4176 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004177 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004178 if (opt->keyword
4179 && (flagtab[fidx].flags == HL_DISPLAY
4180 || flagtab[fidx].flags == HL_FOLD
4181 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 /* treat "display", "fold" and "extend" as a keyword */
4183 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004184 break;
4185 }
4186 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004187 if (fidx < 0) /* no match found */
4188 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004189
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004190 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004191 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004192 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193 {
4194 EMSG(_("E395: contains argument not accepted here"));
4195 return NULL;
4196 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004197 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004198 return NULL;
4199 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004200 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004201 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004202#if 0 /* cannot happen */
4203 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004204 {
4205 EMSG(_("E396: containedin argument not accepted here"));
4206 return NULL;
4207 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004208#endif
4209 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004210 return NULL;
4211 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004212 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004213 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004214 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004215 return NULL;
4216 }
4217 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004218 {
4219 opt->flags |= flagtab[fidx].flags;
4220 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004222 if (flagtab[fidx].flags == HL_SYNC_HERE
4223 || flagtab[fidx].flags == HL_SYNC_THERE)
4224 {
4225 if (opt->sync_idx == NULL)
4226 {
4227 EMSG(_("E393: group[t]here not accepted here"));
4228 return NULL;
4229 }
4230 gname_start = arg;
4231 arg = skiptowhite(arg);
4232 if (gname_start == arg)
4233 return NULL;
4234 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4235 if (gname == NULL)
4236 return NULL;
4237 if (STRCMP(gname, "NONE") == 0)
4238 *opt->sync_idx = NONE_IDX;
4239 else
4240 {
4241 syn_id = syn_name2id(gname);
4242 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4243 if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4244 && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4245 {
4246 *opt->sync_idx = i;
4247 break;
4248 }
4249 if (i < 0)
4250 {
4251 EMSG2(_("E394: Didn't find region item for %s"), gname);
4252 vim_free(gname);
4253 return NULL;
4254 }
4255 }
4256
4257 vim_free(gname);
4258 arg = skipwhite(arg);
4259 }
4260#ifdef FEAT_FOLDING
4261 else if (flagtab[fidx].flags == HL_FOLD
4262 && foldmethodIsSyntax(curwin))
4263 /* Need to update folds later. */
4264 foldUpdateAll(curwin);
4265#endif
4266 }
4267 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268
4269 return arg;
4270}
4271
4272/*
4273 * Adjustments to syntax item when declared in a ":syn include"'d file.
4274 * Set the contained flag, and if the item is not already contained, add it
4275 * to the specified top-level group, if any.
4276 */
4277 static void
4278syn_incl_toplevel(id, flagsp)
4279 int id;
4280 int *flagsp;
4281{
4282 if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4283 return;
4284 *flagsp |= HL_CONTAINED;
4285 if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4286 {
4287 /* We have to alloc this, because syn_combine_list() will free it. */
4288 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4289 int tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4290
4291 if (grp_list != NULL)
4292 {
4293 grp_list[0] = id;
4294 grp_list[1] = 0;
4295 syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4296 CLUSTER_ADD);
4297 }
4298 }
4299}
4300
4301/*
4302 * Handle ":syntax include [@{group-name}] filename" command.
4303 */
4304/* ARGSUSED */
4305 static void
4306syn_cmd_include(eap, syncing)
4307 exarg_T *eap;
4308 int syncing; /* not used */
4309{
4310 char_u *arg = eap->arg;
4311 int sgl_id = 1;
4312 char_u *group_name_end;
4313 char_u *rest;
4314 char_u *errormsg = NULL;
4315 int prev_toplvl_grp;
4316 int prev_syn_inc_tag;
4317 int source = FALSE;
4318
4319 eap->nextcmd = find_nextcmd(arg);
4320 if (eap->skip)
4321 return;
4322
4323 if (arg[0] == '@')
4324 {
4325 ++arg;
4326 rest = get_group_name(arg, &group_name_end);
4327 if (rest == NULL)
4328 {
4329 EMSG((char_u *)_("E397: Filename required"));
4330 return;
4331 }
4332 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4333 /* separate_nextcmd() and expand_filename() depend on this */
4334 eap->arg = rest;
4335 }
4336
4337 /*
4338 * Everything that's left, up to the next command, should be the
4339 * filename to include.
4340 */
4341 eap->argt |= (XFILE | NOSPC);
4342 separate_nextcmd(eap);
4343 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4344 {
4345 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4346 * file. Need to expand the file name first. In other cases
4347 * ":runtime!" is used. */
4348 source = TRUE;
4349 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4350 {
4351 if (errormsg != NULL)
4352 EMSG(errormsg);
4353 return;
4354 }
4355 }
4356
4357 /*
4358 * Save and restore the existing top-level grouplist id and ":syn
4359 * include" tag around the actual inclusion.
4360 */
4361 prev_syn_inc_tag = current_syn_inc_tag;
4362 current_syn_inc_tag = ++running_syn_inc_tag;
4363 prev_toplvl_grp = curbuf->b_syn_topgrp;
4364 curbuf->b_syn_topgrp = sgl_id;
4365 if (source ? do_source(eap->arg, FALSE, FALSE) == FAIL
4366 : cmd_runtime(eap->arg, TRUE) == FAIL)
4367 EMSG2(_(e_notopen), eap->arg);
4368 curbuf->b_syn_topgrp = prev_toplvl_grp;
4369 current_syn_inc_tag = prev_syn_inc_tag;
4370}
4371
4372/*
4373 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4374 */
4375/* ARGSUSED */
4376 static void
4377syn_cmd_keyword(eap, syncing)
4378 exarg_T *eap;
4379 int syncing; /* not used */
4380{
4381 char_u *arg = eap->arg;
4382 char_u *group_name_end;
4383 int syn_id;
4384 char_u *rest;
4385 char_u *keyword_copy;
4386 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004387 char_u *kw;
4388 syn_opt_arg_T syn_opt_arg;
4389 int cnt;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004390
4391 rest = get_group_name(arg, &group_name_end);
4392
4393 if (rest != NULL)
4394 {
4395 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4396
4397 /* allocate a buffer, for removing the backslashes in the keyword */
4398 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4399 if (keyword_copy != NULL)
4400 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004401 syn_opt_arg.flags = 0;
4402 syn_opt_arg.keyword = TRUE;
4403 syn_opt_arg.sync_idx = NULL;
4404 syn_opt_arg.has_cont_list = FALSE;
4405 syn_opt_arg.cont_in_list = NULL;
4406 syn_opt_arg.next_list = NULL;
4407
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408 /*
4409 * The options given apply to ALL keywords, so all options must be
4410 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004411 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004412 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004413 cnt = 0;
4414 p = keyword_copy;
4415 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004417 rest = get_syn_options(rest, &syn_opt_arg);
4418 if (rest == NULL || ends_excmd(*rest))
4419 break;
4420 /* Copy the keyword, removing backslashes, and add a NUL. */
4421 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004422 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004423 if (*rest == '\\' && rest[1] != NUL)
4424 ++rest;
4425 *p++ = *rest++;
4426 }
4427 *p++ = NUL;
4428 ++cnt;
4429 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004430
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004431 if (!eap->skip)
4432 {
4433 /* Adjust flags for use of ":syn include". */
4434 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4435
4436 /*
4437 * 2: Add an entry for each keyword.
4438 */
4439 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4440 {
4441 for (p = vim_strchr(kw, '['); ; )
4442 {
4443 if (p != NULL)
4444 *p = NUL;
4445 add_keyword(kw, syn_id, syn_opt_arg.flags,
4446 syn_opt_arg.cont_in_list,
4447 syn_opt_arg.next_list);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004448 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004449 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004450 if (p[1] == NUL)
4451 {
4452 EMSG2(_("E747: Missing ']': %s"), kw);
4453 kw = p + 2; /* skip over the NUL */
4454 break;
4455 }
4456 if (p[1] == ']')
4457 {
4458 kw = p + 1; /* skip over the "]" */
4459 break;
4460 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004461#ifdef FEAT_MBYTE
4462 if (has_mbyte)
4463 {
4464 int l = (*mb_ptr2len_check)(p + 1);
4465
4466 mch_memmove(p, p + 1, l);
4467 p += l;
4468 }
4469 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004471 {
4472 p[0] = p[1];
4473 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474 }
4475 }
4476 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004477 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004478
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004480 vim_free(syn_opt_arg.cont_in_list);
4481 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004482 }
4483 }
4484
4485 if (rest != NULL)
4486 eap->nextcmd = check_nextcmd(rest);
4487 else
4488 EMSG2(_(e_invarg2), arg);
4489
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490 redraw_curbuf_later(NOT_VALID);
4491 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4492}
4493
4494/*
4495 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4496 *
4497 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4498 */
4499 static void
4500syn_cmd_match(eap, syncing)
4501 exarg_T *eap;
4502 int syncing; /* TRUE for ":syntax sync match .. " */
4503{
4504 char_u *arg = eap->arg;
4505 char_u *group_name_end;
4506 char_u *rest;
4507 synpat_T item; /* the item found in the line */
4508 int syn_id;
4509 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004510 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 int sync_idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004512
4513 /* Isolate the group name, check for validity */
4514 rest = get_group_name(arg, &group_name_end);
4515
4516 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004517 syn_opt_arg.flags = 0;
4518 syn_opt_arg.keyword = FALSE;
4519 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4520 syn_opt_arg.has_cont_list = TRUE;
4521 syn_opt_arg.cont_list = NULL;
4522 syn_opt_arg.cont_in_list = NULL;
4523 syn_opt_arg.next_list = NULL;
4524 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525
4526 /* get the pattern. */
4527 init_syn_patterns();
4528 vim_memset(&item, 0, sizeof(item));
4529 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004530 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4531 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004532
4533 /* Get options after the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004534 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004535
4536 if (rest != NULL) /* all arguments are valid */
4537 {
4538 /*
4539 * Check for trailing command and illegal trailing arguments.
4540 */
4541 eap->nextcmd = check_nextcmd(rest);
4542 if (!ends_excmd(*rest) || eap->skip)
4543 rest = NULL;
4544 else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4545 && (syn_id = syn_check_group(arg,
4546 (int)(group_name_end - arg))) != 0)
4547 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004548 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004549 /*
4550 * Store the pattern in the syn_items list
4551 */
4552 idx = curbuf->b_syn_patterns.ga_len;
4553 SYN_ITEMS(curbuf)[idx] = item;
4554 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4555 SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4556 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4557 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004558 SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004559 SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004560 SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4561 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4562 syn_opt_arg.cont_in_list;
4563 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004565 SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004566 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004567
4568 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004569 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570 curbuf->b_syn_sync_flags |= SF_MATCH;
4571#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004572 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004573 ++curbuf->b_syn_folditems;
4574#endif
4575
4576 redraw_curbuf_later(NOT_VALID);
4577 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4578 return; /* don't free the progs and patterns now */
4579 }
4580 }
4581
4582 /*
4583 * Something failed, free the allocated memory.
4584 */
4585 vim_free(item.sp_prog);
4586 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004587 vim_free(syn_opt_arg.cont_list);
4588 vim_free(syn_opt_arg.cont_in_list);
4589 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004590
4591 if (rest == NULL)
4592 EMSG2(_(e_invarg2), arg);
4593}
4594
4595/*
4596 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4597 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4598 */
4599 static void
4600syn_cmd_region(eap, syncing)
4601 exarg_T *eap;
4602 int syncing; /* TRUE for ":syntax sync region .." */
4603{
4604 char_u *arg = eap->arg;
4605 char_u *group_name_end;
4606 char_u *rest; /* next arg, NULL on error */
4607 char_u *key_end;
4608 char_u *key = NULL;
4609 char_u *p;
4610 int item;
4611#define ITEM_START 0
4612#define ITEM_SKIP 1
4613#define ITEM_END 2
4614#define ITEM_MATCHGROUP 3
4615 struct pat_ptr
4616 {
4617 synpat_T *pp_synp; /* pointer to syn_pattern */
4618 int pp_matchgroup_id; /* matchgroup ID */
4619 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4620 } *(pat_ptrs[3]);
4621 /* patterns found in the line */
4622 struct pat_ptr *ppp;
4623 struct pat_ptr *ppp_next;
4624 int pat_count = 0; /* nr of syn_patterns found */
4625 int syn_id;
4626 int matchgroup_id = 0;
4627 int not_enough = FALSE; /* not enough arguments */
4628 int illegal = FALSE; /* illegal arguments */
4629 int success = FALSE;
4630 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004631 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004632
4633 /* Isolate the group name, check for validity */
4634 rest = get_group_name(arg, &group_name_end);
4635
4636 pat_ptrs[0] = NULL;
4637 pat_ptrs[1] = NULL;
4638 pat_ptrs[2] = NULL;
4639
4640 init_syn_patterns();
4641
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004642 syn_opt_arg.flags = 0;
4643 syn_opt_arg.keyword = FALSE;
4644 syn_opt_arg.sync_idx = NULL;
4645 syn_opt_arg.has_cont_list = TRUE;
4646 syn_opt_arg.cont_list = NULL;
4647 syn_opt_arg.cont_in_list = NULL;
4648 syn_opt_arg.next_list = NULL;
4649
Bram Moolenaar071d4272004-06-13 20:20:40 +00004650 /*
4651 * get the options, patterns and matchgroup.
4652 */
4653 while (rest != NULL && !ends_excmd(*rest))
4654 {
4655 /* Check for option arguments */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004656 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004657 if (rest == NULL || ends_excmd(*rest))
4658 break;
4659
4660 /* must be a pattern or matchgroup then */
4661 key_end = rest;
4662 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4663 ++key_end;
4664 vim_free(key);
4665 key = vim_strnsave_up(rest, (int)(key_end - rest));
4666 if (key == NULL) /* out of memory */
4667 {
4668 rest = NULL;
4669 break;
4670 }
4671 if (STRCMP(key, "MATCHGROUP") == 0)
4672 item = ITEM_MATCHGROUP;
4673 else if (STRCMP(key, "START") == 0)
4674 item = ITEM_START;
4675 else if (STRCMP(key, "END") == 0)
4676 item = ITEM_END;
4677 else if (STRCMP(key, "SKIP") == 0)
4678 {
4679 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4680 {
4681 illegal = TRUE;
4682 break;
4683 }
4684 item = ITEM_SKIP;
4685 }
4686 else
4687 break;
4688 rest = skipwhite(key_end);
4689 if (*rest != '=')
4690 {
4691 rest = NULL;
4692 EMSG2(_("E398: Missing '=': %s"), arg);
4693 break;
4694 }
4695 rest = skipwhite(rest + 1);
4696 if (*rest == NUL)
4697 {
4698 not_enough = TRUE;
4699 break;
4700 }
4701
4702 if (item == ITEM_MATCHGROUP)
4703 {
4704 p = skiptowhite(rest);
4705 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4706 matchgroup_id = 0;
4707 else
4708 {
4709 matchgroup_id = syn_check_group(rest, (int)(p - rest));
4710 if (matchgroup_id == 0)
4711 {
4712 illegal = TRUE;
4713 break;
4714 }
4715 }
4716 rest = skipwhite(p);
4717 }
4718 else
4719 {
4720 /*
4721 * Allocate room for a syn_pattern, and link it in the list of
4722 * syn_patterns for this item, at the start (because the list is
4723 * used from end to start).
4724 */
4725 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4726 if (ppp == NULL)
4727 {
4728 rest = NULL;
4729 break;
4730 }
4731 ppp->pp_next = pat_ptrs[item];
4732 pat_ptrs[item] = ppp;
4733 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4734 if (ppp->pp_synp == NULL)
4735 {
4736 rest = NULL;
4737 break;
4738 }
4739
4740 /*
4741 * Get the syntax pattern and the following offset(s).
4742 */
4743 /* Enable the appropriate \z specials. */
4744 if (item == ITEM_START)
4745 reg_do_extmatch = REX_SET;
4746 else if (item == ITEM_SKIP || item == ITEM_END)
4747 reg_do_extmatch = REX_USE;
4748 rest = get_syn_pattern(rest, ppp->pp_synp);
4749 reg_do_extmatch = 0;
4750 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004751 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004752 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4753 ppp->pp_matchgroup_id = matchgroup_id;
4754 ++pat_count;
4755 }
4756 }
4757 vim_free(key);
4758 if (illegal || not_enough)
4759 rest = NULL;
4760
4761 /*
4762 * Must have a "start" and "end" pattern.
4763 */
4764 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4765 pat_ptrs[ITEM_END] == NULL))
4766 {
4767 not_enough = TRUE;
4768 rest = NULL;
4769 }
4770
4771 if (rest != NULL)
4772 {
4773 /*
4774 * Check for trailing garbage or command.
4775 * If OK, add the item.
4776 */
4777 eap->nextcmd = check_nextcmd(rest);
4778 if (!ends_excmd(*rest) || eap->skip)
4779 rest = NULL;
4780 else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4781 && (syn_id = syn_check_group(arg,
4782 (int)(group_name_end - arg))) != 0)
4783 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004784 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004785 /*
4786 * Store the start/skip/end in the syn_items list
4787 */
4788 idx = curbuf->b_syn_patterns.ga_len;
4789 for (item = ITEM_START; item <= ITEM_END; ++item)
4790 {
4791 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4792 {
4793 SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4794 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4795 SYN_ITEMS(curbuf)[idx].sp_type =
4796 (item == ITEM_START) ? SPTYPE_START :
4797 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004798 SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4800 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4801 SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4802 ppp->pp_matchgroup_id;
4803 if (item == ITEM_START)
4804 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004805 SYN_ITEMS(curbuf)[idx].sp_cont_list =
4806 syn_opt_arg.cont_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004807 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004808 syn_opt_arg.cont_in_list;
4809 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004810 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004811 SYN_ITEMS(curbuf)[idx].sp_next_list =
4812 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813 }
4814 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004815 ++idx;
4816#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004817 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004818 ++curbuf->b_syn_folditems;
4819#endif
4820 }
4821 }
4822
4823 redraw_curbuf_later(NOT_VALID);
4824 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4825 success = TRUE; /* don't free the progs and patterns now */
4826 }
4827 }
4828
4829 /*
4830 * Free the allocated memory.
4831 */
4832 for (item = ITEM_START; item <= ITEM_END; ++item)
4833 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4834 {
4835 if (!success)
4836 {
4837 vim_free(ppp->pp_synp->sp_prog);
4838 vim_free(ppp->pp_synp->sp_pattern);
4839 }
4840 vim_free(ppp->pp_synp);
4841 ppp_next = ppp->pp_next;
4842 vim_free(ppp);
4843 }
4844
4845 if (!success)
4846 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004847 vim_free(syn_opt_arg.cont_list);
4848 vim_free(syn_opt_arg.cont_in_list);
4849 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004850 if (not_enough)
4851 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
4852 else if (illegal || rest == NULL)
4853 EMSG2(_(e_invarg2), arg);
4854 }
4855}
4856
4857/*
4858 * A simple syntax group ID comparison function suitable for use in qsort()
4859 */
4860 static int
4861#ifdef __BORLANDC__
4862_RTLENTRYF
4863#endif
4864syn_compare_stub(v1, v2)
4865 const void *v1;
4866 const void *v2;
4867{
4868 const short *s1 = v1;
4869 const short *s2 = v2;
4870
4871 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
4872}
4873
4874/*
4875 * Combines lists of syntax clusters.
4876 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
4877 */
4878 static void
4879syn_combine_list(clstr1, clstr2, list_op)
4880 short **clstr1;
4881 short **clstr2;
4882 int list_op;
4883{
4884 int count1 = 0;
4885 int count2 = 0;
4886 short *g1;
4887 short *g2;
4888 short *clstr = NULL;
4889 int count;
4890 int round;
4891
4892 /*
4893 * Handle degenerate cases.
4894 */
4895 if (*clstr2 == NULL)
4896 return;
4897 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
4898 {
4899 if (list_op == CLUSTER_REPLACE)
4900 vim_free(*clstr1);
4901 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
4902 *clstr1 = *clstr2;
4903 else
4904 vim_free(*clstr2);
4905 return;
4906 }
4907
4908 for (g1 = *clstr1; *g1; g1++)
4909 ++count1;
4910 for (g2 = *clstr2; *g2; g2++)
4911 ++count2;
4912
4913 /*
4914 * For speed purposes, sort both lists.
4915 */
4916 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
4917 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
4918
4919 /*
4920 * We proceed in two passes; in round 1, we count the elements to place
4921 * in the new list, and in round 2, we allocate and populate the new
4922 * list. For speed, we use a mergesort-like method, adding the smaller
4923 * of the current elements in each list to the new list.
4924 */
4925 for (round = 1; round <= 2; round++)
4926 {
4927 g1 = *clstr1;
4928 g2 = *clstr2;
4929 count = 0;
4930
4931 /*
4932 * First, loop through the lists until one of them is empty.
4933 */
4934 while (*g1 && *g2)
4935 {
4936 /*
4937 * We always want to add from the first list.
4938 */
4939 if (*g1 < *g2)
4940 {
4941 if (round == 2)
4942 clstr[count] = *g1;
4943 count++;
4944 g1++;
4945 continue;
4946 }
4947 /*
4948 * We only want to add from the second list if we're adding the
4949 * lists.
4950 */
4951 if (list_op == CLUSTER_ADD)
4952 {
4953 if (round == 2)
4954 clstr[count] = *g2;
4955 count++;
4956 }
4957 if (*g1 == *g2)
4958 g1++;
4959 g2++;
4960 }
4961
4962 /*
4963 * Now add the leftovers from whichever list didn't get finished
4964 * first. As before, we only want to add from the second list if
4965 * we're adding the lists.
4966 */
4967 for (; *g1; g1++, count++)
4968 if (round == 2)
4969 clstr[count] = *g1;
4970 if (list_op == CLUSTER_ADD)
4971 for (; *g2; g2++, count++)
4972 if (round == 2)
4973 clstr[count] = *g2;
4974
4975 if (round == 1)
4976 {
4977 /*
4978 * If the group ended up empty, we don't need to allocate any
4979 * space for it.
4980 */
4981 if (count == 0)
4982 {
4983 clstr = NULL;
4984 break;
4985 }
4986 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
4987 if (clstr == NULL)
4988 break;
4989 clstr[count] = 0;
4990 }
4991 }
4992
4993 /*
4994 * Finally, put the new list in place.
4995 */
4996 vim_free(*clstr1);
4997 vim_free(*clstr2);
4998 *clstr1 = clstr;
4999}
5000
5001/*
5002 * Lookup a syntax cluster name and return it's ID.
5003 * If it is not found, 0 is returned.
5004 */
5005 static int
5006syn_scl_name2id(name)
5007 char_u *name;
5008{
5009 int i;
5010 char_u *name_u;
5011
5012 /* Avoid using stricmp() too much, it's slow on some systems */
5013 name_u = vim_strsave_up(name);
5014 if (name_u == NULL)
5015 return 0;
5016 for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
5017 if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
5018 && STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
5019 break;
5020 vim_free(name_u);
5021 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5022}
5023
5024/*
5025 * Like syn_scl_name2id(), but take a pointer + length argument.
5026 */
5027 static int
5028syn_scl_namen2id(linep, len)
5029 char_u *linep;
5030 int len;
5031{
5032 char_u *name;
5033 int id = 0;
5034
5035 name = vim_strnsave(linep, len);
5036 if (name != NULL)
5037 {
5038 id = syn_scl_name2id(name);
5039 vim_free(name);
5040 }
5041 return id;
5042}
5043
5044/*
5045 * Find syntax cluster name in the table and return it's ID.
5046 * The argument is a pointer to the name and the length of the name.
5047 * If it doesn't exist yet, a new entry is created.
5048 * Return 0 for failure.
5049 */
5050 static int
5051syn_check_cluster(pp, len)
5052 char_u *pp;
5053 int len;
5054{
5055 int id;
5056 char_u *name;
5057
5058 name = vim_strnsave(pp, len);
5059 if (name == NULL)
5060 return 0;
5061
5062 id = syn_scl_name2id(name);
5063 if (id == 0) /* doesn't exist yet */
5064 id = syn_add_cluster(name);
5065 else
5066 vim_free(name);
5067 return id;
5068}
5069
5070/*
5071 * Add new syntax cluster and return it's ID.
5072 * "name" must be an allocated string, it will be consumed.
5073 * Return 0 for failure.
5074 */
5075 static int
5076syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005077 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005078{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005079 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005080
5081 /*
5082 * First call for this growarray: init growing array.
5083 */
5084 if (curbuf->b_syn_clusters.ga_data == NULL)
5085 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00005086 curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005087 curbuf->b_syn_clusters.ga_growsize = 10;
5088 }
5089
5090 /*
5091 * Make room for at least one other cluster entry.
5092 */
5093 if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5094 {
5095 vim_free(name);
5096 return 0;
5097 }
5098 len = curbuf->b_syn_clusters.ga_len;
5099
Bram Moolenaar217ad922005-03-20 22:37:15 +00005100 vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005101 SYN_CLSTR(curbuf)[len].scl_name = name;
5102 SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5103 SYN_CLSTR(curbuf)[len].scl_list = NULL;
5104 ++curbuf->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005105
Bram Moolenaar217ad922005-03-20 22:37:15 +00005106 if (STRICMP(name, "Spell") == 0)
5107 curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005108 if (STRICMP(name, "NoSpell") == 0)
5109 curbuf->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005110
Bram Moolenaar071d4272004-06-13 20:20:40 +00005111 return len + SYNID_CLUSTER;
5112}
5113
5114/*
5115 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5116 * [add={groupname},..] [remove={groupname},..]".
5117 */
5118/* ARGSUSED */
5119 static void
5120syn_cmd_cluster(eap, syncing)
5121 exarg_T *eap;
5122 int syncing; /* not used */
5123{
5124 char_u *arg = eap->arg;
5125 char_u *group_name_end;
5126 char_u *rest;
5127 int scl_id;
5128 short *clstr_list;
5129 int got_clstr = FALSE;
5130 int opt_len;
5131 int list_op;
5132
5133 eap->nextcmd = find_nextcmd(arg);
5134 if (eap->skip)
5135 return;
5136
5137 rest = get_group_name(arg, &group_name_end);
5138
5139 if (rest != NULL)
5140 {
5141 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005142 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005143
5144 for (;;)
5145 {
5146 if (STRNICMP(rest, "add", 3) == 0
5147 && (vim_iswhite(rest[3]) || rest[3] == '='))
5148 {
5149 opt_len = 3;
5150 list_op = CLUSTER_ADD;
5151 }
5152 else if (STRNICMP(rest, "remove", 6) == 0
5153 && (vim_iswhite(rest[6]) || rest[6] == '='))
5154 {
5155 opt_len = 6;
5156 list_op = CLUSTER_SUBTRACT;
5157 }
5158 else if (STRNICMP(rest, "contains", 8) == 0
5159 && (vim_iswhite(rest[8]) || rest[8] == '='))
5160 {
5161 opt_len = 8;
5162 list_op = CLUSTER_REPLACE;
5163 }
5164 else
5165 break;
5166
5167 clstr_list = NULL;
5168 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5169 {
5170 EMSG2(_(e_invarg2), rest);
5171 break;
5172 }
5173 syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5174 &clstr_list, list_op);
5175 got_clstr = TRUE;
5176 }
5177
5178 if (got_clstr)
5179 {
5180 redraw_curbuf_later(NOT_VALID);
5181 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5182 }
5183 }
5184
5185 if (!got_clstr)
5186 EMSG(_("E400: No cluster specified"));
5187 if (rest == NULL || !ends_excmd(*rest))
5188 EMSG2(_(e_invarg2), arg);
5189}
5190
5191/*
5192 * On first call for current buffer: Init growing array.
5193 */
5194 static void
5195init_syn_patterns()
5196{
5197 curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5198 curbuf->b_syn_patterns.ga_growsize = 10;
5199}
5200
5201/*
5202 * Get one pattern for a ":syntax match" or ":syntax region" command.
5203 * Stores the pattern and program in a synpat_T.
5204 * Returns a pointer to the next argument, or NULL in case of an error.
5205 */
5206 static char_u *
5207get_syn_pattern(arg, ci)
5208 char_u *arg;
5209 synpat_T *ci;
5210{
5211 char_u *end;
5212 int *p;
5213 int idx;
5214 char_u *cpo_save;
5215
5216 /* need at least three chars */
5217 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5218 return NULL;
5219
5220 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5221 if (*end != *arg) /* end delimiter not found */
5222 {
5223 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5224 return NULL;
5225 }
5226 /* store the pattern and compiled regexp program */
5227 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5228 return NULL;
5229
5230 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5231 cpo_save = p_cpo;
5232 p_cpo = (char_u *)"";
5233 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5234 p_cpo = cpo_save;
5235
5236 if (ci->sp_prog == NULL)
5237 return NULL;
5238 ci->sp_ic = curbuf->b_syn_ic;
5239
5240 /*
5241 * Check for a match, highlight or region offset.
5242 */
5243 ++end;
5244 do
5245 {
5246 for (idx = SPO_COUNT; --idx >= 0; )
5247 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5248 break;
5249 if (idx >= 0)
5250 {
5251 p = &(ci->sp_offsets[idx]);
5252 if (idx != SPO_LC_OFF)
5253 switch (end[3])
5254 {
5255 case 's': break;
5256 case 'b': break;
5257 case 'e': idx += SPO_COUNT; break;
5258 default: idx = -1; break;
5259 }
5260 if (idx >= 0)
5261 {
5262 ci->sp_off_flags |= (1 << idx);
5263 if (idx == SPO_LC_OFF) /* lc=99 */
5264 {
5265 end += 3;
5266 *p = getdigits(&end);
5267
5268 /* "lc=" offset automatically sets "ms=" offset */
5269 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5270 {
5271 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5272 ci->sp_offsets[SPO_MS_OFF] = *p;
5273 }
5274 }
5275 else /* yy=x+99 */
5276 {
5277 end += 4;
5278 if (*end == '+')
5279 {
5280 ++end;
5281 *p = getdigits(&end); /* positive offset */
5282 }
5283 else if (*end == '-')
5284 {
5285 ++end;
5286 *p = -getdigits(&end); /* negative offset */
5287 }
5288 }
5289 if (*end != ',')
5290 break;
5291 ++end;
5292 }
5293 }
5294 } while (idx >= 0);
5295
5296 if (!ends_excmd(*end) && !vim_iswhite(*end))
5297 {
5298 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5299 return NULL;
5300 }
5301 return skipwhite(end);
5302}
5303
5304/*
5305 * Handle ":syntax sync .." command.
5306 */
5307/* ARGSUSED */
5308 static void
5309syn_cmd_sync(eap, syncing)
5310 exarg_T *eap;
5311 int syncing; /* not used */
5312{
5313 char_u *arg_start = eap->arg;
5314 char_u *arg_end;
5315 char_u *key = NULL;
5316 char_u *next_arg;
5317 int illegal = FALSE;
5318 int finished = FALSE;
5319 long n;
5320 char_u *cpo_save;
5321
5322 if (ends_excmd(*arg_start))
5323 {
5324 syn_cmd_list(eap, TRUE);
5325 return;
5326 }
5327
5328 while (!ends_excmd(*arg_start))
5329 {
5330 arg_end = skiptowhite(arg_start);
5331 next_arg = skipwhite(arg_end);
5332 vim_free(key);
5333 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5334 if (STRCMP(key, "CCOMMENT") == 0)
5335 {
5336 if (!eap->skip)
5337 curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5338 if (!ends_excmd(*next_arg))
5339 {
5340 arg_end = skiptowhite(next_arg);
5341 if (!eap->skip)
5342 curbuf->b_syn_sync_id = syn_check_group(next_arg,
5343 (int)(arg_end - next_arg));
5344 next_arg = skipwhite(arg_end);
5345 }
5346 else if (!eap->skip)
5347 curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5348 }
5349 else if ( STRNCMP(key, "LINES", 5) == 0
5350 || STRNCMP(key, "MINLINES", 8) == 0
5351 || STRNCMP(key, "MAXLINES", 8) == 0
5352 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5353 {
5354 if (key[4] == 'S')
5355 arg_end = key + 6;
5356 else if (key[0] == 'L')
5357 arg_end = key + 11;
5358 else
5359 arg_end = key + 9;
5360 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5361 {
5362 illegal = TRUE;
5363 break;
5364 }
5365 n = getdigits(&arg_end);
5366 if (!eap->skip)
5367 {
5368 if (key[4] == 'B')
5369 curbuf->b_syn_sync_linebreaks = n;
5370 else if (key[1] == 'A')
5371 curbuf->b_syn_sync_maxlines = n;
5372 else
5373 curbuf->b_syn_sync_minlines = n;
5374 }
5375 }
5376 else if (STRCMP(key, "FROMSTART") == 0)
5377 {
5378 if (!eap->skip)
5379 {
5380 curbuf->b_syn_sync_minlines = MAXLNUM;
5381 curbuf->b_syn_sync_maxlines = 0;
5382 }
5383 }
5384 else if (STRCMP(key, "LINECONT") == 0)
5385 {
5386 if (curbuf->b_syn_linecont_pat != NULL)
5387 {
5388 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5389 finished = TRUE;
5390 break;
5391 }
5392 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5393 if (*arg_end != *next_arg) /* end delimiter not found */
5394 {
5395 illegal = TRUE;
5396 break;
5397 }
5398
5399 if (!eap->skip)
5400 {
5401 /* store the pattern and compiled regexp program */
5402 if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5403 (int)(arg_end - next_arg - 1))) == NULL)
5404 {
5405 finished = TRUE;
5406 break;
5407 }
5408 curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5409
5410 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5411 cpo_save = p_cpo;
5412 p_cpo = (char_u *)"";
5413 curbuf->b_syn_linecont_prog =
5414 vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5415 p_cpo = cpo_save;
5416
5417 if (curbuf->b_syn_linecont_prog == NULL)
5418 {
5419 vim_free(curbuf->b_syn_linecont_pat);
5420 curbuf->b_syn_linecont_pat = NULL;
5421 finished = TRUE;
5422 break;
5423 }
5424 }
5425 next_arg = skipwhite(arg_end + 1);
5426 }
5427 else
5428 {
5429 eap->arg = next_arg;
5430 if (STRCMP(key, "MATCH") == 0)
5431 syn_cmd_match(eap, TRUE);
5432 else if (STRCMP(key, "REGION") == 0)
5433 syn_cmd_region(eap, TRUE);
5434 else if (STRCMP(key, "CLEAR") == 0)
5435 syn_cmd_clear(eap, TRUE);
5436 else
5437 illegal = TRUE;
5438 finished = TRUE;
5439 break;
5440 }
5441 arg_start = next_arg;
5442 }
5443 vim_free(key);
5444 if (illegal)
5445 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5446 else if (!finished)
5447 {
5448 eap->nextcmd = check_nextcmd(arg_start);
5449 redraw_curbuf_later(NOT_VALID);
5450 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5451 }
5452}
5453
5454/*
5455 * Convert a line of highlight group names into a list of group ID numbers.
5456 * "arg" should point to the "contains" or "nextgroup" keyword.
5457 * "arg" is advanced to after the last group name.
5458 * Careful: the argument is modified (NULs added).
5459 * returns FAIL for some error, OK for success.
5460 */
5461 static int
5462get_id_list(arg, keylen, list)
5463 char_u **arg;
5464 int keylen; /* length of keyword */
5465 short **list; /* where to store the resulting list, if not
5466 NULL, the list is silently skipped! */
5467{
5468 char_u *p = NULL;
5469 char_u *end;
5470 int round;
5471 int count;
5472 int total_count = 0;
5473 short *retval = NULL;
5474 char_u *name;
5475 regmatch_T regmatch;
5476 int id;
5477 int i;
5478 int failed = FALSE;
5479
5480 /*
5481 * We parse the list twice:
5482 * round == 1: count the number of items, allocate the array.
5483 * round == 2: fill the array with the items.
5484 * In round 1 new groups may be added, causing the number of items to
5485 * grow when a regexp is used. In that case round 1 is done once again.
5486 */
5487 for (round = 1; round <= 2; ++round)
5488 {
5489 /*
5490 * skip "contains"
5491 */
5492 p = skipwhite(*arg + keylen);
5493 if (*p != '=')
5494 {
5495 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5496 break;
5497 }
5498 p = skipwhite(p + 1);
5499 if (ends_excmd(*p))
5500 {
5501 EMSG2(_("E406: Empty argument: %s"), *arg);
5502 break;
5503 }
5504
5505 /*
5506 * parse the arguments after "contains"
5507 */
5508 count = 0;
5509 while (!ends_excmd(*p))
5510 {
5511 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5512 ;
5513 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5514 if (name == NULL)
5515 {
5516 failed = TRUE;
5517 break;
5518 }
5519 STRNCPY(name + 1, p, end - p);
5520 name[end - p + 1] = NUL;
5521 if ( STRCMP(name + 1, "ALLBUT") == 0
5522 || STRCMP(name + 1, "ALL") == 0
5523 || STRCMP(name + 1, "TOP") == 0
5524 || STRCMP(name + 1, "CONTAINED") == 0)
5525 {
5526 if (TOUPPER_ASC(**arg) != 'C')
5527 {
5528 EMSG2(_("E407: %s not allowed here"), name + 1);
5529 failed = TRUE;
5530 vim_free(name);
5531 break;
5532 }
5533 if (count != 0)
5534 {
5535 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5536 failed = TRUE;
5537 vim_free(name);
5538 break;
5539 }
5540 if (name[1] == 'A')
5541 id = SYNID_ALLBUT;
5542 else if (name[1] == 'T')
5543 id = SYNID_TOP;
5544 else
5545 id = SYNID_CONTAINED;
5546 id += current_syn_inc_tag;
5547 }
5548 else if (name[1] == '@')
5549 {
5550 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5551 }
5552 else
5553 {
5554 /*
5555 * Handle full group name.
5556 */
5557 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5558 id = syn_check_group(name + 1, (int)(end - p));
5559 else
5560 {
5561 /*
5562 * Handle match of regexp with group names.
5563 */
5564 *name = '^';
5565 STRCAT(name, "$");
5566 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5567 if (regmatch.regprog == NULL)
5568 {
5569 failed = TRUE;
5570 vim_free(name);
5571 break;
5572 }
5573
5574 regmatch.rm_ic = TRUE;
5575 id = 0;
5576 for (i = highlight_ga.ga_len; --i >= 0; )
5577 {
5578 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5579 (colnr_T)0))
5580 {
5581 if (round == 2)
5582 {
5583 /* Got more items than expected; can happen
5584 * when adding items that match:
5585 * "contains=a.*b,axb".
5586 * Go back to first round */
5587 if (count >= total_count)
5588 {
5589 vim_free(retval);
5590 round = 1;
5591 }
5592 else
5593 retval[count] = i + 1;
5594 }
5595 ++count;
5596 id = -1; /* remember that we found one */
5597 }
5598 }
5599 vim_free(regmatch.regprog);
5600 }
5601 }
5602 vim_free(name);
5603 if (id == 0)
5604 {
5605 EMSG2(_("E409: Unknown group name: %s"), p);
5606 failed = TRUE;
5607 break;
5608 }
5609 if (id > 0)
5610 {
5611 if (round == 2)
5612 {
5613 /* Got more items than expected, go back to first round */
5614 if (count >= total_count)
5615 {
5616 vim_free(retval);
5617 round = 1;
5618 }
5619 else
5620 retval[count] = id;
5621 }
5622 ++count;
5623 }
5624 p = skipwhite(end);
5625 if (*p != ',')
5626 break;
5627 p = skipwhite(p + 1); /* skip comma in between arguments */
5628 }
5629 if (failed)
5630 break;
5631 if (round == 1)
5632 {
5633 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5634 if (retval == NULL)
5635 break;
5636 retval[count] = 0; /* zero means end of the list */
5637 total_count = count;
5638 }
5639 }
5640
5641 *arg = p;
5642 if (failed || retval == NULL)
5643 {
5644 vim_free(retval);
5645 return FAIL;
5646 }
5647
5648 if (*list == NULL)
5649 *list = retval;
5650 else
5651 vim_free(retval); /* list already found, don't overwrite it */
5652
5653 return OK;
5654}
5655
5656/*
5657 * Make a copy of an ID list.
5658 */
5659 static short *
5660copy_id_list(list)
5661 short *list;
5662{
5663 int len;
5664 int count;
5665 short *retval;
5666
5667 if (list == NULL)
5668 return NULL;
5669
5670 for (count = 0; list[count]; ++count)
5671 ;
5672 len = (count + 1) * sizeof(short);
5673 retval = (short *)alloc((unsigned)len);
5674 if (retval != NULL)
5675 mch_memmove(retval, list, (size_t)len);
5676
5677 return retval;
5678}
5679
5680/*
5681 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5682 * "cur_si" can be NULL if not checking the "containedin" list.
5683 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5684 * the current item.
5685 * This function is called very often, keep it fast!!
5686 */
5687 static int
5688in_id_list(cur_si, list, ssp, contained)
5689 stateitem_T *cur_si; /* current item or NULL */
5690 short *list; /* id list */
5691 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
5692 int contained; /* group id is contained */
5693{
5694 int retval;
5695 short *scl_list;
5696 short item;
5697 short id = ssp->id;
5698 static int depth = 0;
5699 int r;
5700
5701 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00005702 if (cur_si != NULL && ssp->cont_in_list != NULL
5703 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005704 {
5705 /* Ignore transparent items without a contains argument. Double check
5706 * that we don't go back past the first one. */
5707 while ((cur_si->si_flags & HL_TRANS_CONT)
5708 && cur_si > (stateitem_T *)(current_state.ga_data))
5709 --cur_si;
5710 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
5711 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5712 &(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5713 SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5714 return TRUE;
5715 }
5716
5717 if (list == NULL)
5718 return FALSE;
5719
5720 /*
5721 * If list is ID_LIST_ALL, we are in a transparent item that isn't
5722 * inside anything. Only allow not-contained groups.
5723 */
5724 if (list == ID_LIST_ALL)
5725 return !contained;
5726
5727 /*
5728 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5729 * contains list. We also require that "id" is at the same ":syn include"
5730 * level as the list.
5731 */
5732 item = *list;
5733 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5734 {
5735 if (item < SYNID_TOP)
5736 {
5737 /* ALL or ALLBUT: accept all groups in the same file */
5738 if (item - SYNID_ALLBUT != ssp->inc_tag)
5739 return FALSE;
5740 }
5741 else if (item < SYNID_CONTAINED)
5742 {
5743 /* TOP: accept all not-contained groups in the same file */
5744 if (item - SYNID_TOP != ssp->inc_tag || contained)
5745 return FALSE;
5746 }
5747 else
5748 {
5749 /* CONTAINED: accept all contained groups in the same file */
5750 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5751 return FALSE;
5752 }
5753 item = *++list;
5754 retval = FALSE;
5755 }
5756 else
5757 retval = TRUE;
5758
5759 /*
5760 * Return "retval" if id is in the contains list.
5761 */
5762 while (item != 0)
5763 {
5764 if (item == id)
5765 return retval;
5766 if (item >= SYNID_CLUSTER)
5767 {
5768 scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5769 /* restrict recursiveness to 30 to avoid an endless loop for a
5770 * cluster that includes itself (indirectly) */
5771 if (scl_list != NULL && depth < 30)
5772 {
5773 ++depth;
5774 r = in_id_list(NULL, scl_list, ssp, contained);
5775 --depth;
5776 if (r)
5777 return retval;
5778 }
5779 }
5780 item = *++list;
5781 }
5782 return !retval;
5783}
5784
5785struct subcommand
5786{
5787 char *name; /* subcommand name */
5788 void (*func)__ARGS((exarg_T *, int)); /* function to call */
5789};
5790
5791static struct subcommand subcommands[] =
5792{
5793 {"case", syn_cmd_case},
5794 {"clear", syn_cmd_clear},
5795 {"cluster", syn_cmd_cluster},
5796 {"enable", syn_cmd_enable},
5797 {"include", syn_cmd_include},
5798 {"keyword", syn_cmd_keyword},
5799 {"list", syn_cmd_list},
5800 {"manual", syn_cmd_manual},
5801 {"match", syn_cmd_match},
5802 {"on", syn_cmd_on},
5803 {"off", syn_cmd_off},
5804 {"region", syn_cmd_region},
5805 {"reset", syn_cmd_reset},
5806 {"sync", syn_cmd_sync},
5807 {"", syn_cmd_list},
5808 {NULL, NULL}
5809};
5810
5811/*
5812 * ":syntax".
5813 * This searches the subcommands[] table for the subcommand name, and calls a
5814 * syntax_subcommand() function to do the rest.
5815 */
5816 void
5817ex_syntax(eap)
5818 exarg_T *eap;
5819{
5820 char_u *arg = eap->arg;
5821 char_u *subcmd_end;
5822 char_u *subcmd_name;
5823 int i;
5824
5825 syn_cmdlinep = eap->cmdlinep;
5826
5827 /* isolate subcommand name */
5828 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5829 ;
5830 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5831 if (subcmd_name != NULL)
5832 {
5833 if (eap->skip) /* skip error messages for all subcommands */
5834 ++emsg_skip;
5835 for (i = 0; ; ++i)
5836 {
5837 if (subcommands[i].name == NULL)
5838 {
5839 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5840 break;
5841 }
5842 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5843 {
5844 eap->arg = skipwhite(subcmd_end);
5845 (subcommands[i].func)(eap, FALSE);
5846 break;
5847 }
5848 }
5849 vim_free(subcmd_name);
5850 if (eap->skip)
5851 --emsg_skip;
5852 }
5853}
5854
5855 int
5856syntax_present(buf)
5857 buf_T *buf;
5858{
5859 return (buf->b_syn_patterns.ga_len != 0
5860 || buf->b_syn_clusters.ga_len != 0
Bram Moolenaardad6b692005-01-25 22:14:34 +00005861 || curbuf->b_keywtab.ht_used > 0
5862 || curbuf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005863}
5864
5865#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5866
5867static enum
5868{
5869 EXP_SUBCMD, /* expand ":syn" sub-commands */
5870 EXP_CASE /* expand ":syn case" arguments */
5871} expand_what;
5872
5873
5874/*
5875 * Handle command line completion for :syntax command.
5876 */
5877 void
5878set_context_in_syntax_cmd(xp, arg)
5879 expand_T *xp;
5880 char_u *arg;
5881{
5882 char_u *p;
5883
5884 /* Default: expand subcommands */
5885 xp->xp_context = EXPAND_SYNTAX;
5886 expand_what = EXP_SUBCMD;
5887 xp->xp_pattern = arg;
5888 include_link = FALSE;
5889 include_default = FALSE;
5890
5891 /* (part of) subcommand already typed */
5892 if (*arg != NUL)
5893 {
5894 p = skiptowhite(arg);
5895 if (*p != NUL) /* past first word */
5896 {
5897 xp->xp_pattern = skipwhite(p);
5898 if (*skiptowhite(xp->xp_pattern) != NUL)
5899 xp->xp_context = EXPAND_NOTHING;
5900 else if (STRNICMP(arg, "case", p - arg) == 0)
5901 expand_what = EXP_CASE;
5902 else if ( STRNICMP(arg, "keyword", p - arg) == 0
5903 || STRNICMP(arg, "region", p - arg) == 0
5904 || STRNICMP(arg, "match", p - arg) == 0
5905 || STRNICMP(arg, "list", p - arg) == 0)
5906 xp->xp_context = EXPAND_HIGHLIGHT;
5907 else
5908 xp->xp_context = EXPAND_NOTHING;
5909 }
5910 }
5911}
5912
5913static char *(case_args[]) = {"match", "ignore", NULL};
5914
5915/*
5916 * Function given to ExpandGeneric() to obtain the list syntax names for
5917 * expansion.
5918 */
5919/*ARGSUSED*/
5920 char_u *
5921get_syntax_name(xp, idx)
5922 expand_T *xp;
5923 int idx;
5924{
5925 if (expand_what == EXP_SUBCMD)
5926 return (char_u *)subcommands[idx].name;
5927 return (char_u *)case_args[idx];
5928}
5929
5930#endif /* FEAT_CMDL_COMPL */
5931
5932#if defined(FEAT_EVAL) || defined(FEAT_PRINTER) || defined(PROTO)
5933/*
5934 * Function called for expression evaluation: get syntax ID at file position.
5935 */
5936 int
5937syn_get_id(lnum, col, trans)
5938 long lnum;
5939 long col;
5940 int trans; /* remove transparancy */
5941{
5942 /* When the position is not after the current position and in the same
5943 * line of the same buffer, need to restart parsing. */
5944 if (curwin->w_buffer != syn_buf
5945 || lnum != current_lnum
5946 || col < (long)current_col)
5947 syntax_start(curwin, lnum);
5948
Bram Moolenaar217ad922005-03-20 22:37:15 +00005949 (void)get_syntax_attr((colnr_T)col, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005950
5951 return (trans ? current_trans_id : current_id);
5952}
5953#endif
5954
5955#if defined(FEAT_FOLDING) || defined(PROTO)
5956/*
5957 * Function called to get folding level for line "lnum" in window "wp".
5958 */
5959 int
5960syn_get_foldlevel(wp, lnum)
5961 win_T *wp;
5962 long lnum;
5963{
5964 int level = 0;
5965 int i;
5966
5967 /* Return quickly when there are no fold items at all. */
5968 if (wp->w_buffer->b_syn_folditems != 0)
5969 {
5970 syntax_start(wp, lnum);
5971
5972 for (i = 0; i < current_state.ga_len; ++i)
5973 if (CUR_STATE(i).si_flags & HL_FOLD)
5974 ++level;
5975 }
5976 if (level > wp->w_p_fdn)
5977 level = wp->w_p_fdn;
5978 return level;
5979}
5980#endif
5981
5982#endif /* FEAT_SYN_HL */
5983
5984
5985/**************************************
5986 * Highlighting stuff *
5987 **************************************/
5988
5989/*
5990 * The default highlight groups. These are compiled-in for fast startup and
5991 * they still work when the runtime files can't be found.
5992 * When making changes here, also change runtime/colors/default.vim!
5993 */
5994static char *(highlight_init_both[]) =
5995 {
5996#ifdef FEAT_GUI
5997 "Cursor guibg=fg guifg=bg",
5998 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
5999#endif
6000 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
6001 "IncSearch term=reverse cterm=reverse gui=reverse",
6002 "ModeMsg term=bold cterm=bold gui=bold",
6003 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue",
6004 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold",
6005 "StatusLineNC term=reverse cterm=reverse gui=reverse",
6006 "VertSplit term=reverse cterm=reverse gui=reverse",
6007 "Visual term=reverse cterm=reverse gui=reverse guifg=Grey guibg=fg",
6008 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold",
6009 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red",
6010 NULL
6011 };
6012
6013static char *(highlight_init_light[]) =
6014 {
6015 "Directory term=bold ctermfg=DarkBlue guifg=Blue",
6016 "LineNr term=underline ctermfg=Brown guifg=Brown",
6017 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6018 "Normal gui=NONE",
6019 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6020 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
Bram Moolenaar217ad922005-03-20 22:37:15 +00006021 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl",
6022 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl",
6023 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006024 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue",
6025 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta",
6026 "WarningMsg term=standout ctermfg=DarkRed guifg=Red",
6027 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6028 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
6029 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6030 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6031 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue",
6032 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta",
6033 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
6034 NULL
6035 };
6036
6037static char *(highlight_init_dark[]) =
6038 {
6039 "Directory term=bold ctermfg=LightCyan guifg=Cyan",
6040 "LineNr term=underline ctermfg=Yellow guifg=Yellow",
6041 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen",
6042 "Normal gui=NONE",
6043 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green",
6044 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6045 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan",
Bram Moolenaar217ad922005-03-20 22:37:15 +00006046 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl",
6047 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl",
6048 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006049 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta",
6050 "WarningMsg term=standout ctermfg=LightRed guifg=Red",
6051 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6052 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
6053 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6054 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6055 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue",
6056 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta",
6057 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
6058 NULL
6059 };
6060
6061 void
6062init_highlight(both, reset)
6063 int both; /* include groups where 'bg' doesn't matter */
6064 int reset; /* clear group first */
6065{
6066 int i;
6067 char **pp;
6068 static int had_both = FALSE;
6069#ifdef FEAT_EVAL
6070 char_u *p;
6071
6072 /*
6073 * Try finding the color scheme file. Used when a color file was loaded
6074 * and 'background' or 't_Co' is changed.
6075 */
6076 p = get_var_value((char_u *)"g:colors_name");
6077 if (p != NULL && load_colors(p) == OK)
6078 return;
6079#endif
6080
6081 /*
6082 * Didn't use a color file, use the compiled-in colors.
6083 */
6084 if (both)
6085 {
6086 had_both = TRUE;
6087 pp = highlight_init_both;
6088 for (i = 0; pp[i] != NULL; ++i)
6089 do_highlight((char_u *)pp[i], reset, TRUE);
6090 }
6091 else if (!had_both)
6092 /* Don't do anything before the call with both == TRUE from main().
6093 * Not everything has been setup then, and that call will overrule
6094 * everything anyway. */
6095 return;
6096
6097 if (*p_bg == 'l')
6098 pp = highlight_init_light;
6099 else
6100 pp = highlight_init_dark;
6101 for (i = 0; pp[i] != NULL; ++i)
6102 do_highlight((char_u *)pp[i], reset, TRUE);
6103
6104#ifdef FEAT_SYN_HL
6105 /*
6106 * If syntax highlighting is enabled load the highlighting for it.
6107 */
6108 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006109 {
6110 static int recursive = 0;
6111
6112 if (recursive >= 5)
6113 EMSG(_("E679: recursive loop loading syncolor.vim"));
6114 else
6115 {
6116 ++recursive;
6117 (void)cmd_runtime((char_u *)"syntax/syncolor.vim", TRUE);
6118 --recursive;
6119 }
6120 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006121#endif
6122}
6123
6124/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006125 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006126 * Return OK for success, FAIL for failure.
6127 */
6128 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006129load_colors(name)
6130 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006131{
6132 char_u *buf;
6133 int retval = FAIL;
6134 static int recursive = FALSE;
6135
6136 /* When being called recursively, this is probably because setting
6137 * 'background' caused the highlighting to be reloaded. This means it is
6138 * working, thus we should return OK. */
6139 if (recursive)
6140 return OK;
6141
6142 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006143 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006144 if (buf != NULL)
6145 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006146 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006147 retval = cmd_runtime(buf, FALSE);
6148 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006149#ifdef FEAT_AUTOCMD
6150 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6151#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006152 }
6153 recursive = FALSE;
6154
6155 return retval;
6156}
6157
6158/*
6159 * Handle the ":highlight .." command.
6160 * When using ":hi clear" this is called recursively for each group with
6161 * "forceit" and "init" both TRUE.
6162 */
6163 void
6164do_highlight(line, forceit, init)
6165 char_u *line;
6166 int forceit;
6167 int init; /* TRUE when called for initializing */
6168{
6169 char_u *name_end;
6170 char_u *p;
6171 char_u *linep;
6172 char_u *key_start;
6173 char_u *arg_start;
6174 char_u *key = NULL, *arg = NULL;
6175 long i;
6176 int off;
6177 int len;
6178 int attr;
6179 int id;
6180 int idx;
6181 int dodefault = FALSE;
6182 int doclear = FALSE;
6183 int dolink = FALSE;
6184 int error = FALSE;
6185 int color;
6186 int is_normal_group = FALSE; /* "Normal" group */
6187#ifdef FEAT_GUI_X11
6188 int is_menu_group = FALSE; /* "Menu" group */
6189 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6190 int is_tooltip_group = FALSE; /* "Tooltip" group */
6191 int do_colors = FALSE; /* need to update colors? */
6192#else
6193# define is_menu_group 0
6194# define is_tooltip_group 0
6195#endif
6196
6197 /*
6198 * If no argument, list current highlighting.
6199 */
6200 if (ends_excmd(*line))
6201 {
6202 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6203 /* TODO: only call when the group has attributes set */
6204 highlight_list_one((int)i);
6205 return;
6206 }
6207
6208 /*
6209 * Isolate the name.
6210 */
6211 name_end = skiptowhite(line);
6212 linep = skipwhite(name_end);
6213
6214 /*
6215 * Check for "default" argument.
6216 */
6217 if (STRNCMP(line, "default", name_end - line) == 0)
6218 {
6219 dodefault = TRUE;
6220 line = linep;
6221 name_end = skiptowhite(line);
6222 linep = skipwhite(name_end);
6223 }
6224
6225 /*
6226 * Check for "clear" or "link" argument.
6227 */
6228 if (STRNCMP(line, "clear", name_end - line) == 0)
6229 doclear = TRUE;
6230 if (STRNCMP(line, "link", name_end - line) == 0)
6231 dolink = TRUE;
6232
6233 /*
6234 * ":highlight {group-name}": list highlighting for one group.
6235 */
6236 if (!doclear && !dolink && ends_excmd(*linep))
6237 {
6238 id = syn_namen2id(line, (int)(name_end - line));
6239 if (id == 0)
6240 EMSG2(_("E411: highlight group not found: %s"), line);
6241 else
6242 highlight_list_one(id);
6243 return;
6244 }
6245
6246 /*
6247 * Handle ":highlight link {from} {to}" command.
6248 */
6249 if (dolink)
6250 {
6251 char_u *from_start = linep;
6252 char_u *from_end;
6253 char_u *to_start;
6254 char_u *to_end;
6255 int from_id;
6256 int to_id;
6257
6258 from_end = skiptowhite(from_start);
6259 to_start = skipwhite(from_end);
6260 to_end = skiptowhite(to_start);
6261
6262 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6263 {
6264 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6265 from_start);
6266 return;
6267 }
6268
6269 if (!ends_excmd(*skipwhite(to_end)))
6270 {
6271 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6272 return;
6273 }
6274
6275 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6276 if (STRNCMP(to_start, "NONE", 4) == 0)
6277 to_id = 0;
6278 else
6279 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6280
6281 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6282 {
6283 /*
6284 * Don't allow a link when there already is some highlighting
6285 * for the group, unless '!' is used
6286 */
6287 if (to_id > 0 && !forceit && !init
6288 && hl_has_settings(from_id - 1, dodefault))
6289 {
6290 if (sourcing_name == NULL && !dodefault)
6291 EMSG(_("E414: group has settings, highlight link ignored"));
6292 }
6293 else
6294 {
6295 if (!init)
6296 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6297 HL_TABLE()[from_id - 1].sg_link = to_id;
6298 redraw_all_later(NOT_VALID);
6299 }
6300 }
6301
6302 /* Only call highlight_changed() once, after sourcing a syntax file */
6303 need_highlight_changed = TRUE;
6304
6305 return;
6306 }
6307
6308 if (doclear)
6309 {
6310 /*
6311 * ":highlight clear [group]" command.
6312 */
6313 line = linep;
6314 if (ends_excmd(*line))
6315 {
6316#ifdef FEAT_GUI
6317 /* First, we do not destroy the old values, but allocate the new
6318 * ones and update the display. THEN we destroy the old values.
6319 * If we destroy the old values first, then the old values
6320 * (such as GuiFont's or GuiFontset's) will still be displayed but
6321 * invalid because they were free'd.
6322 */
6323 if (gui.in_use)
6324 {
6325# ifdef FEAT_BEVAL_TIP
6326 gui_init_tooltip_font();
6327# endif
6328# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6329 gui_init_menu_font();
6330# endif
6331 }
6332# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6333 gui_mch_def_colors();
6334# endif
6335# ifdef FEAT_GUI_X11
6336# ifdef FEAT_MENU
6337
6338 /* This only needs to be done when there is no Menu highlight
6339 * group defined by default, which IS currently the case.
6340 */
6341 gui_mch_new_menu_colors();
6342# endif
6343 if (gui.in_use)
6344 {
6345 gui_new_scrollbar_colors();
6346# ifdef FEAT_BEVAL
6347 gui_mch_new_tooltip_colors();
6348# endif
6349# ifdef FEAT_MENU
6350 gui_mch_new_menu_font();
6351# endif
6352 }
6353# endif
6354
6355 /* Ok, we're done allocating the new default graphics items.
6356 * The screen should already be refreshed at this point.
6357 * It is now Ok to clear out the old data.
6358 */
6359#endif
6360#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006361 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006362#endif
6363 restore_cterm_colors();
6364
6365 /*
6366 * Clear all default highlight groups and load the defaults.
6367 */
6368 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6369 highlight_clear(idx);
6370 init_highlight(TRUE, TRUE);
6371#ifdef FEAT_GUI
6372 if (gui.in_use)
6373 highlight_gui_started();
6374#endif
6375 highlight_changed();
6376 redraw_later_clear();
6377 return;
6378 }
6379 name_end = skiptowhite(line);
6380 linep = skipwhite(name_end);
6381 }
6382
6383 /*
6384 * Find the group name in the table. If it does not exist yet, add it.
6385 */
6386 id = syn_check_group(line, (int)(name_end - line));
6387 if (id == 0) /* failed (out of memory) */
6388 return;
6389 idx = id - 1; /* index is ID minus one */
6390
6391 /* Return if "default" was used and the group already has settings. */
6392 if (dodefault && hl_has_settings(idx, TRUE))
6393 return;
6394
6395 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6396 is_normal_group = TRUE;
6397#ifdef FEAT_GUI_X11
6398 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6399 is_menu_group = TRUE;
6400 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6401 is_scrollbar_group = TRUE;
6402 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6403 is_tooltip_group = TRUE;
6404#endif
6405
6406 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6407 if (doclear || (forceit && init))
6408 {
6409 highlight_clear(idx);
6410 if (!doclear)
6411 HL_TABLE()[idx].sg_set = 0;
6412 }
6413
6414 if (!doclear)
6415 while (!ends_excmd(*linep))
6416 {
6417 key_start = linep;
6418 if (*linep == '=')
6419 {
6420 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6421 error = TRUE;
6422 break;
6423 }
6424
6425 /*
6426 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6427 * "guibg").
6428 */
6429 while (*linep && !vim_iswhite(*linep) && *linep != '=')
6430 ++linep;
6431 vim_free(key);
6432 key = vim_strnsave_up(key_start, (int)(linep - key_start));
6433 if (key == NULL)
6434 {
6435 error = TRUE;
6436 break;
6437 }
6438 linep = skipwhite(linep);
6439
6440 if (STRCMP(key, "NONE") == 0)
6441 {
6442 if (!init || HL_TABLE()[idx].sg_set == 0)
6443 {
6444 if (!init)
6445 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6446 highlight_clear(idx);
6447 }
6448 continue;
6449 }
6450
6451 /*
6452 * Check for the equal sign.
6453 */
6454 if (*linep != '=')
6455 {
6456 EMSG2(_("E416: missing equal sign: %s"), key_start);
6457 error = TRUE;
6458 break;
6459 }
6460 ++linep;
6461
6462 /*
6463 * Isolate the argument.
6464 */
6465 linep = skipwhite(linep);
6466 if (*linep == '\'') /* guifg='color name' */
6467 {
6468 arg_start = ++linep;
6469 linep = vim_strchr(linep, '\'');
6470 if (linep == NULL)
6471 {
6472 EMSG2(_(e_invarg2), key_start);
6473 error = TRUE;
6474 break;
6475 }
6476 }
6477 else
6478 {
6479 arg_start = linep;
6480 linep = skiptowhite(linep);
6481 }
6482 if (linep == arg_start)
6483 {
6484 EMSG2(_("E417: missing argument: %s"), key_start);
6485 error = TRUE;
6486 break;
6487 }
6488 vim_free(arg);
6489 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6490 if (arg == NULL)
6491 {
6492 error = TRUE;
6493 break;
6494 }
6495 if (*linep == '\'')
6496 ++linep;
6497
6498 /*
6499 * Store the argument.
6500 */
6501 if ( STRCMP(key, "TERM") == 0
6502 || STRCMP(key, "CTERM") == 0
6503 || STRCMP(key, "GUI") == 0)
6504 {
6505 attr = 0;
6506 off = 0;
6507 while (arg[off] != NUL)
6508 {
6509 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6510 {
6511 len = (int)STRLEN(hl_name_table[i]);
6512 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6513 {
6514 attr |= hl_attr_table[i];
6515 off += len;
6516 break;
6517 }
6518 }
6519 if (i < 0)
6520 {
6521 EMSG2(_("E418: Illegal value: %s"), arg);
6522 error = TRUE;
6523 break;
6524 }
6525 if (arg[off] == ',') /* another one follows */
6526 ++off;
6527 }
6528 if (error)
6529 break;
6530 if (*key == 'T')
6531 {
6532 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6533 {
6534 if (!init)
6535 HL_TABLE()[idx].sg_set |= SG_TERM;
6536 HL_TABLE()[idx].sg_term = attr;
6537 }
6538 }
6539 else if (*key == 'C')
6540 {
6541 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6542 {
6543 if (!init)
6544 HL_TABLE()[idx].sg_set |= SG_CTERM;
6545 HL_TABLE()[idx].sg_cterm = attr;
6546 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6547 }
6548 }
6549#ifdef FEAT_GUI
6550 else
6551 {
6552 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6553 {
6554 if (!init)
6555 HL_TABLE()[idx].sg_set |= SG_GUI;
6556 HL_TABLE()[idx].sg_gui = attr;
6557 }
6558 }
6559#endif
6560 }
6561 else if (STRCMP(key, "FONT") == 0)
6562 {
6563 /* in non-GUI fonts are simply ignored */
6564#ifdef FEAT_GUI
6565 if (!gui.shell_created)
6566 {
6567 /* GUI not started yet, always accept the name. */
6568 vim_free(HL_TABLE()[idx].sg_font_name);
6569 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6570 }
6571 else
6572 {
6573 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6574# ifdef FEAT_XFONTSET
6575 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6576# endif
6577 /* First, save the current font/fontset.
6578 * Then try to allocate the font/fontset.
6579 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6580 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6581 */
6582
6583 HL_TABLE()[idx].sg_font = NOFONT;
6584# ifdef FEAT_XFONTSET
6585 HL_TABLE()[idx].sg_fontset = NOFONTSET;
6586# endif
6587 hl_do_font(idx, arg, is_normal_group, is_menu_group,
6588 is_tooltip_group);
6589
6590# ifdef FEAT_XFONTSET
6591 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6592 {
6593 /* New fontset was accepted. Free the old one, if there was
6594 * one.
6595 */
6596 gui_mch_free_fontset(temp_sg_fontset);
6597 vim_free(HL_TABLE()[idx].sg_font_name);
6598 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6599 }
6600 else
6601 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6602# endif
6603 if (HL_TABLE()[idx].sg_font != NOFONT)
6604 {
6605 /* New font was accepted. Free the old one, if there was
6606 * one.
6607 */
6608 gui_mch_free_font(temp_sg_font);
6609 vim_free(HL_TABLE()[idx].sg_font_name);
6610 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6611 }
6612 else
6613 HL_TABLE()[idx].sg_font = temp_sg_font;
6614 }
6615#endif
6616 }
6617 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6618 {
6619 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6620 {
6621 if (!init)
6622 HL_TABLE()[idx].sg_set |= SG_CTERM;
6623
6624 /* When setting the foreground color, and previously the "bold"
6625 * flag was set for a light color, reset it now */
6626 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6627 {
6628 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6629 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6630 }
6631
6632 if (VIM_ISDIGIT(*arg))
6633 color = atoi((char *)arg);
6634 else if (STRICMP(arg, "fg") == 0)
6635 {
6636 if (cterm_normal_fg_color)
6637 color = cterm_normal_fg_color - 1;
6638 else
6639 {
6640 EMSG(_("E419: FG color unknown"));
6641 error = TRUE;
6642 break;
6643 }
6644 }
6645 else if (STRICMP(arg, "bg") == 0)
6646 {
6647 if (cterm_normal_bg_color > 0)
6648 color = cterm_normal_bg_color - 1;
6649 else
6650 {
6651 EMSG(_("E420: BG color unknown"));
6652 error = TRUE;
6653 break;
6654 }
6655 }
6656 else
6657 {
6658 static char *(color_names[28]) = {
6659 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6660 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6661 "Gray", "Grey",
6662 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6663 "Blue", "LightBlue", "Green", "LightGreen",
6664 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6665 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6666 static int color_numbers_16[28] = {0, 1, 2, 3,
6667 4, 5, 6, 6,
6668 7, 7,
6669 7, 7, 8, 8,
6670 9, 9, 10, 10,
6671 11, 11, 12, 12, 13,
6672 13, 14, 14, 15, -1};
6673 /* for xterm with 88 colors... */
6674 static int color_numbers_88[28] = {0, 4, 2, 6,
6675 1, 5, 32, 72,
6676 84, 84,
6677 7, 7, 82, 82,
6678 12, 43, 10, 61,
6679 14, 63, 9, 74, 13,
6680 75, 11, 78, 15, -1};
6681 /* for xterm with 256 colors... */
6682 static int color_numbers_256[28] = {0, 4, 2, 6,
6683 1, 5, 130, 130,
6684 248, 248,
6685 7, 7, 242, 242,
6686 12, 81, 10, 121,
6687 14, 159, 9, 224, 13,
6688 225, 11, 229, 15, -1};
6689 /* for terminals with less than 16 colors... */
6690 static int color_numbers_8[28] = {0, 4, 2, 6,
6691 1, 5, 3, 3,
6692 7, 7,
6693 7, 7, 0+8, 0+8,
6694 4+8, 4+8, 2+8, 2+8,
6695 6+8, 6+8, 1+8, 1+8, 5+8,
6696 5+8, 3+8, 3+8, 7+8, -1};
6697#if defined(__QNXNTO__)
6698 static int *color_numbers_8_qansi = color_numbers_8;
6699 /* On qnx, the 8 & 16 color arrays are the same */
6700 if (STRNCMP(T_NAME, "qansi", 5) == 0)
6701 color_numbers_8_qansi = color_numbers_16;
6702#endif
6703
6704 /* reduce calls to STRICMP a bit, it can be slow */
6705 off = TOUPPER_ASC(*arg);
6706 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6707 if (off == color_names[i][0]
6708 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6709 break;
6710 if (i < 0)
6711 {
6712 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6713 error = TRUE;
6714 break;
6715 }
6716
6717 /* Use the _16 table to check if its a valid color name. */
6718 color = color_numbers_16[i];
6719 if (color >= 0)
6720 {
6721 if (t_colors == 8)
6722 {
6723 /* t_Co is 8: use the 8 colors table */
6724#if defined(__QNXNTO__)
6725 color = color_numbers_8_qansi[i];
6726#else
6727 color = color_numbers_8[i];
6728#endif
6729 if (key[5] == 'F')
6730 {
6731 /* set/reset bold attribute to get light foreground
6732 * colors (on some terminals, e.g. "linux") */
6733 if (color & 8)
6734 {
6735 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
6736 HL_TABLE()[idx].sg_cterm_bold = TRUE;
6737 }
6738 else
6739 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6740 }
6741 color &= 7; /* truncate to 8 colors */
6742 }
6743 else if (t_colors == 16 || t_colors == 88
6744 || t_colors == 256)
6745 {
6746 /*
6747 * Guess: if the termcap entry ends in 'm', it is
6748 * probably an xterm-like terminal. Use the changed
6749 * order for colors.
6750 */
6751 if (*T_CAF != NUL)
6752 p = T_CAF;
6753 else
6754 p = T_CSF;
6755 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
6756 switch (t_colors)
6757 {
6758 case 16:
6759 color = color_numbers_8[i];
6760 break;
6761 case 88:
6762 color = color_numbers_88[i];
6763 break;
6764 case 256:
6765 color = color_numbers_256[i];
6766 break;
6767 }
6768 }
6769 }
6770 }
6771 /* Add one to the argument, to avoid zero */
6772 if (key[5] == 'F')
6773 {
6774 HL_TABLE()[idx].sg_cterm_fg = color + 1;
6775 if (is_normal_group)
6776 {
6777 cterm_normal_fg_color = color + 1;
6778 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
6779#ifdef FEAT_GUI
6780 /* Don't do this if the GUI is used. */
6781 if (!gui.in_use && !gui.starting)
6782#endif
6783 {
6784 must_redraw = CLEAR;
6785 if (termcap_active)
6786 term_fg_color(color);
6787 }
6788 }
6789 }
6790 else
6791 {
6792 HL_TABLE()[idx].sg_cterm_bg = color + 1;
6793 if (is_normal_group)
6794 {
6795 cterm_normal_bg_color = color + 1;
6796#ifdef FEAT_GUI
6797 /* Don't mess with 'background' if the GUI is used. */
6798 if (!gui.in_use && !gui.starting)
6799#endif
6800 {
6801 must_redraw = CLEAR;
6802 if (termcap_active)
6803 term_bg_color(color);
6804 if (t_colors < 16)
6805 i = (color == 0 || color == 4);
6806 else
6807 i = (color < 7 || color == 8);
6808 /* Set the 'background' option if the value is wrong. */
6809 if (i != (*p_bg == 'd'))
6810 set_option_value((char_u *)"bg", 0L,
6811 i ? (char_u *)"dark" : (char_u *)"light", 0);
6812 }
6813 }
6814 }
6815 }
6816 }
6817 else if (STRCMP(key, "GUIFG") == 0)
6818 {
6819#ifdef FEAT_GUI /* in non-GUI guifg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006820 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006821 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006822 if (!init)
6823 HL_TABLE()[idx].sg_set |= SG_GUI;
6824
6825 i = color_name2handle(arg);
6826 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6827 {
6828 HL_TABLE()[idx].sg_gui_fg = i;
6829 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
6830 if (STRCMP(arg, "NONE"))
6831 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
6832 else
6833 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006834# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006835 if (is_menu_group)
6836 gui.menu_fg_pixel = i;
6837 if (is_scrollbar_group)
6838 gui.scroll_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006839# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006840 if (is_tooltip_group)
6841 gui.tooltip_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006842# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006843 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006844# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006845 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006846 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006847#endif
6848 }
6849 else if (STRCMP(key, "GUIBG") == 0)
6850 {
6851#ifdef FEAT_GUI /* in non-GUI guibg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006852 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006853 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006854 if (!init)
6855 HL_TABLE()[idx].sg_set |= SG_GUI;
6856
6857 i = color_name2handle(arg);
6858 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6859 {
6860 HL_TABLE()[idx].sg_gui_bg = i;
6861 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
6862 if (STRCMP(arg, "NONE") != 0)
6863 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
6864 else
6865 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006866# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006867 if (is_menu_group)
6868 gui.menu_bg_pixel = i;
6869 if (is_scrollbar_group)
6870 gui.scroll_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006871# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006872 if (is_tooltip_group)
6873 gui.tooltip_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006874# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006875 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006876# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006877 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006878 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006879#endif
6880 }
6881 else if (STRCMP(key, "GUISP") == 0)
6882 {
6883#ifdef FEAT_GUI /* in non-GUI guisp colors are simply ignored */
6884 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6885 {
6886 if (!init)
6887 HL_TABLE()[idx].sg_set |= SG_GUI;
6888
6889 i = color_name2handle(arg);
6890 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6891 {
6892 HL_TABLE()[idx].sg_gui_sp = i;
6893 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
6894 if (STRCMP(arg, "NONE") != 0)
6895 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
6896 else
6897 HL_TABLE()[idx].sg_gui_sp_name = NULL;
6898 }
6899 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006900#endif
6901 }
6902 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
6903 {
6904 char_u buf[100];
6905 char_u *tname;
6906
6907 if (!init)
6908 HL_TABLE()[idx].sg_set |= SG_TERM;
6909
6910 /*
6911 * The "start" and "stop" arguments can be a literal escape
6912 * sequence, or a comma seperated list of terminal codes.
6913 */
6914 if (STRNCMP(arg, "t_", 2) == 0)
6915 {
6916 off = 0;
6917 buf[0] = 0;
6918 while (arg[off] != NUL)
6919 {
6920 /* Isolate one termcap name */
6921 for (len = 0; arg[off + len] &&
6922 arg[off + len] != ','; ++len)
6923 ;
6924 tname = vim_strnsave(arg + off, len);
6925 if (tname == NULL) /* out of memory */
6926 {
6927 error = TRUE;
6928 break;
6929 }
6930 /* lookup the escape sequence for the item */
6931 p = get_term_code(tname);
6932 vim_free(tname);
6933 if (p == NULL) /* ignore non-existing things */
6934 p = (char_u *)"";
6935
6936 /* Append it to the already found stuff */
6937 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
6938 {
6939 EMSG2(_("E422: terminal code too long: %s"), arg);
6940 error = TRUE;
6941 break;
6942 }
6943 STRCAT(buf, p);
6944
6945 /* Advance to the next item */
6946 off += len;
6947 if (arg[off] == ',') /* another one follows */
6948 ++off;
6949 }
6950 }
6951 else
6952 {
6953 /*
6954 * Copy characters from arg[] to buf[], translating <> codes.
6955 */
6956 for (p = arg, off = 0; off < 100 && *p; )
6957 {
6958 len = trans_special(&p, buf + off, FALSE);
6959 if (len) /* recognized special char */
6960 off += len;
6961 else /* copy as normal char */
6962 buf[off++] = *p++;
6963 }
6964 buf[off] = NUL;
6965 }
6966 if (error)
6967 break;
6968
6969 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
6970 p = NULL;
6971 else
6972 p = vim_strsave(buf);
6973 if (key[2] == 'A')
6974 {
6975 vim_free(HL_TABLE()[idx].sg_start);
6976 HL_TABLE()[idx].sg_start = p;
6977 }
6978 else
6979 {
6980 vim_free(HL_TABLE()[idx].sg_stop);
6981 HL_TABLE()[idx].sg_stop = p;
6982 }
6983 }
6984 else
6985 {
6986 EMSG2(_("E423: Illegal argument: %s"), key_start);
6987 error = TRUE;
6988 break;
6989 }
6990
6991 /*
6992 * When highlighting has been given for a group, don't link it.
6993 */
6994 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
6995 HL_TABLE()[idx].sg_link = 0;
6996
6997 /*
6998 * Continue with next argument.
6999 */
7000 linep = skipwhite(linep);
7001 }
7002
7003 /*
7004 * If there is an error, and it's a new entry, remove it from the table.
7005 */
7006 if (error && idx == highlight_ga.ga_len)
7007 syn_unadd_group();
7008 else
7009 {
7010 if (is_normal_group)
7011 {
7012 HL_TABLE()[idx].sg_term_attr = 0;
7013 HL_TABLE()[idx].sg_cterm_attr = 0;
7014#ifdef FEAT_GUI
7015 HL_TABLE()[idx].sg_gui_attr = 0;
7016 /*
7017 * Need to update all groups, because they might be using "bg"
7018 * and/or "fg", which have been changed now.
7019 */
7020 if (gui.in_use)
7021 highlight_gui_started();
7022#endif
7023 }
7024#ifdef FEAT_GUI_X11
7025# ifdef FEAT_MENU
7026 else if (is_menu_group)
7027 {
7028 if (gui.in_use && do_colors)
7029 gui_mch_new_menu_colors();
7030 }
7031# endif
7032 else if (is_scrollbar_group)
7033 {
7034 if (gui.in_use && do_colors)
7035 gui_new_scrollbar_colors();
7036 }
7037# ifdef FEAT_BEVAL
7038 else if (is_tooltip_group)
7039 {
7040 if (gui.in_use && do_colors)
7041 gui_mch_new_tooltip_colors();
7042 }
7043# endif
7044#endif
7045 else
7046 set_hl_attr(idx);
7047 redraw_all_later(NOT_VALID);
7048 }
7049 vim_free(key);
7050 vim_free(arg);
7051
7052 /* Only call highlight_changed() once, after sourcing a syntax file */
7053 need_highlight_changed = TRUE;
7054}
7055
7056/*
7057 * Reset the cterm colors to what they were before Vim was started, if
7058 * possible. Otherwise reset them to zero.
7059 */
7060 void
7061restore_cterm_colors()
7062{
7063#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7064 /* Since t_me has been set, this probably means that the user
7065 * wants to use this as default colors. Need to reset default
7066 * background/foreground colors. */
7067 mch_set_normal_colors();
7068#else
7069 cterm_normal_fg_color = 0;
7070 cterm_normal_fg_bold = 0;
7071 cterm_normal_bg_color = 0;
7072#endif
7073}
7074
7075/*
7076 * Return TRUE if highlight group "idx" has any settings.
7077 * When "check_link" is TRUE also check for an existing link.
7078 */
7079 static int
7080hl_has_settings(idx, check_link)
7081 int idx;
7082 int check_link;
7083{
7084 return ( HL_TABLE()[idx].sg_term_attr != 0
7085 || HL_TABLE()[idx].sg_cterm_attr != 0
7086#ifdef FEAT_GUI
7087 || HL_TABLE()[idx].sg_gui_attr != 0
7088#endif
7089 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7090}
7091
7092/*
7093 * Clear highlighting for one group.
7094 */
7095 static void
7096highlight_clear(idx)
7097 int idx;
7098{
7099 HL_TABLE()[idx].sg_term = 0;
7100 vim_free(HL_TABLE()[idx].sg_start);
7101 HL_TABLE()[idx].sg_start = NULL;
7102 vim_free(HL_TABLE()[idx].sg_stop);
7103 HL_TABLE()[idx].sg_stop = NULL;
7104 HL_TABLE()[idx].sg_term_attr = 0;
7105 HL_TABLE()[idx].sg_cterm = 0;
7106 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7107 HL_TABLE()[idx].sg_cterm_fg = 0;
7108 HL_TABLE()[idx].sg_cterm_bg = 0;
7109 HL_TABLE()[idx].sg_cterm_attr = 0;
7110#ifdef FEAT_GUI /* in non-GUI fonts are simply ignored */
7111 HL_TABLE()[idx].sg_gui = 0;
7112 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7113 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7114 HL_TABLE()[idx].sg_gui_fg_name = NULL;
7115 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7116 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7117 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007118 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7119 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7120 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007121 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7122 HL_TABLE()[idx].sg_font = NOFONT;
7123# ifdef FEAT_XFONTSET
7124 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7125 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7126# endif
7127 vim_free(HL_TABLE()[idx].sg_font_name);
7128 HL_TABLE()[idx].sg_font_name = NULL;
7129 HL_TABLE()[idx].sg_gui_attr = 0;
7130#endif
7131}
7132
7133#if defined(FEAT_GUI) || defined(PROTO)
7134/*
7135 * Set the normal foreground and background colors according to the "Normal"
7136 * highlighighting group. For X11 also set "Menu", "Scrollbar", and
7137 * "Tooltip" colors.
7138 */
7139 void
7140set_normal_colors()
7141{
7142 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007143 &gui.norm_pixel, &gui.back_pixel,
7144 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007145 {
7146 gui_mch_new_colors();
7147 must_redraw = CLEAR;
7148 }
7149#ifdef FEAT_GUI_X11
7150 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007151 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7152 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007153 {
7154# ifdef FEAT_MENU
7155 gui_mch_new_menu_colors();
7156# endif
7157 must_redraw = CLEAR;
7158 }
7159# ifdef FEAT_BEVAL
7160 if (set_group_colors((char_u *)"Tooltip",
7161 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7162 FALSE, FALSE, TRUE))
7163 {
7164# ifdef FEAT_TOOLBAR
7165 gui_mch_new_tooltip_colors();
7166# endif
7167 must_redraw = CLEAR;
7168 }
7169#endif
7170 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007171 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7172 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007173 {
7174 gui_new_scrollbar_colors();
7175 must_redraw = CLEAR;
7176 }
7177#endif
7178}
7179
7180/*
7181 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7182 */
7183 static int
7184set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7185 char_u *name;
7186 guicolor_T *fgp;
7187 guicolor_T *bgp;
7188 int do_menu;
7189 int use_norm;
7190 int do_tooltip;
7191{
7192 int idx;
7193
7194 idx = syn_name2id(name) - 1;
7195 if (idx >= 0)
7196 {
7197 gui_do_one_color(idx, do_menu, do_tooltip);
7198
7199 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7200 *fgp = HL_TABLE()[idx].sg_gui_fg;
7201 else if (use_norm)
7202 *fgp = gui.def_norm_pixel;
7203 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7204 *bgp = HL_TABLE()[idx].sg_gui_bg;
7205 else if (use_norm)
7206 *bgp = gui.def_back_pixel;
7207 return TRUE;
7208 }
7209 return FALSE;
7210}
7211
7212/*
7213 * Get the font of the "Normal" group.
7214 * Returns "" when it's not found or not set.
7215 */
7216 char_u *
7217hl_get_font_name()
7218{
7219 int id;
7220 char_u *s;
7221
7222 id = syn_name2id((char_u *)"Normal");
7223 if (id > 0)
7224 {
7225 s = HL_TABLE()[id - 1].sg_font_name;
7226 if (s != NULL)
7227 return s;
7228 }
7229 return (char_u *)"";
7230}
7231
7232/*
7233 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7234 * actually chosen to be used.
7235 */
7236 void
7237hl_set_font_name(font_name)
7238 char_u *font_name;
7239{
7240 int id;
7241
7242 id = syn_name2id((char_u *)"Normal");
7243 if (id > 0)
7244 {
7245 vim_free(HL_TABLE()[id - 1].sg_font_name);
7246 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7247 }
7248}
7249
7250/*
7251 * Set background color for "Normal" group. Called by gui_set_bg_color()
7252 * when the color is known.
7253 */
7254 void
7255hl_set_bg_color_name(name)
7256 char_u *name; /* must have been allocated */
7257{
7258 int id;
7259
7260 if (name != NULL)
7261 {
7262 id = syn_name2id((char_u *)"Normal");
7263 if (id > 0)
7264 {
7265 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7266 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7267 }
7268 }
7269}
7270
7271/*
7272 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7273 * when the color is known.
7274 */
7275 void
7276hl_set_fg_color_name(name)
7277 char_u *name; /* must have been allocated */
7278{
7279 int id;
7280
7281 if (name != NULL)
7282 {
7283 id = syn_name2id((char_u *)"Normal");
7284 if (id > 0)
7285 {
7286 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7287 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7288 }
7289 }
7290}
7291
7292/*
7293 * Return the handle for a color name.
7294 * Returns INVALCOLOR when failed.
7295 */
7296 static guicolor_T
7297color_name2handle(name)
7298 char_u *name;
7299{
7300 if (STRCMP(name, "NONE") == 0)
7301 return INVALCOLOR;
7302
7303 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7304 return gui.norm_pixel;
7305 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7306 return gui.back_pixel;
7307
7308 return gui_get_color(name);
7309}
7310
7311/*
7312 * Return the handle for a font name.
7313 * Returns NOFONT when failed.
7314 */
7315 static GuiFont
7316font_name2handle(name)
7317 char_u *name;
7318{
7319 if (STRCMP(name, "NONE") == 0)
7320 return NOFONT;
7321
7322 return gui_mch_get_font(name, TRUE);
7323}
7324
7325# ifdef FEAT_XFONTSET
7326/*
7327 * Return the handle for a fontset name.
7328 * Returns NOFONTSET when failed.
7329 */
7330 static GuiFontset
7331fontset_name2handle(name, fixed_width)
7332 char_u *name;
7333 int fixed_width;
7334{
7335 if (STRCMP(name, "NONE") == 0)
7336 return NOFONTSET;
7337
7338 return gui_mch_get_fontset(name, TRUE, fixed_width);
7339}
7340# endif
7341
7342/*
7343 * Get the font or fontset for one highlight group.
7344 */
7345/*ARGSUSED*/
7346 static void
7347hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7348 int idx;
7349 char_u *arg;
7350 int do_normal; /* set normal font */
7351 int do_menu; /* set menu font */
7352 int do_tooltip; /* set tooltip font */
7353{
7354# ifdef FEAT_XFONTSET
7355 /* If 'guifontset' is not empty, first try using the name as a
7356 * fontset. If that doesn't work, use it as a font name. */
7357 if (*p_guifontset != NUL
7358# ifdef FONTSET_ALWAYS
7359 || do_menu
7360# endif
7361# ifdef FEAT_BEVAL_TIP
7362 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
7363 || do_tooltip
7364# endif
7365 )
7366 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7367# ifdef FONTSET_ALWAYS
7368 || do_menu
7369# endif
7370# ifdef FEAT_BEVAL_TIP
7371 || do_tooltip
7372# endif
7373 );
7374 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7375 {
7376 /* If it worked and it's the Normal group, use it as the
7377 * normal fontset. Same for the Menu group. */
7378 if (do_normal)
7379 gui_init_font(arg, TRUE);
7380# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7381 if (do_menu)
7382 {
7383# ifdef FONTSET_ALWAYS
7384 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7385# else
7386 /* YIKES! This is a bug waiting to crash the program */
7387 gui.menu_font = HL_TABLE()[idx].sg_fontset;
7388# endif
7389 gui_mch_new_menu_font();
7390 }
7391# ifdef FEAT_BEVAL
7392 if (do_tooltip)
7393 {
7394 /* The Athena widget set cannot currently handle switching between
7395 * displaying a single font and a fontset.
7396 * If the XtNinternational resource is set to True at widget
7397 * creation, then a fontset is always used, othwise an
7398 * XFontStruct is used.
7399 */
7400 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7401 gui_mch_new_tooltip_font();
7402 }
7403# endif
7404# endif
7405 }
7406 else
7407# endif
7408 {
7409 HL_TABLE()[idx].sg_font = font_name2handle(arg);
7410 /* If it worked and it's the Normal group, use it as the
7411 * normal font. Same for the Menu group. */
7412 if (HL_TABLE()[idx].sg_font != NOFONT)
7413 {
7414 if (do_normal)
7415 gui_init_font(arg, FALSE);
7416#ifndef FONTSET_ALWAYS
7417# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7418 if (do_menu)
7419 {
7420 gui.menu_font = HL_TABLE()[idx].sg_font;
7421 gui_mch_new_menu_font();
7422 }
7423# endif
7424#endif
7425 }
7426 }
7427}
7428
7429#endif /* FEAT_GUI */
7430
7431/*
7432 * Table with the specifications for an attribute number.
7433 * Note that this table is used by ALL buffers. This is required because the
7434 * GUI can redraw at any time for any buffer.
7435 */
7436garray_T term_attr_table = {0, 0, 0, 0, NULL};
7437
7438#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7439
7440garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
7441
7442#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7443
7444#ifdef FEAT_GUI
7445garray_T gui_attr_table = {0, 0, 0, 0, NULL};
7446
7447#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7448#endif
7449
7450/*
7451 * Return the attr number for a set of colors and font.
7452 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7453 * if the combination is new.
7454 * Return 0 for error (no more room).
7455 */
7456 static int
7457get_attr_entry(table, aep)
7458 garray_T *table;
7459 attrentry_T *aep;
7460{
7461 int i;
7462 attrentry_T *gap;
7463 static int recursive = FALSE;
7464
7465 /*
7466 * Init the table, in case it wasn't done yet.
7467 */
7468 table->ga_itemsize = sizeof(attrentry_T);
7469 table->ga_growsize = 7;
7470
7471 /*
7472 * Try to find an entry with the same specifications.
7473 */
7474 for (i = 0; i < table->ga_len; ++i)
7475 {
7476 gap = &(((attrentry_T *)table->ga_data)[i]);
7477 if ( aep->ae_attr == gap->ae_attr
7478 && (
7479#ifdef FEAT_GUI
7480 (table == &gui_attr_table
7481 && (aep->ae_u.gui.fg_color == gap->ae_u.gui.fg_color
7482 && aep->ae_u.gui.bg_color == gap->ae_u.gui.bg_color
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007483 && aep->ae_u.gui.sp_color == gap->ae_u.gui.sp_color
Bram Moolenaar071d4272004-06-13 20:20:40 +00007484 && aep->ae_u.gui.font == gap->ae_u.gui.font
7485# ifdef FEAT_XFONTSET
7486 && aep->ae_u.gui.fontset == gap->ae_u.gui.fontset
7487# endif
7488 ))
7489 ||
7490#endif
7491 (table == &term_attr_table
7492 && (aep->ae_u.term.start == NULL) ==
7493 (gap->ae_u.term.start == NULL)
7494 && (aep->ae_u.term.start == NULL
7495 || STRCMP(aep->ae_u.term.start,
7496 gap->ae_u.term.start) == 0)
7497 && (aep->ae_u.term.stop == NULL) ==
7498 (gap->ae_u.term.stop == NULL)
7499 && (aep->ae_u.term.stop == NULL
7500 || STRCMP(aep->ae_u.term.stop,
7501 gap->ae_u.term.stop) == 0))
7502 || (table == &cterm_attr_table
7503 && aep->ae_u.cterm.fg_color == gap->ae_u.cterm.fg_color
7504 && aep->ae_u.cterm.bg_color == gap->ae_u.cterm.bg_color)
7505 ))
7506
7507 return i + ATTR_OFF;
7508 }
7509
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007510 if (table->ga_len + ATTR_OFF >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007511 {
7512 /*
7513 * Running out of attribute entries! remove all attributes, and
7514 * compute new ones for all groups.
7515 * When called recursively, we are really out of numbers.
7516 */
7517 if (recursive)
7518 {
7519 EMSG(_("E424: Too many different highlighting attributes in use"));
7520 return 0;
7521 }
7522 recursive = TRUE;
7523
7524#ifdef FEAT_GUI
7525 ga_clear(&gui_attr_table);
7526#endif
7527 ga_clear(&term_attr_table);
7528 ga_clear(&cterm_attr_table);
7529 must_redraw = CLEAR;
7530
7531 for (i = 0; i < highlight_ga.ga_len; ++i)
7532 set_hl_attr(i);
7533
7534 recursive = FALSE;
7535 }
7536
7537 /*
7538 * This is a new combination of colors and font, add an entry.
7539 */
7540 if (ga_grow(table, 1) == FAIL)
7541 return 0;
7542
7543 gap = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7544 vim_memset(gap, 0, sizeof(attrentry_T));
7545 gap->ae_attr = aep->ae_attr;
7546#ifdef FEAT_GUI
7547 if (table == &gui_attr_table)
7548 {
7549 gap->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7550 gap->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007551 gap->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007552 gap->ae_u.gui.font = aep->ae_u.gui.font;
7553# ifdef FEAT_XFONTSET
7554 gap->ae_u.gui.fontset = aep->ae_u.gui.fontset;
7555# endif
7556 }
7557#endif
7558 if (table == &term_attr_table)
7559 {
7560 if (aep->ae_u.term.start == NULL)
7561 gap->ae_u.term.start = NULL;
7562 else
7563 gap->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
7564 if (aep->ae_u.term.stop == NULL)
7565 gap->ae_u.term.stop = NULL;
7566 else
7567 gap->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
7568 }
7569 else if (table == &cterm_attr_table)
7570 {
7571 gap->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7572 gap->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
7573 }
7574 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007575 return (table->ga_len - 1 + ATTR_OFF);
7576}
7577
Bram Moolenaar217ad922005-03-20 22:37:15 +00007578#if defined(FEAT_SYN_HL) || defined(PROTO)
7579/*
7580 * Combine the spelling attributes with other attributes. "spell_attr"
7581 * overrules "char_attr".
7582 * This creates a new group when required.
7583 * Since we expect there to be few spelling mistakes we don't cache the
7584 * result.
7585 * Return the resulting attributes.
7586 */
7587 int
7588hl_combine_attr(char_attr, spell_attr)
7589 int char_attr;
7590 int spell_attr;
7591{
7592 attrentry_T *char_aep = NULL;
7593 attrentry_T *spell_aep;
7594 attrentry_T new_en;
7595
7596 if (char_attr == 0)
7597 return spell_attr;
7598 if (char_attr <= HL_ALL && spell_attr <= HL_ALL)
7599 return char_attr | spell_attr;
7600#ifdef FEAT_GUI
7601 if (gui.in_use)
7602 {
7603 if (char_attr > HL_ALL)
7604 char_aep = syn_gui_attr2entry(char_attr);
7605 if (char_aep != NULL)
7606 new_en = *char_aep;
7607 else
7608 {
7609 vim_memset(&new_en, 0, sizeof(new_en));
7610 if (char_attr <= HL_ALL)
7611 new_en.ae_attr = char_attr;
7612 }
7613
7614 if (spell_attr <= HL_ALL)
7615 new_en.ae_attr |= spell_attr;
7616 else
7617 {
7618 spell_aep = syn_gui_attr2entry(spell_attr);
7619 if (spell_aep != NULL)
7620 {
7621 new_en.ae_attr |= spell_aep->ae_attr;
7622 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7623 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7624 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7625 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7626 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7627 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7628 if (spell_aep->ae_u.gui.font != NOFONT)
7629 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7630# ifdef FEAT_XFONTSET
7631 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7632 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7633# endif
7634 }
7635 }
7636 return get_attr_entry(&gui_attr_table, &new_en);
7637 }
7638#endif
7639
7640 if (t_colors > 1)
7641 {
7642 if (char_attr > HL_ALL)
7643 char_aep = syn_cterm_attr2entry(char_attr);
7644 if (char_aep != NULL)
7645 new_en = *char_aep;
7646 else
7647 {
7648 vim_memset(&new_en, 0, sizeof(new_en));
7649 if (char_attr <= HL_ALL)
7650 new_en.ae_attr = char_attr;
7651 }
7652
7653 if (spell_attr <= HL_ALL)
7654 new_en.ae_attr |= spell_attr;
7655 else
7656 {
7657 spell_aep = syn_cterm_attr2entry(spell_attr);
7658 if (spell_aep != NULL)
7659 {
7660 new_en.ae_attr |= spell_aep->ae_attr;
7661 if (spell_aep->ae_u.cterm.fg_color > 0)
7662 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
7663 if (spell_aep->ae_u.cterm.bg_color > 0)
7664 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
7665 }
7666 }
7667 return get_attr_entry(&cterm_attr_table, &new_en);
7668 }
7669
7670 if (char_attr > HL_ALL)
7671 char_aep = syn_term_attr2entry(char_attr);
7672 if (char_aep != NULL)
7673 new_en = *char_aep;
7674 else
7675 {
7676 vim_memset(&new_en, 0, sizeof(new_en));
7677 if (char_attr <= HL_ALL)
7678 new_en.ae_attr = char_attr;
7679 }
7680
7681 if (spell_attr <= HL_ALL)
7682 new_en.ae_attr |= spell_attr;
7683 else
7684 {
7685 spell_aep = syn_cterm_attr2entry(spell_attr);
7686 if (spell_aep != NULL)
7687 {
7688 new_en.ae_attr |= spell_aep->ae_attr;
7689 if (spell_aep->ae_u.term.start != NULL)
7690 {
7691 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
7692 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
7693 }
7694 }
7695 }
7696 return get_attr_entry(&term_attr_table, &new_en);
7697}
7698#endif
7699
Bram Moolenaar071d4272004-06-13 20:20:40 +00007700#ifdef FEAT_GUI
7701
7702 attrentry_T *
7703syn_gui_attr2entry(attr)
7704 int attr;
7705{
7706 attr -= ATTR_OFF;
7707 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
7708 return NULL;
7709 return &(GUI_ATTR_ENTRY(attr));
7710}
7711
7712#endif /* FEAT_GUI */
7713
7714 attrentry_T *
7715syn_term_attr2entry(attr)
7716 int attr;
7717{
7718 attr -= ATTR_OFF;
7719 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
7720 return NULL;
7721 return &(TERM_ATTR_ENTRY(attr));
7722}
7723
7724 attrentry_T *
7725syn_cterm_attr2entry(attr)
7726 int attr;
7727{
7728 attr -= ATTR_OFF;
7729 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
7730 return NULL;
7731 return &(CTERM_ATTR_ENTRY(attr));
7732}
7733
7734#define LIST_ATTR 1
7735#define LIST_STRING 2
7736#define LIST_INT 3
7737
7738 static void
7739highlight_list_one(id)
7740 int id;
7741{
7742 struct hl_group *sgp;
7743 int didh = FALSE;
7744
7745 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
7746
7747 didh = highlight_list_arg(id, didh, LIST_ATTR,
7748 sgp->sg_term, NULL, "term");
7749 didh = highlight_list_arg(id, didh, LIST_STRING,
7750 0, sgp->sg_start, "start");
7751 didh = highlight_list_arg(id, didh, LIST_STRING,
7752 0, sgp->sg_stop, "stop");
7753
7754 didh = highlight_list_arg(id, didh, LIST_ATTR,
7755 sgp->sg_cterm, NULL, "cterm");
7756 didh = highlight_list_arg(id, didh, LIST_INT,
7757 sgp->sg_cterm_fg, NULL, "ctermfg");
7758 didh = highlight_list_arg(id, didh, LIST_INT,
7759 sgp->sg_cterm_bg, NULL, "ctermbg");
7760
7761#ifdef FEAT_GUI
7762 didh = highlight_list_arg(id, didh, LIST_ATTR,
7763 sgp->sg_gui, NULL, "gui");
7764 didh = highlight_list_arg(id, didh, LIST_STRING,
7765 0, sgp->sg_gui_fg_name, "guifg");
7766 didh = highlight_list_arg(id, didh, LIST_STRING,
7767 0, sgp->sg_gui_bg_name, "guibg");
7768 didh = highlight_list_arg(id, didh, LIST_STRING,
7769 0, sgp->sg_font_name, "font");
7770#endif
7771
7772 if (sgp->sg_link)
7773 {
7774 (void)syn_list_header(didh, 9999, id);
7775 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
7776 msg_putchar(' ');
7777 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
7778 }
7779}
7780
7781 static int
7782highlight_list_arg(id, didh, type, iarg, sarg, name)
7783 int id;
7784 int didh;
7785 int type;
7786 int iarg;
7787 char_u *sarg;
7788 char *name;
7789{
7790 char_u buf[100];
7791 char_u *ts;
7792 int i;
7793
7794 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
7795 {
7796 ts = buf;
7797 if (type == LIST_INT)
7798 sprintf((char *)buf, "%d", iarg - 1);
7799 else if (type == LIST_STRING)
7800 ts = sarg;
7801 else /* type == LIST_ATTR */
7802 {
7803 buf[0] = NUL;
7804 for (i = 0; hl_attr_table[i] != 0; ++i)
7805 {
7806 if (iarg & hl_attr_table[i])
7807 {
7808 if (buf[0] != NUL)
7809 STRCAT(buf, ",");
7810 STRCAT(buf, hl_name_table[i]);
7811 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
7812 }
7813 }
7814 }
7815
7816 (void)syn_list_header(didh,
7817 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
7818 didh = TRUE;
7819
7820 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
7821 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
7822 msg_outtrans(ts);
7823 }
7824 return didh;
7825}
7826
7827#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
7828/*
7829 * Return "1" if highlight group "id" has attribute "flag".
7830 * Return NULL otherwise.
7831 */
7832 char_u *
7833highlight_has_attr(id, flag, modec)
7834 int id;
7835 int flag;
7836 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
7837{
7838 int attr;
7839
7840 if (id <= 0 || id > highlight_ga.ga_len)
7841 return NULL;
7842
7843#ifdef FEAT_GUI
7844 if (modec == 'g')
7845 attr = HL_TABLE()[id - 1].sg_gui;
7846 else
7847#endif
7848 if (modec == 'c')
7849 attr = HL_TABLE()[id - 1].sg_cterm;
7850 else
7851 attr = HL_TABLE()[id - 1].sg_term;
7852
7853 if (attr & flag)
7854 return (char_u *)"1";
7855 return NULL;
7856}
7857#endif
7858
7859#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
7860/*
7861 * Return color name of highlight group "id".
7862 */
7863 char_u *
7864highlight_color(id, what, modec)
7865 int id;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007866 char_u *what; /* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007867 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
7868{
7869 static char_u name[20];
7870 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007871 int fg = FALSE;
7872# ifdef FEAT_GUI
7873 int sp = FALSE;
7874# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007875
7876 if (id <= 0 || id > highlight_ga.ga_len)
7877 return NULL;
7878
7879 if (TOLOWER_ASC(what[0]) == 'f')
7880 fg = TRUE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007881# ifdef FEAT_GUI
7882 else if (TOLOWER_ASC(what[0]) == 's')
7883 sp = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007884 if (modec == 'g')
7885 {
7886 /* return #RRGGBB form (only possible when GUI is running) */
7887 if (gui.in_use && what[1] && what[2] == '#')
7888 {
7889 guicolor_T color;
7890 long_u rgb;
7891 static char_u buf[10];
7892
7893 if (fg)
7894 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007895 else if (sp)
7896 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007897 else
7898 color = HL_TABLE()[id - 1].sg_gui_bg;
7899 if (color == INVALCOLOR)
7900 return NULL;
7901 rgb = gui_mch_get_rgb(color);
7902 sprintf((char *)buf, "#%02x%02x%02x",
7903 (unsigned)(rgb >> 16),
7904 (unsigned)(rgb >> 8) & 255,
7905 (unsigned)rgb & 255);
7906 return buf;
7907 }
7908 if (fg)
7909 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007910 if (sp)
7911 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007912 return (HL_TABLE()[id - 1].sg_gui_bg_name);
7913 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007914# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007915 if (modec == 'c')
7916 {
7917 if (fg)
7918 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
7919 else
7920 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
7921 sprintf((char *)name, "%d", n);
7922 return name;
7923 }
7924 /* term doesn't have color */
7925 return NULL;
7926}
7927#endif
7928
7929#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
7930 || defined(PROTO)
7931/*
7932 * Return color name of highlight group "id" as RGB value.
7933 */
7934 long_u
7935highlight_gui_color_rgb(id, fg)
7936 int id;
7937 int fg; /* TRUE = fg, FALSE = bg */
7938{
7939 guicolor_T color;
7940
7941 if (id <= 0 || id > highlight_ga.ga_len)
7942 return 0L;
7943
7944 if (fg)
7945 color = HL_TABLE()[id - 1].sg_gui_fg;
7946 else
7947 color = HL_TABLE()[id - 1].sg_gui_bg;
7948
7949 if (color == INVALCOLOR)
7950 return 0L;
7951
7952 return gui_mch_get_rgb(color);
7953}
7954#endif
7955
7956/*
7957 * Output the syntax list header.
7958 * Return TRUE when started a new line.
7959 */
7960 static int
7961syn_list_header(did_header, outlen, id)
7962 int did_header; /* did header already */
7963 int outlen; /* length of string that comes */
7964 int id; /* highlight group id */
7965{
7966 int endcol = 19;
7967 int newline = TRUE;
7968
7969 if (!did_header)
7970 {
7971 msg_putchar('\n');
7972 msg_outtrans(HL_TABLE()[id - 1].sg_name);
7973 endcol = 15;
7974 }
7975 else if (msg_col + outlen + 1 >= Columns)
7976 msg_putchar('\n');
7977 else
7978 {
7979 if (msg_col >= endcol) /* wrap around is like starting a new line */
7980 newline = FALSE;
7981 }
7982
7983 if (msg_col >= endcol) /* output at least one space */
7984 endcol = msg_col + 1;
7985 if (Columns <= endcol) /* avoid hang for tiny window */
7986 endcol = Columns - 1;
7987
7988 msg_advance(endcol);
7989
7990 /* Show "xxx" with the attributes. */
7991 if (!did_header)
7992 {
7993 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
7994 msg_putchar(' ');
7995 }
7996
7997 return newline;
7998}
7999
8000/*
8001 * Set the attribute numbers for a highlight group.
8002 * Called after one of the attributes has changed.
8003 */
8004 static void
8005set_hl_attr(idx)
8006 int idx; /* index in array */
8007{
8008 attrentry_T at_en;
8009 struct hl_group *sgp = HL_TABLE() + idx;
8010
8011 /* The "Normal" group doesn't need an attribute number */
8012 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8013 return;
8014
8015#ifdef FEAT_GUI
8016 /*
8017 * For the GUI mode: If there are other than "normal" highlighting
8018 * attributes, need to allocate an attr number.
8019 */
8020 if (sgp->sg_gui_fg == INVALCOLOR
8021 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008022 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008023 && sgp->sg_font == NOFONT
8024# ifdef FEAT_XFONTSET
8025 && sgp->sg_fontset == NOFONTSET
8026# endif
8027 )
8028 {
8029 sgp->sg_gui_attr = sgp->sg_gui;
8030 }
8031 else
8032 {
8033 at_en.ae_attr = sgp->sg_gui;
8034 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8035 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008036 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008037 at_en.ae_u.gui.font = sgp->sg_font;
8038# ifdef FEAT_XFONTSET
8039 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8040# endif
8041 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8042 }
8043#endif
8044 /*
8045 * For the term mode: If there are other than "normal" highlighting
8046 * attributes, need to allocate an attr number.
8047 */
8048 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8049 sgp->sg_term_attr = sgp->sg_term;
8050 else
8051 {
8052 at_en.ae_attr = sgp->sg_term;
8053 at_en.ae_u.term.start = sgp->sg_start;
8054 at_en.ae_u.term.stop = sgp->sg_stop;
8055 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8056 }
8057
8058 /*
8059 * For the color term mode: If there are other than "normal"
8060 * highlighting attributes, need to allocate an attr number.
8061 */
8062 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8063 sgp->sg_cterm_attr = sgp->sg_cterm;
8064 else
8065 {
8066 at_en.ae_attr = sgp->sg_cterm;
8067 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8068 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8069 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8070 }
8071}
8072
8073/*
8074 * Lookup a highlight group name and return it's ID.
8075 * If it is not found, 0 is returned.
8076 */
8077 int
8078syn_name2id(name)
8079 char_u *name;
8080{
8081 int i;
8082 char_u name_u[200];
8083
8084 /* Avoid using stricmp() too much, it's slow on some systems */
8085 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8086 * don't deserve to be found! */
8087 STRNCPY(name_u, name, 199);
8088 name_u[199] = NUL;
8089 vim_strup(name_u);
8090 for (i = highlight_ga.ga_len; --i >= 0; )
8091 if (HL_TABLE()[i].sg_name_u != NULL
8092 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8093 break;
8094 return i + 1;
8095}
8096
8097#if defined(FEAT_EVAL) || defined(PROTO)
8098/*
8099 * Return TRUE if highlight group "name" exists.
8100 */
8101 int
8102highlight_exists(name)
8103 char_u *name;
8104{
8105 return (syn_name2id(name) > 0);
8106}
8107#endif
8108
8109/*
8110 * Like syn_name2id(), but take a pointer + length argument.
8111 */
8112 int
8113syn_namen2id(linep, len)
8114 char_u *linep;
8115 int len;
8116{
8117 char_u *name;
8118 int id = 0;
8119
8120 name = vim_strnsave(linep, len);
8121 if (name != NULL)
8122 {
8123 id = syn_name2id(name);
8124 vim_free(name);
8125 }
8126 return id;
8127}
8128
8129/*
8130 * Find highlight group name in the table and return it's ID.
8131 * The argument is a pointer to the name and the length of the name.
8132 * If it doesn't exist yet, a new entry is created.
8133 * Return 0 for failure.
8134 */
8135 int
8136syn_check_group(pp, len)
8137 char_u *pp;
8138 int len;
8139{
8140 int id;
8141 char_u *name;
8142
8143 name = vim_strnsave(pp, len);
8144 if (name == NULL)
8145 return 0;
8146
8147 id = syn_name2id(name);
8148 if (id == 0) /* doesn't exist yet */
8149 id = syn_add_group(name);
8150 else
8151 vim_free(name);
8152 return id;
8153}
8154
8155/*
8156 * Add new highlight group and return it's ID.
8157 * "name" must be an allocated string, it will be consumed.
8158 * Return 0 for failure.
8159 */
8160 static int
8161syn_add_group(name)
8162 char_u *name;
8163{
8164 char_u *p;
8165
8166 /* Check that the name is ASCII letters, digits and underscore. */
8167 for (p = name; *p != NUL; ++p)
8168 {
8169 if (!vim_isprintc(*p))
8170 {
8171 EMSG(_("E669: Unprintable character in group name"));
8172 return 0;
8173 }
8174 else if (!ASCII_ISALNUM(*p) && *p != '_')
8175 {
8176 /* This is an error, but since there previously was no check only
8177 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008178 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008179 MSG(_("W18: Invalid character in group name"));
8180 break;
8181 }
8182 }
8183
8184 /*
8185 * First call for this growarray: init growing array.
8186 */
8187 if (highlight_ga.ga_data == NULL)
8188 {
8189 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8190 highlight_ga.ga_growsize = 10;
8191 }
8192
8193 /*
8194 * Make room for at least one other syntax_highlight entry.
8195 */
8196 if (ga_grow(&highlight_ga, 1) == FAIL)
8197 {
8198 vim_free(name);
8199 return 0;
8200 }
8201
8202 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8203 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8204 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8205#ifdef FEAT_GUI
8206 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8207 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008208 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008209#endif
8210 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008211
8212 return highlight_ga.ga_len; /* ID is index plus one */
8213}
8214
8215/*
8216 * When, just after calling syn_add_group(), an error is discovered, this
8217 * function deletes the new name.
8218 */
8219 static void
8220syn_unadd_group()
8221{
8222 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008223 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8224 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8225}
8226
8227/*
8228 * Translate a group ID to highlight attributes.
8229 */
8230 int
8231syn_id2attr(hl_id)
8232 int hl_id;
8233{
8234 int attr;
8235 struct hl_group *sgp;
8236
8237 hl_id = syn_get_final_id(hl_id);
8238 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8239
8240#ifdef FEAT_GUI
8241 /*
8242 * Only use GUI attr when the GUI is being used.
8243 */
8244 if (gui.in_use)
8245 attr = sgp->sg_gui_attr;
8246 else
8247#endif
8248 if (t_colors > 1)
8249 attr = sgp->sg_cterm_attr;
8250 else
8251 attr = sgp->sg_term_attr;
8252
8253 return attr;
8254}
8255
8256#ifdef FEAT_GUI
8257/*
8258 * Get the GUI colors and attributes for a group ID.
8259 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8260 */
8261 int
8262syn_id2colors(hl_id, fgp, bgp)
8263 int hl_id;
8264 guicolor_T *fgp;
8265 guicolor_T *bgp;
8266{
8267 struct hl_group *sgp;
8268
8269 hl_id = syn_get_final_id(hl_id);
8270 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8271
8272 *fgp = sgp->sg_gui_fg;
8273 *bgp = sgp->sg_gui_bg;
8274 return sgp->sg_gui;
8275}
8276#endif
8277
8278/*
8279 * Translate a group ID to the final group ID (following links).
8280 */
8281 int
8282syn_get_final_id(hl_id)
8283 int hl_id;
8284{
8285 int count;
8286 struct hl_group *sgp;
8287
8288 if (hl_id > highlight_ga.ga_len || hl_id < 1)
8289 return 0; /* Can be called from eval!! */
8290
8291 /*
8292 * Follow links until there is no more.
8293 * Look out for loops! Break after 100 links.
8294 */
8295 for (count = 100; --count >= 0; )
8296 {
8297 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8298 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8299 break;
8300 hl_id = sgp->sg_link;
8301 }
8302
8303 return hl_id;
8304}
8305
8306#ifdef FEAT_GUI
8307/*
8308 * Call this function just after the GUI has started.
8309 * It finds the font and color handles for the highlighting groups.
8310 */
8311 void
8312highlight_gui_started()
8313{
8314 int idx;
8315
8316 /* First get the colors from the "Normal" and "Menu" group, if set */
8317 set_normal_colors();
8318
8319 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8320 gui_do_one_color(idx, FALSE, FALSE);
8321
8322 highlight_changed();
8323}
8324
8325 static void
8326gui_do_one_color(idx, do_menu, do_tooltip)
8327 int idx;
8328 int do_menu; /* TRUE: might set the menu font */
8329 int do_tooltip; /* TRUE: might set the tooltip font */
8330{
8331 int didit = FALSE;
8332
8333 if (HL_TABLE()[idx].sg_font_name != NULL)
8334 {
8335 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8336 do_tooltip);
8337 didit = TRUE;
8338 }
8339 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8340 {
8341 HL_TABLE()[idx].sg_gui_fg =
8342 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8343 didit = TRUE;
8344 }
8345 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8346 {
8347 HL_TABLE()[idx].sg_gui_bg =
8348 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8349 didit = TRUE;
8350 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008351 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8352 {
8353 HL_TABLE()[idx].sg_gui_sp =
8354 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8355 didit = TRUE;
8356 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008357 if (didit) /* need to get a new attr number */
8358 set_hl_attr(idx);
8359}
8360
8361#endif
8362
8363/*
8364 * Translate the 'highlight' option into attributes in highlight_attr[] and
8365 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
8366 * corresponding highlights to use on top of HLF_SNC is computed.
8367 * Called only when the 'highlight' option has been changed and upon first
8368 * screen redraw after any :highlight command.
8369 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
8370 */
8371 int
8372highlight_changed()
8373{
8374 int hlf;
8375 int i;
8376 char_u *p;
8377 int attr;
8378 char_u *end;
8379 int id;
8380#ifdef USER_HIGHLIGHT
8381 char_u userhl[10];
8382# ifdef FEAT_STL_OPT
8383 int id_SNC = -1;
8384 int id_S = -1;
8385 int hlcnt;
8386# endif
8387#endif
8388 static int hl_flags[HLF_COUNT] = HL_FLAGS;
8389
8390 need_highlight_changed = FALSE;
8391
8392 /*
8393 * Clear all attributes.
8394 */
8395 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8396 highlight_attr[hlf] = 0;
8397
8398 /*
8399 * First set all attributes to their default value.
8400 * Then use the attributes from the 'highlight' option.
8401 */
8402 for (i = 0; i < 2; ++i)
8403 {
8404 if (i)
8405 p = p_hl;
8406 else
8407 p = get_highlight_default();
8408 if (p == NULL) /* just in case */
8409 continue;
8410
8411 while (*p)
8412 {
8413 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8414 if (hl_flags[hlf] == *p)
8415 break;
8416 ++p;
8417 if (hlf == (int)HLF_COUNT || *p == NUL)
8418 return FAIL;
8419
8420 /*
8421 * Allow several hl_flags to be combined, like "bu" for
8422 * bold-underlined.
8423 */
8424 attr = 0;
8425 for ( ; *p && *p != ','; ++p) /* parse upto comma */
8426 {
8427 if (vim_iswhite(*p)) /* ignore white space */
8428 continue;
8429
8430 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
8431 return FAIL;
8432
8433 switch (*p)
8434 {
8435 case 'b': attr |= HL_BOLD;
8436 break;
8437 case 'i': attr |= HL_ITALIC;
8438 break;
8439 case '-':
8440 case 'n': /* no highlighting */
8441 break;
8442 case 'r': attr |= HL_INVERSE;
8443 break;
8444 case 's': attr |= HL_STANDOUT;
8445 break;
8446 case 'u': attr |= HL_UNDERLINE;
8447 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008448 case 'c': attr |= HL_UNDERCURL;
8449 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008450 case ':': ++p; /* highlight group name */
8451 if (attr || *p == NUL) /* no combinations */
8452 return FAIL;
8453 end = vim_strchr(p, ',');
8454 if (end == NULL)
8455 end = p + STRLEN(p);
8456 id = syn_check_group(p, (int)(end - p));
8457 if (id == 0)
8458 return FAIL;
8459 attr = syn_id2attr(id);
8460 p = end - 1;
8461#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8462 if (hlf == (int)HLF_SNC)
8463 id_SNC = syn_get_final_id(id);
8464 else if (hlf == (int)HLF_S)
8465 id_S = syn_get_final_id(id);
8466#endif
8467 break;
8468 default: return FAIL;
8469 }
8470 }
8471 highlight_attr[hlf] = attr;
8472
8473 p = skip_to_option_part(p); /* skip comma and spaces */
8474 }
8475 }
8476
8477#ifdef USER_HIGHLIGHT
8478 /* Setup the user highlights
8479 *
8480 * Temporarily utilize 10 more hl entries. Have to be in there
8481 * simultaneously in case of table overflows in get_attr_entry()
8482 */
8483# ifdef FEAT_STL_OPT
8484 if (ga_grow(&highlight_ga, 10) == FAIL)
8485 return FAIL;
8486 hlcnt = highlight_ga.ga_len;
8487 if (id_S == 0)
8488 { /* Make sure id_S is always valid to simplify code below */
8489 memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8490 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8491 id_S = hlcnt + 10;
8492 }
8493# endif
8494 for (i = 0; i < 9; i++)
8495 {
8496 sprintf((char *)userhl, "User%d", i + 1);
8497 id = syn_name2id(userhl);
8498 if (id == 0)
8499 {
8500 highlight_user[i] = 0;
8501# ifdef FEAT_STL_OPT
8502 highlight_stlnc[i] = 0;
8503# endif
8504 }
8505 else
8506 {
8507# ifdef FEAT_STL_OPT
8508 struct hl_group *hlt = HL_TABLE();
8509# endif
8510
8511 highlight_user[i] = syn_id2attr(id);
8512# ifdef FEAT_STL_OPT
8513 if (id_SNC == 0)
8514 {
8515 memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8516 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8517 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8518# ifdef FEAT_GUI
8519 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8520# endif
8521 }
8522 else
8523 mch_memmove(&hlt[hlcnt + i],
8524 &hlt[id_SNC - 1],
8525 sizeof(struct hl_group));
8526 hlt[hlcnt + i].sg_link = 0;
8527
8528 /* Apply difference between UserX and HLF_S to HLF_SNC */
8529 hlt[hlcnt + i].sg_term ^=
8530 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8531 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8532 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8533 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8534 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8535 hlt[hlcnt + i].sg_cterm ^=
8536 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8537 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8538 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8539 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8540 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8541# ifdef FEAT_GUI
8542 hlt[hlcnt + i].sg_gui ^=
8543 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8544 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8545 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8546 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8547 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008548 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8549 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008550 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8551 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8552# ifdef FEAT_XFONTSET
8553 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8554 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8555# endif
8556# endif
8557 highlight_ga.ga_len = hlcnt + i + 1;
8558 set_hl_attr(hlcnt + i); /* At long last we can apply */
8559 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8560# endif
8561 }
8562 }
8563# ifdef FEAT_STL_OPT
8564 highlight_ga.ga_len = hlcnt;
8565# endif
8566
8567#endif /* USER_HIGHLIGHT */
8568
8569 return OK;
8570}
8571
8572#ifdef FEAT_CMDL_COMPL
8573
8574static void highlight_list __ARGS((void));
8575static void highlight_list_two __ARGS((int cnt, int attr));
8576
8577/*
8578 * Handle command line completion for :highlight command.
8579 */
8580 void
8581set_context_in_highlight_cmd(xp, arg)
8582 expand_T *xp;
8583 char_u *arg;
8584{
8585 char_u *p;
8586
8587 /* Default: expand group names */
8588 xp->xp_context = EXPAND_HIGHLIGHT;
8589 xp->xp_pattern = arg;
8590 include_link = TRUE;
8591 include_default = TRUE;
8592
8593 /* (part of) subcommand already typed */
8594 if (*arg != NUL)
8595 {
8596 p = skiptowhite(arg);
8597 if (*p != NUL) /* past "default" or group name */
8598 {
8599 include_default = FALSE;
8600 if (STRNCMP("default", arg, p - arg) == 0)
8601 {
8602 arg = skipwhite(p);
8603 xp->xp_pattern = arg;
8604 p = skiptowhite(arg);
8605 }
8606 if (*p != NUL) /* past group name */
8607 {
8608 include_link = FALSE;
8609 if (arg[1] == 'i' && arg[0] == 'N')
8610 highlight_list();
8611 if (STRNCMP("link", arg, p - arg) == 0
8612 || STRNCMP("clear", arg, p - arg) == 0)
8613 {
8614 xp->xp_pattern = skipwhite(p);
8615 p = skiptowhite(xp->xp_pattern);
8616 if (*p != NUL) /* past first group name */
8617 {
8618 xp->xp_pattern = skipwhite(p);
8619 p = skiptowhite(xp->xp_pattern);
8620 }
8621 }
8622 if (*p != NUL) /* past group name(s) */
8623 xp->xp_context = EXPAND_NOTHING;
8624 }
8625 }
8626 }
8627}
8628
8629/*
8630 * List highlighting matches in a nice way.
8631 */
8632 static void
8633highlight_list()
8634{
8635 int i;
8636
8637 for (i = 10; --i >= 0; )
8638 highlight_list_two(i, hl_attr(HLF_D));
8639 for (i = 40; --i >= 0; )
8640 highlight_list_two(99, 0);
8641}
8642
8643 static void
8644highlight_list_two(cnt, attr)
8645 int cnt;
8646 int attr;
8647{
8648 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
8649 msg_clr_eos();
8650 out_flush();
8651 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
8652}
8653
8654#endif /* FEAT_CMDL_COMPL */
8655
8656#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
8657 || defined(FEAT_SIGNS) || defined(PROTO)
8658/*
8659 * Function given to ExpandGeneric() to obtain the list of group names.
8660 * Also used for synIDattr() function.
8661 */
8662/*ARGSUSED*/
8663 char_u *
8664get_highlight_name(xp, idx)
8665 expand_T *xp;
8666 int idx;
8667{
8668 if (idx == highlight_ga.ga_len
8669#ifdef FEAT_CMDL_COMPL
8670 && include_link
8671#endif
8672 )
8673 return (char_u *)"link";
8674 if (idx == highlight_ga.ga_len + 1
8675#ifdef FEAT_CMDL_COMPL
8676 && include_link
8677#endif
8678 )
8679 return (char_u *)"clear";
8680 if (idx == highlight_ga.ga_len + 2
8681#ifdef FEAT_CMDL_COMPL
8682 && include_default
8683#endif
8684 )
8685 return (char_u *)"default";
8686 if (idx < 0 || idx >= highlight_ga.ga_len)
8687 return NULL;
8688 return HL_TABLE()[idx].sg_name;
8689}
8690#endif
8691
8692#ifdef FEAT_GUI
8693/*
8694 * Free all the highlight group fonts.
8695 * Used when quitting for systems which need it.
8696 */
8697 void
8698free_highlight_fonts()
8699{
8700 int idx;
8701
8702 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8703 {
8704 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8705 HL_TABLE()[idx].sg_font = NOFONT;
8706# ifdef FEAT_XFONTSET
8707 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8708 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8709# endif
8710 }
8711
8712 gui_mch_free_font(gui.norm_font);
8713# ifdef FEAT_XFONTSET
8714 gui_mch_free_fontset(gui.fontset);
8715# endif
8716# ifndef HAVE_GTK2
8717 gui_mch_free_font(gui.bold_font);
8718 gui_mch_free_font(gui.ital_font);
8719 gui_mch_free_font(gui.boldital_font);
8720# endif
8721}
8722#endif
8723
8724/**************************************
8725 * End of Highlighting stuff *
8726 **************************************/