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