blob: 003b624309b3d65a9f7ca4d9bc9431232702d885 [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
207struct syn_cluster
208{
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 */
212};
213
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
221#define SYN_CLSTR(buf) ((struct syn_cluster *)((buf)->b_syn_clusters.ga_data))
222
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));
375static int syn_current_attr __ARGS((int syncing, int displaying));
376static 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 {
1663 (void)syn_current_attr(syncing, FALSE);
1664 /*
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.
1696 */
1697 int
1698get_syntax_attr(col)
1699 colnr_T col;
1700{
1701 int attr = 0;
1702
1703 /* check for out of memory situation */
1704 if (syn_buf->b_sst_array == NULL)
1705 return 0;
1706
1707 reg_syn = TRUE; /* let vim_regexec() know we're using syntax */
1708
1709 /* Make sure current_state is valid */
1710 if (INVALID_STATE(&current_state))
1711 validate_current_state();
1712
1713 /*
1714 * Skip from the current column to "col", get the attributes for "col".
1715 */
1716 while (current_col <= col)
1717 {
1718 attr = syn_current_attr(FALSE, TRUE);
1719 ++current_col;
1720 }
1721
1722 reg_syn = FALSE;
1723 return attr;
1724}
1725
1726/*
1727 * Get syntax attributes for current_lnum, current_col.
1728 */
1729 static int
1730syn_current_attr(syncing, displaying)
1731 int syncing; /* When 1: called for syncing */
1732 int displaying; /* result will be displayed */
1733{
1734 int syn_id;
1735 lpos_T endpos; /* was: char_u *endp; */
1736 lpos_T hl_startpos; /* was: int hl_startcol; */
1737 lpos_T hl_endpos;
1738 lpos_T eos_pos; /* end-of-start match (start region) */
1739 lpos_T eoe_pos; /* end-of-end pattern */
1740 int end_idx; /* group ID for end pattern */
1741 int idx;
1742 synpat_T *spp;
1743 stateitem_T *cur_si, *sip;
1744 int startcol;
1745 int endcol;
1746 long flags;
1747 short *next_list;
1748 int found_match; /* found usable match */
1749 static int try_next_column = FALSE; /* must try in next col */
1750 int do_keywords;
1751 regmmatch_T regmatch;
1752 lpos_T pos;
1753 int lc_col;
1754 reg_extmatch_T *cur_extmatch = NULL;
1755 char_u *line; /* current line. NOTE: becomes invalid after
1756 looking for a pattern match! */
1757
1758 /* variables for zero-width matches that have a "nextgroup" argument */
1759 int keep_next_list;
1760 int zero_width_next_list = FALSE;
1761 garray_T zero_width_next_ga;
1762
1763 /*
1764 * No character, no attributes! Past end of line?
1765 * Do try matching with an empty line (could be the start of a region).
1766 */
1767 line = syn_getcurline();
1768 if (line[current_col] == NUL && current_col != 0)
1769 {
1770 /*
1771 * If we found a match after the last column, use it.
1772 */
1773 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1774 && next_match_col != MAXCOL)
1775 (void)push_next_match(NULL);
1776
1777 current_finished = TRUE;
1778 current_state_stored = FALSE;
1779 return 0;
1780 }
1781
1782 /* if the current or next character is NUL, we will finish the line now */
1783 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1784 {
1785 current_finished = TRUE;
1786 current_state_stored = FALSE;
1787 }
1788
1789 /*
1790 * When in the previous column there was a match but it could not be used
1791 * (empty match or already matched in this column) need to try again in
1792 * the next column.
1793 */
1794 if (try_next_column)
1795 {
1796 next_match_idx = -1;
1797 try_next_column = FALSE;
1798 }
1799
1800 /* Only check for keywords when not syncing and there are some. */
1801 do_keywords = !syncing
Bram Moolenaardad6b692005-01-25 22:14:34 +00001802 && (syn_buf->b_keywtab.ht_used > 0
1803 || syn_buf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001804
1805 /* Init the list of zero-width matches with a nextlist. This is used to
1806 * avoid matching the same item in the same position twice. */
1807 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1808
1809 /*
1810 * Repeat matching keywords and patterns, to find contained items at the
1811 * same column. This stops when there are no extra matches at the current
1812 * column.
1813 */
1814 do
1815 {
1816 found_match = FALSE;
1817 keep_next_list = FALSE;
1818 syn_id = 0;
1819
1820 /*
1821 * 1. Check for a current state.
1822 * Only when there is no current state, or if the current state may
1823 * contain other things, we need to check for keywords and patterns.
1824 * Always need to check for contained items if some item has the
1825 * "containedin" argument (takes extra time!).
1826 */
1827 if (current_state.ga_len)
1828 cur_si = &CUR_STATE(current_state.ga_len - 1);
1829 else
1830 cur_si = NULL;
1831
1832 if (syn_buf->b_syn_containedin || cur_si == NULL
1833 || cur_si->si_cont_list != NULL)
1834 {
1835 /*
1836 * 2. Check for keywords, if on a keyword char after a non-keyword
1837 * char. Don't do this when syncing.
1838 */
1839 if (do_keywords)
1840 {
1841 line = syn_getcurline();
1842 if (vim_iswordc_buf(line + current_col, syn_buf)
1843 && (current_col == 0
1844 || !vim_iswordc_buf(line + current_col - 1
1845#ifdef FEAT_MBYTE
1846 - (has_mbyte
1847 ? (*mb_head_off)(line, line + current_col - 1)
1848 : 0)
1849#endif
1850 , syn_buf)))
1851 {
1852 syn_id = check_keyword_id(line, (int)current_col,
1853 &endcol, &flags, &next_list, cur_si);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001854 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001855 {
1856 if (push_current_state(KEYWORD_IDX) == OK)
1857 {
1858 cur_si = &CUR_STATE(current_state.ga_len - 1);
1859 cur_si->si_m_startcol = current_col;
1860 cur_si->si_h_startpos.lnum = current_lnum;
1861 cur_si->si_h_startpos.col = 0; /* starts right away */
1862 cur_si->si_m_endpos.lnum = current_lnum;
1863 cur_si->si_m_endpos.col = endcol;
1864 cur_si->si_h_endpos.lnum = current_lnum;
1865 cur_si->si_h_endpos.col = endcol;
1866 cur_si->si_ends = TRUE;
1867 cur_si->si_end_idx = 0;
1868 cur_si->si_flags = flags;
1869 cur_si->si_id = syn_id;
1870 cur_si->si_trans_id = syn_id;
1871 if (flags & HL_TRANSP)
1872 {
1873 if (current_state.ga_len < 2)
1874 {
1875 cur_si->si_attr = 0;
1876 cur_si->si_trans_id = 0;
1877 }
1878 else
1879 {
1880 cur_si->si_attr = CUR_STATE(
1881 current_state.ga_len - 2).si_attr;
1882 cur_si->si_trans_id = CUR_STATE(
1883 current_state.ga_len - 2).si_trans_id;
1884 }
1885 }
1886 else
1887 cur_si->si_attr = syn_id2attr(syn_id);
1888 cur_si->si_cont_list = NULL;
1889 cur_si->si_next_list = next_list;
1890 check_keepend();
1891 }
1892 else
1893 vim_free(next_list);
1894 }
1895 }
1896 }
1897
1898 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001899 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001900 */
1901 if (syn_id == 0 && syn_buf->b_syn_patterns.ga_len)
1902 {
1903 /*
1904 * If we didn't check for a match yet, or we are past it, check
1905 * for any match with a pattern.
1906 */
1907 if (next_match_idx < 0 || next_match_col < (int)current_col)
1908 {
1909 /*
1910 * Check all relevant patterns for a match at this
1911 * position. This is complicated, because matching with a
1912 * pattern takes quite a bit of time, thus we want to
1913 * avoid doing it when it's not needed.
1914 */
1915 next_match_idx = 0; /* no match in this line yet */
1916 next_match_col = MAXCOL;
1917 for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
1918 {
1919 spp = &(SYN_ITEMS(syn_buf)[idx]);
1920 if ( spp->sp_syncing == syncing
1921 && (displaying || !(spp->sp_flags & HL_DISPLAY))
1922 && (spp->sp_type == SPTYPE_MATCH
1923 || spp->sp_type == SPTYPE_START)
1924 && (current_next_list != NULL
1925 ? in_id_list(NULL, current_next_list,
1926 &spp->sp_syn, 0)
1927 : (cur_si == NULL
1928 ? !(spp->sp_flags & HL_CONTAINED)
1929 : in_id_list(cur_si,
1930 cur_si->si_cont_list, &spp->sp_syn,
1931 spp->sp_flags & HL_CONTAINED))))
1932 {
1933 /* If we already tried matching in this line, and
1934 * there isn't a match before next_match_col, skip
1935 * this item. */
1936 if (spp->sp_line_id == current_line_id
1937 && spp->sp_startcol >= next_match_col)
1938 continue;
1939 spp->sp_line_id = current_line_id;
1940
1941 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1942 if (lc_col < 0)
1943 lc_col = 0;
1944
1945 regmatch.rmm_ic = spp->sp_ic;
1946 regmatch.regprog = spp->sp_prog;
1947 if (!syn_regexec(&regmatch, current_lnum,
1948 (colnr_T)lc_col))
1949 {
1950 /* no match in this line, try another one */
1951 spp->sp_startcol = MAXCOL;
1952 continue;
1953 }
1954
1955 /*
1956 * Compute the first column of the match.
1957 */
1958 syn_add_start_off(&pos, &regmatch,
1959 spp, SPO_MS_OFF, -1);
1960 if (pos.lnum > current_lnum)
1961 {
1962 /* must have used end of match in a next line,
1963 * we can't handle that */
1964 spp->sp_startcol = MAXCOL;
1965 continue;
1966 }
1967 startcol = pos.col;
1968
1969 /* remember the next column where this pattern
1970 * matches in the current line */
1971 spp->sp_startcol = startcol;
1972
1973 /*
1974 * If a previously found match starts at a lower
1975 * column number, don't use this one.
1976 */
1977 if (startcol >= next_match_col)
1978 continue;
1979
1980 /*
1981 * If we matched this pattern at this position
1982 * before, skip it. Must retry in the next
1983 * column, because it may match from there.
1984 */
1985 if (did_match_already(idx, &zero_width_next_ga))
1986 {
1987 try_next_column = TRUE;
1988 continue;
1989 }
1990
1991 endpos.lnum = regmatch.endpos[0].lnum;
1992 endpos.col = regmatch.endpos[0].col;
1993
1994 /* Compute the highlight start. */
1995 syn_add_start_off(&hl_startpos, &regmatch,
1996 spp, SPO_HS_OFF, -1);
1997
1998 /* Compute the region start. */
1999 /* Default is to use the end of the match. */
2000 syn_add_end_off(&eos_pos, &regmatch,
2001 spp, SPO_RS_OFF, 0);
2002
2003 /*
2004 * Grab the external submatches before they get
2005 * overwritten. Reference count doesn't change.
2006 */
2007 unref_extmatch(cur_extmatch);
2008 cur_extmatch = re_extmatch_out;
2009 re_extmatch_out = NULL;
2010
2011 flags = 0;
2012 eoe_pos.lnum = 0; /* avoid warning */
2013 eoe_pos.col = 0;
2014 end_idx = 0;
2015 hl_endpos.lnum = 0;
2016
2017 /*
2018 * For a "oneline" the end must be found in the
2019 * same line too. Search for it after the end of
2020 * the match with the start pattern. Set the
2021 * resulting end positions at the same time.
2022 */
2023 if (spp->sp_type == SPTYPE_START
2024 && (spp->sp_flags & HL_ONELINE))
2025 {
2026 lpos_T startpos;
2027
2028 startpos = endpos;
2029 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2030 &flags, &eoe_pos, &end_idx, cur_extmatch);
2031 if (endpos.lnum == 0)
2032 continue; /* not found */
2033 }
2034
2035 /*
2036 * For a "match" the size must be > 0 after the
2037 * end offset needs has been added. Except when
2038 * syncing.
2039 */
2040 else if (spp->sp_type == SPTYPE_MATCH)
2041 {
2042 syn_add_end_off(&hl_endpos, &regmatch, spp,
2043 SPO_HE_OFF, 0);
2044 syn_add_end_off(&endpos, &regmatch, spp,
2045 SPO_ME_OFF, 0);
2046 if (endpos.lnum == current_lnum
2047 && (int)endpos.col + syncing < startcol)
2048 {
2049 /*
2050 * If an empty string is matched, may need
2051 * to try matching again at next column.
2052 */
2053 if (regmatch.startpos[0].col
2054 == regmatch.endpos[0].col)
2055 try_next_column = TRUE;
2056 continue;
2057 }
2058 }
2059
2060 /*
2061 * keep the best match so far in next_match_*
2062 */
2063 /* Highlighting must start after startpos and end
2064 * before endpos. */
2065 if (hl_startpos.lnum == current_lnum
2066 && (int)hl_startpos.col < startcol)
2067 hl_startpos.col = startcol;
2068 limit_pos_zero(&hl_endpos, &endpos);
2069
2070 next_match_idx = idx;
2071 next_match_col = startcol;
2072 next_match_m_endpos = endpos;
2073 next_match_h_endpos = hl_endpos;
2074 next_match_h_startpos = hl_startpos;
2075 next_match_flags = flags;
2076 next_match_eos_pos = eos_pos;
2077 next_match_eoe_pos = eoe_pos;
2078 next_match_end_idx = end_idx;
2079 unref_extmatch(next_match_extmatch);
2080 next_match_extmatch = cur_extmatch;
2081 cur_extmatch = NULL;
2082 }
2083 }
2084 }
2085
2086 /*
2087 * If we found a match at the current column, use it.
2088 */
2089 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2090 {
2091 synpat_T *lspp;
2092
2093 /* When a zero-width item matched which has a nextgroup,
2094 * don't push the item but set nextgroup. */
2095 lspp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2096 if (next_match_m_endpos.lnum == current_lnum
2097 && next_match_m_endpos.col == current_col
2098 && lspp->sp_next_list != NULL)
2099 {
2100 current_next_list = lspp->sp_next_list;
2101 current_next_flags = lspp->sp_flags;
2102 keep_next_list = TRUE;
2103 zero_width_next_list = TRUE;
2104
2105 /* Add the index to a list, so that we can check
2106 * later that we don't match it again (and cause an
2107 * endless loop). */
2108 if (ga_grow(&zero_width_next_ga, 1) == OK)
2109 {
2110 ((int *)(zero_width_next_ga.ga_data))
2111 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002112 }
2113 next_match_idx = -1;
2114 }
2115 else
2116 cur_si = push_next_match(cur_si);
2117 found_match = TRUE;
2118 }
2119 }
2120 }
2121
2122 /*
2123 * Handle searching for nextgroup match.
2124 */
2125 if (current_next_list != NULL && !keep_next_list)
2126 {
2127 /*
2128 * If a nextgroup was not found, continue looking for one if:
2129 * - this is an empty line and the "skipempty" option was given
2130 * - we are on white space and the "skipwhite" option was given
2131 */
2132 if (!found_match)
2133 {
2134 line = syn_getcurline();
2135 if (((current_next_flags & HL_SKIPWHITE)
2136 && vim_iswhite(line[current_col]))
2137 || ((current_next_flags & HL_SKIPEMPTY)
2138 && *line == NUL))
2139 break;
2140 }
2141
2142 /*
2143 * If a nextgroup was found: Use it, and continue looking for
2144 * contained matches.
2145 * If a nextgroup was not found: Continue looking for a normal
2146 * match.
2147 * When did set current_next_list for a zero-width item and no
2148 * match was found don't loop (would get stuck).
2149 */
2150 current_next_list = NULL;
2151 next_match_idx = -1;
2152 if (!zero_width_next_list)
2153 found_match = TRUE;
2154 }
2155
2156 } while (found_match);
2157
2158 /*
2159 * Use attributes from the current state, if within its highlighting.
2160 * If not, use attributes from the current-but-one state, etc.
2161 */
2162 current_attr = 0;
2163#ifdef FEAT_EVAL
2164 current_id = 0;
2165 current_trans_id = 0;
2166#endif
2167 if (cur_si != NULL)
2168 {
2169 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2170 {
2171 sip = &CUR_STATE(idx);
2172 if ((current_lnum > sip->si_h_startpos.lnum
2173 || (current_lnum == sip->si_h_startpos.lnum
2174 && current_col >= sip->si_h_startpos.col))
2175 && (sip->si_h_endpos.lnum == 0
2176 || current_lnum < sip->si_h_endpos.lnum
2177 || (current_lnum == sip->si_h_endpos.lnum
2178 && current_col < sip->si_h_endpos.col)))
2179 {
2180 current_attr = sip->si_attr;
2181#ifdef FEAT_EVAL
2182 current_id = sip->si_id;
2183 current_trans_id = sip->si_trans_id;
2184#endif
2185 break;
2186 }
2187 }
2188
2189 /*
2190 * Check for end of current state (and the states before it) at the
2191 * next column. Don't do this for syncing, because we would miss a
2192 * single character match.
2193 * First check if the current state ends at the current column. It
2194 * may be for an empty match and a containing item might end in the
2195 * current column.
2196 */
2197 if (!syncing)
2198 {
2199 check_state_ends();
2200 if (current_state.ga_len > 0)
2201 {
2202 ++current_col;
2203 check_state_ends();
2204 --current_col;
2205 }
2206 }
2207 }
2208
2209 /* nextgroup ends at end of line, unless "skipnl" or "skipemtpy" present */
2210 if (current_next_list != NULL
2211 && syn_getcurline()[current_col + 1] == NUL
2212 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2213 current_next_list = NULL;
2214
2215 if (zero_width_next_ga.ga_len > 0)
2216 ga_clear(&zero_width_next_ga);
2217
2218 /* No longer need external matches. But keep next_match_extmatch. */
2219 unref_extmatch(re_extmatch_out);
2220 re_extmatch_out = NULL;
2221 unref_extmatch(cur_extmatch);
2222
2223 return current_attr;
2224}
2225
2226
2227/*
2228 * Check if we already matched pattern "idx" at the current column.
2229 */
2230 static int
2231did_match_already(idx, gap)
2232 int idx;
2233 garray_T *gap;
2234{
2235 int i;
2236
2237 for (i = current_state.ga_len; --i >= 0; )
2238 if (CUR_STATE(i).si_m_startcol == (int)current_col
2239 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2240 && CUR_STATE(i).si_idx == idx)
2241 return TRUE;
2242
2243 /* Zero-width matches with a nextgroup argument are not put on the syntax
2244 * stack, and can only be matched once anyway. */
2245 for (i = gap->ga_len; --i >= 0; )
2246 if (((int *)(gap->ga_data))[i] == idx)
2247 return TRUE;
2248
2249 return FALSE;
2250}
2251
2252/*
2253 * Push the next match onto the stack.
2254 */
2255 static stateitem_T *
2256push_next_match(cur_si)
2257 stateitem_T *cur_si;
2258{
2259 synpat_T *spp;
2260
2261 spp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2262
2263 /*
2264 * Push the item in current_state stack;
2265 */
2266 if (push_current_state(next_match_idx) == OK)
2267 {
2268 /*
2269 * If it's a start-skip-end type that crosses lines, figure out how
2270 * much it continues in this line. Otherwise just fill in the length.
2271 */
2272 cur_si = &CUR_STATE(current_state.ga_len - 1);
2273 cur_si->si_h_startpos = next_match_h_startpos;
2274 cur_si->si_m_startcol = current_col;
2275 cur_si->si_m_lnum = current_lnum;
2276 cur_si->si_flags = spp->sp_flags;
2277 cur_si->si_next_list = spp->sp_next_list;
2278 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2279 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2280 {
2281 /* Try to find the end pattern in the current line */
2282 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2283 check_keepend();
2284 }
2285 else
2286 {
2287 cur_si->si_m_endpos = next_match_m_endpos;
2288 cur_si->si_h_endpos = next_match_h_endpos;
2289 cur_si->si_ends = TRUE;
2290 cur_si->si_flags |= next_match_flags;
2291 cur_si->si_eoe_pos = next_match_eoe_pos;
2292 cur_si->si_end_idx = next_match_end_idx;
2293 }
2294 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2295 keepend_level = current_state.ga_len - 1;
2296 check_keepend();
2297 update_si_attr(current_state.ga_len - 1);
2298
2299 /*
2300 * If the start pattern has another highlight group, push another item
2301 * on the stack for the start pattern.
2302 */
2303 if ( spp->sp_type == SPTYPE_START
2304 && spp->sp_syn_match_id != 0
2305 && push_current_state(next_match_idx) == OK)
2306 {
2307 cur_si = &CUR_STATE(current_state.ga_len - 1);
2308 cur_si->si_h_startpos = next_match_h_startpos;
2309 cur_si->si_m_startcol = current_col;
2310 cur_si->si_m_lnum = current_lnum;
2311 cur_si->si_m_endpos = next_match_eos_pos;
2312 cur_si->si_h_endpos = next_match_eos_pos;
2313 cur_si->si_ends = TRUE;
2314 cur_si->si_end_idx = 0;
2315 cur_si->si_flags = HL_MATCH;
2316 cur_si->si_next_list = NULL;
2317 check_keepend();
2318 update_si_attr(current_state.ga_len - 1);
2319 }
2320 }
2321
2322 next_match_idx = -1; /* try other match next time */
2323
2324 return cur_si;
2325}
2326
2327/*
2328 * Check for end of current state (and the states before it).
2329 */
2330 static void
2331check_state_ends()
2332{
2333 stateitem_T *cur_si;
2334 int had_extend = FALSE;
2335
2336 cur_si = &CUR_STATE(current_state.ga_len - 1);
2337 for (;;)
2338 {
2339 if (cur_si->si_ends
2340 && (cur_si->si_m_endpos.lnum < current_lnum
2341 || (cur_si->si_m_endpos.lnum == current_lnum
2342 && cur_si->si_m_endpos.col <= current_col)))
2343 {
2344 /*
2345 * If there is an end pattern group ID, highlight the end pattern
2346 * now. No need to pop the current item from the stack.
2347 * Only do this if the end pattern continues beyond the current
2348 * position.
2349 */
2350 if (cur_si->si_end_idx
2351 && (cur_si->si_eoe_pos.lnum > current_lnum
2352 || (cur_si->si_eoe_pos.lnum == current_lnum
2353 && cur_si->si_eoe_pos.col > current_col)))
2354 {
2355 cur_si->si_idx = cur_si->si_end_idx;
2356 cur_si->si_end_idx = 0;
2357 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2358 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2359 cur_si->si_flags |= HL_MATCH;
2360 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002361
2362 /* what matches next may be different now, clear it */
2363 next_match_idx = 0;
2364 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002365 break;
2366 }
2367 else
2368 {
2369 /* handle next_list, unless at end of line and no "skipnl" or
2370 * "skipempty" */
2371 current_next_list = cur_si->si_next_list;
2372 current_next_flags = cur_si->si_flags;
2373 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2374 && syn_getcurline()[current_col] == NUL)
2375 current_next_list = NULL;
2376
2377 /* When the ended item has "extend", another item with
2378 * "keepend" now needs to check for its end. */
2379 if (cur_si->si_flags & HL_EXTEND)
2380 had_extend = TRUE;
2381
2382 pop_current_state();
2383
2384 if (current_state.ga_len == 0)
2385 break;
2386
2387 if (had_extend)
2388 {
2389 syn_update_ends(FALSE);
2390 if (current_state.ga_len == 0)
2391 break;
2392 }
2393
2394 cur_si = &CUR_STATE(current_state.ga_len - 1);
2395
2396 /*
2397 * Only for a region the search for the end continues after
2398 * the end of the contained item. If the contained match
2399 * included the end-of-line, break here, the region continues.
2400 * Don't do this when:
2401 * - "keepend" is used for the contained item
2402 * - not at the end of the line (could be end="x$"me=e-1).
2403 * - "excludenl" is used (HL_HAS_EOL won't be set)
2404 */
2405 if (cur_si->si_idx >= 0
2406 && SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_type
2407 == SPTYPE_START
2408 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2409 {
2410 update_si_end(cur_si, (int)current_col, TRUE);
2411 check_keepend();
2412 if ((current_next_flags & HL_HAS_EOL)
2413 && keepend_level < 0
2414 && syn_getcurline()[current_col] == NUL)
2415 break;
2416 }
2417 }
2418 }
2419 else
2420 break;
2421 }
2422}
2423
2424/*
2425 * Update an entry in the current_state stack for a match or region. This
2426 * fills in si_attr, si_next_list and si_cont_list.
2427 */
2428 static void
2429update_si_attr(idx)
2430 int idx;
2431{
2432 stateitem_T *sip = &CUR_STATE(idx);
2433 synpat_T *spp;
2434
2435 spp = &(SYN_ITEMS(syn_buf)[sip->si_idx]);
2436 if (sip->si_flags & HL_MATCH)
2437 sip->si_id = spp->sp_syn_match_id;
2438 else
2439 sip->si_id = spp->sp_syn.id;
2440 sip->si_attr = syn_id2attr(sip->si_id);
2441 sip->si_trans_id = sip->si_id;
2442 if (sip->si_flags & HL_MATCH)
2443 sip->si_cont_list = NULL;
2444 else
2445 sip->si_cont_list = spp->sp_cont_list;
2446
2447 /*
2448 * For transparent items, take attr from outer item.
2449 * Also take cont_list, if there is none.
2450 * Don't do this for the matchgroup of a start or end pattern.
2451 */
2452 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2453 {
2454 if (idx == 0)
2455 {
2456 sip->si_attr = 0;
2457 sip->si_trans_id = 0;
2458 if (sip->si_cont_list == NULL)
2459 sip->si_cont_list = ID_LIST_ALL;
2460 }
2461 else
2462 {
2463 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2464 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002465 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2466 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002467 if (sip->si_cont_list == NULL)
2468 {
2469 sip->si_flags |= HL_TRANS_CONT;
2470 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2471 }
2472 }
2473 }
2474}
2475
2476/*
2477 * Check the current stack for patterns with "keepend" flag.
2478 * Propagate the match-end to contained items, until a "skipend" item is found.
2479 */
2480 static void
2481check_keepend()
2482{
2483 int i;
2484 lpos_T maxpos;
2485 stateitem_T *sip;
2486
2487 /*
2488 * This check can consume a lot of time; only do it from the level where
2489 * there really is a keepend.
2490 */
2491 if (keepend_level < 0)
2492 return;
2493
2494 /*
2495 * Find the last index of an "extend" item. "keepend" items before that
2496 * won't do anything. If there is no "extend" item "i" will be
2497 * "keepend_level" and all "keepend" items will work normally.
2498 */
2499 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2500 if (CUR_STATE(i).si_flags & HL_EXTEND)
2501 break;
2502
2503 maxpos.lnum = 0;
2504 for ( ; i < current_state.ga_len; ++i)
2505 {
2506 sip = &CUR_STATE(i);
2507 if (maxpos.lnum != 0)
2508 {
2509 limit_pos_zero(&sip->si_m_endpos, &maxpos);
2510 limit_pos_zero(&sip->si_h_endpos, &maxpos);
2511 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2512 sip->si_ends = TRUE;
2513 }
2514 if (sip->si_ends
2515 && (sip->si_flags & HL_KEEPEND)
2516 && (maxpos.lnum == 0
2517 || maxpos.lnum > sip->si_m_endpos.lnum
2518 || (maxpos.lnum == sip->si_m_endpos.lnum
2519 && maxpos.col > sip->si_m_endpos.col)))
2520 maxpos = sip->si_m_endpos;
2521 }
2522}
2523
2524/*
2525 * Update an entry in the current_state stack for a start-skip-end pattern.
2526 * This finds the end of the current item, if it's in the current line.
2527 *
2528 * Return the flags for the matched END.
2529 */
2530 static void
2531update_si_end(sip, startcol, force)
2532 stateitem_T *sip;
2533 int startcol; /* where to start searching for the end */
2534 int force; /* when TRUE overrule a previous end */
2535{
2536 lpos_T startpos;
2537 lpos_T endpos;
2538 lpos_T hl_endpos;
2539 lpos_T end_endpos;
2540 int end_idx;
2541
2542 /* Don't update when it's already done. Can be a match of an end pattern
2543 * that started in a previous line. Watch out: can also be a "keepend"
2544 * from a containing item. */
2545 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2546 return;
2547
2548 /*
2549 * We need to find the end of the region. It may continue in the next
2550 * line.
2551 */
2552 end_idx = 0;
2553 startpos.lnum = current_lnum;
2554 startpos.col = startcol;
2555 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2556 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2557
2558 if (endpos.lnum == 0)
2559 {
2560 /* No end pattern matched. */
2561 if (SYN_ITEMS(syn_buf)[sip->si_idx].sp_flags & HL_ONELINE)
2562 {
2563 /* a "oneline" never continues in the next line */
2564 sip->si_ends = TRUE;
2565 sip->si_m_endpos.lnum = current_lnum;
2566 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2567 }
2568 else
2569 {
2570 /* continues in the next line */
2571 sip->si_ends = FALSE;
2572 sip->si_m_endpos.lnum = 0;
2573 }
2574 sip->si_h_endpos = sip->si_m_endpos;
2575 }
2576 else
2577 {
2578 /* match within this line */
2579 sip->si_m_endpos = endpos;
2580 sip->si_h_endpos = hl_endpos;
2581 sip->si_eoe_pos = end_endpos;
2582 sip->si_ends = TRUE;
2583 sip->si_end_idx = end_idx;
2584 }
2585}
2586
2587/*
2588 * Add a new state to the current state stack.
2589 * It is cleared and the index set to "idx".
2590 * Return FAIL if it's not possible (out of memory).
2591 */
2592 static int
2593push_current_state(idx)
2594 int idx;
2595{
2596 if (ga_grow(&current_state, 1) == FAIL)
2597 return FAIL;
2598 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2599 CUR_STATE(current_state.ga_len).si_idx = idx;
2600 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002601 return OK;
2602}
2603
2604/*
2605 * Remove a state from the current_state stack.
2606 */
2607 static void
2608pop_current_state()
2609{
2610 if (current_state.ga_len)
2611 {
2612 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2613 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002614 }
2615 /* after the end of a pattern, try matching a keyword or pattern */
2616 next_match_idx = -1;
2617
2618 /* if first state with "keepend" is popped, reset keepend_level */
2619 if (keepend_level >= current_state.ga_len)
2620 keepend_level = -1;
2621}
2622
2623/*
2624 * Find the end of a start/skip/end syntax region after "startpos".
2625 * Only checks one line.
2626 * Also handles a match item that continued from a previous line.
2627 * If not found, the syntax item continues in the next line. m_endpos->lnum
2628 * will be 0.
2629 * If found, the end of the region and the end of the highlighting is
2630 * computed.
2631 */
2632 static void
2633find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2634 end_idx, start_ext)
2635 int idx; /* index of the pattern */
2636 lpos_T *startpos; /* where to start looking for an END match */
2637 lpos_T *m_endpos; /* return: end of match */
2638 lpos_T *hl_endpos; /* return: end of highlighting */
2639 long *flagsp; /* return: flags of matching END */
2640 lpos_T *end_endpos; /* return: end of end pattern match */
2641 int *end_idx; /* return: group ID for end pat. match, or 0 */
2642 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2643{
2644 colnr_T matchcol;
2645 synpat_T *spp, *spp_skip;
2646 int start_idx;
2647 int best_idx;
2648 regmmatch_T regmatch;
2649 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2650 lpos_T pos;
2651 char_u *line;
2652 int had_match = FALSE;
2653
2654 /*
2655 * Check for being called with a START pattern.
2656 * Can happen with a match that continues to the next line, because it
2657 * contained a region.
2658 */
2659 spp = &(SYN_ITEMS(syn_buf)[idx]);
2660 if (spp->sp_type != SPTYPE_START)
2661 {
2662 *hl_endpos = *startpos;
2663 return;
2664 }
2665
2666 /*
2667 * Find the SKIP or first END pattern after the last START pattern.
2668 */
2669 for (;;)
2670 {
2671 spp = &(SYN_ITEMS(syn_buf)[idx]);
2672 if (spp->sp_type != SPTYPE_START)
2673 break;
2674 ++idx;
2675 }
2676
2677 /*
2678 * Lookup the SKIP pattern (if present)
2679 */
2680 if (spp->sp_type == SPTYPE_SKIP)
2681 {
2682 spp_skip = spp;
2683 ++idx;
2684 }
2685 else
2686 spp_skip = NULL;
2687
2688 /* Setup external matches for syn_regexec(). */
2689 unref_extmatch(re_extmatch_in);
2690 re_extmatch_in = ref_extmatch(start_ext);
2691
2692 matchcol = startpos->col; /* start looking for a match at sstart */
2693 start_idx = idx; /* remember the first END pattern. */
2694 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2695 for (;;)
2696 {
2697 /*
2698 * Find end pattern that matches first after "matchcol".
2699 */
2700 best_idx = -1;
2701 for (idx = start_idx; idx < syn_buf->b_syn_patterns.ga_len; ++idx)
2702 {
2703 int lc_col = matchcol;
2704
2705 spp = &(SYN_ITEMS(syn_buf)[idx]);
2706 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2707 break;
2708 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2709 if (lc_col < 0)
2710 lc_col = 0;
2711
2712 regmatch.rmm_ic = spp->sp_ic;
2713 regmatch.regprog = spp->sp_prog;
2714 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2715 {
2716 if (best_idx == -1 || regmatch.startpos[0].col
2717 < best_regmatch.startpos[0].col)
2718 {
2719 best_idx = idx;
2720 best_regmatch.startpos[0] = regmatch.startpos[0];
2721 best_regmatch.endpos[0] = regmatch.endpos[0];
2722 }
2723 }
2724 }
2725
2726 /*
2727 * If all end patterns have been tried, and there is no match, the
2728 * item continues until end-of-line.
2729 */
2730 if (best_idx == -1)
2731 break;
2732
2733 /*
2734 * If the skip pattern matches before the end pattern,
2735 * continue searching after the skip pattern.
2736 */
2737 if (spp_skip != NULL)
2738 {
2739 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2740
2741 if (lc_col < 0)
2742 lc_col = 0;
2743 regmatch.rmm_ic = spp_skip->sp_ic;
2744 regmatch.regprog = spp_skip->sp_prog;
2745 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2746 && regmatch.startpos[0].col
2747 <= best_regmatch.startpos[0].col)
2748 {
2749 /* Add offset to skip pattern match */
2750 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2751
2752 /* If the skip pattern goes on to the next line, there is no
2753 * match with an end pattern in this line. */
2754 if (pos.lnum > startpos->lnum)
2755 break;
2756
2757 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2758
2759 /* take care of an empty match or negative offset */
2760 if (pos.col <= matchcol)
2761 ++matchcol;
2762 else if (pos.col <= regmatch.endpos[0].col)
2763 matchcol = pos.col;
2764 else
2765 /* Be careful not to jump over the NUL at the end-of-line */
2766 for (matchcol = regmatch.endpos[0].col;
2767 line[matchcol] != NUL && matchcol < pos.col;
2768 ++matchcol)
2769 ;
2770
2771 /* if the skip pattern includes end-of-line, break here */
2772 if (line[matchcol] == NUL)
2773 break;
2774
2775 continue; /* start with first end pattern again */
2776 }
2777 }
2778
2779 /*
2780 * Match from start pattern to end pattern.
2781 * Correct for match and highlight offset of end pattern.
2782 */
2783 spp = &(SYN_ITEMS(syn_buf)[best_idx]);
2784 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2785 /* can't end before the start */
2786 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2787 m_endpos->col = startpos->col;
2788
2789 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2790 /* can't end before the start */
2791 if (end_endpos->lnum == startpos->lnum
2792 && end_endpos->col < startpos->col)
2793 end_endpos->col = startpos->col;
2794 /* can't end after the match */
2795 limit_pos(end_endpos, m_endpos);
2796
2797 /*
2798 * If the end group is highlighted differently, adjust the pointers.
2799 */
2800 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2801 {
2802 *end_idx = best_idx;
2803 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2804 {
2805 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2806 hl_endpos->col = best_regmatch.endpos[0].col;
2807 }
2808 else
2809 {
2810 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2811 hl_endpos->col = best_regmatch.startpos[0].col;
2812 }
2813 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2814
2815 /* can't end before the start */
2816 if (hl_endpos->lnum == startpos->lnum
2817 && hl_endpos->col < startpos->col)
2818 hl_endpos->col = startpos->col;
2819 limit_pos(hl_endpos, m_endpos);
2820
2821 /* now the match ends where the highlighting ends, it is turned
2822 * into the matchgroup for the end */
2823 *m_endpos = *hl_endpos;
2824 }
2825 else
2826 {
2827 *end_idx = 0;
2828 *hl_endpos = *end_endpos;
2829 }
2830
2831 *flagsp = spp->sp_flags;
2832
2833 had_match = TRUE;
2834 break;
2835 }
2836
2837 /* no match for an END pattern in this line */
2838 if (!had_match)
2839 m_endpos->lnum = 0;
2840
2841 /* Remove external matches. */
2842 unref_extmatch(re_extmatch_in);
2843 re_extmatch_in = NULL;
2844}
2845
2846/*
2847 * Limit "pos" not to be after "limit".
2848 */
2849 static void
2850limit_pos(pos, limit)
2851 lpos_T *pos;
2852 lpos_T *limit;
2853{
2854 if (pos->lnum > limit->lnum)
2855 *pos = *limit;
2856 else if (pos->lnum == limit->lnum && pos->col > limit->col)
2857 pos->col = limit->col;
2858}
2859
2860/*
2861 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
2862 */
2863 static void
2864limit_pos_zero(pos, limit)
2865 lpos_T *pos;
2866 lpos_T *limit;
2867{
2868 if (pos->lnum == 0)
2869 *pos = *limit;
2870 else
2871 limit_pos(pos, limit);
2872}
2873
2874/*
2875 * Add offset to matched text for end of match or highlight.
2876 */
2877 static void
2878syn_add_end_off(result, regmatch, spp, idx, extra)
2879 lpos_T *result; /* returned position */
2880 regmmatch_T *regmatch; /* start/end of match */
2881 synpat_T *spp; /* matched pattern */
2882 int idx; /* index of offset */
2883 int extra; /* extra chars for offset to start */
2884{
2885 int col;
2886
2887 if (spp->sp_off_flags & (1 << idx))
2888 {
2889 result->lnum = regmatch->startpos[0].lnum;
2890 col = regmatch->startpos[0].col + extra;
2891 }
2892 else
2893 {
2894 result->lnum = regmatch->endpos[0].lnum;
2895 col = regmatch->endpos[0].col;
2896 }
2897 col += spp->sp_offsets[idx];
2898 if (col < 0)
2899 result->col = 0;
2900 else
2901 result->col = col;
2902}
2903
2904/*
2905 * Add offset to matched text for start of match or highlight.
2906 * Avoid resulting column to become negative.
2907 */
2908 static void
2909syn_add_start_off(result, regmatch, spp, idx, extra)
2910 lpos_T *result; /* returned position */
2911 regmmatch_T *regmatch; /* start/end of match */
2912 synpat_T *spp;
2913 int idx;
2914 int extra; /* extra chars for offset to end */
2915{
2916 int col;
2917
2918 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
2919 {
2920 result->lnum = regmatch->endpos[0].lnum;
2921 col = regmatch->endpos[0].col + extra;
2922 }
2923 else
2924 {
2925 result->lnum = regmatch->startpos[0].lnum;
2926 col = regmatch->startpos[0].col;
2927 }
2928 col += spp->sp_offsets[idx];
2929 if (col < 0)
2930 result->col = 0;
2931 else
2932 result->col = col;
2933}
2934
2935/*
2936 * Get current line in syntax buffer.
2937 */
2938 static char_u *
2939syn_getcurline()
2940{
2941 return ml_get_buf(syn_buf, current_lnum, FALSE);
2942}
2943
2944/*
2945 * Call vim_regexec() to match in syn_buf.
2946 * Returns TRUE when there is a match.
2947 */
2948 static int
2949syn_regexec(rmp, lnum, col)
2950 regmmatch_T *rmp;
2951 linenr_T lnum;
2952 colnr_T col;
2953{
2954 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col) > 0)
2955 {
2956 rmp->startpos[0].lnum += lnum;
2957 rmp->endpos[0].lnum += lnum;
2958 return TRUE;
2959 }
2960 return FALSE;
2961}
2962
2963/*
2964 * Check one position in a line for a matching keyword.
2965 * The caller must check if a keyword can start at startcol.
2966 * Return it's ID if found, 0 otherwise.
2967 */
2968 static int
2969check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
2970 char_u *line;
2971 int startcol; /* position in line to check for keyword */
2972 int *endcolp; /* return: character after found keyword */
2973 long *flagsp; /* return: flags of matching keyword */
2974 short **next_listp; /* return: next_list of matching keyword */
2975 stateitem_T *cur_si; /* item at the top of the stack */
2976{
Bram Moolenaardad6b692005-01-25 22:14:34 +00002977 keyentry_T *kp;
2978 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002979 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00002980 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002981 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00002982 hashtab_T *ht;
2983 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984
2985 /* Find first character after the keyword. First character was already
2986 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00002987 kwp = line + startcol;
2988 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002989 do
2990 {
2991#ifdef FEAT_MBYTE
2992 if (has_mbyte)
Bram Moolenaardad6b692005-01-25 22:14:34 +00002993 kwlen += (*mb_ptr2len_check)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002994 else
2995#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00002996 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002997 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00002998 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00002999
Bram Moolenaardad6b692005-01-25 22:14:34 +00003000 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003001 return 0;
3002
3003 /*
3004 * Must make a copy of the keyword, so we can add a NUL and make it
3005 * lowercase.
3006 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003007 STRNCPY(keyword, kwp, kwlen);
3008 keyword[kwlen] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003009
3010 /*
3011 * Try twice:
3012 * 1. matching case
3013 * 2. ignoring case
3014 */
3015 for (round = 1; round <= 2; ++round)
3016 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003017 ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
3018 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003019 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003020 if (round == 2) /* ignore case */
3021 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003022
3023 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003024 * Find keywords that match. There can be several with different
3025 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003026 * When current_next_list is non-zero accept only that group, otherwise:
3027 * Accept a not-contained keyword at toplevel.
3028 * Accept a keyword at other levels only if it is in the contains list.
3029 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003030 hi = hash_find(ht, keyword);
3031 if (!HASHITEM_EMPTY(hi))
3032 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003034 if (current_next_list != 0
3035 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3036 : (cur_si == NULL
3037 ? !(kp->flags & HL_CONTAINED)
3038 : in_id_list(cur_si, cur_si->si_cont_list,
3039 &kp->k_syn, kp->flags & HL_CONTAINED)))
3040 {
3041 *endcolp = startcol + kwlen;
3042 *flagsp = kp->flags;
3043 *next_listp = kp->next_list;
3044 return kp->k_syn.id;
3045 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003046 }
3047 }
3048 return 0;
3049}
3050
3051/*
3052 * Handle ":syntax case" command.
3053 */
3054/* ARGSUSED */
3055 static void
3056syn_cmd_case(eap, syncing)
3057 exarg_T *eap;
3058 int syncing; /* not used */
3059{
3060 char_u *arg = eap->arg;
3061 char_u *next;
3062
3063 eap->nextcmd = find_nextcmd(arg);
3064 if (eap->skip)
3065 return;
3066
3067 next = skiptowhite(arg);
3068 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3069 curbuf->b_syn_ic = FALSE;
3070 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3071 curbuf->b_syn_ic = TRUE;
3072 else
3073 EMSG2(_("E390: Illegal argument: %s"), arg);
3074}
3075
3076/*
3077 * Clear all syntax info for one buffer.
3078 */
3079 void
3080syntax_clear(buf)
3081 buf_T *buf;
3082{
3083 int i;
3084
3085 curbuf->b_syn_ic = FALSE; /* Use case, by default */
3086 curbuf->b_syn_containedin = FALSE;
3087
3088 /* free the keywords */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003089 clear_keywtab(&buf->b_keywtab);
3090 clear_keywtab(&buf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003091
3092 /* free the syntax patterns */
3093 for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3094 syn_clear_pattern(buf, i);
3095 ga_clear(&buf->b_syn_patterns);
3096
3097 /* free the syntax clusters */
3098 for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3099 syn_clear_cluster(buf, i);
3100 ga_clear(&buf->b_syn_clusters);
3101
3102 buf->b_syn_sync_flags = 0;
3103 buf->b_syn_sync_minlines = 0;
3104 buf->b_syn_sync_maxlines = 0;
3105 buf->b_syn_sync_linebreaks = 0;
3106
3107 vim_free(buf->b_syn_linecont_prog);
3108 buf->b_syn_linecont_prog = NULL;
3109 vim_free(buf->b_syn_linecont_pat);
3110 buf->b_syn_linecont_pat = NULL;
3111#ifdef FEAT_FOLDING
3112 buf->b_syn_folditems = 0;
3113#endif
3114
3115 /* free the stored states */
3116 syn_stack_free_all(buf);
3117 invalidate_current_state();
3118}
3119
3120/*
3121 * Clear syncing info for one buffer.
3122 */
3123 static void
3124syntax_sync_clear()
3125{
3126 int i;
3127
3128 /* free the syntax patterns */
3129 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3130 if (SYN_ITEMS(curbuf)[i].sp_syncing)
3131 syn_remove_pattern(curbuf, i);
3132
3133 curbuf->b_syn_sync_flags = 0;
3134 curbuf->b_syn_sync_minlines = 0;
3135 curbuf->b_syn_sync_maxlines = 0;
3136 curbuf->b_syn_sync_linebreaks = 0;
3137
3138 vim_free(curbuf->b_syn_linecont_prog);
3139 curbuf->b_syn_linecont_prog = NULL;
3140 vim_free(curbuf->b_syn_linecont_pat);
3141 curbuf->b_syn_linecont_pat = NULL;
3142
3143 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3144}
3145
3146/*
3147 * Remove one pattern from the buffer's pattern list.
3148 */
3149 static void
3150syn_remove_pattern(buf, idx)
3151 buf_T *buf;
3152 int idx;
3153{
3154 synpat_T *spp;
3155
3156 spp = &(SYN_ITEMS(buf)[idx]);
3157#ifdef FEAT_FOLDING
3158 if (spp->sp_flags & HL_FOLD)
3159 --buf->b_syn_folditems;
3160#endif
3161 syn_clear_pattern(buf, idx);
3162 mch_memmove(spp, spp + 1,
3163 sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3164 --buf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003165}
3166
3167/*
3168 * Clear and free one syntax pattern. When clearing all, must be called from
3169 * last to first!
3170 */
3171 static void
3172syn_clear_pattern(buf, i)
3173 buf_T *buf;
3174 int i;
3175{
3176 vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3177 vim_free(SYN_ITEMS(buf)[i].sp_prog);
3178 /* Only free sp_cont_list and sp_next_list of first start pattern */
3179 if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3180 {
3181 vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3182 vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3183 }
3184}
3185
3186/*
3187 * Clear and free one syntax cluster.
3188 */
3189 static void
3190syn_clear_cluster(buf, i)
3191 buf_T *buf;
3192 int i;
3193{
3194 vim_free(SYN_CLSTR(buf)[i].scl_name);
3195 vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3196 vim_free(SYN_CLSTR(buf)[i].scl_list);
3197}
3198
3199/*
3200 * Handle ":syntax clear" command.
3201 */
3202 static void
3203syn_cmd_clear(eap, syncing)
3204 exarg_T *eap;
3205 int syncing;
3206{
3207 char_u *arg = eap->arg;
3208 char_u *arg_end;
3209 int id;
3210
3211 eap->nextcmd = find_nextcmd(arg);
3212 if (eap->skip)
3213 return;
3214
3215 /*
3216 * We have to disable this within ":syn include @group filename",
3217 * because otherwise @group would get deleted.
3218 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3219 * clear".
3220 */
3221 if (curbuf->b_syn_topgrp != 0)
3222 return;
3223
3224 if (ends_excmd(*arg))
3225 {
3226 /*
3227 * No argument: Clear all syntax items.
3228 */
3229 if (syncing)
3230 syntax_sync_clear();
3231 else
3232 {
3233 syntax_clear(curbuf);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003234 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003235 }
3236 }
3237 else
3238 {
3239 /*
3240 * Clear the group IDs that are in the argument.
3241 */
3242 while (!ends_excmd(*arg))
3243 {
3244 arg_end = skiptowhite(arg);
3245 if (*arg == '@')
3246 {
3247 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3248 if (id == 0)
3249 {
3250 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3251 break;
3252 }
3253 else
3254 {
3255 /*
3256 * We can't physically delete a cluster without changing
3257 * the IDs of other clusters, so we do the next best thing
3258 * and make it empty.
3259 */
3260 short scl_id = id - SYNID_CLUSTER;
3261
3262 vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3263 SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3264 }
3265 }
3266 else
3267 {
3268 id = syn_namen2id(arg, (int)(arg_end - arg));
3269 if (id == 0)
3270 {
3271 EMSG2(_(e_nogroup), arg);
3272 break;
3273 }
3274 else
3275 syn_clear_one(id, syncing);
3276 }
3277 arg = skipwhite(arg_end);
3278 }
3279 }
3280 redraw_curbuf_later(NOT_VALID);
3281 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3282}
3283
3284/*
3285 * Clear one syntax group for the current buffer.
3286 */
3287 static void
3288syn_clear_one(id, syncing)
3289 int id;
3290 int syncing;
3291{
3292 synpat_T *spp;
3293 int idx;
3294
3295 /* Clear keywords only when not ":syn sync clear group-name" */
3296 if (!syncing)
3297 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003298 (void)syn_clear_keyword(id, &curbuf->b_keywtab);
3299 (void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003300 }
3301
3302 /* clear the patterns for "id" */
3303 for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3304 {
3305 spp = &(SYN_ITEMS(curbuf)[idx]);
3306 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3307 continue;
3308 syn_remove_pattern(curbuf, idx);
3309 }
3310}
3311
3312/*
3313 * Handle ":syntax on" command.
3314 */
3315/* ARGSUSED */
3316 static void
3317syn_cmd_on(eap, syncing)
3318 exarg_T *eap;
3319 int syncing; /* not used */
3320{
3321 syn_cmd_onoff(eap, "syntax");
3322}
3323
3324/*
3325 * Handle ":syntax enable" command.
3326 */
3327/* ARGSUSED */
3328 static void
3329syn_cmd_enable(eap, syncing)
3330 exarg_T *eap;
3331 int syncing; /* not used */
3332{
3333 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3334 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003335 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003336}
3337
3338/*
3339 * Handle ":syntax reset" command.
3340 */
3341/* ARGSUSED */
3342 static void
3343syn_cmd_reset(eap, syncing)
3344 exarg_T *eap;
3345 int syncing; /* not used */
3346{
3347 eap->nextcmd = check_nextcmd(eap->arg);
3348 if (!eap->skip)
3349 {
3350 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3351 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003352 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003353 }
3354}
3355
3356/*
3357 * Handle ":syntax manual" command.
3358 */
3359/* ARGSUSED */
3360 static void
3361syn_cmd_manual(eap, syncing)
3362 exarg_T *eap;
3363 int syncing; /* not used */
3364{
3365 syn_cmd_onoff(eap, "manual");
3366}
3367
3368/*
3369 * Handle ":syntax off" command.
3370 */
3371/* ARGSUSED */
3372 static void
3373syn_cmd_off(eap, syncing)
3374 exarg_T *eap;
3375 int syncing; /* not used */
3376{
3377 syn_cmd_onoff(eap, "nosyntax");
3378}
3379
3380 static void
3381syn_cmd_onoff(eap, name)
3382 exarg_T *eap;
3383 char *name;
3384{
3385 char_u buf[100];
3386
3387 eap->nextcmd = check_nextcmd(eap->arg);
3388 if (!eap->skip)
3389 {
3390 STRCPY(buf, "so ");
3391 sprintf((char *)buf + 3, SYNTAX_FNAME, name);
3392 do_cmdline_cmd(buf);
3393 }
3394}
3395
3396/*
3397 * Handle ":syntax [list]" command: list current syntax words.
3398 */
3399 static void
3400syn_cmd_list(eap, syncing)
3401 exarg_T *eap;
3402 int syncing; /* when TRUE: list syncing items */
3403{
3404 char_u *arg = eap->arg;
3405 int id;
3406 char_u *arg_end;
3407
3408 eap->nextcmd = find_nextcmd(arg);
3409 if (eap->skip)
3410 return;
3411
3412 if (!syntax_present(curbuf))
3413 {
3414 MSG(_("No Syntax items defined for this buffer"));
3415 return;
3416 }
3417
3418 if (syncing)
3419 {
3420 if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3421 {
3422 MSG_PUTS(_("syncing on C-style comments"));
3423 syn_lines_msg();
3424 syn_match_msg();
3425 return;
3426 }
3427 else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3428 {
3429 if (curbuf->b_syn_sync_minlines == 0)
3430 MSG_PUTS(_("no syncing"));
3431 else
3432 {
3433 MSG_PUTS(_("syncing starts "));
3434 msg_outnum(curbuf->b_syn_sync_minlines);
3435 MSG_PUTS(_(" lines before top line"));
3436 syn_match_msg();
3437 }
3438 return;
3439 }
3440 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3441 if (curbuf->b_syn_sync_minlines > 0
3442 || curbuf->b_syn_sync_maxlines > 0
3443 || curbuf->b_syn_sync_linebreaks > 0)
3444 {
3445 MSG_PUTS(_("\nsyncing on items"));
3446 syn_lines_msg();
3447 syn_match_msg();
3448 }
3449 }
3450 else
3451 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3452 if (ends_excmd(*arg))
3453 {
3454 /*
3455 * No argument: List all group IDs and all syntax clusters.
3456 */
3457 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3458 syn_list_one(id, syncing, FALSE);
3459 for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3460 syn_list_cluster(id);
3461 }
3462 else
3463 {
3464 /*
3465 * List the group IDs and syntax clusters that are in the argument.
3466 */
3467 while (!ends_excmd(*arg) && !got_int)
3468 {
3469 arg_end = skiptowhite(arg);
3470 if (*arg == '@')
3471 {
3472 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3473 if (id == 0)
3474 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3475 else
3476 syn_list_cluster(id - SYNID_CLUSTER);
3477 }
3478 else
3479 {
3480 id = syn_namen2id(arg, (int)(arg_end - arg));
3481 if (id == 0)
3482 EMSG2(_(e_nogroup), arg);
3483 else
3484 syn_list_one(id, syncing, TRUE);
3485 }
3486 arg = skipwhite(arg_end);
3487 }
3488 }
3489 eap->nextcmd = check_nextcmd(arg);
3490}
3491
3492 static void
3493syn_lines_msg()
3494{
3495 if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3496 {
3497 MSG_PUTS("; ");
3498 if (curbuf->b_syn_sync_minlines > 0)
3499 {
3500 MSG_PUTS(_("minimal "));
3501 msg_outnum(curbuf->b_syn_sync_minlines);
3502 if (curbuf->b_syn_sync_maxlines)
3503 MSG_PUTS(", ");
3504 }
3505 if (curbuf->b_syn_sync_maxlines > 0)
3506 {
3507 MSG_PUTS(_("maximal "));
3508 msg_outnum(curbuf->b_syn_sync_maxlines);
3509 }
3510 MSG_PUTS(_(" lines before top line"));
3511 }
3512}
3513
3514 static void
3515syn_match_msg()
3516{
3517 if (curbuf->b_syn_sync_linebreaks > 0)
3518 {
3519 MSG_PUTS(_("; match "));
3520 msg_outnum(curbuf->b_syn_sync_linebreaks);
3521 MSG_PUTS(_(" line breaks"));
3522 }
3523}
3524
3525static int last_matchgroup;
3526
3527struct name_list
3528{
3529 int flag;
3530 char *name;
3531};
3532
3533static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3534
3535/*
3536 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3537 */
3538 static void
3539syn_list_one(id, syncing, link_only)
3540 int id;
3541 int syncing; /* when TRUE: list syncing items */
3542 int link_only; /* when TRUE; list link-only too */
3543{
3544 int attr;
3545 int idx;
3546 int did_header = FALSE;
3547 synpat_T *spp;
3548 static struct name_list namelist1[] =
3549 {
3550 {HL_DISPLAY, "display"},
3551 {HL_CONTAINED, "contained"},
3552 {HL_ONELINE, "oneline"},
3553 {HL_KEEPEND, "keepend"},
3554 {HL_EXTEND, "extend"},
3555 {HL_EXCLUDENL, "excludenl"},
3556 {HL_TRANSP, "transparent"},
3557 {HL_FOLD, "fold"},
3558 {0, NULL}
3559 };
3560 static struct name_list namelist2[] =
3561 {
3562 {HL_SKIPWHITE, "skipwhite"},
3563 {HL_SKIPNL, "skipnl"},
3564 {HL_SKIPEMPTY, "skipempty"},
3565 {0, NULL}
3566 };
3567
3568 attr = hl_attr(HLF_D); /* highlight like directories */
3569
3570 /* list the keywords for "id" */
3571 if (!syncing)
3572 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003573 did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3574 did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003575 did_header, attr);
3576 }
3577
3578 /* list the patterns for "id" */
3579 for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3580 {
3581 spp = &(SYN_ITEMS(curbuf)[idx]);
3582 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3583 continue;
3584
3585 (void)syn_list_header(did_header, 999, id);
3586 did_header = TRUE;
3587 last_matchgroup = 0;
3588 if (spp->sp_type == SPTYPE_MATCH)
3589 {
3590 put_pattern("match", ' ', spp, attr);
3591 msg_putchar(' ');
3592 }
3593 else if (spp->sp_type == SPTYPE_START)
3594 {
3595 while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3596 put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3597 if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3598 put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3599 while (idx < curbuf->b_syn_patterns.ga_len
3600 && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3601 put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3602 --idx;
3603 msg_putchar(' ');
3604 }
3605 syn_list_flags(namelist1, spp->sp_flags, attr);
3606
3607 if (spp->sp_cont_list != NULL)
3608 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3609
3610 if (spp->sp_syn.cont_in_list != NULL)
3611 put_id_list((char_u *)"containedin",
3612 spp->sp_syn.cont_in_list, attr);
3613
3614 if (spp->sp_next_list != NULL)
3615 {
3616 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3617 syn_list_flags(namelist2, spp->sp_flags, attr);
3618 }
3619 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3620 {
3621 if (spp->sp_flags & HL_SYNC_HERE)
3622 msg_puts_attr((char_u *)"grouphere", attr);
3623 else
3624 msg_puts_attr((char_u *)"groupthere", attr);
3625 msg_putchar(' ');
3626 if (spp->sp_sync_idx >= 0)
3627 msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3628 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3629 else
3630 MSG_PUTS("NONE");
3631 msg_putchar(' ');
3632 }
3633 }
3634
3635 /* list the link, if there is one */
3636 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3637 {
3638 (void)syn_list_header(did_header, 999, id);
3639 msg_puts_attr((char_u *)"links to", attr);
3640 msg_putchar(' ');
3641 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3642 }
3643}
3644
3645 static void
3646syn_list_flags(nl, flags, attr)
3647 struct name_list *nl;
3648 int flags;
3649 int attr;
3650{
3651 int i;
3652
3653 for (i = 0; nl[i].flag != 0; ++i)
3654 if (flags & nl[i].flag)
3655 {
3656 msg_puts_attr((char_u *)nl[i].name, attr);
3657 msg_putchar(' ');
3658 }
3659}
3660
3661/*
3662 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3663 */
3664 static void
3665syn_list_cluster(id)
3666 int id;
3667{
3668 int endcol = 15;
3669
3670 /* slight hack: roughly duplicate the guts of syn_list_header() */
3671 msg_putchar('\n');
3672 msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3673
3674 if (msg_col >= endcol) /* output at least one space */
3675 endcol = msg_col + 1;
3676 if (Columns <= endcol) /* avoid hang for tiny window */
3677 endcol = Columns - 1;
3678
3679 msg_advance(endcol);
3680 if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3681 {
3682 put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3683 hl_attr(HLF_D));
3684 }
3685 else
3686 {
3687 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3688 msg_puts((char_u *)"=NONE");
3689 }
3690}
3691
3692 static void
3693put_id_list(name, list, attr)
3694 char_u *name;
3695 short *list;
3696 int attr;
3697{
3698 short *p;
3699
3700 msg_puts_attr(name, attr);
3701 msg_putchar('=');
3702 for (p = list; *p; ++p)
3703 {
3704 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3705 {
3706 if (p[1])
3707 MSG_PUTS("ALLBUT");
3708 else
3709 MSG_PUTS("ALL");
3710 }
3711 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3712 {
3713 MSG_PUTS("TOP");
3714 }
3715 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3716 {
3717 MSG_PUTS("CONTAINED");
3718 }
3719 else if (*p >= SYNID_CLUSTER)
3720 {
3721 short scl_id = *p - SYNID_CLUSTER;
3722
3723 msg_putchar('@');
3724 msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3725 }
3726 else
3727 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3728 if (p[1])
3729 msg_putchar(',');
3730 }
3731 msg_putchar(' ');
3732}
3733
3734 static void
3735put_pattern(s, c, spp, attr)
3736 char *s;
3737 int c;
3738 synpat_T *spp;
3739 int attr;
3740{
3741 long n;
3742 int mask;
3743 int first;
3744 static char *sepchars = "/+=-#@\"|'^&";
3745 int i;
3746
3747 /* May have to write "matchgroup=group" */
3748 if (last_matchgroup != spp->sp_syn_match_id)
3749 {
3750 last_matchgroup = spp->sp_syn_match_id;
3751 msg_puts_attr((char_u *)"matchgroup", attr);
3752 msg_putchar('=');
3753 if (last_matchgroup == 0)
3754 msg_outtrans((char_u *)"NONE");
3755 else
3756 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3757 msg_putchar(' ');
3758 }
3759
3760 /* output the name of the pattern and an '=' or ' ' */
3761 msg_puts_attr((char_u *)s, attr);
3762 msg_putchar(c);
3763
3764 /* output the pattern, in between a char that is not in the pattern */
3765 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3766 if (sepchars[++i] == NUL)
3767 {
3768 i = 0; /* no good char found, just use the first one */
3769 break;
3770 }
3771 msg_putchar(sepchars[i]);
3772 msg_outtrans(spp->sp_pattern);
3773 msg_putchar(sepchars[i]);
3774
3775 /* output any pattern options */
3776 first = TRUE;
3777 for (i = 0; i < SPO_COUNT; ++i)
3778 {
3779 mask = (1 << i);
3780 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3781 {
3782 if (!first)
3783 msg_putchar(','); /* separate with commas */
3784 msg_puts((char_u *)spo_name_tab[i]);
3785 n = spp->sp_offsets[i];
3786 if (i != SPO_LC_OFF)
3787 {
3788 if (spp->sp_off_flags & mask)
3789 msg_putchar('s');
3790 else
3791 msg_putchar('e');
3792 if (n > 0)
3793 msg_putchar('+');
3794 }
3795 if (n || i == SPO_LC_OFF)
3796 msg_outnum(n);
3797 first = FALSE;
3798 }
3799 }
3800 msg_putchar(' ');
3801}
3802
3803/*
3804 * List or clear the keywords for one syntax group.
3805 * Return TRUE if the header has been printed.
3806 */
3807 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00003808syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003809 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003810 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003811 int did_header; /* header has already been printed */
3812 int attr;
3813{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003814 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003815 hashitem_T *hi;
3816 keyentry_T *kp;
3817 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003818 int prev_contained = 0;
3819 short *prev_next_list = NULL;
3820 short *prev_cont_in_list = NULL;
3821 int prev_skipnl = 0;
3822 int prev_skipwhite = 0;
3823 int prev_skipempty = 0;
3824
Bram Moolenaar071d4272004-06-13 20:20:40 +00003825 /*
3826 * Unfortunately, this list of keywords is not sorted on alphabet but on
3827 * hash value...
3828 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003829 todo = ht->ht_used;
3830 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003831 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003832 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003833 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003834 --todo;
3835 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003836 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003837 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003838 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003839 if (prev_contained != (kp->flags & HL_CONTAINED)
3840 || prev_skipnl != (kp->flags & HL_SKIPNL)
3841 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
3842 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
3843 || prev_cont_in_list != kp->k_syn.cont_in_list
3844 || prev_next_list != kp->next_list)
3845 outlen = 9999;
3846 else
3847 outlen = (int)STRLEN(kp->keyword);
3848 /* output "contained" and "nextgroup" on each line */
3849 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003850 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003851 prev_contained = 0;
3852 prev_next_list = NULL;
3853 prev_cont_in_list = NULL;
3854 prev_skipnl = 0;
3855 prev_skipwhite = 0;
3856 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003857 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003858 did_header = TRUE;
3859 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003860 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003861 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003862 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00003863 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003864 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003865 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003866 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003867 put_id_list((char_u *)"containedin",
3868 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00003870 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003871 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003872 if (kp->next_list != prev_next_list)
3873 {
3874 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
3875 msg_putchar(' ');
3876 prev_next_list = kp->next_list;
3877 if (kp->flags & HL_SKIPNL)
3878 {
3879 msg_puts_attr((char_u *)"skipnl", attr);
3880 msg_putchar(' ');
3881 prev_skipnl = (kp->flags & HL_SKIPNL);
3882 }
3883 if (kp->flags & HL_SKIPWHITE)
3884 {
3885 msg_puts_attr((char_u *)"skipwhite", attr);
3886 msg_putchar(' ');
3887 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
3888 }
3889 if (kp->flags & HL_SKIPEMPTY)
3890 {
3891 msg_puts_attr((char_u *)"skipempty", attr);
3892 msg_putchar(' ');
3893 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
3894 }
3895 }
3896 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003897 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898 }
3899 }
3900 }
3901
3902 return did_header;
3903}
3904
3905 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00003906syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003908 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003909{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003910 hashitem_T *hi;
3911 keyentry_T *kp;
3912 keyentry_T *kp_prev;
3913 keyentry_T *kp_next;
3914 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003915
Bram Moolenaardad6b692005-01-25 22:14:34 +00003916 hash_lock(ht);
3917 todo = ht->ht_used;
3918 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003919 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003920 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003921 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003922 --todo;
3923 kp_prev = NULL;
3924 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003926 if (kp->k_syn.id == id)
3927 {
3928 kp_next = kp->ke_next;
3929 if (kp_prev == NULL)
3930 {
3931 if (kp_next == NULL)
3932 hash_remove(ht, hi);
3933 else
3934 hi->hi_key = KE2HIKEY(kp_next);
3935 }
3936 else
3937 kp_prev->ke_next = kp_next;
3938 vim_free(kp->next_list);
3939 vim_free(kp->k_syn.cont_in_list);
3940 vim_free(kp);
3941 kp = kp_next;
3942 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003943 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003944 {
3945 kp_prev = kp;
3946 kp = kp->ke_next;
3947 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003948 }
3949 }
3950 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003951 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952}
3953
3954/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003955 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003956 */
3957 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00003958clear_keywtab(ht)
3959 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003960{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003961 hashitem_T *hi;
3962 int todo;
3963 keyentry_T *kp;
3964 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003965
Bram Moolenaardad6b692005-01-25 22:14:34 +00003966 todo = ht->ht_used;
3967 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003968 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003969 if (!HASHITEM_EMPTY(hi))
3970 {
3971 --todo;
3972 kp = HI2KE(hi);
3973 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003974 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003975 kp_next = kp->ke_next;
3976 vim_free(kp->next_list);
3977 vim_free(kp->k_syn.cont_in_list);
3978 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003979 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003980 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003982 hash_clear(ht);
3983 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984}
3985
3986/*
3987 * Add a keyword to the list of keywords.
3988 */
3989 static void
3990add_keyword(name, id, flags, cont_in_list, next_list)
3991 char_u *name; /* name of keyword */
3992 int id; /* group ID for this keyword */
3993 int flags; /* flags for this keyword */
3994 short *cont_in_list; /* containedin for this keyword */
3995 short *next_list; /* nextgroup for this keyword */
3996{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003997 keyentry_T *kp;
3998 hashtab_T *ht;
3999 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004000 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004001 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004002 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004003
4004 if (curbuf->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004005 name_ic = str_foldcase(name, (int)STRLEN(name),
4006 name_folded, MAXKEYWLEN + 1);
4007 else
4008 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004009 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4010 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004011 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004012 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004013 kp->k_syn.id = id;
4014 kp->k_syn.inc_tag = current_syn_inc_tag;
4015 kp->flags = flags;
4016 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004017 if (cont_in_list != NULL)
4018 curbuf->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004019 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004020
4021 if (curbuf->b_syn_ic)
Bram Moolenaardad6b692005-01-25 22:14:34 +00004022 ht = &curbuf->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004023 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004024 ht = &curbuf->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004025
Bram Moolenaardad6b692005-01-25 22:14:34 +00004026 hash = hash_hash(kp->keyword);
4027 hi = hash_lookup(ht, kp->keyword, hash);
4028 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004029 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004030 /* new keyword, add to hashtable */
4031 kp->ke_next = NULL;
4032 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004033 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004034 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004036 /* keyword already exists, prepend to list */
4037 kp->ke_next = HI2KE(hi);
4038 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004039 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040}
4041
4042/*
4043 * Get the start and end of the group name argument.
4044 * Return a pointer to the first argument.
4045 * Return NULL if the end of the command was found instead of further args.
4046 */
4047 static char_u *
4048get_group_name(arg, name_end)
4049 char_u *arg; /* start of the argument */
4050 char_u **name_end; /* pointer to end of the name */
4051{
4052 char_u *rest;
4053
4054 *name_end = skiptowhite(arg);
4055 rest = skipwhite(*name_end);
4056
4057 /*
4058 * Check if there are enough arguments. The first argument may be a
4059 * pattern, where '|' is allowed, so only check for NUL.
4060 */
4061 if (ends_excmd(*arg) || *rest == NUL)
4062 return NULL;
4063 return rest;
4064}
4065
4066/*
4067 * Check for syntax command option arguments.
4068 * This can be called at any place in the list of arguments, and just picks
4069 * out the arguments that are known. Can be called several times in a row to
4070 * collect all options in between other arguments.
4071 * Return a pointer to the next argument (which isn't an option).
4072 * Return NULL for any error;
4073 */
4074 static char_u *
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004075get_syn_options(arg, opt)
4076 char_u *arg; /* next argument to be checked */
4077 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004079 char_u *gname_start, *gname;
4080 int syn_id;
4081 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004082 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004083 int i;
4084 int fidx;
4085 static struct flag
4086 {
4087 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004088 int argtype;
4089 int flags;
4090 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4091 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4092 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4093 {"eExXtTeEnNdD", 0, HL_EXTEND},
4094 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4095 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4096 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4097 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4098 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4099 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4100 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4101 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4102 {"fFoOlLdD", 0, HL_FOLD},
4103 {"cCoOnNtTaAiInNsS", 1, 0},
4104 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4105 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004106 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004107 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004108
4109 if (arg == NULL) /* already detected error */
4110 return NULL;
4111
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112 for (;;)
4113 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004114 /*
4115 * This is used very often when a large number of keywords is defined.
4116 * Need to skip quickly when no option name is found.
4117 * Also avoid tolower(), it's slow.
4118 */
4119 if (strchr(first_letters, *arg) == NULL)
4120 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004121
4122 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4123 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004124 p = flagtab[fidx].name;
4125 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4126 if (arg[len] != p[i] && arg[len] != p[i + 1])
4127 break;
4128 if (p[i] == NUL && (vim_iswhite(arg[len])
4129 || (flagtab[fidx].argtype > 0
4130 ? arg[len] == '='
4131 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004132 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004133 if (opt->keyword
4134 && (flagtab[fidx].flags == HL_DISPLAY
4135 || flagtab[fidx].flags == HL_FOLD
4136 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004137 /* treat "display", "fold" and "extend" as a keyword */
4138 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004139 break;
4140 }
4141 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004142 if (fidx < 0) /* no match found */
4143 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004144
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004145 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004147 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148 {
4149 EMSG(_("E395: contains argument not accepted here"));
4150 return NULL;
4151 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004152 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153 return NULL;
4154 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004155 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004156 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004157#if 0 /* cannot happen */
4158 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004159 {
4160 EMSG(_("E396: containedin argument not accepted here"));
4161 return NULL;
4162 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004163#endif
4164 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004165 return NULL;
4166 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004167 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004169 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004170 return NULL;
4171 }
4172 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004173 {
4174 opt->flags |= flagtab[fidx].flags;
4175 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004177 if (flagtab[fidx].flags == HL_SYNC_HERE
4178 || flagtab[fidx].flags == HL_SYNC_THERE)
4179 {
4180 if (opt->sync_idx == NULL)
4181 {
4182 EMSG(_("E393: group[t]here not accepted here"));
4183 return NULL;
4184 }
4185 gname_start = arg;
4186 arg = skiptowhite(arg);
4187 if (gname_start == arg)
4188 return NULL;
4189 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4190 if (gname == NULL)
4191 return NULL;
4192 if (STRCMP(gname, "NONE") == 0)
4193 *opt->sync_idx = NONE_IDX;
4194 else
4195 {
4196 syn_id = syn_name2id(gname);
4197 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4198 if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4199 && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4200 {
4201 *opt->sync_idx = i;
4202 break;
4203 }
4204 if (i < 0)
4205 {
4206 EMSG2(_("E394: Didn't find region item for %s"), gname);
4207 vim_free(gname);
4208 return NULL;
4209 }
4210 }
4211
4212 vim_free(gname);
4213 arg = skipwhite(arg);
4214 }
4215#ifdef FEAT_FOLDING
4216 else if (flagtab[fidx].flags == HL_FOLD
4217 && foldmethodIsSyntax(curwin))
4218 /* Need to update folds later. */
4219 foldUpdateAll(curwin);
4220#endif
4221 }
4222 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223
4224 return arg;
4225}
4226
4227/*
4228 * Adjustments to syntax item when declared in a ":syn include"'d file.
4229 * Set the contained flag, and if the item is not already contained, add it
4230 * to the specified top-level group, if any.
4231 */
4232 static void
4233syn_incl_toplevel(id, flagsp)
4234 int id;
4235 int *flagsp;
4236{
4237 if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4238 return;
4239 *flagsp |= HL_CONTAINED;
4240 if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4241 {
4242 /* We have to alloc this, because syn_combine_list() will free it. */
4243 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4244 int tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4245
4246 if (grp_list != NULL)
4247 {
4248 grp_list[0] = id;
4249 grp_list[1] = 0;
4250 syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4251 CLUSTER_ADD);
4252 }
4253 }
4254}
4255
4256/*
4257 * Handle ":syntax include [@{group-name}] filename" command.
4258 */
4259/* ARGSUSED */
4260 static void
4261syn_cmd_include(eap, syncing)
4262 exarg_T *eap;
4263 int syncing; /* not used */
4264{
4265 char_u *arg = eap->arg;
4266 int sgl_id = 1;
4267 char_u *group_name_end;
4268 char_u *rest;
4269 char_u *errormsg = NULL;
4270 int prev_toplvl_grp;
4271 int prev_syn_inc_tag;
4272 int source = FALSE;
4273
4274 eap->nextcmd = find_nextcmd(arg);
4275 if (eap->skip)
4276 return;
4277
4278 if (arg[0] == '@')
4279 {
4280 ++arg;
4281 rest = get_group_name(arg, &group_name_end);
4282 if (rest == NULL)
4283 {
4284 EMSG((char_u *)_("E397: Filename required"));
4285 return;
4286 }
4287 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4288 /* separate_nextcmd() and expand_filename() depend on this */
4289 eap->arg = rest;
4290 }
4291
4292 /*
4293 * Everything that's left, up to the next command, should be the
4294 * filename to include.
4295 */
4296 eap->argt |= (XFILE | NOSPC);
4297 separate_nextcmd(eap);
4298 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4299 {
4300 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4301 * file. Need to expand the file name first. In other cases
4302 * ":runtime!" is used. */
4303 source = TRUE;
4304 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4305 {
4306 if (errormsg != NULL)
4307 EMSG(errormsg);
4308 return;
4309 }
4310 }
4311
4312 /*
4313 * Save and restore the existing top-level grouplist id and ":syn
4314 * include" tag around the actual inclusion.
4315 */
4316 prev_syn_inc_tag = current_syn_inc_tag;
4317 current_syn_inc_tag = ++running_syn_inc_tag;
4318 prev_toplvl_grp = curbuf->b_syn_topgrp;
4319 curbuf->b_syn_topgrp = sgl_id;
4320 if (source ? do_source(eap->arg, FALSE, FALSE) == FAIL
4321 : cmd_runtime(eap->arg, TRUE) == FAIL)
4322 EMSG2(_(e_notopen), eap->arg);
4323 curbuf->b_syn_topgrp = prev_toplvl_grp;
4324 current_syn_inc_tag = prev_syn_inc_tag;
4325}
4326
4327/*
4328 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4329 */
4330/* ARGSUSED */
4331 static void
4332syn_cmd_keyword(eap, syncing)
4333 exarg_T *eap;
4334 int syncing; /* not used */
4335{
4336 char_u *arg = eap->arg;
4337 char_u *group_name_end;
4338 int syn_id;
4339 char_u *rest;
4340 char_u *keyword_copy;
4341 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004342 char_u *kw;
4343 syn_opt_arg_T syn_opt_arg;
4344 int cnt;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345
4346 rest = get_group_name(arg, &group_name_end);
4347
4348 if (rest != NULL)
4349 {
4350 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4351
4352 /* allocate a buffer, for removing the backslashes in the keyword */
4353 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4354 if (keyword_copy != NULL)
4355 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004356 syn_opt_arg.flags = 0;
4357 syn_opt_arg.keyword = TRUE;
4358 syn_opt_arg.sync_idx = NULL;
4359 syn_opt_arg.has_cont_list = FALSE;
4360 syn_opt_arg.cont_in_list = NULL;
4361 syn_opt_arg.next_list = NULL;
4362
Bram Moolenaar071d4272004-06-13 20:20:40 +00004363 /*
4364 * The options given apply to ALL keywords, so all options must be
4365 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004366 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004367 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004368 cnt = 0;
4369 p = keyword_copy;
4370 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004371 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004372 rest = get_syn_options(rest, &syn_opt_arg);
4373 if (rest == NULL || ends_excmd(*rest))
4374 break;
4375 /* Copy the keyword, removing backslashes, and add a NUL. */
4376 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004378 if (*rest == '\\' && rest[1] != NUL)
4379 ++rest;
4380 *p++ = *rest++;
4381 }
4382 *p++ = NUL;
4383 ++cnt;
4384 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004385
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004386 if (!eap->skip)
4387 {
4388 /* Adjust flags for use of ":syn include". */
4389 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4390
4391 /*
4392 * 2: Add an entry for each keyword.
4393 */
4394 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4395 {
4396 for (p = vim_strchr(kw, '['); ; )
4397 {
4398 if (p != NULL)
4399 *p = NUL;
4400 add_keyword(kw, syn_id, syn_opt_arg.flags,
4401 syn_opt_arg.cont_in_list,
4402 syn_opt_arg.next_list);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004403 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004404 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004405 if (p[1] == NUL)
4406 {
4407 EMSG2(_("E747: Missing ']': %s"), kw);
4408 kw = p + 2; /* skip over the NUL */
4409 break;
4410 }
4411 if (p[1] == ']')
4412 {
4413 kw = p + 1; /* skip over the "]" */
4414 break;
4415 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004416#ifdef FEAT_MBYTE
4417 if (has_mbyte)
4418 {
4419 int l = (*mb_ptr2len_check)(p + 1);
4420
4421 mch_memmove(p, p + 1, l);
4422 p += l;
4423 }
4424 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004425#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004426 {
4427 p[0] = p[1];
4428 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429 }
4430 }
4431 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004432 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004433
Bram Moolenaar071d4272004-06-13 20:20:40 +00004434 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004435 vim_free(syn_opt_arg.cont_in_list);
4436 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004437 }
4438 }
4439
4440 if (rest != NULL)
4441 eap->nextcmd = check_nextcmd(rest);
4442 else
4443 EMSG2(_(e_invarg2), arg);
4444
Bram Moolenaar071d4272004-06-13 20:20:40 +00004445 redraw_curbuf_later(NOT_VALID);
4446 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4447}
4448
4449/*
4450 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4451 *
4452 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4453 */
4454 static void
4455syn_cmd_match(eap, syncing)
4456 exarg_T *eap;
4457 int syncing; /* TRUE for ":syntax sync match .. " */
4458{
4459 char_u *arg = eap->arg;
4460 char_u *group_name_end;
4461 char_u *rest;
4462 synpat_T item; /* the item found in the line */
4463 int syn_id;
4464 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004465 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 int sync_idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467
4468 /* Isolate the group name, check for validity */
4469 rest = get_group_name(arg, &group_name_end);
4470
4471 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004472 syn_opt_arg.flags = 0;
4473 syn_opt_arg.keyword = FALSE;
4474 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4475 syn_opt_arg.has_cont_list = TRUE;
4476 syn_opt_arg.cont_list = NULL;
4477 syn_opt_arg.cont_in_list = NULL;
4478 syn_opt_arg.next_list = NULL;
4479 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004480
4481 /* get the pattern. */
4482 init_syn_patterns();
4483 vim_memset(&item, 0, sizeof(item));
4484 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004485 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4486 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004487
4488 /* Get options after the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004489 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490
4491 if (rest != NULL) /* all arguments are valid */
4492 {
4493 /*
4494 * Check for trailing command and illegal trailing arguments.
4495 */
4496 eap->nextcmd = check_nextcmd(rest);
4497 if (!ends_excmd(*rest) || eap->skip)
4498 rest = NULL;
4499 else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4500 && (syn_id = syn_check_group(arg,
4501 (int)(group_name_end - arg))) != 0)
4502 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004503 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004504 /*
4505 * Store the pattern in the syn_items list
4506 */
4507 idx = curbuf->b_syn_patterns.ga_len;
4508 SYN_ITEMS(curbuf)[idx] = item;
4509 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4510 SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4511 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4512 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004513 SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514 SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004515 SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4516 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4517 syn_opt_arg.cont_in_list;
4518 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004519 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004520 SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004521 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004522
4523 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525 curbuf->b_syn_sync_flags |= SF_MATCH;
4526#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004527 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528 ++curbuf->b_syn_folditems;
4529#endif
4530
4531 redraw_curbuf_later(NOT_VALID);
4532 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4533 return; /* don't free the progs and patterns now */
4534 }
4535 }
4536
4537 /*
4538 * Something failed, free the allocated memory.
4539 */
4540 vim_free(item.sp_prog);
4541 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004542 vim_free(syn_opt_arg.cont_list);
4543 vim_free(syn_opt_arg.cont_in_list);
4544 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545
4546 if (rest == NULL)
4547 EMSG2(_(e_invarg2), arg);
4548}
4549
4550/*
4551 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4552 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4553 */
4554 static void
4555syn_cmd_region(eap, syncing)
4556 exarg_T *eap;
4557 int syncing; /* TRUE for ":syntax sync region .." */
4558{
4559 char_u *arg = eap->arg;
4560 char_u *group_name_end;
4561 char_u *rest; /* next arg, NULL on error */
4562 char_u *key_end;
4563 char_u *key = NULL;
4564 char_u *p;
4565 int item;
4566#define ITEM_START 0
4567#define ITEM_SKIP 1
4568#define ITEM_END 2
4569#define ITEM_MATCHGROUP 3
4570 struct pat_ptr
4571 {
4572 synpat_T *pp_synp; /* pointer to syn_pattern */
4573 int pp_matchgroup_id; /* matchgroup ID */
4574 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4575 } *(pat_ptrs[3]);
4576 /* patterns found in the line */
4577 struct pat_ptr *ppp;
4578 struct pat_ptr *ppp_next;
4579 int pat_count = 0; /* nr of syn_patterns found */
4580 int syn_id;
4581 int matchgroup_id = 0;
4582 int not_enough = FALSE; /* not enough arguments */
4583 int illegal = FALSE; /* illegal arguments */
4584 int success = FALSE;
4585 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004586 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004587
4588 /* Isolate the group name, check for validity */
4589 rest = get_group_name(arg, &group_name_end);
4590
4591 pat_ptrs[0] = NULL;
4592 pat_ptrs[1] = NULL;
4593 pat_ptrs[2] = NULL;
4594
4595 init_syn_patterns();
4596
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004597 syn_opt_arg.flags = 0;
4598 syn_opt_arg.keyword = FALSE;
4599 syn_opt_arg.sync_idx = NULL;
4600 syn_opt_arg.has_cont_list = TRUE;
4601 syn_opt_arg.cont_list = NULL;
4602 syn_opt_arg.cont_in_list = NULL;
4603 syn_opt_arg.next_list = NULL;
4604
Bram Moolenaar071d4272004-06-13 20:20:40 +00004605 /*
4606 * get the options, patterns and matchgroup.
4607 */
4608 while (rest != NULL && !ends_excmd(*rest))
4609 {
4610 /* Check for option arguments */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004611 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004612 if (rest == NULL || ends_excmd(*rest))
4613 break;
4614
4615 /* must be a pattern or matchgroup then */
4616 key_end = rest;
4617 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4618 ++key_end;
4619 vim_free(key);
4620 key = vim_strnsave_up(rest, (int)(key_end - rest));
4621 if (key == NULL) /* out of memory */
4622 {
4623 rest = NULL;
4624 break;
4625 }
4626 if (STRCMP(key, "MATCHGROUP") == 0)
4627 item = ITEM_MATCHGROUP;
4628 else if (STRCMP(key, "START") == 0)
4629 item = ITEM_START;
4630 else if (STRCMP(key, "END") == 0)
4631 item = ITEM_END;
4632 else if (STRCMP(key, "SKIP") == 0)
4633 {
4634 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4635 {
4636 illegal = TRUE;
4637 break;
4638 }
4639 item = ITEM_SKIP;
4640 }
4641 else
4642 break;
4643 rest = skipwhite(key_end);
4644 if (*rest != '=')
4645 {
4646 rest = NULL;
4647 EMSG2(_("E398: Missing '=': %s"), arg);
4648 break;
4649 }
4650 rest = skipwhite(rest + 1);
4651 if (*rest == NUL)
4652 {
4653 not_enough = TRUE;
4654 break;
4655 }
4656
4657 if (item == ITEM_MATCHGROUP)
4658 {
4659 p = skiptowhite(rest);
4660 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4661 matchgroup_id = 0;
4662 else
4663 {
4664 matchgroup_id = syn_check_group(rest, (int)(p - rest));
4665 if (matchgroup_id == 0)
4666 {
4667 illegal = TRUE;
4668 break;
4669 }
4670 }
4671 rest = skipwhite(p);
4672 }
4673 else
4674 {
4675 /*
4676 * Allocate room for a syn_pattern, and link it in the list of
4677 * syn_patterns for this item, at the start (because the list is
4678 * used from end to start).
4679 */
4680 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4681 if (ppp == NULL)
4682 {
4683 rest = NULL;
4684 break;
4685 }
4686 ppp->pp_next = pat_ptrs[item];
4687 pat_ptrs[item] = ppp;
4688 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4689 if (ppp->pp_synp == NULL)
4690 {
4691 rest = NULL;
4692 break;
4693 }
4694
4695 /*
4696 * Get the syntax pattern and the following offset(s).
4697 */
4698 /* Enable the appropriate \z specials. */
4699 if (item == ITEM_START)
4700 reg_do_extmatch = REX_SET;
4701 else if (item == ITEM_SKIP || item == ITEM_END)
4702 reg_do_extmatch = REX_USE;
4703 rest = get_syn_pattern(rest, ppp->pp_synp);
4704 reg_do_extmatch = 0;
4705 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004706 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004707 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4708 ppp->pp_matchgroup_id = matchgroup_id;
4709 ++pat_count;
4710 }
4711 }
4712 vim_free(key);
4713 if (illegal || not_enough)
4714 rest = NULL;
4715
4716 /*
4717 * Must have a "start" and "end" pattern.
4718 */
4719 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4720 pat_ptrs[ITEM_END] == NULL))
4721 {
4722 not_enough = TRUE;
4723 rest = NULL;
4724 }
4725
4726 if (rest != NULL)
4727 {
4728 /*
4729 * Check for trailing garbage or command.
4730 * If OK, add the item.
4731 */
4732 eap->nextcmd = check_nextcmd(rest);
4733 if (!ends_excmd(*rest) || eap->skip)
4734 rest = NULL;
4735 else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4736 && (syn_id = syn_check_group(arg,
4737 (int)(group_name_end - arg))) != 0)
4738 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004739 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004740 /*
4741 * Store the start/skip/end in the syn_items list
4742 */
4743 idx = curbuf->b_syn_patterns.ga_len;
4744 for (item = ITEM_START; item <= ITEM_END; ++item)
4745 {
4746 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4747 {
4748 SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4749 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4750 SYN_ITEMS(curbuf)[idx].sp_type =
4751 (item == ITEM_START) ? SPTYPE_START :
4752 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004753 SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4755 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4756 SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4757 ppp->pp_matchgroup_id;
4758 if (item == ITEM_START)
4759 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004760 SYN_ITEMS(curbuf)[idx].sp_cont_list =
4761 syn_opt_arg.cont_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004762 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004763 syn_opt_arg.cont_in_list;
4764 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004765 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004766 SYN_ITEMS(curbuf)[idx].sp_next_list =
4767 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004768 }
4769 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004770 ++idx;
4771#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004772 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004773 ++curbuf->b_syn_folditems;
4774#endif
4775 }
4776 }
4777
4778 redraw_curbuf_later(NOT_VALID);
4779 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4780 success = TRUE; /* don't free the progs and patterns now */
4781 }
4782 }
4783
4784 /*
4785 * Free the allocated memory.
4786 */
4787 for (item = ITEM_START; item <= ITEM_END; ++item)
4788 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4789 {
4790 if (!success)
4791 {
4792 vim_free(ppp->pp_synp->sp_prog);
4793 vim_free(ppp->pp_synp->sp_pattern);
4794 }
4795 vim_free(ppp->pp_synp);
4796 ppp_next = ppp->pp_next;
4797 vim_free(ppp);
4798 }
4799
4800 if (!success)
4801 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004802 vim_free(syn_opt_arg.cont_list);
4803 vim_free(syn_opt_arg.cont_in_list);
4804 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805 if (not_enough)
4806 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
4807 else if (illegal || rest == NULL)
4808 EMSG2(_(e_invarg2), arg);
4809 }
4810}
4811
4812/*
4813 * A simple syntax group ID comparison function suitable for use in qsort()
4814 */
4815 static int
4816#ifdef __BORLANDC__
4817_RTLENTRYF
4818#endif
4819syn_compare_stub(v1, v2)
4820 const void *v1;
4821 const void *v2;
4822{
4823 const short *s1 = v1;
4824 const short *s2 = v2;
4825
4826 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
4827}
4828
4829/*
4830 * Combines lists of syntax clusters.
4831 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
4832 */
4833 static void
4834syn_combine_list(clstr1, clstr2, list_op)
4835 short **clstr1;
4836 short **clstr2;
4837 int list_op;
4838{
4839 int count1 = 0;
4840 int count2 = 0;
4841 short *g1;
4842 short *g2;
4843 short *clstr = NULL;
4844 int count;
4845 int round;
4846
4847 /*
4848 * Handle degenerate cases.
4849 */
4850 if (*clstr2 == NULL)
4851 return;
4852 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
4853 {
4854 if (list_op == CLUSTER_REPLACE)
4855 vim_free(*clstr1);
4856 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
4857 *clstr1 = *clstr2;
4858 else
4859 vim_free(*clstr2);
4860 return;
4861 }
4862
4863 for (g1 = *clstr1; *g1; g1++)
4864 ++count1;
4865 for (g2 = *clstr2; *g2; g2++)
4866 ++count2;
4867
4868 /*
4869 * For speed purposes, sort both lists.
4870 */
4871 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
4872 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
4873
4874 /*
4875 * We proceed in two passes; in round 1, we count the elements to place
4876 * in the new list, and in round 2, we allocate and populate the new
4877 * list. For speed, we use a mergesort-like method, adding the smaller
4878 * of the current elements in each list to the new list.
4879 */
4880 for (round = 1; round <= 2; round++)
4881 {
4882 g1 = *clstr1;
4883 g2 = *clstr2;
4884 count = 0;
4885
4886 /*
4887 * First, loop through the lists until one of them is empty.
4888 */
4889 while (*g1 && *g2)
4890 {
4891 /*
4892 * We always want to add from the first list.
4893 */
4894 if (*g1 < *g2)
4895 {
4896 if (round == 2)
4897 clstr[count] = *g1;
4898 count++;
4899 g1++;
4900 continue;
4901 }
4902 /*
4903 * We only want to add from the second list if we're adding the
4904 * lists.
4905 */
4906 if (list_op == CLUSTER_ADD)
4907 {
4908 if (round == 2)
4909 clstr[count] = *g2;
4910 count++;
4911 }
4912 if (*g1 == *g2)
4913 g1++;
4914 g2++;
4915 }
4916
4917 /*
4918 * Now add the leftovers from whichever list didn't get finished
4919 * first. As before, we only want to add from the second list if
4920 * we're adding the lists.
4921 */
4922 for (; *g1; g1++, count++)
4923 if (round == 2)
4924 clstr[count] = *g1;
4925 if (list_op == CLUSTER_ADD)
4926 for (; *g2; g2++, count++)
4927 if (round == 2)
4928 clstr[count] = *g2;
4929
4930 if (round == 1)
4931 {
4932 /*
4933 * If the group ended up empty, we don't need to allocate any
4934 * space for it.
4935 */
4936 if (count == 0)
4937 {
4938 clstr = NULL;
4939 break;
4940 }
4941 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
4942 if (clstr == NULL)
4943 break;
4944 clstr[count] = 0;
4945 }
4946 }
4947
4948 /*
4949 * Finally, put the new list in place.
4950 */
4951 vim_free(*clstr1);
4952 vim_free(*clstr2);
4953 *clstr1 = clstr;
4954}
4955
4956/*
4957 * Lookup a syntax cluster name and return it's ID.
4958 * If it is not found, 0 is returned.
4959 */
4960 static int
4961syn_scl_name2id(name)
4962 char_u *name;
4963{
4964 int i;
4965 char_u *name_u;
4966
4967 /* Avoid using stricmp() too much, it's slow on some systems */
4968 name_u = vim_strsave_up(name);
4969 if (name_u == NULL)
4970 return 0;
4971 for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
4972 if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
4973 && STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
4974 break;
4975 vim_free(name_u);
4976 return (i < 0 ? 0 : i + SYNID_CLUSTER);
4977}
4978
4979/*
4980 * Like syn_scl_name2id(), but take a pointer + length argument.
4981 */
4982 static int
4983syn_scl_namen2id(linep, len)
4984 char_u *linep;
4985 int len;
4986{
4987 char_u *name;
4988 int id = 0;
4989
4990 name = vim_strnsave(linep, len);
4991 if (name != NULL)
4992 {
4993 id = syn_scl_name2id(name);
4994 vim_free(name);
4995 }
4996 return id;
4997}
4998
4999/*
5000 * Find syntax cluster name in the table and return it's ID.
5001 * The argument is a pointer to the name and the length of the name.
5002 * If it doesn't exist yet, a new entry is created.
5003 * Return 0 for failure.
5004 */
5005 static int
5006syn_check_cluster(pp, len)
5007 char_u *pp;
5008 int len;
5009{
5010 int id;
5011 char_u *name;
5012
5013 name = vim_strnsave(pp, len);
5014 if (name == NULL)
5015 return 0;
5016
5017 id = syn_scl_name2id(name);
5018 if (id == 0) /* doesn't exist yet */
5019 id = syn_add_cluster(name);
5020 else
5021 vim_free(name);
5022 return id;
5023}
5024
5025/*
5026 * Add new syntax cluster and return it's ID.
5027 * "name" must be an allocated string, it will be consumed.
5028 * Return 0 for failure.
5029 */
5030 static int
5031syn_add_cluster(name)
5032 char_u *name;
5033{
5034 int len;
5035
5036 /*
5037 * First call for this growarray: init growing array.
5038 */
5039 if (curbuf->b_syn_clusters.ga_data == NULL)
5040 {
5041 curbuf->b_syn_clusters.ga_itemsize = sizeof(struct syn_cluster);
5042 curbuf->b_syn_clusters.ga_growsize = 10;
5043 }
5044
5045 /*
5046 * Make room for at least one other cluster entry.
5047 */
5048 if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5049 {
5050 vim_free(name);
5051 return 0;
5052 }
5053 len = curbuf->b_syn_clusters.ga_len;
5054
5055 vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(struct syn_cluster));
5056 SYN_CLSTR(curbuf)[len].scl_name = name;
5057 SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5058 SYN_CLSTR(curbuf)[len].scl_list = NULL;
5059 ++curbuf->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005060
5061 return len + SYNID_CLUSTER;
5062}
5063
5064/*
5065 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5066 * [add={groupname},..] [remove={groupname},..]".
5067 */
5068/* ARGSUSED */
5069 static void
5070syn_cmd_cluster(eap, syncing)
5071 exarg_T *eap;
5072 int syncing; /* not used */
5073{
5074 char_u *arg = eap->arg;
5075 char_u *group_name_end;
5076 char_u *rest;
5077 int scl_id;
5078 short *clstr_list;
5079 int got_clstr = FALSE;
5080 int opt_len;
5081 int list_op;
5082
5083 eap->nextcmd = find_nextcmd(arg);
5084 if (eap->skip)
5085 return;
5086
5087 rest = get_group_name(arg, &group_name_end);
5088
5089 if (rest != NULL)
5090 {
5091 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
5092 - SYNID_CLUSTER;
5093
5094 for (;;)
5095 {
5096 if (STRNICMP(rest, "add", 3) == 0
5097 && (vim_iswhite(rest[3]) || rest[3] == '='))
5098 {
5099 opt_len = 3;
5100 list_op = CLUSTER_ADD;
5101 }
5102 else if (STRNICMP(rest, "remove", 6) == 0
5103 && (vim_iswhite(rest[6]) || rest[6] == '='))
5104 {
5105 opt_len = 6;
5106 list_op = CLUSTER_SUBTRACT;
5107 }
5108 else if (STRNICMP(rest, "contains", 8) == 0
5109 && (vim_iswhite(rest[8]) || rest[8] == '='))
5110 {
5111 opt_len = 8;
5112 list_op = CLUSTER_REPLACE;
5113 }
5114 else
5115 break;
5116
5117 clstr_list = NULL;
5118 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5119 {
5120 EMSG2(_(e_invarg2), rest);
5121 break;
5122 }
5123 syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5124 &clstr_list, list_op);
5125 got_clstr = TRUE;
5126 }
5127
5128 if (got_clstr)
5129 {
5130 redraw_curbuf_later(NOT_VALID);
5131 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5132 }
5133 }
5134
5135 if (!got_clstr)
5136 EMSG(_("E400: No cluster specified"));
5137 if (rest == NULL || !ends_excmd(*rest))
5138 EMSG2(_(e_invarg2), arg);
5139}
5140
5141/*
5142 * On first call for current buffer: Init growing array.
5143 */
5144 static void
5145init_syn_patterns()
5146{
5147 curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5148 curbuf->b_syn_patterns.ga_growsize = 10;
5149}
5150
5151/*
5152 * Get one pattern for a ":syntax match" or ":syntax region" command.
5153 * Stores the pattern and program in a synpat_T.
5154 * Returns a pointer to the next argument, or NULL in case of an error.
5155 */
5156 static char_u *
5157get_syn_pattern(arg, ci)
5158 char_u *arg;
5159 synpat_T *ci;
5160{
5161 char_u *end;
5162 int *p;
5163 int idx;
5164 char_u *cpo_save;
5165
5166 /* need at least three chars */
5167 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5168 return NULL;
5169
5170 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5171 if (*end != *arg) /* end delimiter not found */
5172 {
5173 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5174 return NULL;
5175 }
5176 /* store the pattern and compiled regexp program */
5177 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5178 return NULL;
5179
5180 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5181 cpo_save = p_cpo;
5182 p_cpo = (char_u *)"";
5183 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5184 p_cpo = cpo_save;
5185
5186 if (ci->sp_prog == NULL)
5187 return NULL;
5188 ci->sp_ic = curbuf->b_syn_ic;
5189
5190 /*
5191 * Check for a match, highlight or region offset.
5192 */
5193 ++end;
5194 do
5195 {
5196 for (idx = SPO_COUNT; --idx >= 0; )
5197 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5198 break;
5199 if (idx >= 0)
5200 {
5201 p = &(ci->sp_offsets[idx]);
5202 if (idx != SPO_LC_OFF)
5203 switch (end[3])
5204 {
5205 case 's': break;
5206 case 'b': break;
5207 case 'e': idx += SPO_COUNT; break;
5208 default: idx = -1; break;
5209 }
5210 if (idx >= 0)
5211 {
5212 ci->sp_off_flags |= (1 << idx);
5213 if (idx == SPO_LC_OFF) /* lc=99 */
5214 {
5215 end += 3;
5216 *p = getdigits(&end);
5217
5218 /* "lc=" offset automatically sets "ms=" offset */
5219 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5220 {
5221 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5222 ci->sp_offsets[SPO_MS_OFF] = *p;
5223 }
5224 }
5225 else /* yy=x+99 */
5226 {
5227 end += 4;
5228 if (*end == '+')
5229 {
5230 ++end;
5231 *p = getdigits(&end); /* positive offset */
5232 }
5233 else if (*end == '-')
5234 {
5235 ++end;
5236 *p = -getdigits(&end); /* negative offset */
5237 }
5238 }
5239 if (*end != ',')
5240 break;
5241 ++end;
5242 }
5243 }
5244 } while (idx >= 0);
5245
5246 if (!ends_excmd(*end) && !vim_iswhite(*end))
5247 {
5248 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5249 return NULL;
5250 }
5251 return skipwhite(end);
5252}
5253
5254/*
5255 * Handle ":syntax sync .." command.
5256 */
5257/* ARGSUSED */
5258 static void
5259syn_cmd_sync(eap, syncing)
5260 exarg_T *eap;
5261 int syncing; /* not used */
5262{
5263 char_u *arg_start = eap->arg;
5264 char_u *arg_end;
5265 char_u *key = NULL;
5266 char_u *next_arg;
5267 int illegal = FALSE;
5268 int finished = FALSE;
5269 long n;
5270 char_u *cpo_save;
5271
5272 if (ends_excmd(*arg_start))
5273 {
5274 syn_cmd_list(eap, TRUE);
5275 return;
5276 }
5277
5278 while (!ends_excmd(*arg_start))
5279 {
5280 arg_end = skiptowhite(arg_start);
5281 next_arg = skipwhite(arg_end);
5282 vim_free(key);
5283 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5284 if (STRCMP(key, "CCOMMENT") == 0)
5285 {
5286 if (!eap->skip)
5287 curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5288 if (!ends_excmd(*next_arg))
5289 {
5290 arg_end = skiptowhite(next_arg);
5291 if (!eap->skip)
5292 curbuf->b_syn_sync_id = syn_check_group(next_arg,
5293 (int)(arg_end - next_arg));
5294 next_arg = skipwhite(arg_end);
5295 }
5296 else if (!eap->skip)
5297 curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5298 }
5299 else if ( STRNCMP(key, "LINES", 5) == 0
5300 || STRNCMP(key, "MINLINES", 8) == 0
5301 || STRNCMP(key, "MAXLINES", 8) == 0
5302 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5303 {
5304 if (key[4] == 'S')
5305 arg_end = key + 6;
5306 else if (key[0] == 'L')
5307 arg_end = key + 11;
5308 else
5309 arg_end = key + 9;
5310 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5311 {
5312 illegal = TRUE;
5313 break;
5314 }
5315 n = getdigits(&arg_end);
5316 if (!eap->skip)
5317 {
5318 if (key[4] == 'B')
5319 curbuf->b_syn_sync_linebreaks = n;
5320 else if (key[1] == 'A')
5321 curbuf->b_syn_sync_maxlines = n;
5322 else
5323 curbuf->b_syn_sync_minlines = n;
5324 }
5325 }
5326 else if (STRCMP(key, "FROMSTART") == 0)
5327 {
5328 if (!eap->skip)
5329 {
5330 curbuf->b_syn_sync_minlines = MAXLNUM;
5331 curbuf->b_syn_sync_maxlines = 0;
5332 }
5333 }
5334 else if (STRCMP(key, "LINECONT") == 0)
5335 {
5336 if (curbuf->b_syn_linecont_pat != NULL)
5337 {
5338 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5339 finished = TRUE;
5340 break;
5341 }
5342 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5343 if (*arg_end != *next_arg) /* end delimiter not found */
5344 {
5345 illegal = TRUE;
5346 break;
5347 }
5348
5349 if (!eap->skip)
5350 {
5351 /* store the pattern and compiled regexp program */
5352 if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5353 (int)(arg_end - next_arg - 1))) == NULL)
5354 {
5355 finished = TRUE;
5356 break;
5357 }
5358 curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5359
5360 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5361 cpo_save = p_cpo;
5362 p_cpo = (char_u *)"";
5363 curbuf->b_syn_linecont_prog =
5364 vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5365 p_cpo = cpo_save;
5366
5367 if (curbuf->b_syn_linecont_prog == NULL)
5368 {
5369 vim_free(curbuf->b_syn_linecont_pat);
5370 curbuf->b_syn_linecont_pat = NULL;
5371 finished = TRUE;
5372 break;
5373 }
5374 }
5375 next_arg = skipwhite(arg_end + 1);
5376 }
5377 else
5378 {
5379 eap->arg = next_arg;
5380 if (STRCMP(key, "MATCH") == 0)
5381 syn_cmd_match(eap, TRUE);
5382 else if (STRCMP(key, "REGION") == 0)
5383 syn_cmd_region(eap, TRUE);
5384 else if (STRCMP(key, "CLEAR") == 0)
5385 syn_cmd_clear(eap, TRUE);
5386 else
5387 illegal = TRUE;
5388 finished = TRUE;
5389 break;
5390 }
5391 arg_start = next_arg;
5392 }
5393 vim_free(key);
5394 if (illegal)
5395 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5396 else if (!finished)
5397 {
5398 eap->nextcmd = check_nextcmd(arg_start);
5399 redraw_curbuf_later(NOT_VALID);
5400 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5401 }
5402}
5403
5404/*
5405 * Convert a line of highlight group names into a list of group ID numbers.
5406 * "arg" should point to the "contains" or "nextgroup" keyword.
5407 * "arg" is advanced to after the last group name.
5408 * Careful: the argument is modified (NULs added).
5409 * returns FAIL for some error, OK for success.
5410 */
5411 static int
5412get_id_list(arg, keylen, list)
5413 char_u **arg;
5414 int keylen; /* length of keyword */
5415 short **list; /* where to store the resulting list, if not
5416 NULL, the list is silently skipped! */
5417{
5418 char_u *p = NULL;
5419 char_u *end;
5420 int round;
5421 int count;
5422 int total_count = 0;
5423 short *retval = NULL;
5424 char_u *name;
5425 regmatch_T regmatch;
5426 int id;
5427 int i;
5428 int failed = FALSE;
5429
5430 /*
5431 * We parse the list twice:
5432 * round == 1: count the number of items, allocate the array.
5433 * round == 2: fill the array with the items.
5434 * In round 1 new groups may be added, causing the number of items to
5435 * grow when a regexp is used. In that case round 1 is done once again.
5436 */
5437 for (round = 1; round <= 2; ++round)
5438 {
5439 /*
5440 * skip "contains"
5441 */
5442 p = skipwhite(*arg + keylen);
5443 if (*p != '=')
5444 {
5445 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5446 break;
5447 }
5448 p = skipwhite(p + 1);
5449 if (ends_excmd(*p))
5450 {
5451 EMSG2(_("E406: Empty argument: %s"), *arg);
5452 break;
5453 }
5454
5455 /*
5456 * parse the arguments after "contains"
5457 */
5458 count = 0;
5459 while (!ends_excmd(*p))
5460 {
5461 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5462 ;
5463 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5464 if (name == NULL)
5465 {
5466 failed = TRUE;
5467 break;
5468 }
5469 STRNCPY(name + 1, p, end - p);
5470 name[end - p + 1] = NUL;
5471 if ( STRCMP(name + 1, "ALLBUT") == 0
5472 || STRCMP(name + 1, "ALL") == 0
5473 || STRCMP(name + 1, "TOP") == 0
5474 || STRCMP(name + 1, "CONTAINED") == 0)
5475 {
5476 if (TOUPPER_ASC(**arg) != 'C')
5477 {
5478 EMSG2(_("E407: %s not allowed here"), name + 1);
5479 failed = TRUE;
5480 vim_free(name);
5481 break;
5482 }
5483 if (count != 0)
5484 {
5485 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5486 failed = TRUE;
5487 vim_free(name);
5488 break;
5489 }
5490 if (name[1] == 'A')
5491 id = SYNID_ALLBUT;
5492 else if (name[1] == 'T')
5493 id = SYNID_TOP;
5494 else
5495 id = SYNID_CONTAINED;
5496 id += current_syn_inc_tag;
5497 }
5498 else if (name[1] == '@')
5499 {
5500 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5501 }
5502 else
5503 {
5504 /*
5505 * Handle full group name.
5506 */
5507 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5508 id = syn_check_group(name + 1, (int)(end - p));
5509 else
5510 {
5511 /*
5512 * Handle match of regexp with group names.
5513 */
5514 *name = '^';
5515 STRCAT(name, "$");
5516 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5517 if (regmatch.regprog == NULL)
5518 {
5519 failed = TRUE;
5520 vim_free(name);
5521 break;
5522 }
5523
5524 regmatch.rm_ic = TRUE;
5525 id = 0;
5526 for (i = highlight_ga.ga_len; --i >= 0; )
5527 {
5528 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5529 (colnr_T)0))
5530 {
5531 if (round == 2)
5532 {
5533 /* Got more items than expected; can happen
5534 * when adding items that match:
5535 * "contains=a.*b,axb".
5536 * Go back to first round */
5537 if (count >= total_count)
5538 {
5539 vim_free(retval);
5540 round = 1;
5541 }
5542 else
5543 retval[count] = i + 1;
5544 }
5545 ++count;
5546 id = -1; /* remember that we found one */
5547 }
5548 }
5549 vim_free(regmatch.regprog);
5550 }
5551 }
5552 vim_free(name);
5553 if (id == 0)
5554 {
5555 EMSG2(_("E409: Unknown group name: %s"), p);
5556 failed = TRUE;
5557 break;
5558 }
5559 if (id > 0)
5560 {
5561 if (round == 2)
5562 {
5563 /* Got more items than expected, go back to first round */
5564 if (count >= total_count)
5565 {
5566 vim_free(retval);
5567 round = 1;
5568 }
5569 else
5570 retval[count] = id;
5571 }
5572 ++count;
5573 }
5574 p = skipwhite(end);
5575 if (*p != ',')
5576 break;
5577 p = skipwhite(p + 1); /* skip comma in between arguments */
5578 }
5579 if (failed)
5580 break;
5581 if (round == 1)
5582 {
5583 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5584 if (retval == NULL)
5585 break;
5586 retval[count] = 0; /* zero means end of the list */
5587 total_count = count;
5588 }
5589 }
5590
5591 *arg = p;
5592 if (failed || retval == NULL)
5593 {
5594 vim_free(retval);
5595 return FAIL;
5596 }
5597
5598 if (*list == NULL)
5599 *list = retval;
5600 else
5601 vim_free(retval); /* list already found, don't overwrite it */
5602
5603 return OK;
5604}
5605
5606/*
5607 * Make a copy of an ID list.
5608 */
5609 static short *
5610copy_id_list(list)
5611 short *list;
5612{
5613 int len;
5614 int count;
5615 short *retval;
5616
5617 if (list == NULL)
5618 return NULL;
5619
5620 for (count = 0; list[count]; ++count)
5621 ;
5622 len = (count + 1) * sizeof(short);
5623 retval = (short *)alloc((unsigned)len);
5624 if (retval != NULL)
5625 mch_memmove(retval, list, (size_t)len);
5626
5627 return retval;
5628}
5629
5630/*
5631 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5632 * "cur_si" can be NULL if not checking the "containedin" list.
5633 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5634 * the current item.
5635 * This function is called very often, keep it fast!!
5636 */
5637 static int
5638in_id_list(cur_si, list, ssp, contained)
5639 stateitem_T *cur_si; /* current item or NULL */
5640 short *list; /* id list */
5641 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
5642 int contained; /* group id is contained */
5643{
5644 int retval;
5645 short *scl_list;
5646 short item;
5647 short id = ssp->id;
5648 static int depth = 0;
5649 int r;
5650
5651 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00005652 if (cur_si != NULL && ssp->cont_in_list != NULL
5653 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005654 {
5655 /* Ignore transparent items without a contains argument. Double check
5656 * that we don't go back past the first one. */
5657 while ((cur_si->si_flags & HL_TRANS_CONT)
5658 && cur_si > (stateitem_T *)(current_state.ga_data))
5659 --cur_si;
5660 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
5661 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5662 &(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5663 SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5664 return TRUE;
5665 }
5666
5667 if (list == NULL)
5668 return FALSE;
5669
5670 /*
5671 * If list is ID_LIST_ALL, we are in a transparent item that isn't
5672 * inside anything. Only allow not-contained groups.
5673 */
5674 if (list == ID_LIST_ALL)
5675 return !contained;
5676
5677 /*
5678 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5679 * contains list. We also require that "id" is at the same ":syn include"
5680 * level as the list.
5681 */
5682 item = *list;
5683 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5684 {
5685 if (item < SYNID_TOP)
5686 {
5687 /* ALL or ALLBUT: accept all groups in the same file */
5688 if (item - SYNID_ALLBUT != ssp->inc_tag)
5689 return FALSE;
5690 }
5691 else if (item < SYNID_CONTAINED)
5692 {
5693 /* TOP: accept all not-contained groups in the same file */
5694 if (item - SYNID_TOP != ssp->inc_tag || contained)
5695 return FALSE;
5696 }
5697 else
5698 {
5699 /* CONTAINED: accept all contained groups in the same file */
5700 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5701 return FALSE;
5702 }
5703 item = *++list;
5704 retval = FALSE;
5705 }
5706 else
5707 retval = TRUE;
5708
5709 /*
5710 * Return "retval" if id is in the contains list.
5711 */
5712 while (item != 0)
5713 {
5714 if (item == id)
5715 return retval;
5716 if (item >= SYNID_CLUSTER)
5717 {
5718 scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5719 /* restrict recursiveness to 30 to avoid an endless loop for a
5720 * cluster that includes itself (indirectly) */
5721 if (scl_list != NULL && depth < 30)
5722 {
5723 ++depth;
5724 r = in_id_list(NULL, scl_list, ssp, contained);
5725 --depth;
5726 if (r)
5727 return retval;
5728 }
5729 }
5730 item = *++list;
5731 }
5732 return !retval;
5733}
5734
5735struct subcommand
5736{
5737 char *name; /* subcommand name */
5738 void (*func)__ARGS((exarg_T *, int)); /* function to call */
5739};
5740
5741static struct subcommand subcommands[] =
5742{
5743 {"case", syn_cmd_case},
5744 {"clear", syn_cmd_clear},
5745 {"cluster", syn_cmd_cluster},
5746 {"enable", syn_cmd_enable},
5747 {"include", syn_cmd_include},
5748 {"keyword", syn_cmd_keyword},
5749 {"list", syn_cmd_list},
5750 {"manual", syn_cmd_manual},
5751 {"match", syn_cmd_match},
5752 {"on", syn_cmd_on},
5753 {"off", syn_cmd_off},
5754 {"region", syn_cmd_region},
5755 {"reset", syn_cmd_reset},
5756 {"sync", syn_cmd_sync},
5757 {"", syn_cmd_list},
5758 {NULL, NULL}
5759};
5760
5761/*
5762 * ":syntax".
5763 * This searches the subcommands[] table for the subcommand name, and calls a
5764 * syntax_subcommand() function to do the rest.
5765 */
5766 void
5767ex_syntax(eap)
5768 exarg_T *eap;
5769{
5770 char_u *arg = eap->arg;
5771 char_u *subcmd_end;
5772 char_u *subcmd_name;
5773 int i;
5774
5775 syn_cmdlinep = eap->cmdlinep;
5776
5777 /* isolate subcommand name */
5778 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5779 ;
5780 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5781 if (subcmd_name != NULL)
5782 {
5783 if (eap->skip) /* skip error messages for all subcommands */
5784 ++emsg_skip;
5785 for (i = 0; ; ++i)
5786 {
5787 if (subcommands[i].name == NULL)
5788 {
5789 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5790 break;
5791 }
5792 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5793 {
5794 eap->arg = skipwhite(subcmd_end);
5795 (subcommands[i].func)(eap, FALSE);
5796 break;
5797 }
5798 }
5799 vim_free(subcmd_name);
5800 if (eap->skip)
5801 --emsg_skip;
5802 }
5803}
5804
5805 int
5806syntax_present(buf)
5807 buf_T *buf;
5808{
5809 return (buf->b_syn_patterns.ga_len != 0
5810 || buf->b_syn_clusters.ga_len != 0
Bram Moolenaardad6b692005-01-25 22:14:34 +00005811 || curbuf->b_keywtab.ht_used > 0
5812 || curbuf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005813}
5814
5815#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5816
5817static enum
5818{
5819 EXP_SUBCMD, /* expand ":syn" sub-commands */
5820 EXP_CASE /* expand ":syn case" arguments */
5821} expand_what;
5822
5823
5824/*
5825 * Handle command line completion for :syntax command.
5826 */
5827 void
5828set_context_in_syntax_cmd(xp, arg)
5829 expand_T *xp;
5830 char_u *arg;
5831{
5832 char_u *p;
5833
5834 /* Default: expand subcommands */
5835 xp->xp_context = EXPAND_SYNTAX;
5836 expand_what = EXP_SUBCMD;
5837 xp->xp_pattern = arg;
5838 include_link = FALSE;
5839 include_default = FALSE;
5840
5841 /* (part of) subcommand already typed */
5842 if (*arg != NUL)
5843 {
5844 p = skiptowhite(arg);
5845 if (*p != NUL) /* past first word */
5846 {
5847 xp->xp_pattern = skipwhite(p);
5848 if (*skiptowhite(xp->xp_pattern) != NUL)
5849 xp->xp_context = EXPAND_NOTHING;
5850 else if (STRNICMP(arg, "case", p - arg) == 0)
5851 expand_what = EXP_CASE;
5852 else if ( STRNICMP(arg, "keyword", p - arg) == 0
5853 || STRNICMP(arg, "region", p - arg) == 0
5854 || STRNICMP(arg, "match", p - arg) == 0
5855 || STRNICMP(arg, "list", p - arg) == 0)
5856 xp->xp_context = EXPAND_HIGHLIGHT;
5857 else
5858 xp->xp_context = EXPAND_NOTHING;
5859 }
5860 }
5861}
5862
5863static char *(case_args[]) = {"match", "ignore", NULL};
5864
5865/*
5866 * Function given to ExpandGeneric() to obtain the list syntax names for
5867 * expansion.
5868 */
5869/*ARGSUSED*/
5870 char_u *
5871get_syntax_name(xp, idx)
5872 expand_T *xp;
5873 int idx;
5874{
5875 if (expand_what == EXP_SUBCMD)
5876 return (char_u *)subcommands[idx].name;
5877 return (char_u *)case_args[idx];
5878}
5879
5880#endif /* FEAT_CMDL_COMPL */
5881
5882#if defined(FEAT_EVAL) || defined(FEAT_PRINTER) || defined(PROTO)
5883/*
5884 * Function called for expression evaluation: get syntax ID at file position.
5885 */
5886 int
5887syn_get_id(lnum, col, trans)
5888 long lnum;
5889 long col;
5890 int trans; /* remove transparancy */
5891{
5892 /* When the position is not after the current position and in the same
5893 * line of the same buffer, need to restart parsing. */
5894 if (curwin->w_buffer != syn_buf
5895 || lnum != current_lnum
5896 || col < (long)current_col)
5897 syntax_start(curwin, lnum);
5898
5899 (void)get_syntax_attr((colnr_T)col);
5900
5901 return (trans ? current_trans_id : current_id);
5902}
5903#endif
5904
5905#if defined(FEAT_FOLDING) || defined(PROTO)
5906/*
5907 * Function called to get folding level for line "lnum" in window "wp".
5908 */
5909 int
5910syn_get_foldlevel(wp, lnum)
5911 win_T *wp;
5912 long lnum;
5913{
5914 int level = 0;
5915 int i;
5916
5917 /* Return quickly when there are no fold items at all. */
5918 if (wp->w_buffer->b_syn_folditems != 0)
5919 {
5920 syntax_start(wp, lnum);
5921
5922 for (i = 0; i < current_state.ga_len; ++i)
5923 if (CUR_STATE(i).si_flags & HL_FOLD)
5924 ++level;
5925 }
5926 if (level > wp->w_p_fdn)
5927 level = wp->w_p_fdn;
5928 return level;
5929}
5930#endif
5931
5932#endif /* FEAT_SYN_HL */
5933
5934
5935/**************************************
5936 * Highlighting stuff *
5937 **************************************/
5938
5939/*
5940 * The default highlight groups. These are compiled-in for fast startup and
5941 * they still work when the runtime files can't be found.
5942 * When making changes here, also change runtime/colors/default.vim!
5943 */
5944static char *(highlight_init_both[]) =
5945 {
5946#ifdef FEAT_GUI
5947 "Cursor guibg=fg guifg=bg",
5948 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
5949#endif
5950 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
5951 "IncSearch term=reverse cterm=reverse gui=reverse",
5952 "ModeMsg term=bold cterm=bold gui=bold",
5953 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue",
5954 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold",
5955 "StatusLineNC term=reverse cterm=reverse gui=reverse",
5956 "VertSplit term=reverse cterm=reverse gui=reverse",
5957 "Visual term=reverse cterm=reverse gui=reverse guifg=Grey guibg=fg",
5958 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold",
5959 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red",
5960 NULL
5961 };
5962
5963static char *(highlight_init_light[]) =
5964 {
5965 "Directory term=bold ctermfg=DarkBlue guifg=Blue",
5966 "LineNr term=underline ctermfg=Brown guifg=Brown",
5967 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen",
5968 "Normal gui=NONE",
5969 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen",
5970 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
5971 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue",
5972 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta",
5973 "WarningMsg term=standout ctermfg=DarkRed guifg=Red",
5974 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
5975 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
5976 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
5977 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
5978 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue",
5979 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta",
5980 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
5981 NULL
5982 };
5983
5984static char *(highlight_init_dark[]) =
5985 {
5986 "Directory term=bold ctermfg=LightCyan guifg=Cyan",
5987 "LineNr term=underline ctermfg=Yellow guifg=Yellow",
5988 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen",
5989 "Normal gui=NONE",
5990 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green",
5991 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
5992 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan",
5993 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta",
5994 "WarningMsg term=standout ctermfg=LightRed guifg=Red",
5995 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
5996 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
5997 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
5998 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
5999 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue",
6000 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta",
6001 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
6002 NULL
6003 };
6004
6005 void
6006init_highlight(both, reset)
6007 int both; /* include groups where 'bg' doesn't matter */
6008 int reset; /* clear group first */
6009{
6010 int i;
6011 char **pp;
6012 static int had_both = FALSE;
6013#ifdef FEAT_EVAL
6014 char_u *p;
6015
6016 /*
6017 * Try finding the color scheme file. Used when a color file was loaded
6018 * and 'background' or 't_Co' is changed.
6019 */
6020 p = get_var_value((char_u *)"g:colors_name");
6021 if (p != NULL && load_colors(p) == OK)
6022 return;
6023#endif
6024
6025 /*
6026 * Didn't use a color file, use the compiled-in colors.
6027 */
6028 if (both)
6029 {
6030 had_both = TRUE;
6031 pp = highlight_init_both;
6032 for (i = 0; pp[i] != NULL; ++i)
6033 do_highlight((char_u *)pp[i], reset, TRUE);
6034 }
6035 else if (!had_both)
6036 /* Don't do anything before the call with both == TRUE from main().
6037 * Not everything has been setup then, and that call will overrule
6038 * everything anyway. */
6039 return;
6040
6041 if (*p_bg == 'l')
6042 pp = highlight_init_light;
6043 else
6044 pp = highlight_init_dark;
6045 for (i = 0; pp[i] != NULL; ++i)
6046 do_highlight((char_u *)pp[i], reset, TRUE);
6047
6048#ifdef FEAT_SYN_HL
6049 /*
6050 * If syntax highlighting is enabled load the highlighting for it.
6051 */
6052 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006053 {
6054 static int recursive = 0;
6055
6056 if (recursive >= 5)
6057 EMSG(_("E679: recursive loop loading syncolor.vim"));
6058 else
6059 {
6060 ++recursive;
6061 (void)cmd_runtime((char_u *)"syntax/syncolor.vim", TRUE);
6062 --recursive;
6063 }
6064 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006065#endif
6066}
6067
6068/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006069 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006070 * Return OK for success, FAIL for failure.
6071 */
6072 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006073load_colors(name)
6074 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006075{
6076 char_u *buf;
6077 int retval = FAIL;
6078 static int recursive = FALSE;
6079
6080 /* When being called recursively, this is probably because setting
6081 * 'background' caused the highlighting to be reloaded. This means it is
6082 * working, thus we should return OK. */
6083 if (recursive)
6084 return OK;
6085
6086 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006087 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006088 if (buf != NULL)
6089 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006090 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006091 retval = cmd_runtime(buf, FALSE);
6092 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006093#ifdef FEAT_AUTOCMD
6094 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6095#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006096 }
6097 recursive = FALSE;
6098
6099 return retval;
6100}
6101
6102/*
6103 * Handle the ":highlight .." command.
6104 * When using ":hi clear" this is called recursively for each group with
6105 * "forceit" and "init" both TRUE.
6106 */
6107 void
6108do_highlight(line, forceit, init)
6109 char_u *line;
6110 int forceit;
6111 int init; /* TRUE when called for initializing */
6112{
6113 char_u *name_end;
6114 char_u *p;
6115 char_u *linep;
6116 char_u *key_start;
6117 char_u *arg_start;
6118 char_u *key = NULL, *arg = NULL;
6119 long i;
6120 int off;
6121 int len;
6122 int attr;
6123 int id;
6124 int idx;
6125 int dodefault = FALSE;
6126 int doclear = FALSE;
6127 int dolink = FALSE;
6128 int error = FALSE;
6129 int color;
6130 int is_normal_group = FALSE; /* "Normal" group */
6131#ifdef FEAT_GUI_X11
6132 int is_menu_group = FALSE; /* "Menu" group */
6133 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6134 int is_tooltip_group = FALSE; /* "Tooltip" group */
6135 int do_colors = FALSE; /* need to update colors? */
6136#else
6137# define is_menu_group 0
6138# define is_tooltip_group 0
6139#endif
6140
6141 /*
6142 * If no argument, list current highlighting.
6143 */
6144 if (ends_excmd(*line))
6145 {
6146 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6147 /* TODO: only call when the group has attributes set */
6148 highlight_list_one((int)i);
6149 return;
6150 }
6151
6152 /*
6153 * Isolate the name.
6154 */
6155 name_end = skiptowhite(line);
6156 linep = skipwhite(name_end);
6157
6158 /*
6159 * Check for "default" argument.
6160 */
6161 if (STRNCMP(line, "default", name_end - line) == 0)
6162 {
6163 dodefault = TRUE;
6164 line = linep;
6165 name_end = skiptowhite(line);
6166 linep = skipwhite(name_end);
6167 }
6168
6169 /*
6170 * Check for "clear" or "link" argument.
6171 */
6172 if (STRNCMP(line, "clear", name_end - line) == 0)
6173 doclear = TRUE;
6174 if (STRNCMP(line, "link", name_end - line) == 0)
6175 dolink = TRUE;
6176
6177 /*
6178 * ":highlight {group-name}": list highlighting for one group.
6179 */
6180 if (!doclear && !dolink && ends_excmd(*linep))
6181 {
6182 id = syn_namen2id(line, (int)(name_end - line));
6183 if (id == 0)
6184 EMSG2(_("E411: highlight group not found: %s"), line);
6185 else
6186 highlight_list_one(id);
6187 return;
6188 }
6189
6190 /*
6191 * Handle ":highlight link {from} {to}" command.
6192 */
6193 if (dolink)
6194 {
6195 char_u *from_start = linep;
6196 char_u *from_end;
6197 char_u *to_start;
6198 char_u *to_end;
6199 int from_id;
6200 int to_id;
6201
6202 from_end = skiptowhite(from_start);
6203 to_start = skipwhite(from_end);
6204 to_end = skiptowhite(to_start);
6205
6206 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6207 {
6208 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6209 from_start);
6210 return;
6211 }
6212
6213 if (!ends_excmd(*skipwhite(to_end)))
6214 {
6215 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6216 return;
6217 }
6218
6219 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6220 if (STRNCMP(to_start, "NONE", 4) == 0)
6221 to_id = 0;
6222 else
6223 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6224
6225 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6226 {
6227 /*
6228 * Don't allow a link when there already is some highlighting
6229 * for the group, unless '!' is used
6230 */
6231 if (to_id > 0 && !forceit && !init
6232 && hl_has_settings(from_id - 1, dodefault))
6233 {
6234 if (sourcing_name == NULL && !dodefault)
6235 EMSG(_("E414: group has settings, highlight link ignored"));
6236 }
6237 else
6238 {
6239 if (!init)
6240 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6241 HL_TABLE()[from_id - 1].sg_link = to_id;
6242 redraw_all_later(NOT_VALID);
6243 }
6244 }
6245
6246 /* Only call highlight_changed() once, after sourcing a syntax file */
6247 need_highlight_changed = TRUE;
6248
6249 return;
6250 }
6251
6252 if (doclear)
6253 {
6254 /*
6255 * ":highlight clear [group]" command.
6256 */
6257 line = linep;
6258 if (ends_excmd(*line))
6259 {
6260#ifdef FEAT_GUI
6261 /* First, we do not destroy the old values, but allocate the new
6262 * ones and update the display. THEN we destroy the old values.
6263 * If we destroy the old values first, then the old values
6264 * (such as GuiFont's or GuiFontset's) will still be displayed but
6265 * invalid because they were free'd.
6266 */
6267 if (gui.in_use)
6268 {
6269# ifdef FEAT_BEVAL_TIP
6270 gui_init_tooltip_font();
6271# endif
6272# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6273 gui_init_menu_font();
6274# endif
6275 }
6276# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6277 gui_mch_def_colors();
6278# endif
6279# ifdef FEAT_GUI_X11
6280# ifdef FEAT_MENU
6281
6282 /* This only needs to be done when there is no Menu highlight
6283 * group defined by default, which IS currently the case.
6284 */
6285 gui_mch_new_menu_colors();
6286# endif
6287 if (gui.in_use)
6288 {
6289 gui_new_scrollbar_colors();
6290# ifdef FEAT_BEVAL
6291 gui_mch_new_tooltip_colors();
6292# endif
6293# ifdef FEAT_MENU
6294 gui_mch_new_menu_font();
6295# endif
6296 }
6297# endif
6298
6299 /* Ok, we're done allocating the new default graphics items.
6300 * The screen should already be refreshed at this point.
6301 * It is now Ok to clear out the old data.
6302 */
6303#endif
6304#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006305 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006306#endif
6307 restore_cterm_colors();
6308
6309 /*
6310 * Clear all default highlight groups and load the defaults.
6311 */
6312 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6313 highlight_clear(idx);
6314 init_highlight(TRUE, TRUE);
6315#ifdef FEAT_GUI
6316 if (gui.in_use)
6317 highlight_gui_started();
6318#endif
6319 highlight_changed();
6320 redraw_later_clear();
6321 return;
6322 }
6323 name_end = skiptowhite(line);
6324 linep = skipwhite(name_end);
6325 }
6326
6327 /*
6328 * Find the group name in the table. If it does not exist yet, add it.
6329 */
6330 id = syn_check_group(line, (int)(name_end - line));
6331 if (id == 0) /* failed (out of memory) */
6332 return;
6333 idx = id - 1; /* index is ID minus one */
6334
6335 /* Return if "default" was used and the group already has settings. */
6336 if (dodefault && hl_has_settings(idx, TRUE))
6337 return;
6338
6339 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6340 is_normal_group = TRUE;
6341#ifdef FEAT_GUI_X11
6342 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6343 is_menu_group = TRUE;
6344 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6345 is_scrollbar_group = TRUE;
6346 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6347 is_tooltip_group = TRUE;
6348#endif
6349
6350 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6351 if (doclear || (forceit && init))
6352 {
6353 highlight_clear(idx);
6354 if (!doclear)
6355 HL_TABLE()[idx].sg_set = 0;
6356 }
6357
6358 if (!doclear)
6359 while (!ends_excmd(*linep))
6360 {
6361 key_start = linep;
6362 if (*linep == '=')
6363 {
6364 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6365 error = TRUE;
6366 break;
6367 }
6368
6369 /*
6370 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6371 * "guibg").
6372 */
6373 while (*linep && !vim_iswhite(*linep) && *linep != '=')
6374 ++linep;
6375 vim_free(key);
6376 key = vim_strnsave_up(key_start, (int)(linep - key_start));
6377 if (key == NULL)
6378 {
6379 error = TRUE;
6380 break;
6381 }
6382 linep = skipwhite(linep);
6383
6384 if (STRCMP(key, "NONE") == 0)
6385 {
6386 if (!init || HL_TABLE()[idx].sg_set == 0)
6387 {
6388 if (!init)
6389 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6390 highlight_clear(idx);
6391 }
6392 continue;
6393 }
6394
6395 /*
6396 * Check for the equal sign.
6397 */
6398 if (*linep != '=')
6399 {
6400 EMSG2(_("E416: missing equal sign: %s"), key_start);
6401 error = TRUE;
6402 break;
6403 }
6404 ++linep;
6405
6406 /*
6407 * Isolate the argument.
6408 */
6409 linep = skipwhite(linep);
6410 if (*linep == '\'') /* guifg='color name' */
6411 {
6412 arg_start = ++linep;
6413 linep = vim_strchr(linep, '\'');
6414 if (linep == NULL)
6415 {
6416 EMSG2(_(e_invarg2), key_start);
6417 error = TRUE;
6418 break;
6419 }
6420 }
6421 else
6422 {
6423 arg_start = linep;
6424 linep = skiptowhite(linep);
6425 }
6426 if (linep == arg_start)
6427 {
6428 EMSG2(_("E417: missing argument: %s"), key_start);
6429 error = TRUE;
6430 break;
6431 }
6432 vim_free(arg);
6433 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6434 if (arg == NULL)
6435 {
6436 error = TRUE;
6437 break;
6438 }
6439 if (*linep == '\'')
6440 ++linep;
6441
6442 /*
6443 * Store the argument.
6444 */
6445 if ( STRCMP(key, "TERM") == 0
6446 || STRCMP(key, "CTERM") == 0
6447 || STRCMP(key, "GUI") == 0)
6448 {
6449 attr = 0;
6450 off = 0;
6451 while (arg[off] != NUL)
6452 {
6453 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6454 {
6455 len = (int)STRLEN(hl_name_table[i]);
6456 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6457 {
6458 attr |= hl_attr_table[i];
6459 off += len;
6460 break;
6461 }
6462 }
6463 if (i < 0)
6464 {
6465 EMSG2(_("E418: Illegal value: %s"), arg);
6466 error = TRUE;
6467 break;
6468 }
6469 if (arg[off] == ',') /* another one follows */
6470 ++off;
6471 }
6472 if (error)
6473 break;
6474 if (*key == 'T')
6475 {
6476 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6477 {
6478 if (!init)
6479 HL_TABLE()[idx].sg_set |= SG_TERM;
6480 HL_TABLE()[idx].sg_term = attr;
6481 }
6482 }
6483 else if (*key == 'C')
6484 {
6485 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6486 {
6487 if (!init)
6488 HL_TABLE()[idx].sg_set |= SG_CTERM;
6489 HL_TABLE()[idx].sg_cterm = attr;
6490 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6491 }
6492 }
6493#ifdef FEAT_GUI
6494 else
6495 {
6496 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6497 {
6498 if (!init)
6499 HL_TABLE()[idx].sg_set |= SG_GUI;
6500 HL_TABLE()[idx].sg_gui = attr;
6501 }
6502 }
6503#endif
6504 }
6505 else if (STRCMP(key, "FONT") == 0)
6506 {
6507 /* in non-GUI fonts are simply ignored */
6508#ifdef FEAT_GUI
6509 if (!gui.shell_created)
6510 {
6511 /* GUI not started yet, always accept the name. */
6512 vim_free(HL_TABLE()[idx].sg_font_name);
6513 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6514 }
6515 else
6516 {
6517 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6518# ifdef FEAT_XFONTSET
6519 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6520# endif
6521 /* First, save the current font/fontset.
6522 * Then try to allocate the font/fontset.
6523 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6524 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6525 */
6526
6527 HL_TABLE()[idx].sg_font = NOFONT;
6528# ifdef FEAT_XFONTSET
6529 HL_TABLE()[idx].sg_fontset = NOFONTSET;
6530# endif
6531 hl_do_font(idx, arg, is_normal_group, is_menu_group,
6532 is_tooltip_group);
6533
6534# ifdef FEAT_XFONTSET
6535 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6536 {
6537 /* New fontset was accepted. Free the old one, if there was
6538 * one.
6539 */
6540 gui_mch_free_fontset(temp_sg_fontset);
6541 vim_free(HL_TABLE()[idx].sg_font_name);
6542 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6543 }
6544 else
6545 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6546# endif
6547 if (HL_TABLE()[idx].sg_font != NOFONT)
6548 {
6549 /* New font was accepted. Free the old one, if there was
6550 * one.
6551 */
6552 gui_mch_free_font(temp_sg_font);
6553 vim_free(HL_TABLE()[idx].sg_font_name);
6554 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6555 }
6556 else
6557 HL_TABLE()[idx].sg_font = temp_sg_font;
6558 }
6559#endif
6560 }
6561 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6562 {
6563 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6564 {
6565 if (!init)
6566 HL_TABLE()[idx].sg_set |= SG_CTERM;
6567
6568 /* When setting the foreground color, and previously the "bold"
6569 * flag was set for a light color, reset it now */
6570 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6571 {
6572 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6573 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6574 }
6575
6576 if (VIM_ISDIGIT(*arg))
6577 color = atoi((char *)arg);
6578 else if (STRICMP(arg, "fg") == 0)
6579 {
6580 if (cterm_normal_fg_color)
6581 color = cterm_normal_fg_color - 1;
6582 else
6583 {
6584 EMSG(_("E419: FG color unknown"));
6585 error = TRUE;
6586 break;
6587 }
6588 }
6589 else if (STRICMP(arg, "bg") == 0)
6590 {
6591 if (cterm_normal_bg_color > 0)
6592 color = cterm_normal_bg_color - 1;
6593 else
6594 {
6595 EMSG(_("E420: BG color unknown"));
6596 error = TRUE;
6597 break;
6598 }
6599 }
6600 else
6601 {
6602 static char *(color_names[28]) = {
6603 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6604 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6605 "Gray", "Grey",
6606 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6607 "Blue", "LightBlue", "Green", "LightGreen",
6608 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6609 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6610 static int color_numbers_16[28] = {0, 1, 2, 3,
6611 4, 5, 6, 6,
6612 7, 7,
6613 7, 7, 8, 8,
6614 9, 9, 10, 10,
6615 11, 11, 12, 12, 13,
6616 13, 14, 14, 15, -1};
6617 /* for xterm with 88 colors... */
6618 static int color_numbers_88[28] = {0, 4, 2, 6,
6619 1, 5, 32, 72,
6620 84, 84,
6621 7, 7, 82, 82,
6622 12, 43, 10, 61,
6623 14, 63, 9, 74, 13,
6624 75, 11, 78, 15, -1};
6625 /* for xterm with 256 colors... */
6626 static int color_numbers_256[28] = {0, 4, 2, 6,
6627 1, 5, 130, 130,
6628 248, 248,
6629 7, 7, 242, 242,
6630 12, 81, 10, 121,
6631 14, 159, 9, 224, 13,
6632 225, 11, 229, 15, -1};
6633 /* for terminals with less than 16 colors... */
6634 static int color_numbers_8[28] = {0, 4, 2, 6,
6635 1, 5, 3, 3,
6636 7, 7,
6637 7, 7, 0+8, 0+8,
6638 4+8, 4+8, 2+8, 2+8,
6639 6+8, 6+8, 1+8, 1+8, 5+8,
6640 5+8, 3+8, 3+8, 7+8, -1};
6641#if defined(__QNXNTO__)
6642 static int *color_numbers_8_qansi = color_numbers_8;
6643 /* On qnx, the 8 & 16 color arrays are the same */
6644 if (STRNCMP(T_NAME, "qansi", 5) == 0)
6645 color_numbers_8_qansi = color_numbers_16;
6646#endif
6647
6648 /* reduce calls to STRICMP a bit, it can be slow */
6649 off = TOUPPER_ASC(*arg);
6650 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6651 if (off == color_names[i][0]
6652 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6653 break;
6654 if (i < 0)
6655 {
6656 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6657 error = TRUE;
6658 break;
6659 }
6660
6661 /* Use the _16 table to check if its a valid color name. */
6662 color = color_numbers_16[i];
6663 if (color >= 0)
6664 {
6665 if (t_colors == 8)
6666 {
6667 /* t_Co is 8: use the 8 colors table */
6668#if defined(__QNXNTO__)
6669 color = color_numbers_8_qansi[i];
6670#else
6671 color = color_numbers_8[i];
6672#endif
6673 if (key[5] == 'F')
6674 {
6675 /* set/reset bold attribute to get light foreground
6676 * colors (on some terminals, e.g. "linux") */
6677 if (color & 8)
6678 {
6679 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
6680 HL_TABLE()[idx].sg_cterm_bold = TRUE;
6681 }
6682 else
6683 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6684 }
6685 color &= 7; /* truncate to 8 colors */
6686 }
6687 else if (t_colors == 16 || t_colors == 88
6688 || t_colors == 256)
6689 {
6690 /*
6691 * Guess: if the termcap entry ends in 'm', it is
6692 * probably an xterm-like terminal. Use the changed
6693 * order for colors.
6694 */
6695 if (*T_CAF != NUL)
6696 p = T_CAF;
6697 else
6698 p = T_CSF;
6699 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
6700 switch (t_colors)
6701 {
6702 case 16:
6703 color = color_numbers_8[i];
6704 break;
6705 case 88:
6706 color = color_numbers_88[i];
6707 break;
6708 case 256:
6709 color = color_numbers_256[i];
6710 break;
6711 }
6712 }
6713 }
6714 }
6715 /* Add one to the argument, to avoid zero */
6716 if (key[5] == 'F')
6717 {
6718 HL_TABLE()[idx].sg_cterm_fg = color + 1;
6719 if (is_normal_group)
6720 {
6721 cterm_normal_fg_color = color + 1;
6722 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
6723#ifdef FEAT_GUI
6724 /* Don't do this if the GUI is used. */
6725 if (!gui.in_use && !gui.starting)
6726#endif
6727 {
6728 must_redraw = CLEAR;
6729 if (termcap_active)
6730 term_fg_color(color);
6731 }
6732 }
6733 }
6734 else
6735 {
6736 HL_TABLE()[idx].sg_cterm_bg = color + 1;
6737 if (is_normal_group)
6738 {
6739 cterm_normal_bg_color = color + 1;
6740#ifdef FEAT_GUI
6741 /* Don't mess with 'background' if the GUI is used. */
6742 if (!gui.in_use && !gui.starting)
6743#endif
6744 {
6745 must_redraw = CLEAR;
6746 if (termcap_active)
6747 term_bg_color(color);
6748 if (t_colors < 16)
6749 i = (color == 0 || color == 4);
6750 else
6751 i = (color < 7 || color == 8);
6752 /* Set the 'background' option if the value is wrong. */
6753 if (i != (*p_bg == 'd'))
6754 set_option_value((char_u *)"bg", 0L,
6755 i ? (char_u *)"dark" : (char_u *)"light", 0);
6756 }
6757 }
6758 }
6759 }
6760 }
6761 else if (STRCMP(key, "GUIFG") == 0)
6762 {
6763#ifdef FEAT_GUI /* in non-GUI guifg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006764 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006765 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006766 if (!init)
6767 HL_TABLE()[idx].sg_set |= SG_GUI;
6768
6769 i = color_name2handle(arg);
6770 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6771 {
6772 HL_TABLE()[idx].sg_gui_fg = i;
6773 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
6774 if (STRCMP(arg, "NONE"))
6775 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
6776 else
6777 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006778# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006779 if (is_menu_group)
6780 gui.menu_fg_pixel = i;
6781 if (is_scrollbar_group)
6782 gui.scroll_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006783# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006784 if (is_tooltip_group)
6785 gui.tooltip_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006786# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006787 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006788# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006789 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006790 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006791#endif
6792 }
6793 else if (STRCMP(key, "GUIBG") == 0)
6794 {
6795#ifdef FEAT_GUI /* in non-GUI guibg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006796 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006797 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006798 if (!init)
6799 HL_TABLE()[idx].sg_set |= SG_GUI;
6800
6801 i = color_name2handle(arg);
6802 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6803 {
6804 HL_TABLE()[idx].sg_gui_bg = i;
6805 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
6806 if (STRCMP(arg, "NONE") != 0)
6807 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
6808 else
6809 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006810# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006811 if (is_menu_group)
6812 gui.menu_bg_pixel = i;
6813 if (is_scrollbar_group)
6814 gui.scroll_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006815# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006816 if (is_tooltip_group)
6817 gui.tooltip_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006818# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006819 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006820# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006821 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006822 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006823#endif
6824 }
6825 else if (STRCMP(key, "GUISP") == 0)
6826 {
6827#ifdef FEAT_GUI /* in non-GUI guisp colors are simply ignored */
6828 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6829 {
6830 if (!init)
6831 HL_TABLE()[idx].sg_set |= SG_GUI;
6832
6833 i = color_name2handle(arg);
6834 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6835 {
6836 HL_TABLE()[idx].sg_gui_sp = i;
6837 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
6838 if (STRCMP(arg, "NONE") != 0)
6839 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
6840 else
6841 HL_TABLE()[idx].sg_gui_sp_name = NULL;
6842 }
6843 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006844#endif
6845 }
6846 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
6847 {
6848 char_u buf[100];
6849 char_u *tname;
6850
6851 if (!init)
6852 HL_TABLE()[idx].sg_set |= SG_TERM;
6853
6854 /*
6855 * The "start" and "stop" arguments can be a literal escape
6856 * sequence, or a comma seperated list of terminal codes.
6857 */
6858 if (STRNCMP(arg, "t_", 2) == 0)
6859 {
6860 off = 0;
6861 buf[0] = 0;
6862 while (arg[off] != NUL)
6863 {
6864 /* Isolate one termcap name */
6865 for (len = 0; arg[off + len] &&
6866 arg[off + len] != ','; ++len)
6867 ;
6868 tname = vim_strnsave(arg + off, len);
6869 if (tname == NULL) /* out of memory */
6870 {
6871 error = TRUE;
6872 break;
6873 }
6874 /* lookup the escape sequence for the item */
6875 p = get_term_code(tname);
6876 vim_free(tname);
6877 if (p == NULL) /* ignore non-existing things */
6878 p = (char_u *)"";
6879
6880 /* Append it to the already found stuff */
6881 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
6882 {
6883 EMSG2(_("E422: terminal code too long: %s"), arg);
6884 error = TRUE;
6885 break;
6886 }
6887 STRCAT(buf, p);
6888
6889 /* Advance to the next item */
6890 off += len;
6891 if (arg[off] == ',') /* another one follows */
6892 ++off;
6893 }
6894 }
6895 else
6896 {
6897 /*
6898 * Copy characters from arg[] to buf[], translating <> codes.
6899 */
6900 for (p = arg, off = 0; off < 100 && *p; )
6901 {
6902 len = trans_special(&p, buf + off, FALSE);
6903 if (len) /* recognized special char */
6904 off += len;
6905 else /* copy as normal char */
6906 buf[off++] = *p++;
6907 }
6908 buf[off] = NUL;
6909 }
6910 if (error)
6911 break;
6912
6913 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
6914 p = NULL;
6915 else
6916 p = vim_strsave(buf);
6917 if (key[2] == 'A')
6918 {
6919 vim_free(HL_TABLE()[idx].sg_start);
6920 HL_TABLE()[idx].sg_start = p;
6921 }
6922 else
6923 {
6924 vim_free(HL_TABLE()[idx].sg_stop);
6925 HL_TABLE()[idx].sg_stop = p;
6926 }
6927 }
6928 else
6929 {
6930 EMSG2(_("E423: Illegal argument: %s"), key_start);
6931 error = TRUE;
6932 break;
6933 }
6934
6935 /*
6936 * When highlighting has been given for a group, don't link it.
6937 */
6938 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
6939 HL_TABLE()[idx].sg_link = 0;
6940
6941 /*
6942 * Continue with next argument.
6943 */
6944 linep = skipwhite(linep);
6945 }
6946
6947 /*
6948 * If there is an error, and it's a new entry, remove it from the table.
6949 */
6950 if (error && idx == highlight_ga.ga_len)
6951 syn_unadd_group();
6952 else
6953 {
6954 if (is_normal_group)
6955 {
6956 HL_TABLE()[idx].sg_term_attr = 0;
6957 HL_TABLE()[idx].sg_cterm_attr = 0;
6958#ifdef FEAT_GUI
6959 HL_TABLE()[idx].sg_gui_attr = 0;
6960 /*
6961 * Need to update all groups, because they might be using "bg"
6962 * and/or "fg", which have been changed now.
6963 */
6964 if (gui.in_use)
6965 highlight_gui_started();
6966#endif
6967 }
6968#ifdef FEAT_GUI_X11
6969# ifdef FEAT_MENU
6970 else if (is_menu_group)
6971 {
6972 if (gui.in_use && do_colors)
6973 gui_mch_new_menu_colors();
6974 }
6975# endif
6976 else if (is_scrollbar_group)
6977 {
6978 if (gui.in_use && do_colors)
6979 gui_new_scrollbar_colors();
6980 }
6981# ifdef FEAT_BEVAL
6982 else if (is_tooltip_group)
6983 {
6984 if (gui.in_use && do_colors)
6985 gui_mch_new_tooltip_colors();
6986 }
6987# endif
6988#endif
6989 else
6990 set_hl_attr(idx);
6991 redraw_all_later(NOT_VALID);
6992 }
6993 vim_free(key);
6994 vim_free(arg);
6995
6996 /* Only call highlight_changed() once, after sourcing a syntax file */
6997 need_highlight_changed = TRUE;
6998}
6999
7000/*
7001 * Reset the cterm colors to what they were before Vim was started, if
7002 * possible. Otherwise reset them to zero.
7003 */
7004 void
7005restore_cterm_colors()
7006{
7007#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7008 /* Since t_me has been set, this probably means that the user
7009 * wants to use this as default colors. Need to reset default
7010 * background/foreground colors. */
7011 mch_set_normal_colors();
7012#else
7013 cterm_normal_fg_color = 0;
7014 cterm_normal_fg_bold = 0;
7015 cterm_normal_bg_color = 0;
7016#endif
7017}
7018
7019/*
7020 * Return TRUE if highlight group "idx" has any settings.
7021 * When "check_link" is TRUE also check for an existing link.
7022 */
7023 static int
7024hl_has_settings(idx, check_link)
7025 int idx;
7026 int check_link;
7027{
7028 return ( HL_TABLE()[idx].sg_term_attr != 0
7029 || HL_TABLE()[idx].sg_cterm_attr != 0
7030#ifdef FEAT_GUI
7031 || HL_TABLE()[idx].sg_gui_attr != 0
7032#endif
7033 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7034}
7035
7036/*
7037 * Clear highlighting for one group.
7038 */
7039 static void
7040highlight_clear(idx)
7041 int idx;
7042{
7043 HL_TABLE()[idx].sg_term = 0;
7044 vim_free(HL_TABLE()[idx].sg_start);
7045 HL_TABLE()[idx].sg_start = NULL;
7046 vim_free(HL_TABLE()[idx].sg_stop);
7047 HL_TABLE()[idx].sg_stop = NULL;
7048 HL_TABLE()[idx].sg_term_attr = 0;
7049 HL_TABLE()[idx].sg_cterm = 0;
7050 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7051 HL_TABLE()[idx].sg_cterm_fg = 0;
7052 HL_TABLE()[idx].sg_cterm_bg = 0;
7053 HL_TABLE()[idx].sg_cterm_attr = 0;
7054#ifdef FEAT_GUI /* in non-GUI fonts are simply ignored */
7055 HL_TABLE()[idx].sg_gui = 0;
7056 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7057 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7058 HL_TABLE()[idx].sg_gui_fg_name = NULL;
7059 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7060 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7061 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007062 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7063 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7064 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007065 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7066 HL_TABLE()[idx].sg_font = NOFONT;
7067# ifdef FEAT_XFONTSET
7068 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7069 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7070# endif
7071 vim_free(HL_TABLE()[idx].sg_font_name);
7072 HL_TABLE()[idx].sg_font_name = NULL;
7073 HL_TABLE()[idx].sg_gui_attr = 0;
7074#endif
7075}
7076
7077#if defined(FEAT_GUI) || defined(PROTO)
7078/*
7079 * Set the normal foreground and background colors according to the "Normal"
7080 * highlighighting group. For X11 also set "Menu", "Scrollbar", and
7081 * "Tooltip" colors.
7082 */
7083 void
7084set_normal_colors()
7085{
7086 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007087 &gui.norm_pixel, &gui.back_pixel,
7088 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007089 {
7090 gui_mch_new_colors();
7091 must_redraw = CLEAR;
7092 }
7093#ifdef FEAT_GUI_X11
7094 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007095 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7096 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007097 {
7098# ifdef FEAT_MENU
7099 gui_mch_new_menu_colors();
7100# endif
7101 must_redraw = CLEAR;
7102 }
7103# ifdef FEAT_BEVAL
7104 if (set_group_colors((char_u *)"Tooltip",
7105 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7106 FALSE, FALSE, TRUE))
7107 {
7108# ifdef FEAT_TOOLBAR
7109 gui_mch_new_tooltip_colors();
7110# endif
7111 must_redraw = CLEAR;
7112 }
7113#endif
7114 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007115 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7116 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007117 {
7118 gui_new_scrollbar_colors();
7119 must_redraw = CLEAR;
7120 }
7121#endif
7122}
7123
7124/*
7125 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7126 */
7127 static int
7128set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7129 char_u *name;
7130 guicolor_T *fgp;
7131 guicolor_T *bgp;
7132 int do_menu;
7133 int use_norm;
7134 int do_tooltip;
7135{
7136 int idx;
7137
7138 idx = syn_name2id(name) - 1;
7139 if (idx >= 0)
7140 {
7141 gui_do_one_color(idx, do_menu, do_tooltip);
7142
7143 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7144 *fgp = HL_TABLE()[idx].sg_gui_fg;
7145 else if (use_norm)
7146 *fgp = gui.def_norm_pixel;
7147 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7148 *bgp = HL_TABLE()[idx].sg_gui_bg;
7149 else if (use_norm)
7150 *bgp = gui.def_back_pixel;
7151 return TRUE;
7152 }
7153 return FALSE;
7154}
7155
7156/*
7157 * Get the font of the "Normal" group.
7158 * Returns "" when it's not found or not set.
7159 */
7160 char_u *
7161hl_get_font_name()
7162{
7163 int id;
7164 char_u *s;
7165
7166 id = syn_name2id((char_u *)"Normal");
7167 if (id > 0)
7168 {
7169 s = HL_TABLE()[id - 1].sg_font_name;
7170 if (s != NULL)
7171 return s;
7172 }
7173 return (char_u *)"";
7174}
7175
7176/*
7177 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7178 * actually chosen to be used.
7179 */
7180 void
7181hl_set_font_name(font_name)
7182 char_u *font_name;
7183{
7184 int id;
7185
7186 id = syn_name2id((char_u *)"Normal");
7187 if (id > 0)
7188 {
7189 vim_free(HL_TABLE()[id - 1].sg_font_name);
7190 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7191 }
7192}
7193
7194/*
7195 * Set background color for "Normal" group. Called by gui_set_bg_color()
7196 * when the color is known.
7197 */
7198 void
7199hl_set_bg_color_name(name)
7200 char_u *name; /* must have been allocated */
7201{
7202 int id;
7203
7204 if (name != NULL)
7205 {
7206 id = syn_name2id((char_u *)"Normal");
7207 if (id > 0)
7208 {
7209 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7210 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7211 }
7212 }
7213}
7214
7215/*
7216 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7217 * when the color is known.
7218 */
7219 void
7220hl_set_fg_color_name(name)
7221 char_u *name; /* must have been allocated */
7222{
7223 int id;
7224
7225 if (name != NULL)
7226 {
7227 id = syn_name2id((char_u *)"Normal");
7228 if (id > 0)
7229 {
7230 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7231 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7232 }
7233 }
7234}
7235
7236/*
7237 * Return the handle for a color name.
7238 * Returns INVALCOLOR when failed.
7239 */
7240 static guicolor_T
7241color_name2handle(name)
7242 char_u *name;
7243{
7244 if (STRCMP(name, "NONE") == 0)
7245 return INVALCOLOR;
7246
7247 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7248 return gui.norm_pixel;
7249 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7250 return gui.back_pixel;
7251
7252 return gui_get_color(name);
7253}
7254
7255/*
7256 * Return the handle for a font name.
7257 * Returns NOFONT when failed.
7258 */
7259 static GuiFont
7260font_name2handle(name)
7261 char_u *name;
7262{
7263 if (STRCMP(name, "NONE") == 0)
7264 return NOFONT;
7265
7266 return gui_mch_get_font(name, TRUE);
7267}
7268
7269# ifdef FEAT_XFONTSET
7270/*
7271 * Return the handle for a fontset name.
7272 * Returns NOFONTSET when failed.
7273 */
7274 static GuiFontset
7275fontset_name2handle(name, fixed_width)
7276 char_u *name;
7277 int fixed_width;
7278{
7279 if (STRCMP(name, "NONE") == 0)
7280 return NOFONTSET;
7281
7282 return gui_mch_get_fontset(name, TRUE, fixed_width);
7283}
7284# endif
7285
7286/*
7287 * Get the font or fontset for one highlight group.
7288 */
7289/*ARGSUSED*/
7290 static void
7291hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7292 int idx;
7293 char_u *arg;
7294 int do_normal; /* set normal font */
7295 int do_menu; /* set menu font */
7296 int do_tooltip; /* set tooltip font */
7297{
7298# ifdef FEAT_XFONTSET
7299 /* If 'guifontset' is not empty, first try using the name as a
7300 * fontset. If that doesn't work, use it as a font name. */
7301 if (*p_guifontset != NUL
7302# ifdef FONTSET_ALWAYS
7303 || do_menu
7304# endif
7305# ifdef FEAT_BEVAL_TIP
7306 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
7307 || do_tooltip
7308# endif
7309 )
7310 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7311# ifdef FONTSET_ALWAYS
7312 || do_menu
7313# endif
7314# ifdef FEAT_BEVAL_TIP
7315 || do_tooltip
7316# endif
7317 );
7318 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7319 {
7320 /* If it worked and it's the Normal group, use it as the
7321 * normal fontset. Same for the Menu group. */
7322 if (do_normal)
7323 gui_init_font(arg, TRUE);
7324# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7325 if (do_menu)
7326 {
7327# ifdef FONTSET_ALWAYS
7328 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7329# else
7330 /* YIKES! This is a bug waiting to crash the program */
7331 gui.menu_font = HL_TABLE()[idx].sg_fontset;
7332# endif
7333 gui_mch_new_menu_font();
7334 }
7335# ifdef FEAT_BEVAL
7336 if (do_tooltip)
7337 {
7338 /* The Athena widget set cannot currently handle switching between
7339 * displaying a single font and a fontset.
7340 * If the XtNinternational resource is set to True at widget
7341 * creation, then a fontset is always used, othwise an
7342 * XFontStruct is used.
7343 */
7344 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7345 gui_mch_new_tooltip_font();
7346 }
7347# endif
7348# endif
7349 }
7350 else
7351# endif
7352 {
7353 HL_TABLE()[idx].sg_font = font_name2handle(arg);
7354 /* If it worked and it's the Normal group, use it as the
7355 * normal font. Same for the Menu group. */
7356 if (HL_TABLE()[idx].sg_font != NOFONT)
7357 {
7358 if (do_normal)
7359 gui_init_font(arg, FALSE);
7360#ifndef FONTSET_ALWAYS
7361# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7362 if (do_menu)
7363 {
7364 gui.menu_font = HL_TABLE()[idx].sg_font;
7365 gui_mch_new_menu_font();
7366 }
7367# endif
7368#endif
7369 }
7370 }
7371}
7372
7373#endif /* FEAT_GUI */
7374
7375/*
7376 * Table with the specifications for an attribute number.
7377 * Note that this table is used by ALL buffers. This is required because the
7378 * GUI can redraw at any time for any buffer.
7379 */
7380garray_T term_attr_table = {0, 0, 0, 0, NULL};
7381
7382#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7383
7384garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
7385
7386#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7387
7388#ifdef FEAT_GUI
7389garray_T gui_attr_table = {0, 0, 0, 0, NULL};
7390
7391#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7392#endif
7393
7394/*
7395 * Return the attr number for a set of colors and font.
7396 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7397 * if the combination is new.
7398 * Return 0 for error (no more room).
7399 */
7400 static int
7401get_attr_entry(table, aep)
7402 garray_T *table;
7403 attrentry_T *aep;
7404{
7405 int i;
7406 attrentry_T *gap;
7407 static int recursive = FALSE;
7408
7409 /*
7410 * Init the table, in case it wasn't done yet.
7411 */
7412 table->ga_itemsize = sizeof(attrentry_T);
7413 table->ga_growsize = 7;
7414
7415 /*
7416 * Try to find an entry with the same specifications.
7417 */
7418 for (i = 0; i < table->ga_len; ++i)
7419 {
7420 gap = &(((attrentry_T *)table->ga_data)[i]);
7421 if ( aep->ae_attr == gap->ae_attr
7422 && (
7423#ifdef FEAT_GUI
7424 (table == &gui_attr_table
7425 && (aep->ae_u.gui.fg_color == gap->ae_u.gui.fg_color
7426 && aep->ae_u.gui.bg_color == gap->ae_u.gui.bg_color
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007427 && aep->ae_u.gui.sp_color == gap->ae_u.gui.sp_color
Bram Moolenaar071d4272004-06-13 20:20:40 +00007428 && aep->ae_u.gui.font == gap->ae_u.gui.font
7429# ifdef FEAT_XFONTSET
7430 && aep->ae_u.gui.fontset == gap->ae_u.gui.fontset
7431# endif
7432 ))
7433 ||
7434#endif
7435 (table == &term_attr_table
7436 && (aep->ae_u.term.start == NULL) ==
7437 (gap->ae_u.term.start == NULL)
7438 && (aep->ae_u.term.start == NULL
7439 || STRCMP(aep->ae_u.term.start,
7440 gap->ae_u.term.start) == 0)
7441 && (aep->ae_u.term.stop == NULL) ==
7442 (gap->ae_u.term.stop == NULL)
7443 && (aep->ae_u.term.stop == NULL
7444 || STRCMP(aep->ae_u.term.stop,
7445 gap->ae_u.term.stop) == 0))
7446 || (table == &cterm_attr_table
7447 && aep->ae_u.cterm.fg_color == gap->ae_u.cterm.fg_color
7448 && aep->ae_u.cterm.bg_color == gap->ae_u.cterm.bg_color)
7449 ))
7450
7451 return i + ATTR_OFF;
7452 }
7453
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007454 if (table->ga_len + ATTR_OFF >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007455 {
7456 /*
7457 * Running out of attribute entries! remove all attributes, and
7458 * compute new ones for all groups.
7459 * When called recursively, we are really out of numbers.
7460 */
7461 if (recursive)
7462 {
7463 EMSG(_("E424: Too many different highlighting attributes in use"));
7464 return 0;
7465 }
7466 recursive = TRUE;
7467
7468#ifdef FEAT_GUI
7469 ga_clear(&gui_attr_table);
7470#endif
7471 ga_clear(&term_attr_table);
7472 ga_clear(&cterm_attr_table);
7473 must_redraw = CLEAR;
7474
7475 for (i = 0; i < highlight_ga.ga_len; ++i)
7476 set_hl_attr(i);
7477
7478 recursive = FALSE;
7479 }
7480
7481 /*
7482 * This is a new combination of colors and font, add an entry.
7483 */
7484 if (ga_grow(table, 1) == FAIL)
7485 return 0;
7486
7487 gap = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7488 vim_memset(gap, 0, sizeof(attrentry_T));
7489 gap->ae_attr = aep->ae_attr;
7490#ifdef FEAT_GUI
7491 if (table == &gui_attr_table)
7492 {
7493 gap->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7494 gap->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007495 gap->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007496 gap->ae_u.gui.font = aep->ae_u.gui.font;
7497# ifdef FEAT_XFONTSET
7498 gap->ae_u.gui.fontset = aep->ae_u.gui.fontset;
7499# endif
7500 }
7501#endif
7502 if (table == &term_attr_table)
7503 {
7504 if (aep->ae_u.term.start == NULL)
7505 gap->ae_u.term.start = NULL;
7506 else
7507 gap->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
7508 if (aep->ae_u.term.stop == NULL)
7509 gap->ae_u.term.stop = NULL;
7510 else
7511 gap->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
7512 }
7513 else if (table == &cterm_attr_table)
7514 {
7515 gap->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7516 gap->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
7517 }
7518 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007519 return (table->ga_len - 1 + ATTR_OFF);
7520}
7521
7522#ifdef FEAT_GUI
7523
7524 attrentry_T *
7525syn_gui_attr2entry(attr)
7526 int attr;
7527{
7528 attr -= ATTR_OFF;
7529 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
7530 return NULL;
7531 return &(GUI_ATTR_ENTRY(attr));
7532}
7533
7534#endif /* FEAT_GUI */
7535
7536 attrentry_T *
7537syn_term_attr2entry(attr)
7538 int attr;
7539{
7540 attr -= ATTR_OFF;
7541 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
7542 return NULL;
7543 return &(TERM_ATTR_ENTRY(attr));
7544}
7545
7546 attrentry_T *
7547syn_cterm_attr2entry(attr)
7548 int attr;
7549{
7550 attr -= ATTR_OFF;
7551 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
7552 return NULL;
7553 return &(CTERM_ATTR_ENTRY(attr));
7554}
7555
7556#define LIST_ATTR 1
7557#define LIST_STRING 2
7558#define LIST_INT 3
7559
7560 static void
7561highlight_list_one(id)
7562 int id;
7563{
7564 struct hl_group *sgp;
7565 int didh = FALSE;
7566
7567 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
7568
7569 didh = highlight_list_arg(id, didh, LIST_ATTR,
7570 sgp->sg_term, NULL, "term");
7571 didh = highlight_list_arg(id, didh, LIST_STRING,
7572 0, sgp->sg_start, "start");
7573 didh = highlight_list_arg(id, didh, LIST_STRING,
7574 0, sgp->sg_stop, "stop");
7575
7576 didh = highlight_list_arg(id, didh, LIST_ATTR,
7577 sgp->sg_cterm, NULL, "cterm");
7578 didh = highlight_list_arg(id, didh, LIST_INT,
7579 sgp->sg_cterm_fg, NULL, "ctermfg");
7580 didh = highlight_list_arg(id, didh, LIST_INT,
7581 sgp->sg_cterm_bg, NULL, "ctermbg");
7582
7583#ifdef FEAT_GUI
7584 didh = highlight_list_arg(id, didh, LIST_ATTR,
7585 sgp->sg_gui, NULL, "gui");
7586 didh = highlight_list_arg(id, didh, LIST_STRING,
7587 0, sgp->sg_gui_fg_name, "guifg");
7588 didh = highlight_list_arg(id, didh, LIST_STRING,
7589 0, sgp->sg_gui_bg_name, "guibg");
7590 didh = highlight_list_arg(id, didh, LIST_STRING,
7591 0, sgp->sg_font_name, "font");
7592#endif
7593
7594 if (sgp->sg_link)
7595 {
7596 (void)syn_list_header(didh, 9999, id);
7597 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
7598 msg_putchar(' ');
7599 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
7600 }
7601}
7602
7603 static int
7604highlight_list_arg(id, didh, type, iarg, sarg, name)
7605 int id;
7606 int didh;
7607 int type;
7608 int iarg;
7609 char_u *sarg;
7610 char *name;
7611{
7612 char_u buf[100];
7613 char_u *ts;
7614 int i;
7615
7616 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
7617 {
7618 ts = buf;
7619 if (type == LIST_INT)
7620 sprintf((char *)buf, "%d", iarg - 1);
7621 else if (type == LIST_STRING)
7622 ts = sarg;
7623 else /* type == LIST_ATTR */
7624 {
7625 buf[0] = NUL;
7626 for (i = 0; hl_attr_table[i] != 0; ++i)
7627 {
7628 if (iarg & hl_attr_table[i])
7629 {
7630 if (buf[0] != NUL)
7631 STRCAT(buf, ",");
7632 STRCAT(buf, hl_name_table[i]);
7633 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
7634 }
7635 }
7636 }
7637
7638 (void)syn_list_header(didh,
7639 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
7640 didh = TRUE;
7641
7642 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
7643 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
7644 msg_outtrans(ts);
7645 }
7646 return didh;
7647}
7648
7649#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
7650/*
7651 * Return "1" if highlight group "id" has attribute "flag".
7652 * Return NULL otherwise.
7653 */
7654 char_u *
7655highlight_has_attr(id, flag, modec)
7656 int id;
7657 int flag;
7658 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
7659{
7660 int attr;
7661
7662 if (id <= 0 || id > highlight_ga.ga_len)
7663 return NULL;
7664
7665#ifdef FEAT_GUI
7666 if (modec == 'g')
7667 attr = HL_TABLE()[id - 1].sg_gui;
7668 else
7669#endif
7670 if (modec == 'c')
7671 attr = HL_TABLE()[id - 1].sg_cterm;
7672 else
7673 attr = HL_TABLE()[id - 1].sg_term;
7674
7675 if (attr & flag)
7676 return (char_u *)"1";
7677 return NULL;
7678}
7679#endif
7680
7681#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
7682/*
7683 * Return color name of highlight group "id".
7684 */
7685 char_u *
7686highlight_color(id, what, modec)
7687 int id;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007688 char_u *what; /* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007689 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
7690{
7691 static char_u name[20];
7692 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007693 int fg = FALSE;
7694# ifdef FEAT_GUI
7695 int sp = FALSE;
7696# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007697
7698 if (id <= 0 || id > highlight_ga.ga_len)
7699 return NULL;
7700
7701 if (TOLOWER_ASC(what[0]) == 'f')
7702 fg = TRUE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007703# ifdef FEAT_GUI
7704 else if (TOLOWER_ASC(what[0]) == 's')
7705 sp = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007706 if (modec == 'g')
7707 {
7708 /* return #RRGGBB form (only possible when GUI is running) */
7709 if (gui.in_use && what[1] && what[2] == '#')
7710 {
7711 guicolor_T color;
7712 long_u rgb;
7713 static char_u buf[10];
7714
7715 if (fg)
7716 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007717 else if (sp)
7718 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007719 else
7720 color = HL_TABLE()[id - 1].sg_gui_bg;
7721 if (color == INVALCOLOR)
7722 return NULL;
7723 rgb = gui_mch_get_rgb(color);
7724 sprintf((char *)buf, "#%02x%02x%02x",
7725 (unsigned)(rgb >> 16),
7726 (unsigned)(rgb >> 8) & 255,
7727 (unsigned)rgb & 255);
7728 return buf;
7729 }
7730 if (fg)
7731 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007732 if (sp)
7733 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007734 return (HL_TABLE()[id - 1].sg_gui_bg_name);
7735 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007736# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007737 if (modec == 'c')
7738 {
7739 if (fg)
7740 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
7741 else
7742 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
7743 sprintf((char *)name, "%d", n);
7744 return name;
7745 }
7746 /* term doesn't have color */
7747 return NULL;
7748}
7749#endif
7750
7751#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
7752 || defined(PROTO)
7753/*
7754 * Return color name of highlight group "id" as RGB value.
7755 */
7756 long_u
7757highlight_gui_color_rgb(id, fg)
7758 int id;
7759 int fg; /* TRUE = fg, FALSE = bg */
7760{
7761 guicolor_T color;
7762
7763 if (id <= 0 || id > highlight_ga.ga_len)
7764 return 0L;
7765
7766 if (fg)
7767 color = HL_TABLE()[id - 1].sg_gui_fg;
7768 else
7769 color = HL_TABLE()[id - 1].sg_gui_bg;
7770
7771 if (color == INVALCOLOR)
7772 return 0L;
7773
7774 return gui_mch_get_rgb(color);
7775}
7776#endif
7777
7778/*
7779 * Output the syntax list header.
7780 * Return TRUE when started a new line.
7781 */
7782 static int
7783syn_list_header(did_header, outlen, id)
7784 int did_header; /* did header already */
7785 int outlen; /* length of string that comes */
7786 int id; /* highlight group id */
7787{
7788 int endcol = 19;
7789 int newline = TRUE;
7790
7791 if (!did_header)
7792 {
7793 msg_putchar('\n');
7794 msg_outtrans(HL_TABLE()[id - 1].sg_name);
7795 endcol = 15;
7796 }
7797 else if (msg_col + outlen + 1 >= Columns)
7798 msg_putchar('\n');
7799 else
7800 {
7801 if (msg_col >= endcol) /* wrap around is like starting a new line */
7802 newline = FALSE;
7803 }
7804
7805 if (msg_col >= endcol) /* output at least one space */
7806 endcol = msg_col + 1;
7807 if (Columns <= endcol) /* avoid hang for tiny window */
7808 endcol = Columns - 1;
7809
7810 msg_advance(endcol);
7811
7812 /* Show "xxx" with the attributes. */
7813 if (!did_header)
7814 {
7815 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
7816 msg_putchar(' ');
7817 }
7818
7819 return newline;
7820}
7821
7822/*
7823 * Set the attribute numbers for a highlight group.
7824 * Called after one of the attributes has changed.
7825 */
7826 static void
7827set_hl_attr(idx)
7828 int idx; /* index in array */
7829{
7830 attrentry_T at_en;
7831 struct hl_group *sgp = HL_TABLE() + idx;
7832
7833 /* The "Normal" group doesn't need an attribute number */
7834 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
7835 return;
7836
7837#ifdef FEAT_GUI
7838 /*
7839 * For the GUI mode: If there are other than "normal" highlighting
7840 * attributes, need to allocate an attr number.
7841 */
7842 if (sgp->sg_gui_fg == INVALCOLOR
7843 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007844 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00007845 && sgp->sg_font == NOFONT
7846# ifdef FEAT_XFONTSET
7847 && sgp->sg_fontset == NOFONTSET
7848# endif
7849 )
7850 {
7851 sgp->sg_gui_attr = sgp->sg_gui;
7852 }
7853 else
7854 {
7855 at_en.ae_attr = sgp->sg_gui;
7856 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
7857 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007858 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007859 at_en.ae_u.gui.font = sgp->sg_font;
7860# ifdef FEAT_XFONTSET
7861 at_en.ae_u.gui.fontset = sgp->sg_fontset;
7862# endif
7863 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
7864 }
7865#endif
7866 /*
7867 * For the term mode: If there are other than "normal" highlighting
7868 * attributes, need to allocate an attr number.
7869 */
7870 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
7871 sgp->sg_term_attr = sgp->sg_term;
7872 else
7873 {
7874 at_en.ae_attr = sgp->sg_term;
7875 at_en.ae_u.term.start = sgp->sg_start;
7876 at_en.ae_u.term.stop = sgp->sg_stop;
7877 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
7878 }
7879
7880 /*
7881 * For the color term mode: If there are other than "normal"
7882 * highlighting attributes, need to allocate an attr number.
7883 */
7884 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
7885 sgp->sg_cterm_attr = sgp->sg_cterm;
7886 else
7887 {
7888 at_en.ae_attr = sgp->sg_cterm;
7889 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
7890 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
7891 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
7892 }
7893}
7894
7895/*
7896 * Lookup a highlight group name and return it's ID.
7897 * If it is not found, 0 is returned.
7898 */
7899 int
7900syn_name2id(name)
7901 char_u *name;
7902{
7903 int i;
7904 char_u name_u[200];
7905
7906 /* Avoid using stricmp() too much, it's slow on some systems */
7907 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
7908 * don't deserve to be found! */
7909 STRNCPY(name_u, name, 199);
7910 name_u[199] = NUL;
7911 vim_strup(name_u);
7912 for (i = highlight_ga.ga_len; --i >= 0; )
7913 if (HL_TABLE()[i].sg_name_u != NULL
7914 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
7915 break;
7916 return i + 1;
7917}
7918
7919#if defined(FEAT_EVAL) || defined(PROTO)
7920/*
7921 * Return TRUE if highlight group "name" exists.
7922 */
7923 int
7924highlight_exists(name)
7925 char_u *name;
7926{
7927 return (syn_name2id(name) > 0);
7928}
7929#endif
7930
7931/*
7932 * Like syn_name2id(), but take a pointer + length argument.
7933 */
7934 int
7935syn_namen2id(linep, len)
7936 char_u *linep;
7937 int len;
7938{
7939 char_u *name;
7940 int id = 0;
7941
7942 name = vim_strnsave(linep, len);
7943 if (name != NULL)
7944 {
7945 id = syn_name2id(name);
7946 vim_free(name);
7947 }
7948 return id;
7949}
7950
7951/*
7952 * Find highlight group name in the table and return it's ID.
7953 * The argument is a pointer to the name and the length of the name.
7954 * If it doesn't exist yet, a new entry is created.
7955 * Return 0 for failure.
7956 */
7957 int
7958syn_check_group(pp, len)
7959 char_u *pp;
7960 int len;
7961{
7962 int id;
7963 char_u *name;
7964
7965 name = vim_strnsave(pp, len);
7966 if (name == NULL)
7967 return 0;
7968
7969 id = syn_name2id(name);
7970 if (id == 0) /* doesn't exist yet */
7971 id = syn_add_group(name);
7972 else
7973 vim_free(name);
7974 return id;
7975}
7976
7977/*
7978 * Add new highlight group and return it's ID.
7979 * "name" must be an allocated string, it will be consumed.
7980 * Return 0 for failure.
7981 */
7982 static int
7983syn_add_group(name)
7984 char_u *name;
7985{
7986 char_u *p;
7987
7988 /* Check that the name is ASCII letters, digits and underscore. */
7989 for (p = name; *p != NUL; ++p)
7990 {
7991 if (!vim_isprintc(*p))
7992 {
7993 EMSG(_("E669: Unprintable character in group name"));
7994 return 0;
7995 }
7996 else if (!ASCII_ISALNUM(*p) && *p != '_')
7997 {
7998 /* This is an error, but since there previously was no check only
7999 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008000 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008001 MSG(_("W18: Invalid character in group name"));
8002 break;
8003 }
8004 }
8005
8006 /*
8007 * First call for this growarray: init growing array.
8008 */
8009 if (highlight_ga.ga_data == NULL)
8010 {
8011 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8012 highlight_ga.ga_growsize = 10;
8013 }
8014
8015 /*
8016 * Make room for at least one other syntax_highlight entry.
8017 */
8018 if (ga_grow(&highlight_ga, 1) == FAIL)
8019 {
8020 vim_free(name);
8021 return 0;
8022 }
8023
8024 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8025 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8026 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8027#ifdef FEAT_GUI
8028 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8029 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008030 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008031#endif
8032 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008033
8034 return highlight_ga.ga_len; /* ID is index plus one */
8035}
8036
8037/*
8038 * When, just after calling syn_add_group(), an error is discovered, this
8039 * function deletes the new name.
8040 */
8041 static void
8042syn_unadd_group()
8043{
8044 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008045 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8046 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8047}
8048
8049/*
8050 * Translate a group ID to highlight attributes.
8051 */
8052 int
8053syn_id2attr(hl_id)
8054 int hl_id;
8055{
8056 int attr;
8057 struct hl_group *sgp;
8058
8059 hl_id = syn_get_final_id(hl_id);
8060 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8061
8062#ifdef FEAT_GUI
8063 /*
8064 * Only use GUI attr when the GUI is being used.
8065 */
8066 if (gui.in_use)
8067 attr = sgp->sg_gui_attr;
8068 else
8069#endif
8070 if (t_colors > 1)
8071 attr = sgp->sg_cterm_attr;
8072 else
8073 attr = sgp->sg_term_attr;
8074
8075 return attr;
8076}
8077
8078#ifdef FEAT_GUI
8079/*
8080 * Get the GUI colors and attributes for a group ID.
8081 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8082 */
8083 int
8084syn_id2colors(hl_id, fgp, bgp)
8085 int hl_id;
8086 guicolor_T *fgp;
8087 guicolor_T *bgp;
8088{
8089 struct hl_group *sgp;
8090
8091 hl_id = syn_get_final_id(hl_id);
8092 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8093
8094 *fgp = sgp->sg_gui_fg;
8095 *bgp = sgp->sg_gui_bg;
8096 return sgp->sg_gui;
8097}
8098#endif
8099
8100/*
8101 * Translate a group ID to the final group ID (following links).
8102 */
8103 int
8104syn_get_final_id(hl_id)
8105 int hl_id;
8106{
8107 int count;
8108 struct hl_group *sgp;
8109
8110 if (hl_id > highlight_ga.ga_len || hl_id < 1)
8111 return 0; /* Can be called from eval!! */
8112
8113 /*
8114 * Follow links until there is no more.
8115 * Look out for loops! Break after 100 links.
8116 */
8117 for (count = 100; --count >= 0; )
8118 {
8119 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8120 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8121 break;
8122 hl_id = sgp->sg_link;
8123 }
8124
8125 return hl_id;
8126}
8127
8128#ifdef FEAT_GUI
8129/*
8130 * Call this function just after the GUI has started.
8131 * It finds the font and color handles for the highlighting groups.
8132 */
8133 void
8134highlight_gui_started()
8135{
8136 int idx;
8137
8138 /* First get the colors from the "Normal" and "Menu" group, if set */
8139 set_normal_colors();
8140
8141 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8142 gui_do_one_color(idx, FALSE, FALSE);
8143
8144 highlight_changed();
8145}
8146
8147 static void
8148gui_do_one_color(idx, do_menu, do_tooltip)
8149 int idx;
8150 int do_menu; /* TRUE: might set the menu font */
8151 int do_tooltip; /* TRUE: might set the tooltip font */
8152{
8153 int didit = FALSE;
8154
8155 if (HL_TABLE()[idx].sg_font_name != NULL)
8156 {
8157 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8158 do_tooltip);
8159 didit = TRUE;
8160 }
8161 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8162 {
8163 HL_TABLE()[idx].sg_gui_fg =
8164 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8165 didit = TRUE;
8166 }
8167 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8168 {
8169 HL_TABLE()[idx].sg_gui_bg =
8170 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8171 didit = TRUE;
8172 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008173 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8174 {
8175 HL_TABLE()[idx].sg_gui_sp =
8176 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8177 didit = TRUE;
8178 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008179 if (didit) /* need to get a new attr number */
8180 set_hl_attr(idx);
8181}
8182
8183#endif
8184
8185/*
8186 * Translate the 'highlight' option into attributes in highlight_attr[] and
8187 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
8188 * corresponding highlights to use on top of HLF_SNC is computed.
8189 * Called only when the 'highlight' option has been changed and upon first
8190 * screen redraw after any :highlight command.
8191 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
8192 */
8193 int
8194highlight_changed()
8195{
8196 int hlf;
8197 int i;
8198 char_u *p;
8199 int attr;
8200 char_u *end;
8201 int id;
8202#ifdef USER_HIGHLIGHT
8203 char_u userhl[10];
8204# ifdef FEAT_STL_OPT
8205 int id_SNC = -1;
8206 int id_S = -1;
8207 int hlcnt;
8208# endif
8209#endif
8210 static int hl_flags[HLF_COUNT] = HL_FLAGS;
8211
8212 need_highlight_changed = FALSE;
8213
8214 /*
8215 * Clear all attributes.
8216 */
8217 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8218 highlight_attr[hlf] = 0;
8219
8220 /*
8221 * First set all attributes to their default value.
8222 * Then use the attributes from the 'highlight' option.
8223 */
8224 for (i = 0; i < 2; ++i)
8225 {
8226 if (i)
8227 p = p_hl;
8228 else
8229 p = get_highlight_default();
8230 if (p == NULL) /* just in case */
8231 continue;
8232
8233 while (*p)
8234 {
8235 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8236 if (hl_flags[hlf] == *p)
8237 break;
8238 ++p;
8239 if (hlf == (int)HLF_COUNT || *p == NUL)
8240 return FAIL;
8241
8242 /*
8243 * Allow several hl_flags to be combined, like "bu" for
8244 * bold-underlined.
8245 */
8246 attr = 0;
8247 for ( ; *p && *p != ','; ++p) /* parse upto comma */
8248 {
8249 if (vim_iswhite(*p)) /* ignore white space */
8250 continue;
8251
8252 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
8253 return FAIL;
8254
8255 switch (*p)
8256 {
8257 case 'b': attr |= HL_BOLD;
8258 break;
8259 case 'i': attr |= HL_ITALIC;
8260 break;
8261 case '-':
8262 case 'n': /* no highlighting */
8263 break;
8264 case 'r': attr |= HL_INVERSE;
8265 break;
8266 case 's': attr |= HL_STANDOUT;
8267 break;
8268 case 'u': attr |= HL_UNDERLINE;
8269 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008270 case 'c': attr |= HL_UNDERCURL;
8271 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008272 case ':': ++p; /* highlight group name */
8273 if (attr || *p == NUL) /* no combinations */
8274 return FAIL;
8275 end = vim_strchr(p, ',');
8276 if (end == NULL)
8277 end = p + STRLEN(p);
8278 id = syn_check_group(p, (int)(end - p));
8279 if (id == 0)
8280 return FAIL;
8281 attr = syn_id2attr(id);
8282 p = end - 1;
8283#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8284 if (hlf == (int)HLF_SNC)
8285 id_SNC = syn_get_final_id(id);
8286 else if (hlf == (int)HLF_S)
8287 id_S = syn_get_final_id(id);
8288#endif
8289 break;
8290 default: return FAIL;
8291 }
8292 }
8293 highlight_attr[hlf] = attr;
8294
8295 p = skip_to_option_part(p); /* skip comma and spaces */
8296 }
8297 }
8298
8299#ifdef USER_HIGHLIGHT
8300 /* Setup the user highlights
8301 *
8302 * Temporarily utilize 10 more hl entries. Have to be in there
8303 * simultaneously in case of table overflows in get_attr_entry()
8304 */
8305# ifdef FEAT_STL_OPT
8306 if (ga_grow(&highlight_ga, 10) == FAIL)
8307 return FAIL;
8308 hlcnt = highlight_ga.ga_len;
8309 if (id_S == 0)
8310 { /* Make sure id_S is always valid to simplify code below */
8311 memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8312 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8313 id_S = hlcnt + 10;
8314 }
8315# endif
8316 for (i = 0; i < 9; i++)
8317 {
8318 sprintf((char *)userhl, "User%d", i + 1);
8319 id = syn_name2id(userhl);
8320 if (id == 0)
8321 {
8322 highlight_user[i] = 0;
8323# ifdef FEAT_STL_OPT
8324 highlight_stlnc[i] = 0;
8325# endif
8326 }
8327 else
8328 {
8329# ifdef FEAT_STL_OPT
8330 struct hl_group *hlt = HL_TABLE();
8331# endif
8332
8333 highlight_user[i] = syn_id2attr(id);
8334# ifdef FEAT_STL_OPT
8335 if (id_SNC == 0)
8336 {
8337 memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8338 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8339 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8340# ifdef FEAT_GUI
8341 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8342# endif
8343 }
8344 else
8345 mch_memmove(&hlt[hlcnt + i],
8346 &hlt[id_SNC - 1],
8347 sizeof(struct hl_group));
8348 hlt[hlcnt + i].sg_link = 0;
8349
8350 /* Apply difference between UserX and HLF_S to HLF_SNC */
8351 hlt[hlcnt + i].sg_term ^=
8352 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8353 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8354 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8355 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8356 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8357 hlt[hlcnt + i].sg_cterm ^=
8358 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8359 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8360 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8361 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8362 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8363# ifdef FEAT_GUI
8364 hlt[hlcnt + i].sg_gui ^=
8365 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8366 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8367 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8368 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8369 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008370 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8371 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008372 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8373 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8374# ifdef FEAT_XFONTSET
8375 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8376 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8377# endif
8378# endif
8379 highlight_ga.ga_len = hlcnt + i + 1;
8380 set_hl_attr(hlcnt + i); /* At long last we can apply */
8381 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8382# endif
8383 }
8384 }
8385# ifdef FEAT_STL_OPT
8386 highlight_ga.ga_len = hlcnt;
8387# endif
8388
8389#endif /* USER_HIGHLIGHT */
8390
8391 return OK;
8392}
8393
8394#ifdef FEAT_CMDL_COMPL
8395
8396static void highlight_list __ARGS((void));
8397static void highlight_list_two __ARGS((int cnt, int attr));
8398
8399/*
8400 * Handle command line completion for :highlight command.
8401 */
8402 void
8403set_context_in_highlight_cmd(xp, arg)
8404 expand_T *xp;
8405 char_u *arg;
8406{
8407 char_u *p;
8408
8409 /* Default: expand group names */
8410 xp->xp_context = EXPAND_HIGHLIGHT;
8411 xp->xp_pattern = arg;
8412 include_link = TRUE;
8413 include_default = TRUE;
8414
8415 /* (part of) subcommand already typed */
8416 if (*arg != NUL)
8417 {
8418 p = skiptowhite(arg);
8419 if (*p != NUL) /* past "default" or group name */
8420 {
8421 include_default = FALSE;
8422 if (STRNCMP("default", arg, p - arg) == 0)
8423 {
8424 arg = skipwhite(p);
8425 xp->xp_pattern = arg;
8426 p = skiptowhite(arg);
8427 }
8428 if (*p != NUL) /* past group name */
8429 {
8430 include_link = FALSE;
8431 if (arg[1] == 'i' && arg[0] == 'N')
8432 highlight_list();
8433 if (STRNCMP("link", arg, p - arg) == 0
8434 || STRNCMP("clear", arg, p - arg) == 0)
8435 {
8436 xp->xp_pattern = skipwhite(p);
8437 p = skiptowhite(xp->xp_pattern);
8438 if (*p != NUL) /* past first group name */
8439 {
8440 xp->xp_pattern = skipwhite(p);
8441 p = skiptowhite(xp->xp_pattern);
8442 }
8443 }
8444 if (*p != NUL) /* past group name(s) */
8445 xp->xp_context = EXPAND_NOTHING;
8446 }
8447 }
8448 }
8449}
8450
8451/*
8452 * List highlighting matches in a nice way.
8453 */
8454 static void
8455highlight_list()
8456{
8457 int i;
8458
8459 for (i = 10; --i >= 0; )
8460 highlight_list_two(i, hl_attr(HLF_D));
8461 for (i = 40; --i >= 0; )
8462 highlight_list_two(99, 0);
8463}
8464
8465 static void
8466highlight_list_two(cnt, attr)
8467 int cnt;
8468 int attr;
8469{
8470 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
8471 msg_clr_eos();
8472 out_flush();
8473 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
8474}
8475
8476#endif /* FEAT_CMDL_COMPL */
8477
8478#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
8479 || defined(FEAT_SIGNS) || defined(PROTO)
8480/*
8481 * Function given to ExpandGeneric() to obtain the list of group names.
8482 * Also used for synIDattr() function.
8483 */
8484/*ARGSUSED*/
8485 char_u *
8486get_highlight_name(xp, idx)
8487 expand_T *xp;
8488 int idx;
8489{
8490 if (idx == highlight_ga.ga_len
8491#ifdef FEAT_CMDL_COMPL
8492 && include_link
8493#endif
8494 )
8495 return (char_u *)"link";
8496 if (idx == highlight_ga.ga_len + 1
8497#ifdef FEAT_CMDL_COMPL
8498 && include_link
8499#endif
8500 )
8501 return (char_u *)"clear";
8502 if (idx == highlight_ga.ga_len + 2
8503#ifdef FEAT_CMDL_COMPL
8504 && include_default
8505#endif
8506 )
8507 return (char_u *)"default";
8508 if (idx < 0 || idx >= highlight_ga.ga_len)
8509 return NULL;
8510 return HL_TABLE()[idx].sg_name;
8511}
8512#endif
8513
8514#ifdef FEAT_GUI
8515/*
8516 * Free all the highlight group fonts.
8517 * Used when quitting for systems which need it.
8518 */
8519 void
8520free_highlight_fonts()
8521{
8522 int idx;
8523
8524 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8525 {
8526 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8527 HL_TABLE()[idx].sg_font = NOFONT;
8528# ifdef FEAT_XFONTSET
8529 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8530 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8531# endif
8532 }
8533
8534 gui_mch_free_font(gui.norm_font);
8535# ifdef FEAT_XFONTSET
8536 gui_mch_free_fontset(gui.fontset);
8537# endif
8538# ifndef HAVE_GTK2
8539 gui_mch_free_font(gui.bold_font);
8540 gui_mch_free_font(gui.ital_font);
8541 gui_mch_free_font(gui.boldital_font);
8542# endif
8543}
8544#endif
8545
8546/**************************************
8547 * End of Highlighting stuff *
8548 **************************************/