blob: 662b8a59c5defcb84eb25bbf2e8b4173225055c5 [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
2205 /* Always do spelling if there is no @Spell cluster. */
2206 if (syn_buf->b_spell_cluster_id == 0)
2207 *can_spell = TRUE;
2208 else if (current_trans_id == 0)
2209 *can_spell = FALSE;
2210 else
2211 {
2212 sps.inc_tag = 0;
2213 sps.id = syn_buf->b_spell_cluster_id;
2214 sps.cont_in_list = NULL;
2215 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2216 }
2217 }
2218
2219
Bram Moolenaar071d4272004-06-13 20:20:40 +00002220 /*
2221 * Check for end of current state (and the states before it) at the
2222 * next column. Don't do this for syncing, because we would miss a
2223 * single character match.
2224 * First check if the current state ends at the current column. It
2225 * may be for an empty match and a containing item might end in the
2226 * current column.
2227 */
2228 if (!syncing)
2229 {
2230 check_state_ends();
2231 if (current_state.ga_len > 0)
2232 {
2233 ++current_col;
2234 check_state_ends();
2235 --current_col;
2236 }
2237 }
2238 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002239 else if (can_spell != NULL)
2240 /* Only do spelling when there is no @Spell cluster. */
2241 *can_spell = (syn_buf->b_spell_cluster_id == 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002242
2243 /* nextgroup ends at end of line, unless "skipnl" or "skipemtpy" present */
2244 if (current_next_list != NULL
2245 && syn_getcurline()[current_col + 1] == NUL
2246 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2247 current_next_list = NULL;
2248
2249 if (zero_width_next_ga.ga_len > 0)
2250 ga_clear(&zero_width_next_ga);
2251
2252 /* No longer need external matches. But keep next_match_extmatch. */
2253 unref_extmatch(re_extmatch_out);
2254 re_extmatch_out = NULL;
2255 unref_extmatch(cur_extmatch);
2256
2257 return current_attr;
2258}
2259
2260
2261/*
2262 * Check if we already matched pattern "idx" at the current column.
2263 */
2264 static int
2265did_match_already(idx, gap)
2266 int idx;
2267 garray_T *gap;
2268{
2269 int i;
2270
2271 for (i = current_state.ga_len; --i >= 0; )
2272 if (CUR_STATE(i).si_m_startcol == (int)current_col
2273 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2274 && CUR_STATE(i).si_idx == idx)
2275 return TRUE;
2276
2277 /* Zero-width matches with a nextgroup argument are not put on the syntax
2278 * stack, and can only be matched once anyway. */
2279 for (i = gap->ga_len; --i >= 0; )
2280 if (((int *)(gap->ga_data))[i] == idx)
2281 return TRUE;
2282
2283 return FALSE;
2284}
2285
2286/*
2287 * Push the next match onto the stack.
2288 */
2289 static stateitem_T *
2290push_next_match(cur_si)
2291 stateitem_T *cur_si;
2292{
2293 synpat_T *spp;
2294
2295 spp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2296
2297 /*
2298 * Push the item in current_state stack;
2299 */
2300 if (push_current_state(next_match_idx) == OK)
2301 {
2302 /*
2303 * If it's a start-skip-end type that crosses lines, figure out how
2304 * much it continues in this line. Otherwise just fill in the length.
2305 */
2306 cur_si = &CUR_STATE(current_state.ga_len - 1);
2307 cur_si->si_h_startpos = next_match_h_startpos;
2308 cur_si->si_m_startcol = current_col;
2309 cur_si->si_m_lnum = current_lnum;
2310 cur_si->si_flags = spp->sp_flags;
2311 cur_si->si_next_list = spp->sp_next_list;
2312 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2313 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2314 {
2315 /* Try to find the end pattern in the current line */
2316 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2317 check_keepend();
2318 }
2319 else
2320 {
2321 cur_si->si_m_endpos = next_match_m_endpos;
2322 cur_si->si_h_endpos = next_match_h_endpos;
2323 cur_si->si_ends = TRUE;
2324 cur_si->si_flags |= next_match_flags;
2325 cur_si->si_eoe_pos = next_match_eoe_pos;
2326 cur_si->si_end_idx = next_match_end_idx;
2327 }
2328 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2329 keepend_level = current_state.ga_len - 1;
2330 check_keepend();
2331 update_si_attr(current_state.ga_len - 1);
2332
2333 /*
2334 * If the start pattern has another highlight group, push another item
2335 * on the stack for the start pattern.
2336 */
2337 if ( spp->sp_type == SPTYPE_START
2338 && spp->sp_syn_match_id != 0
2339 && push_current_state(next_match_idx) == OK)
2340 {
2341 cur_si = &CUR_STATE(current_state.ga_len - 1);
2342 cur_si->si_h_startpos = next_match_h_startpos;
2343 cur_si->si_m_startcol = current_col;
2344 cur_si->si_m_lnum = current_lnum;
2345 cur_si->si_m_endpos = next_match_eos_pos;
2346 cur_si->si_h_endpos = next_match_eos_pos;
2347 cur_si->si_ends = TRUE;
2348 cur_si->si_end_idx = 0;
2349 cur_si->si_flags = HL_MATCH;
2350 cur_si->si_next_list = NULL;
2351 check_keepend();
2352 update_si_attr(current_state.ga_len - 1);
2353 }
2354 }
2355
2356 next_match_idx = -1; /* try other match next time */
2357
2358 return cur_si;
2359}
2360
2361/*
2362 * Check for end of current state (and the states before it).
2363 */
2364 static void
2365check_state_ends()
2366{
2367 stateitem_T *cur_si;
2368 int had_extend = FALSE;
2369
2370 cur_si = &CUR_STATE(current_state.ga_len - 1);
2371 for (;;)
2372 {
2373 if (cur_si->si_ends
2374 && (cur_si->si_m_endpos.lnum < current_lnum
2375 || (cur_si->si_m_endpos.lnum == current_lnum
2376 && cur_si->si_m_endpos.col <= current_col)))
2377 {
2378 /*
2379 * If there is an end pattern group ID, highlight the end pattern
2380 * now. No need to pop the current item from the stack.
2381 * Only do this if the end pattern continues beyond the current
2382 * position.
2383 */
2384 if (cur_si->si_end_idx
2385 && (cur_si->si_eoe_pos.lnum > current_lnum
2386 || (cur_si->si_eoe_pos.lnum == current_lnum
2387 && cur_si->si_eoe_pos.col > current_col)))
2388 {
2389 cur_si->si_idx = cur_si->si_end_idx;
2390 cur_si->si_end_idx = 0;
2391 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2392 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2393 cur_si->si_flags |= HL_MATCH;
2394 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002395
2396 /* what matches next may be different now, clear it */
2397 next_match_idx = 0;
2398 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002399 break;
2400 }
2401 else
2402 {
2403 /* handle next_list, unless at end of line and no "skipnl" or
2404 * "skipempty" */
2405 current_next_list = cur_si->si_next_list;
2406 current_next_flags = cur_si->si_flags;
2407 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2408 && syn_getcurline()[current_col] == NUL)
2409 current_next_list = NULL;
2410
2411 /* When the ended item has "extend", another item with
2412 * "keepend" now needs to check for its end. */
2413 if (cur_si->si_flags & HL_EXTEND)
2414 had_extend = TRUE;
2415
2416 pop_current_state();
2417
2418 if (current_state.ga_len == 0)
2419 break;
2420
2421 if (had_extend)
2422 {
2423 syn_update_ends(FALSE);
2424 if (current_state.ga_len == 0)
2425 break;
2426 }
2427
2428 cur_si = &CUR_STATE(current_state.ga_len - 1);
2429
2430 /*
2431 * Only for a region the search for the end continues after
2432 * the end of the contained item. If the contained match
2433 * included the end-of-line, break here, the region continues.
2434 * Don't do this when:
2435 * - "keepend" is used for the contained item
2436 * - not at the end of the line (could be end="x$"me=e-1).
2437 * - "excludenl" is used (HL_HAS_EOL won't be set)
2438 */
2439 if (cur_si->si_idx >= 0
2440 && SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_type
2441 == SPTYPE_START
2442 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2443 {
2444 update_si_end(cur_si, (int)current_col, TRUE);
2445 check_keepend();
2446 if ((current_next_flags & HL_HAS_EOL)
2447 && keepend_level < 0
2448 && syn_getcurline()[current_col] == NUL)
2449 break;
2450 }
2451 }
2452 }
2453 else
2454 break;
2455 }
2456}
2457
2458/*
2459 * Update an entry in the current_state stack for a match or region. This
2460 * fills in si_attr, si_next_list and si_cont_list.
2461 */
2462 static void
2463update_si_attr(idx)
2464 int idx;
2465{
2466 stateitem_T *sip = &CUR_STATE(idx);
2467 synpat_T *spp;
2468
2469 spp = &(SYN_ITEMS(syn_buf)[sip->si_idx]);
2470 if (sip->si_flags & HL_MATCH)
2471 sip->si_id = spp->sp_syn_match_id;
2472 else
2473 sip->si_id = spp->sp_syn.id;
2474 sip->si_attr = syn_id2attr(sip->si_id);
2475 sip->si_trans_id = sip->si_id;
2476 if (sip->si_flags & HL_MATCH)
2477 sip->si_cont_list = NULL;
2478 else
2479 sip->si_cont_list = spp->sp_cont_list;
2480
2481 /*
2482 * For transparent items, take attr from outer item.
2483 * Also take cont_list, if there is none.
2484 * Don't do this for the matchgroup of a start or end pattern.
2485 */
2486 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2487 {
2488 if (idx == 0)
2489 {
2490 sip->si_attr = 0;
2491 sip->si_trans_id = 0;
2492 if (sip->si_cont_list == NULL)
2493 sip->si_cont_list = ID_LIST_ALL;
2494 }
2495 else
2496 {
2497 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2498 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002499 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2500 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002501 if (sip->si_cont_list == NULL)
2502 {
2503 sip->si_flags |= HL_TRANS_CONT;
2504 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2505 }
2506 }
2507 }
2508}
2509
2510/*
2511 * Check the current stack for patterns with "keepend" flag.
2512 * Propagate the match-end to contained items, until a "skipend" item is found.
2513 */
2514 static void
2515check_keepend()
2516{
2517 int i;
2518 lpos_T maxpos;
2519 stateitem_T *sip;
2520
2521 /*
2522 * This check can consume a lot of time; only do it from the level where
2523 * there really is a keepend.
2524 */
2525 if (keepend_level < 0)
2526 return;
2527
2528 /*
2529 * Find the last index of an "extend" item. "keepend" items before that
2530 * won't do anything. If there is no "extend" item "i" will be
2531 * "keepend_level" and all "keepend" items will work normally.
2532 */
2533 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2534 if (CUR_STATE(i).si_flags & HL_EXTEND)
2535 break;
2536
2537 maxpos.lnum = 0;
2538 for ( ; i < current_state.ga_len; ++i)
2539 {
2540 sip = &CUR_STATE(i);
2541 if (maxpos.lnum != 0)
2542 {
2543 limit_pos_zero(&sip->si_m_endpos, &maxpos);
2544 limit_pos_zero(&sip->si_h_endpos, &maxpos);
2545 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2546 sip->si_ends = TRUE;
2547 }
2548 if (sip->si_ends
2549 && (sip->si_flags & HL_KEEPEND)
2550 && (maxpos.lnum == 0
2551 || maxpos.lnum > sip->si_m_endpos.lnum
2552 || (maxpos.lnum == sip->si_m_endpos.lnum
2553 && maxpos.col > sip->si_m_endpos.col)))
2554 maxpos = sip->si_m_endpos;
2555 }
2556}
2557
2558/*
2559 * Update an entry in the current_state stack for a start-skip-end pattern.
2560 * This finds the end of the current item, if it's in the current line.
2561 *
2562 * Return the flags for the matched END.
2563 */
2564 static void
2565update_si_end(sip, startcol, force)
2566 stateitem_T *sip;
2567 int startcol; /* where to start searching for the end */
2568 int force; /* when TRUE overrule a previous end */
2569{
2570 lpos_T startpos;
2571 lpos_T endpos;
2572 lpos_T hl_endpos;
2573 lpos_T end_endpos;
2574 int end_idx;
2575
2576 /* Don't update when it's already done. Can be a match of an end pattern
2577 * that started in a previous line. Watch out: can also be a "keepend"
2578 * from a containing item. */
2579 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2580 return;
2581
2582 /*
2583 * We need to find the end of the region. It may continue in the next
2584 * line.
2585 */
2586 end_idx = 0;
2587 startpos.lnum = current_lnum;
2588 startpos.col = startcol;
2589 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2590 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2591
2592 if (endpos.lnum == 0)
2593 {
2594 /* No end pattern matched. */
2595 if (SYN_ITEMS(syn_buf)[sip->si_idx].sp_flags & HL_ONELINE)
2596 {
2597 /* a "oneline" never continues in the next line */
2598 sip->si_ends = TRUE;
2599 sip->si_m_endpos.lnum = current_lnum;
2600 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2601 }
2602 else
2603 {
2604 /* continues in the next line */
2605 sip->si_ends = FALSE;
2606 sip->si_m_endpos.lnum = 0;
2607 }
2608 sip->si_h_endpos = sip->si_m_endpos;
2609 }
2610 else
2611 {
2612 /* match within this line */
2613 sip->si_m_endpos = endpos;
2614 sip->si_h_endpos = hl_endpos;
2615 sip->si_eoe_pos = end_endpos;
2616 sip->si_ends = TRUE;
2617 sip->si_end_idx = end_idx;
2618 }
2619}
2620
2621/*
2622 * Add a new state to the current state stack.
2623 * It is cleared and the index set to "idx".
2624 * Return FAIL if it's not possible (out of memory).
2625 */
2626 static int
2627push_current_state(idx)
2628 int idx;
2629{
2630 if (ga_grow(&current_state, 1) == FAIL)
2631 return FAIL;
2632 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2633 CUR_STATE(current_state.ga_len).si_idx = idx;
2634 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002635 return OK;
2636}
2637
2638/*
2639 * Remove a state from the current_state stack.
2640 */
2641 static void
2642pop_current_state()
2643{
2644 if (current_state.ga_len)
2645 {
2646 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2647 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002648 }
2649 /* after the end of a pattern, try matching a keyword or pattern */
2650 next_match_idx = -1;
2651
2652 /* if first state with "keepend" is popped, reset keepend_level */
2653 if (keepend_level >= current_state.ga_len)
2654 keepend_level = -1;
2655}
2656
2657/*
2658 * Find the end of a start/skip/end syntax region after "startpos".
2659 * Only checks one line.
2660 * Also handles a match item that continued from a previous line.
2661 * If not found, the syntax item continues in the next line. m_endpos->lnum
2662 * will be 0.
2663 * If found, the end of the region and the end of the highlighting is
2664 * computed.
2665 */
2666 static void
2667find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2668 end_idx, start_ext)
2669 int idx; /* index of the pattern */
2670 lpos_T *startpos; /* where to start looking for an END match */
2671 lpos_T *m_endpos; /* return: end of match */
2672 lpos_T *hl_endpos; /* return: end of highlighting */
2673 long *flagsp; /* return: flags of matching END */
2674 lpos_T *end_endpos; /* return: end of end pattern match */
2675 int *end_idx; /* return: group ID for end pat. match, or 0 */
2676 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2677{
2678 colnr_T matchcol;
2679 synpat_T *spp, *spp_skip;
2680 int start_idx;
2681 int best_idx;
2682 regmmatch_T regmatch;
2683 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2684 lpos_T pos;
2685 char_u *line;
2686 int had_match = FALSE;
2687
2688 /*
2689 * Check for being called with a START pattern.
2690 * Can happen with a match that continues to the next line, because it
2691 * contained a region.
2692 */
2693 spp = &(SYN_ITEMS(syn_buf)[idx]);
2694 if (spp->sp_type != SPTYPE_START)
2695 {
2696 *hl_endpos = *startpos;
2697 return;
2698 }
2699
2700 /*
2701 * Find the SKIP or first END pattern after the last START pattern.
2702 */
2703 for (;;)
2704 {
2705 spp = &(SYN_ITEMS(syn_buf)[idx]);
2706 if (spp->sp_type != SPTYPE_START)
2707 break;
2708 ++idx;
2709 }
2710
2711 /*
2712 * Lookup the SKIP pattern (if present)
2713 */
2714 if (spp->sp_type == SPTYPE_SKIP)
2715 {
2716 spp_skip = spp;
2717 ++idx;
2718 }
2719 else
2720 spp_skip = NULL;
2721
2722 /* Setup external matches for syn_regexec(). */
2723 unref_extmatch(re_extmatch_in);
2724 re_extmatch_in = ref_extmatch(start_ext);
2725
2726 matchcol = startpos->col; /* start looking for a match at sstart */
2727 start_idx = idx; /* remember the first END pattern. */
2728 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2729 for (;;)
2730 {
2731 /*
2732 * Find end pattern that matches first after "matchcol".
2733 */
2734 best_idx = -1;
2735 for (idx = start_idx; idx < syn_buf->b_syn_patterns.ga_len; ++idx)
2736 {
2737 int lc_col = matchcol;
2738
2739 spp = &(SYN_ITEMS(syn_buf)[idx]);
2740 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2741 break;
2742 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2743 if (lc_col < 0)
2744 lc_col = 0;
2745
2746 regmatch.rmm_ic = spp->sp_ic;
2747 regmatch.regprog = spp->sp_prog;
2748 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2749 {
2750 if (best_idx == -1 || regmatch.startpos[0].col
2751 < best_regmatch.startpos[0].col)
2752 {
2753 best_idx = idx;
2754 best_regmatch.startpos[0] = regmatch.startpos[0];
2755 best_regmatch.endpos[0] = regmatch.endpos[0];
2756 }
2757 }
2758 }
2759
2760 /*
2761 * If all end patterns have been tried, and there is no match, the
2762 * item continues until end-of-line.
2763 */
2764 if (best_idx == -1)
2765 break;
2766
2767 /*
2768 * If the skip pattern matches before the end pattern,
2769 * continue searching after the skip pattern.
2770 */
2771 if (spp_skip != NULL)
2772 {
2773 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2774
2775 if (lc_col < 0)
2776 lc_col = 0;
2777 regmatch.rmm_ic = spp_skip->sp_ic;
2778 regmatch.regprog = spp_skip->sp_prog;
2779 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2780 && regmatch.startpos[0].col
2781 <= best_regmatch.startpos[0].col)
2782 {
2783 /* Add offset to skip pattern match */
2784 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2785
2786 /* If the skip pattern goes on to the next line, there is no
2787 * match with an end pattern in this line. */
2788 if (pos.lnum > startpos->lnum)
2789 break;
2790
2791 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2792
2793 /* take care of an empty match or negative offset */
2794 if (pos.col <= matchcol)
2795 ++matchcol;
2796 else if (pos.col <= regmatch.endpos[0].col)
2797 matchcol = pos.col;
2798 else
2799 /* Be careful not to jump over the NUL at the end-of-line */
2800 for (matchcol = regmatch.endpos[0].col;
2801 line[matchcol] != NUL && matchcol < pos.col;
2802 ++matchcol)
2803 ;
2804
2805 /* if the skip pattern includes end-of-line, break here */
2806 if (line[matchcol] == NUL)
2807 break;
2808
2809 continue; /* start with first end pattern again */
2810 }
2811 }
2812
2813 /*
2814 * Match from start pattern to end pattern.
2815 * Correct for match and highlight offset of end pattern.
2816 */
2817 spp = &(SYN_ITEMS(syn_buf)[best_idx]);
2818 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2819 /* can't end before the start */
2820 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2821 m_endpos->col = startpos->col;
2822
2823 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2824 /* can't end before the start */
2825 if (end_endpos->lnum == startpos->lnum
2826 && end_endpos->col < startpos->col)
2827 end_endpos->col = startpos->col;
2828 /* can't end after the match */
2829 limit_pos(end_endpos, m_endpos);
2830
2831 /*
2832 * If the end group is highlighted differently, adjust the pointers.
2833 */
2834 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2835 {
2836 *end_idx = best_idx;
2837 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2838 {
2839 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2840 hl_endpos->col = best_regmatch.endpos[0].col;
2841 }
2842 else
2843 {
2844 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2845 hl_endpos->col = best_regmatch.startpos[0].col;
2846 }
2847 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2848
2849 /* can't end before the start */
2850 if (hl_endpos->lnum == startpos->lnum
2851 && hl_endpos->col < startpos->col)
2852 hl_endpos->col = startpos->col;
2853 limit_pos(hl_endpos, m_endpos);
2854
2855 /* now the match ends where the highlighting ends, it is turned
2856 * into the matchgroup for the end */
2857 *m_endpos = *hl_endpos;
2858 }
2859 else
2860 {
2861 *end_idx = 0;
2862 *hl_endpos = *end_endpos;
2863 }
2864
2865 *flagsp = spp->sp_flags;
2866
2867 had_match = TRUE;
2868 break;
2869 }
2870
2871 /* no match for an END pattern in this line */
2872 if (!had_match)
2873 m_endpos->lnum = 0;
2874
2875 /* Remove external matches. */
2876 unref_extmatch(re_extmatch_in);
2877 re_extmatch_in = NULL;
2878}
2879
2880/*
2881 * Limit "pos" not to be after "limit".
2882 */
2883 static void
2884limit_pos(pos, limit)
2885 lpos_T *pos;
2886 lpos_T *limit;
2887{
2888 if (pos->lnum > limit->lnum)
2889 *pos = *limit;
2890 else if (pos->lnum == limit->lnum && pos->col > limit->col)
2891 pos->col = limit->col;
2892}
2893
2894/*
2895 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
2896 */
2897 static void
2898limit_pos_zero(pos, limit)
2899 lpos_T *pos;
2900 lpos_T *limit;
2901{
2902 if (pos->lnum == 0)
2903 *pos = *limit;
2904 else
2905 limit_pos(pos, limit);
2906}
2907
2908/*
2909 * Add offset to matched text for end of match or highlight.
2910 */
2911 static void
2912syn_add_end_off(result, regmatch, spp, idx, extra)
2913 lpos_T *result; /* returned position */
2914 regmmatch_T *regmatch; /* start/end of match */
2915 synpat_T *spp; /* matched pattern */
2916 int idx; /* index of offset */
2917 int extra; /* extra chars for offset to start */
2918{
2919 int col;
2920
2921 if (spp->sp_off_flags & (1 << idx))
2922 {
2923 result->lnum = regmatch->startpos[0].lnum;
2924 col = regmatch->startpos[0].col + extra;
2925 }
2926 else
2927 {
2928 result->lnum = regmatch->endpos[0].lnum;
2929 col = regmatch->endpos[0].col;
2930 }
2931 col += spp->sp_offsets[idx];
2932 if (col < 0)
2933 result->col = 0;
2934 else
2935 result->col = col;
2936}
2937
2938/*
2939 * Add offset to matched text for start of match or highlight.
2940 * Avoid resulting column to become negative.
2941 */
2942 static void
2943syn_add_start_off(result, regmatch, spp, idx, extra)
2944 lpos_T *result; /* returned position */
2945 regmmatch_T *regmatch; /* start/end of match */
2946 synpat_T *spp;
2947 int idx;
2948 int extra; /* extra chars for offset to end */
2949{
2950 int col;
2951
2952 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
2953 {
2954 result->lnum = regmatch->endpos[0].lnum;
2955 col = regmatch->endpos[0].col + extra;
2956 }
2957 else
2958 {
2959 result->lnum = regmatch->startpos[0].lnum;
2960 col = regmatch->startpos[0].col;
2961 }
2962 col += spp->sp_offsets[idx];
2963 if (col < 0)
2964 result->col = 0;
2965 else
2966 result->col = col;
2967}
2968
2969/*
2970 * Get current line in syntax buffer.
2971 */
2972 static char_u *
2973syn_getcurline()
2974{
2975 return ml_get_buf(syn_buf, current_lnum, FALSE);
2976}
2977
2978/*
2979 * Call vim_regexec() to match in syn_buf.
2980 * Returns TRUE when there is a match.
2981 */
2982 static int
2983syn_regexec(rmp, lnum, col)
2984 regmmatch_T *rmp;
2985 linenr_T lnum;
2986 colnr_T col;
2987{
2988 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col) > 0)
2989 {
2990 rmp->startpos[0].lnum += lnum;
2991 rmp->endpos[0].lnum += lnum;
2992 return TRUE;
2993 }
2994 return FALSE;
2995}
2996
2997/*
2998 * Check one position in a line for a matching keyword.
2999 * The caller must check if a keyword can start at startcol.
3000 * Return it's ID if found, 0 otherwise.
3001 */
3002 static int
3003check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
3004 char_u *line;
3005 int startcol; /* position in line to check for keyword */
3006 int *endcolp; /* return: character after found keyword */
3007 long *flagsp; /* return: flags of matching keyword */
3008 short **next_listp; /* return: next_list of matching keyword */
3009 stateitem_T *cur_si; /* item at the top of the stack */
3010{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003011 keyentry_T *kp;
3012 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003013 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003014 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003015 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003016 hashtab_T *ht;
3017 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003018
3019 /* Find first character after the keyword. First character was already
3020 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003021 kwp = line + startcol;
3022 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003023 do
3024 {
3025#ifdef FEAT_MBYTE
3026 if (has_mbyte)
Bram Moolenaardad6b692005-01-25 22:14:34 +00003027 kwlen += (*mb_ptr2len_check)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003028 else
3029#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003030 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003031 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003032 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003033
Bram Moolenaardad6b692005-01-25 22:14:34 +00003034 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003035 return 0;
3036
3037 /*
3038 * Must make a copy of the keyword, so we can add a NUL and make it
3039 * lowercase.
3040 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003041 STRNCPY(keyword, kwp, kwlen);
3042 keyword[kwlen] = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003043
3044 /*
3045 * Try twice:
3046 * 1. matching case
3047 * 2. ignoring case
3048 */
3049 for (round = 1; round <= 2; ++round)
3050 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003051 ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
3052 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003053 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003054 if (round == 2) /* ignore case */
3055 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003056
3057 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003058 * Find keywords that match. There can be several with different
3059 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003060 * When current_next_list is non-zero accept only that group, otherwise:
3061 * Accept a not-contained keyword at toplevel.
3062 * Accept a keyword at other levels only if it is in the contains list.
3063 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003064 hi = hash_find(ht, keyword);
3065 if (!HASHITEM_EMPTY(hi))
3066 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003068 if (current_next_list != 0
3069 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3070 : (cur_si == NULL
3071 ? !(kp->flags & HL_CONTAINED)
3072 : in_id_list(cur_si, cur_si->si_cont_list,
3073 &kp->k_syn, kp->flags & HL_CONTAINED)))
3074 {
3075 *endcolp = startcol + kwlen;
3076 *flagsp = kp->flags;
3077 *next_listp = kp->next_list;
3078 return kp->k_syn.id;
3079 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080 }
3081 }
3082 return 0;
3083}
3084
3085/*
3086 * Handle ":syntax case" command.
3087 */
3088/* ARGSUSED */
3089 static void
3090syn_cmd_case(eap, syncing)
3091 exarg_T *eap;
3092 int syncing; /* not used */
3093{
3094 char_u *arg = eap->arg;
3095 char_u *next;
3096
3097 eap->nextcmd = find_nextcmd(arg);
3098 if (eap->skip)
3099 return;
3100
3101 next = skiptowhite(arg);
3102 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3103 curbuf->b_syn_ic = FALSE;
3104 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3105 curbuf->b_syn_ic = TRUE;
3106 else
3107 EMSG2(_("E390: Illegal argument: %s"), arg);
3108}
3109
3110/*
3111 * Clear all syntax info for one buffer.
3112 */
3113 void
3114syntax_clear(buf)
3115 buf_T *buf;
3116{
3117 int i;
3118
3119 curbuf->b_syn_ic = FALSE; /* Use case, by default */
3120 curbuf->b_syn_containedin = FALSE;
3121
3122 /* free the keywords */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003123 clear_keywtab(&buf->b_keywtab);
3124 clear_keywtab(&buf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003125
3126 /* free the syntax patterns */
3127 for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3128 syn_clear_pattern(buf, i);
3129 ga_clear(&buf->b_syn_patterns);
3130
3131 /* free the syntax clusters */
3132 for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3133 syn_clear_cluster(buf, i);
3134 ga_clear(&buf->b_syn_clusters);
3135
3136 buf->b_syn_sync_flags = 0;
3137 buf->b_syn_sync_minlines = 0;
3138 buf->b_syn_sync_maxlines = 0;
3139 buf->b_syn_sync_linebreaks = 0;
3140
3141 vim_free(buf->b_syn_linecont_prog);
3142 buf->b_syn_linecont_prog = NULL;
3143 vim_free(buf->b_syn_linecont_pat);
3144 buf->b_syn_linecont_pat = NULL;
3145#ifdef FEAT_FOLDING
3146 buf->b_syn_folditems = 0;
3147#endif
3148
3149 /* free the stored states */
3150 syn_stack_free_all(buf);
3151 invalidate_current_state();
3152}
3153
3154/*
3155 * Clear syncing info for one buffer.
3156 */
3157 static void
3158syntax_sync_clear()
3159{
3160 int i;
3161
3162 /* free the syntax patterns */
3163 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3164 if (SYN_ITEMS(curbuf)[i].sp_syncing)
3165 syn_remove_pattern(curbuf, i);
3166
3167 curbuf->b_syn_sync_flags = 0;
3168 curbuf->b_syn_sync_minlines = 0;
3169 curbuf->b_syn_sync_maxlines = 0;
3170 curbuf->b_syn_sync_linebreaks = 0;
3171
3172 vim_free(curbuf->b_syn_linecont_prog);
3173 curbuf->b_syn_linecont_prog = NULL;
3174 vim_free(curbuf->b_syn_linecont_pat);
3175 curbuf->b_syn_linecont_pat = NULL;
3176
3177 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3178}
3179
3180/*
3181 * Remove one pattern from the buffer's pattern list.
3182 */
3183 static void
3184syn_remove_pattern(buf, idx)
3185 buf_T *buf;
3186 int idx;
3187{
3188 synpat_T *spp;
3189
3190 spp = &(SYN_ITEMS(buf)[idx]);
3191#ifdef FEAT_FOLDING
3192 if (spp->sp_flags & HL_FOLD)
3193 --buf->b_syn_folditems;
3194#endif
3195 syn_clear_pattern(buf, idx);
3196 mch_memmove(spp, spp + 1,
3197 sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3198 --buf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003199}
3200
3201/*
3202 * Clear and free one syntax pattern. When clearing all, must be called from
3203 * last to first!
3204 */
3205 static void
3206syn_clear_pattern(buf, i)
3207 buf_T *buf;
3208 int i;
3209{
3210 vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3211 vim_free(SYN_ITEMS(buf)[i].sp_prog);
3212 /* Only free sp_cont_list and sp_next_list of first start pattern */
3213 if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3214 {
3215 vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3216 vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3217 }
3218}
3219
3220/*
3221 * Clear and free one syntax cluster.
3222 */
3223 static void
3224syn_clear_cluster(buf, i)
3225 buf_T *buf;
3226 int i;
3227{
3228 vim_free(SYN_CLSTR(buf)[i].scl_name);
3229 vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3230 vim_free(SYN_CLSTR(buf)[i].scl_list);
3231}
3232
3233/*
3234 * Handle ":syntax clear" command.
3235 */
3236 static void
3237syn_cmd_clear(eap, syncing)
3238 exarg_T *eap;
3239 int syncing;
3240{
3241 char_u *arg = eap->arg;
3242 char_u *arg_end;
3243 int id;
3244
3245 eap->nextcmd = find_nextcmd(arg);
3246 if (eap->skip)
3247 return;
3248
3249 /*
3250 * We have to disable this within ":syn include @group filename",
3251 * because otherwise @group would get deleted.
3252 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3253 * clear".
3254 */
3255 if (curbuf->b_syn_topgrp != 0)
3256 return;
3257
3258 if (ends_excmd(*arg))
3259 {
3260 /*
3261 * No argument: Clear all syntax items.
3262 */
3263 if (syncing)
3264 syntax_sync_clear();
3265 else
3266 {
3267 syntax_clear(curbuf);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003268 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003269 }
3270 }
3271 else
3272 {
3273 /*
3274 * Clear the group IDs that are in the argument.
3275 */
3276 while (!ends_excmd(*arg))
3277 {
3278 arg_end = skiptowhite(arg);
3279 if (*arg == '@')
3280 {
3281 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3282 if (id == 0)
3283 {
3284 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3285 break;
3286 }
3287 else
3288 {
3289 /*
3290 * We can't physically delete a cluster without changing
3291 * the IDs of other clusters, so we do the next best thing
3292 * and make it empty.
3293 */
3294 short scl_id = id - SYNID_CLUSTER;
3295
3296 vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3297 SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3298 }
3299 }
3300 else
3301 {
3302 id = syn_namen2id(arg, (int)(arg_end - arg));
3303 if (id == 0)
3304 {
3305 EMSG2(_(e_nogroup), arg);
3306 break;
3307 }
3308 else
3309 syn_clear_one(id, syncing);
3310 }
3311 arg = skipwhite(arg_end);
3312 }
3313 }
3314 redraw_curbuf_later(NOT_VALID);
3315 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3316}
3317
3318/*
3319 * Clear one syntax group for the current buffer.
3320 */
3321 static void
3322syn_clear_one(id, syncing)
3323 int id;
3324 int syncing;
3325{
3326 synpat_T *spp;
3327 int idx;
3328
3329 /* Clear keywords only when not ":syn sync clear group-name" */
3330 if (!syncing)
3331 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003332 (void)syn_clear_keyword(id, &curbuf->b_keywtab);
3333 (void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003334 }
3335
3336 /* clear the patterns for "id" */
3337 for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3338 {
3339 spp = &(SYN_ITEMS(curbuf)[idx]);
3340 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3341 continue;
3342 syn_remove_pattern(curbuf, idx);
3343 }
3344}
3345
3346/*
3347 * Handle ":syntax on" command.
3348 */
3349/* ARGSUSED */
3350 static void
3351syn_cmd_on(eap, syncing)
3352 exarg_T *eap;
3353 int syncing; /* not used */
3354{
3355 syn_cmd_onoff(eap, "syntax");
3356}
3357
3358/*
3359 * Handle ":syntax enable" command.
3360 */
3361/* ARGSUSED */
3362 static void
3363syn_cmd_enable(eap, syncing)
3364 exarg_T *eap;
3365 int syncing; /* not used */
3366{
3367 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3368 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003369 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370}
3371
3372/*
3373 * Handle ":syntax reset" command.
3374 */
3375/* ARGSUSED */
3376 static void
3377syn_cmd_reset(eap, syncing)
3378 exarg_T *eap;
3379 int syncing; /* not used */
3380{
3381 eap->nextcmd = check_nextcmd(eap->arg);
3382 if (!eap->skip)
3383 {
3384 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3385 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003386 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003387 }
3388}
3389
3390/*
3391 * Handle ":syntax manual" command.
3392 */
3393/* ARGSUSED */
3394 static void
3395syn_cmd_manual(eap, syncing)
3396 exarg_T *eap;
3397 int syncing; /* not used */
3398{
3399 syn_cmd_onoff(eap, "manual");
3400}
3401
3402/*
3403 * Handle ":syntax off" command.
3404 */
3405/* ARGSUSED */
3406 static void
3407syn_cmd_off(eap, syncing)
3408 exarg_T *eap;
3409 int syncing; /* not used */
3410{
3411 syn_cmd_onoff(eap, "nosyntax");
3412}
3413
3414 static void
3415syn_cmd_onoff(eap, name)
3416 exarg_T *eap;
3417 char *name;
3418{
3419 char_u buf[100];
3420
3421 eap->nextcmd = check_nextcmd(eap->arg);
3422 if (!eap->skip)
3423 {
3424 STRCPY(buf, "so ");
3425 sprintf((char *)buf + 3, SYNTAX_FNAME, name);
3426 do_cmdline_cmd(buf);
3427 }
3428}
3429
3430/*
3431 * Handle ":syntax [list]" command: list current syntax words.
3432 */
3433 static void
3434syn_cmd_list(eap, syncing)
3435 exarg_T *eap;
3436 int syncing; /* when TRUE: list syncing items */
3437{
3438 char_u *arg = eap->arg;
3439 int id;
3440 char_u *arg_end;
3441
3442 eap->nextcmd = find_nextcmd(arg);
3443 if (eap->skip)
3444 return;
3445
3446 if (!syntax_present(curbuf))
3447 {
3448 MSG(_("No Syntax items defined for this buffer"));
3449 return;
3450 }
3451
3452 if (syncing)
3453 {
3454 if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3455 {
3456 MSG_PUTS(_("syncing on C-style comments"));
3457 syn_lines_msg();
3458 syn_match_msg();
3459 return;
3460 }
3461 else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3462 {
3463 if (curbuf->b_syn_sync_minlines == 0)
3464 MSG_PUTS(_("no syncing"));
3465 else
3466 {
3467 MSG_PUTS(_("syncing starts "));
3468 msg_outnum(curbuf->b_syn_sync_minlines);
3469 MSG_PUTS(_(" lines before top line"));
3470 syn_match_msg();
3471 }
3472 return;
3473 }
3474 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3475 if (curbuf->b_syn_sync_minlines > 0
3476 || curbuf->b_syn_sync_maxlines > 0
3477 || curbuf->b_syn_sync_linebreaks > 0)
3478 {
3479 MSG_PUTS(_("\nsyncing on items"));
3480 syn_lines_msg();
3481 syn_match_msg();
3482 }
3483 }
3484 else
3485 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3486 if (ends_excmd(*arg))
3487 {
3488 /*
3489 * No argument: List all group IDs and all syntax clusters.
3490 */
3491 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3492 syn_list_one(id, syncing, FALSE);
3493 for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3494 syn_list_cluster(id);
3495 }
3496 else
3497 {
3498 /*
3499 * List the group IDs and syntax clusters that are in the argument.
3500 */
3501 while (!ends_excmd(*arg) && !got_int)
3502 {
3503 arg_end = skiptowhite(arg);
3504 if (*arg == '@')
3505 {
3506 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3507 if (id == 0)
3508 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3509 else
3510 syn_list_cluster(id - SYNID_CLUSTER);
3511 }
3512 else
3513 {
3514 id = syn_namen2id(arg, (int)(arg_end - arg));
3515 if (id == 0)
3516 EMSG2(_(e_nogroup), arg);
3517 else
3518 syn_list_one(id, syncing, TRUE);
3519 }
3520 arg = skipwhite(arg_end);
3521 }
3522 }
3523 eap->nextcmd = check_nextcmd(arg);
3524}
3525
3526 static void
3527syn_lines_msg()
3528{
3529 if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3530 {
3531 MSG_PUTS("; ");
3532 if (curbuf->b_syn_sync_minlines > 0)
3533 {
3534 MSG_PUTS(_("minimal "));
3535 msg_outnum(curbuf->b_syn_sync_minlines);
3536 if (curbuf->b_syn_sync_maxlines)
3537 MSG_PUTS(", ");
3538 }
3539 if (curbuf->b_syn_sync_maxlines > 0)
3540 {
3541 MSG_PUTS(_("maximal "));
3542 msg_outnum(curbuf->b_syn_sync_maxlines);
3543 }
3544 MSG_PUTS(_(" lines before top line"));
3545 }
3546}
3547
3548 static void
3549syn_match_msg()
3550{
3551 if (curbuf->b_syn_sync_linebreaks > 0)
3552 {
3553 MSG_PUTS(_("; match "));
3554 msg_outnum(curbuf->b_syn_sync_linebreaks);
3555 MSG_PUTS(_(" line breaks"));
3556 }
3557}
3558
3559static int last_matchgroup;
3560
3561struct name_list
3562{
3563 int flag;
3564 char *name;
3565};
3566
3567static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3568
3569/*
3570 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3571 */
3572 static void
3573syn_list_one(id, syncing, link_only)
3574 int id;
3575 int syncing; /* when TRUE: list syncing items */
3576 int link_only; /* when TRUE; list link-only too */
3577{
3578 int attr;
3579 int idx;
3580 int did_header = FALSE;
3581 synpat_T *spp;
3582 static struct name_list namelist1[] =
3583 {
3584 {HL_DISPLAY, "display"},
3585 {HL_CONTAINED, "contained"},
3586 {HL_ONELINE, "oneline"},
3587 {HL_KEEPEND, "keepend"},
3588 {HL_EXTEND, "extend"},
3589 {HL_EXCLUDENL, "excludenl"},
3590 {HL_TRANSP, "transparent"},
3591 {HL_FOLD, "fold"},
3592 {0, NULL}
3593 };
3594 static struct name_list namelist2[] =
3595 {
3596 {HL_SKIPWHITE, "skipwhite"},
3597 {HL_SKIPNL, "skipnl"},
3598 {HL_SKIPEMPTY, "skipempty"},
3599 {0, NULL}
3600 };
3601
3602 attr = hl_attr(HLF_D); /* highlight like directories */
3603
3604 /* list the keywords for "id" */
3605 if (!syncing)
3606 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003607 did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3608 did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003609 did_header, attr);
3610 }
3611
3612 /* list the patterns for "id" */
3613 for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3614 {
3615 spp = &(SYN_ITEMS(curbuf)[idx]);
3616 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3617 continue;
3618
3619 (void)syn_list_header(did_header, 999, id);
3620 did_header = TRUE;
3621 last_matchgroup = 0;
3622 if (spp->sp_type == SPTYPE_MATCH)
3623 {
3624 put_pattern("match", ' ', spp, attr);
3625 msg_putchar(' ');
3626 }
3627 else if (spp->sp_type == SPTYPE_START)
3628 {
3629 while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3630 put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3631 if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3632 put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3633 while (idx < curbuf->b_syn_patterns.ga_len
3634 && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3635 put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3636 --idx;
3637 msg_putchar(' ');
3638 }
3639 syn_list_flags(namelist1, spp->sp_flags, attr);
3640
3641 if (spp->sp_cont_list != NULL)
3642 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3643
3644 if (spp->sp_syn.cont_in_list != NULL)
3645 put_id_list((char_u *)"containedin",
3646 spp->sp_syn.cont_in_list, attr);
3647
3648 if (spp->sp_next_list != NULL)
3649 {
3650 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3651 syn_list_flags(namelist2, spp->sp_flags, attr);
3652 }
3653 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3654 {
3655 if (spp->sp_flags & HL_SYNC_HERE)
3656 msg_puts_attr((char_u *)"grouphere", attr);
3657 else
3658 msg_puts_attr((char_u *)"groupthere", attr);
3659 msg_putchar(' ');
3660 if (spp->sp_sync_idx >= 0)
3661 msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3662 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3663 else
3664 MSG_PUTS("NONE");
3665 msg_putchar(' ');
3666 }
3667 }
3668
3669 /* list the link, if there is one */
3670 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3671 {
3672 (void)syn_list_header(did_header, 999, id);
3673 msg_puts_attr((char_u *)"links to", attr);
3674 msg_putchar(' ');
3675 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3676 }
3677}
3678
3679 static void
3680syn_list_flags(nl, flags, attr)
3681 struct name_list *nl;
3682 int flags;
3683 int attr;
3684{
3685 int i;
3686
3687 for (i = 0; nl[i].flag != 0; ++i)
3688 if (flags & nl[i].flag)
3689 {
3690 msg_puts_attr((char_u *)nl[i].name, attr);
3691 msg_putchar(' ');
3692 }
3693}
3694
3695/*
3696 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3697 */
3698 static void
3699syn_list_cluster(id)
3700 int id;
3701{
3702 int endcol = 15;
3703
3704 /* slight hack: roughly duplicate the guts of syn_list_header() */
3705 msg_putchar('\n');
3706 msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3707
3708 if (msg_col >= endcol) /* output at least one space */
3709 endcol = msg_col + 1;
3710 if (Columns <= endcol) /* avoid hang for tiny window */
3711 endcol = Columns - 1;
3712
3713 msg_advance(endcol);
3714 if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3715 {
3716 put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3717 hl_attr(HLF_D));
3718 }
3719 else
3720 {
3721 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3722 msg_puts((char_u *)"=NONE");
3723 }
3724}
3725
3726 static void
3727put_id_list(name, list, attr)
3728 char_u *name;
3729 short *list;
3730 int attr;
3731{
3732 short *p;
3733
3734 msg_puts_attr(name, attr);
3735 msg_putchar('=');
3736 for (p = list; *p; ++p)
3737 {
3738 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3739 {
3740 if (p[1])
3741 MSG_PUTS("ALLBUT");
3742 else
3743 MSG_PUTS("ALL");
3744 }
3745 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3746 {
3747 MSG_PUTS("TOP");
3748 }
3749 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3750 {
3751 MSG_PUTS("CONTAINED");
3752 }
3753 else if (*p >= SYNID_CLUSTER)
3754 {
3755 short scl_id = *p - SYNID_CLUSTER;
3756
3757 msg_putchar('@');
3758 msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3759 }
3760 else
3761 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3762 if (p[1])
3763 msg_putchar(',');
3764 }
3765 msg_putchar(' ');
3766}
3767
3768 static void
3769put_pattern(s, c, spp, attr)
3770 char *s;
3771 int c;
3772 synpat_T *spp;
3773 int attr;
3774{
3775 long n;
3776 int mask;
3777 int first;
3778 static char *sepchars = "/+=-#@\"|'^&";
3779 int i;
3780
3781 /* May have to write "matchgroup=group" */
3782 if (last_matchgroup != spp->sp_syn_match_id)
3783 {
3784 last_matchgroup = spp->sp_syn_match_id;
3785 msg_puts_attr((char_u *)"matchgroup", attr);
3786 msg_putchar('=');
3787 if (last_matchgroup == 0)
3788 msg_outtrans((char_u *)"NONE");
3789 else
3790 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3791 msg_putchar(' ');
3792 }
3793
3794 /* output the name of the pattern and an '=' or ' ' */
3795 msg_puts_attr((char_u *)s, attr);
3796 msg_putchar(c);
3797
3798 /* output the pattern, in between a char that is not in the pattern */
3799 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3800 if (sepchars[++i] == NUL)
3801 {
3802 i = 0; /* no good char found, just use the first one */
3803 break;
3804 }
3805 msg_putchar(sepchars[i]);
3806 msg_outtrans(spp->sp_pattern);
3807 msg_putchar(sepchars[i]);
3808
3809 /* output any pattern options */
3810 first = TRUE;
3811 for (i = 0; i < SPO_COUNT; ++i)
3812 {
3813 mask = (1 << i);
3814 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3815 {
3816 if (!first)
3817 msg_putchar(','); /* separate with commas */
3818 msg_puts((char_u *)spo_name_tab[i]);
3819 n = spp->sp_offsets[i];
3820 if (i != SPO_LC_OFF)
3821 {
3822 if (spp->sp_off_flags & mask)
3823 msg_putchar('s');
3824 else
3825 msg_putchar('e');
3826 if (n > 0)
3827 msg_putchar('+');
3828 }
3829 if (n || i == SPO_LC_OFF)
3830 msg_outnum(n);
3831 first = FALSE;
3832 }
3833 }
3834 msg_putchar(' ');
3835}
3836
3837/*
3838 * List or clear the keywords for one syntax group.
3839 * Return TRUE if the header has been printed.
3840 */
3841 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00003842syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003843 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003844 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003845 int did_header; /* header has already been printed */
3846 int attr;
3847{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003848 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003849 hashitem_T *hi;
3850 keyentry_T *kp;
3851 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003852 int prev_contained = 0;
3853 short *prev_next_list = NULL;
3854 short *prev_cont_in_list = NULL;
3855 int prev_skipnl = 0;
3856 int prev_skipwhite = 0;
3857 int prev_skipempty = 0;
3858
Bram Moolenaar071d4272004-06-13 20:20:40 +00003859 /*
3860 * Unfortunately, this list of keywords is not sorted on alphabet but on
3861 * hash value...
3862 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003863 todo = ht->ht_used;
3864 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003865 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003866 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003868 --todo;
3869 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003870 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003871 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003872 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003873 if (prev_contained != (kp->flags & HL_CONTAINED)
3874 || prev_skipnl != (kp->flags & HL_SKIPNL)
3875 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
3876 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
3877 || prev_cont_in_list != kp->k_syn.cont_in_list
3878 || prev_next_list != kp->next_list)
3879 outlen = 9999;
3880 else
3881 outlen = (int)STRLEN(kp->keyword);
3882 /* output "contained" and "nextgroup" on each line */
3883 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003884 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003885 prev_contained = 0;
3886 prev_next_list = NULL;
3887 prev_cont_in_list = NULL;
3888 prev_skipnl = 0;
3889 prev_skipwhite = 0;
3890 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003891 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003892 did_header = TRUE;
3893 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003894 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003895 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003896 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00003897 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003898 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003899 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003900 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003901 put_id_list((char_u *)"containedin",
3902 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003903 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00003904 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003905 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003906 if (kp->next_list != prev_next_list)
3907 {
3908 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
3909 msg_putchar(' ');
3910 prev_next_list = kp->next_list;
3911 if (kp->flags & HL_SKIPNL)
3912 {
3913 msg_puts_attr((char_u *)"skipnl", attr);
3914 msg_putchar(' ');
3915 prev_skipnl = (kp->flags & HL_SKIPNL);
3916 }
3917 if (kp->flags & HL_SKIPWHITE)
3918 {
3919 msg_puts_attr((char_u *)"skipwhite", attr);
3920 msg_putchar(' ');
3921 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
3922 }
3923 if (kp->flags & HL_SKIPEMPTY)
3924 {
3925 msg_puts_attr((char_u *)"skipempty", attr);
3926 msg_putchar(' ');
3927 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
3928 }
3929 }
3930 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003931 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932 }
3933 }
3934 }
3935
3936 return did_header;
3937}
3938
3939 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00003940syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003941 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003942 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003943{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003944 hashitem_T *hi;
3945 keyentry_T *kp;
3946 keyentry_T *kp_prev;
3947 keyentry_T *kp_next;
3948 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949
Bram Moolenaardad6b692005-01-25 22:14:34 +00003950 hash_lock(ht);
3951 todo = ht->ht_used;
3952 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003953 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003954 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003955 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003956 --todo;
3957 kp_prev = NULL;
3958 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003959 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003960 if (kp->k_syn.id == id)
3961 {
3962 kp_next = kp->ke_next;
3963 if (kp_prev == NULL)
3964 {
3965 if (kp_next == NULL)
3966 hash_remove(ht, hi);
3967 else
3968 hi->hi_key = KE2HIKEY(kp_next);
3969 }
3970 else
3971 kp_prev->ke_next = kp_next;
3972 vim_free(kp->next_list);
3973 vim_free(kp->k_syn.cont_in_list);
3974 vim_free(kp);
3975 kp = kp_next;
3976 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003977 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00003978 {
3979 kp_prev = kp;
3980 kp = kp->ke_next;
3981 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003982 }
3983 }
3984 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003985 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003986}
3987
3988/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003989 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003990 */
3991 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00003992clear_keywtab(ht)
3993 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003995 hashitem_T *hi;
3996 int todo;
3997 keyentry_T *kp;
3998 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003999
Bram Moolenaardad6b692005-01-25 22:14:34 +00004000 todo = ht->ht_used;
4001 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004002 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004003 if (!HASHITEM_EMPTY(hi))
4004 {
4005 --todo;
4006 kp = HI2KE(hi);
4007 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004008 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004009 kp_next = kp->ke_next;
4010 vim_free(kp->next_list);
4011 vim_free(kp->k_syn.cont_in_list);
4012 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004013 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004014 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004015 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004016 hash_clear(ht);
4017 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004018}
4019
4020/*
4021 * Add a keyword to the list of keywords.
4022 */
4023 static void
4024add_keyword(name, id, flags, cont_in_list, next_list)
4025 char_u *name; /* name of keyword */
4026 int id; /* group ID for this keyword */
4027 int flags; /* flags for this keyword */
4028 short *cont_in_list; /* containedin for this keyword */
4029 short *next_list; /* nextgroup for this keyword */
4030{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004031 keyentry_T *kp;
4032 hashtab_T *ht;
4033 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004034 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004035 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004036 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004037
4038 if (curbuf->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004039 name_ic = str_foldcase(name, (int)STRLEN(name),
4040 name_folded, MAXKEYWLEN + 1);
4041 else
4042 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004043 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4044 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004045 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004046 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004047 kp->k_syn.id = id;
4048 kp->k_syn.inc_tag = current_syn_inc_tag;
4049 kp->flags = flags;
4050 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004051 if (cont_in_list != NULL)
4052 curbuf->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004053 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004054
4055 if (curbuf->b_syn_ic)
Bram Moolenaardad6b692005-01-25 22:14:34 +00004056 ht = &curbuf->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004057 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004058 ht = &curbuf->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004059
Bram Moolenaardad6b692005-01-25 22:14:34 +00004060 hash = hash_hash(kp->keyword);
4061 hi = hash_lookup(ht, kp->keyword, hash);
4062 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004063 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004064 /* new keyword, add to hashtable */
4065 kp->ke_next = NULL;
4066 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004067 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004068 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004070 /* keyword already exists, prepend to list */
4071 kp->ke_next = HI2KE(hi);
4072 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004074}
4075
4076/*
4077 * Get the start and end of the group name argument.
4078 * Return a pointer to the first argument.
4079 * Return NULL if the end of the command was found instead of further args.
4080 */
4081 static char_u *
4082get_group_name(arg, name_end)
4083 char_u *arg; /* start of the argument */
4084 char_u **name_end; /* pointer to end of the name */
4085{
4086 char_u *rest;
4087
4088 *name_end = skiptowhite(arg);
4089 rest = skipwhite(*name_end);
4090
4091 /*
4092 * Check if there are enough arguments. The first argument may be a
4093 * pattern, where '|' is allowed, so only check for NUL.
4094 */
4095 if (ends_excmd(*arg) || *rest == NUL)
4096 return NULL;
4097 return rest;
4098}
4099
4100/*
4101 * Check for syntax command option arguments.
4102 * This can be called at any place in the list of arguments, and just picks
4103 * out the arguments that are known. Can be called several times in a row to
4104 * collect all options in between other arguments.
4105 * Return a pointer to the next argument (which isn't an option).
4106 * Return NULL for any error;
4107 */
4108 static char_u *
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004109get_syn_options(arg, opt)
4110 char_u *arg; /* next argument to be checked */
4111 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004112{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004113 char_u *gname_start, *gname;
4114 int syn_id;
4115 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004116 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004117 int i;
4118 int fidx;
4119 static struct flag
4120 {
4121 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004122 int argtype;
4123 int flags;
4124 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4125 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4126 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4127 {"eExXtTeEnNdD", 0, HL_EXTEND},
4128 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4129 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4130 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4131 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4132 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4133 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4134 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4135 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4136 {"fFoOlLdD", 0, HL_FOLD},
4137 {"cCoOnNtTaAiInNsS", 1, 0},
4138 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4139 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004140 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004141 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004142
4143 if (arg == NULL) /* already detected error */
4144 return NULL;
4145
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146 for (;;)
4147 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004148 /*
4149 * This is used very often when a large number of keywords is defined.
4150 * Need to skip quickly when no option name is found.
4151 * Also avoid tolower(), it's slow.
4152 */
4153 if (strchr(first_letters, *arg) == NULL)
4154 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155
4156 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4157 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004158 p = flagtab[fidx].name;
4159 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4160 if (arg[len] != p[i] && arg[len] != p[i + 1])
4161 break;
4162 if (p[i] == NUL && (vim_iswhite(arg[len])
4163 || (flagtab[fidx].argtype > 0
4164 ? arg[len] == '='
4165 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004167 if (opt->keyword
4168 && (flagtab[fidx].flags == HL_DISPLAY
4169 || flagtab[fidx].flags == HL_FOLD
4170 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004171 /* treat "display", "fold" and "extend" as a keyword */
4172 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004173 break;
4174 }
4175 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004176 if (fidx < 0) /* no match found */
4177 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004179 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004180 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004181 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 {
4183 EMSG(_("E395: contains argument not accepted here"));
4184 return NULL;
4185 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004186 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004187 return NULL;
4188 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004189 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004190 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004191#if 0 /* cannot happen */
4192 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004193 {
4194 EMSG(_("E396: containedin argument not accepted here"));
4195 return NULL;
4196 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004197#endif
4198 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004199 return NULL;
4200 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004201 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004202 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004203 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004204 return NULL;
4205 }
4206 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004207 {
4208 opt->flags |= flagtab[fidx].flags;
4209 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004210
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004211 if (flagtab[fidx].flags == HL_SYNC_HERE
4212 || flagtab[fidx].flags == HL_SYNC_THERE)
4213 {
4214 if (opt->sync_idx == NULL)
4215 {
4216 EMSG(_("E393: group[t]here not accepted here"));
4217 return NULL;
4218 }
4219 gname_start = arg;
4220 arg = skiptowhite(arg);
4221 if (gname_start == arg)
4222 return NULL;
4223 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4224 if (gname == NULL)
4225 return NULL;
4226 if (STRCMP(gname, "NONE") == 0)
4227 *opt->sync_idx = NONE_IDX;
4228 else
4229 {
4230 syn_id = syn_name2id(gname);
4231 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4232 if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4233 && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4234 {
4235 *opt->sync_idx = i;
4236 break;
4237 }
4238 if (i < 0)
4239 {
4240 EMSG2(_("E394: Didn't find region item for %s"), gname);
4241 vim_free(gname);
4242 return NULL;
4243 }
4244 }
4245
4246 vim_free(gname);
4247 arg = skipwhite(arg);
4248 }
4249#ifdef FEAT_FOLDING
4250 else if (flagtab[fidx].flags == HL_FOLD
4251 && foldmethodIsSyntax(curwin))
4252 /* Need to update folds later. */
4253 foldUpdateAll(curwin);
4254#endif
4255 }
4256 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004257
4258 return arg;
4259}
4260
4261/*
4262 * Adjustments to syntax item when declared in a ":syn include"'d file.
4263 * Set the contained flag, and if the item is not already contained, add it
4264 * to the specified top-level group, if any.
4265 */
4266 static void
4267syn_incl_toplevel(id, flagsp)
4268 int id;
4269 int *flagsp;
4270{
4271 if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4272 return;
4273 *flagsp |= HL_CONTAINED;
4274 if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4275 {
4276 /* We have to alloc this, because syn_combine_list() will free it. */
4277 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4278 int tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4279
4280 if (grp_list != NULL)
4281 {
4282 grp_list[0] = id;
4283 grp_list[1] = 0;
4284 syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4285 CLUSTER_ADD);
4286 }
4287 }
4288}
4289
4290/*
4291 * Handle ":syntax include [@{group-name}] filename" command.
4292 */
4293/* ARGSUSED */
4294 static void
4295syn_cmd_include(eap, syncing)
4296 exarg_T *eap;
4297 int syncing; /* not used */
4298{
4299 char_u *arg = eap->arg;
4300 int sgl_id = 1;
4301 char_u *group_name_end;
4302 char_u *rest;
4303 char_u *errormsg = NULL;
4304 int prev_toplvl_grp;
4305 int prev_syn_inc_tag;
4306 int source = FALSE;
4307
4308 eap->nextcmd = find_nextcmd(arg);
4309 if (eap->skip)
4310 return;
4311
4312 if (arg[0] == '@')
4313 {
4314 ++arg;
4315 rest = get_group_name(arg, &group_name_end);
4316 if (rest == NULL)
4317 {
4318 EMSG((char_u *)_("E397: Filename required"));
4319 return;
4320 }
4321 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4322 /* separate_nextcmd() and expand_filename() depend on this */
4323 eap->arg = rest;
4324 }
4325
4326 /*
4327 * Everything that's left, up to the next command, should be the
4328 * filename to include.
4329 */
4330 eap->argt |= (XFILE | NOSPC);
4331 separate_nextcmd(eap);
4332 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4333 {
4334 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4335 * file. Need to expand the file name first. In other cases
4336 * ":runtime!" is used. */
4337 source = TRUE;
4338 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4339 {
4340 if (errormsg != NULL)
4341 EMSG(errormsg);
4342 return;
4343 }
4344 }
4345
4346 /*
4347 * Save and restore the existing top-level grouplist id and ":syn
4348 * include" tag around the actual inclusion.
4349 */
4350 prev_syn_inc_tag = current_syn_inc_tag;
4351 current_syn_inc_tag = ++running_syn_inc_tag;
4352 prev_toplvl_grp = curbuf->b_syn_topgrp;
4353 curbuf->b_syn_topgrp = sgl_id;
4354 if (source ? do_source(eap->arg, FALSE, FALSE) == FAIL
4355 : cmd_runtime(eap->arg, TRUE) == FAIL)
4356 EMSG2(_(e_notopen), eap->arg);
4357 curbuf->b_syn_topgrp = prev_toplvl_grp;
4358 current_syn_inc_tag = prev_syn_inc_tag;
4359}
4360
4361/*
4362 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4363 */
4364/* ARGSUSED */
4365 static void
4366syn_cmd_keyword(eap, syncing)
4367 exarg_T *eap;
4368 int syncing; /* not used */
4369{
4370 char_u *arg = eap->arg;
4371 char_u *group_name_end;
4372 int syn_id;
4373 char_u *rest;
4374 char_u *keyword_copy;
4375 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004376 char_u *kw;
4377 syn_opt_arg_T syn_opt_arg;
4378 int cnt;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004379
4380 rest = get_group_name(arg, &group_name_end);
4381
4382 if (rest != NULL)
4383 {
4384 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4385
4386 /* allocate a buffer, for removing the backslashes in the keyword */
4387 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4388 if (keyword_copy != NULL)
4389 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004390 syn_opt_arg.flags = 0;
4391 syn_opt_arg.keyword = TRUE;
4392 syn_opt_arg.sync_idx = NULL;
4393 syn_opt_arg.has_cont_list = FALSE;
4394 syn_opt_arg.cont_in_list = NULL;
4395 syn_opt_arg.next_list = NULL;
4396
Bram Moolenaar071d4272004-06-13 20:20:40 +00004397 /*
4398 * The options given apply to ALL keywords, so all options must be
4399 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004400 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004402 cnt = 0;
4403 p = keyword_copy;
4404 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004405 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004406 rest = get_syn_options(rest, &syn_opt_arg);
4407 if (rest == NULL || ends_excmd(*rest))
4408 break;
4409 /* Copy the keyword, removing backslashes, and add a NUL. */
4410 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004411 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004412 if (*rest == '\\' && rest[1] != NUL)
4413 ++rest;
4414 *p++ = *rest++;
4415 }
4416 *p++ = NUL;
4417 ++cnt;
4418 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004419
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004420 if (!eap->skip)
4421 {
4422 /* Adjust flags for use of ":syn include". */
4423 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4424
4425 /*
4426 * 2: Add an entry for each keyword.
4427 */
4428 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4429 {
4430 for (p = vim_strchr(kw, '['); ; )
4431 {
4432 if (p != NULL)
4433 *p = NUL;
4434 add_keyword(kw, syn_id, syn_opt_arg.flags,
4435 syn_opt_arg.cont_in_list,
4436 syn_opt_arg.next_list);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004437 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004438 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004439 if (p[1] == NUL)
4440 {
4441 EMSG2(_("E747: Missing ']': %s"), kw);
4442 kw = p + 2; /* skip over the NUL */
4443 break;
4444 }
4445 if (p[1] == ']')
4446 {
4447 kw = p + 1; /* skip over the "]" */
4448 break;
4449 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004450#ifdef FEAT_MBYTE
4451 if (has_mbyte)
4452 {
4453 int l = (*mb_ptr2len_check)(p + 1);
4454
4455 mch_memmove(p, p + 1, l);
4456 p += l;
4457 }
4458 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004459#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004460 {
4461 p[0] = p[1];
4462 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 }
4464 }
4465 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004467
Bram Moolenaar071d4272004-06-13 20:20:40 +00004468 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004469 vim_free(syn_opt_arg.cont_in_list);
4470 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471 }
4472 }
4473
4474 if (rest != NULL)
4475 eap->nextcmd = check_nextcmd(rest);
4476 else
4477 EMSG2(_(e_invarg2), arg);
4478
Bram Moolenaar071d4272004-06-13 20:20:40 +00004479 redraw_curbuf_later(NOT_VALID);
4480 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4481}
4482
4483/*
4484 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4485 *
4486 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4487 */
4488 static void
4489syn_cmd_match(eap, syncing)
4490 exarg_T *eap;
4491 int syncing; /* TRUE for ":syntax sync match .. " */
4492{
4493 char_u *arg = eap->arg;
4494 char_u *group_name_end;
4495 char_u *rest;
4496 synpat_T item; /* the item found in the line */
4497 int syn_id;
4498 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004499 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004500 int sync_idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004501
4502 /* Isolate the group name, check for validity */
4503 rest = get_group_name(arg, &group_name_end);
4504
4505 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004506 syn_opt_arg.flags = 0;
4507 syn_opt_arg.keyword = FALSE;
4508 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4509 syn_opt_arg.has_cont_list = TRUE;
4510 syn_opt_arg.cont_list = NULL;
4511 syn_opt_arg.cont_in_list = NULL;
4512 syn_opt_arg.next_list = NULL;
4513 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514
4515 /* get the pattern. */
4516 init_syn_patterns();
4517 vim_memset(&item, 0, sizeof(item));
4518 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004519 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4520 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004521
4522 /* Get options after the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004523 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004524
4525 if (rest != NULL) /* all arguments are valid */
4526 {
4527 /*
4528 * Check for trailing command and illegal trailing arguments.
4529 */
4530 eap->nextcmd = check_nextcmd(rest);
4531 if (!ends_excmd(*rest) || eap->skip)
4532 rest = NULL;
4533 else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4534 && (syn_id = syn_check_group(arg,
4535 (int)(group_name_end - arg))) != 0)
4536 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004537 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538 /*
4539 * Store the pattern in the syn_items list
4540 */
4541 idx = curbuf->b_syn_patterns.ga_len;
4542 SYN_ITEMS(curbuf)[idx] = item;
4543 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4544 SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4545 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4546 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004547 SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004548 SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004549 SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4550 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4551 syn_opt_arg.cont_in_list;
4552 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004553 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004554 SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004555 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004556
4557 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004558 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004559 curbuf->b_syn_sync_flags |= SF_MATCH;
4560#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004561 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004562 ++curbuf->b_syn_folditems;
4563#endif
4564
4565 redraw_curbuf_later(NOT_VALID);
4566 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4567 return; /* don't free the progs and patterns now */
4568 }
4569 }
4570
4571 /*
4572 * Something failed, free the allocated memory.
4573 */
4574 vim_free(item.sp_prog);
4575 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004576 vim_free(syn_opt_arg.cont_list);
4577 vim_free(syn_opt_arg.cont_in_list);
4578 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579
4580 if (rest == NULL)
4581 EMSG2(_(e_invarg2), arg);
4582}
4583
4584/*
4585 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4586 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4587 */
4588 static void
4589syn_cmd_region(eap, syncing)
4590 exarg_T *eap;
4591 int syncing; /* TRUE for ":syntax sync region .." */
4592{
4593 char_u *arg = eap->arg;
4594 char_u *group_name_end;
4595 char_u *rest; /* next arg, NULL on error */
4596 char_u *key_end;
4597 char_u *key = NULL;
4598 char_u *p;
4599 int item;
4600#define ITEM_START 0
4601#define ITEM_SKIP 1
4602#define ITEM_END 2
4603#define ITEM_MATCHGROUP 3
4604 struct pat_ptr
4605 {
4606 synpat_T *pp_synp; /* pointer to syn_pattern */
4607 int pp_matchgroup_id; /* matchgroup ID */
4608 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4609 } *(pat_ptrs[3]);
4610 /* patterns found in the line */
4611 struct pat_ptr *ppp;
4612 struct pat_ptr *ppp_next;
4613 int pat_count = 0; /* nr of syn_patterns found */
4614 int syn_id;
4615 int matchgroup_id = 0;
4616 int not_enough = FALSE; /* not enough arguments */
4617 int illegal = FALSE; /* illegal arguments */
4618 int success = FALSE;
4619 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004620 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004621
4622 /* Isolate the group name, check for validity */
4623 rest = get_group_name(arg, &group_name_end);
4624
4625 pat_ptrs[0] = NULL;
4626 pat_ptrs[1] = NULL;
4627 pat_ptrs[2] = NULL;
4628
4629 init_syn_patterns();
4630
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004631 syn_opt_arg.flags = 0;
4632 syn_opt_arg.keyword = FALSE;
4633 syn_opt_arg.sync_idx = NULL;
4634 syn_opt_arg.has_cont_list = TRUE;
4635 syn_opt_arg.cont_list = NULL;
4636 syn_opt_arg.cont_in_list = NULL;
4637 syn_opt_arg.next_list = NULL;
4638
Bram Moolenaar071d4272004-06-13 20:20:40 +00004639 /*
4640 * get the options, patterns and matchgroup.
4641 */
4642 while (rest != NULL && !ends_excmd(*rest))
4643 {
4644 /* Check for option arguments */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004645 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004646 if (rest == NULL || ends_excmd(*rest))
4647 break;
4648
4649 /* must be a pattern or matchgroup then */
4650 key_end = rest;
4651 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4652 ++key_end;
4653 vim_free(key);
4654 key = vim_strnsave_up(rest, (int)(key_end - rest));
4655 if (key == NULL) /* out of memory */
4656 {
4657 rest = NULL;
4658 break;
4659 }
4660 if (STRCMP(key, "MATCHGROUP") == 0)
4661 item = ITEM_MATCHGROUP;
4662 else if (STRCMP(key, "START") == 0)
4663 item = ITEM_START;
4664 else if (STRCMP(key, "END") == 0)
4665 item = ITEM_END;
4666 else if (STRCMP(key, "SKIP") == 0)
4667 {
4668 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4669 {
4670 illegal = TRUE;
4671 break;
4672 }
4673 item = ITEM_SKIP;
4674 }
4675 else
4676 break;
4677 rest = skipwhite(key_end);
4678 if (*rest != '=')
4679 {
4680 rest = NULL;
4681 EMSG2(_("E398: Missing '=': %s"), arg);
4682 break;
4683 }
4684 rest = skipwhite(rest + 1);
4685 if (*rest == NUL)
4686 {
4687 not_enough = TRUE;
4688 break;
4689 }
4690
4691 if (item == ITEM_MATCHGROUP)
4692 {
4693 p = skiptowhite(rest);
4694 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4695 matchgroup_id = 0;
4696 else
4697 {
4698 matchgroup_id = syn_check_group(rest, (int)(p - rest));
4699 if (matchgroup_id == 0)
4700 {
4701 illegal = TRUE;
4702 break;
4703 }
4704 }
4705 rest = skipwhite(p);
4706 }
4707 else
4708 {
4709 /*
4710 * Allocate room for a syn_pattern, and link it in the list of
4711 * syn_patterns for this item, at the start (because the list is
4712 * used from end to start).
4713 */
4714 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4715 if (ppp == NULL)
4716 {
4717 rest = NULL;
4718 break;
4719 }
4720 ppp->pp_next = pat_ptrs[item];
4721 pat_ptrs[item] = ppp;
4722 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4723 if (ppp->pp_synp == NULL)
4724 {
4725 rest = NULL;
4726 break;
4727 }
4728
4729 /*
4730 * Get the syntax pattern and the following offset(s).
4731 */
4732 /* Enable the appropriate \z specials. */
4733 if (item == ITEM_START)
4734 reg_do_extmatch = REX_SET;
4735 else if (item == ITEM_SKIP || item == ITEM_END)
4736 reg_do_extmatch = REX_USE;
4737 rest = get_syn_pattern(rest, ppp->pp_synp);
4738 reg_do_extmatch = 0;
4739 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004740 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004741 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4742 ppp->pp_matchgroup_id = matchgroup_id;
4743 ++pat_count;
4744 }
4745 }
4746 vim_free(key);
4747 if (illegal || not_enough)
4748 rest = NULL;
4749
4750 /*
4751 * Must have a "start" and "end" pattern.
4752 */
4753 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4754 pat_ptrs[ITEM_END] == NULL))
4755 {
4756 not_enough = TRUE;
4757 rest = NULL;
4758 }
4759
4760 if (rest != NULL)
4761 {
4762 /*
4763 * Check for trailing garbage or command.
4764 * If OK, add the item.
4765 */
4766 eap->nextcmd = check_nextcmd(rest);
4767 if (!ends_excmd(*rest) || eap->skip)
4768 rest = NULL;
4769 else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4770 && (syn_id = syn_check_group(arg,
4771 (int)(group_name_end - arg))) != 0)
4772 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004773 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004774 /*
4775 * Store the start/skip/end in the syn_items list
4776 */
4777 idx = curbuf->b_syn_patterns.ga_len;
4778 for (item = ITEM_START; item <= ITEM_END; ++item)
4779 {
4780 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4781 {
4782 SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4783 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4784 SYN_ITEMS(curbuf)[idx].sp_type =
4785 (item == ITEM_START) ? SPTYPE_START :
4786 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004787 SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004788 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4789 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4790 SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4791 ppp->pp_matchgroup_id;
4792 if (item == ITEM_START)
4793 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004794 SYN_ITEMS(curbuf)[idx].sp_cont_list =
4795 syn_opt_arg.cont_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004796 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004797 syn_opt_arg.cont_in_list;
4798 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004799 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004800 SYN_ITEMS(curbuf)[idx].sp_next_list =
4801 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004802 }
4803 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804 ++idx;
4805#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004806 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004807 ++curbuf->b_syn_folditems;
4808#endif
4809 }
4810 }
4811
4812 redraw_curbuf_later(NOT_VALID);
4813 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4814 success = TRUE; /* don't free the progs and patterns now */
4815 }
4816 }
4817
4818 /*
4819 * Free the allocated memory.
4820 */
4821 for (item = ITEM_START; item <= ITEM_END; ++item)
4822 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4823 {
4824 if (!success)
4825 {
4826 vim_free(ppp->pp_synp->sp_prog);
4827 vim_free(ppp->pp_synp->sp_pattern);
4828 }
4829 vim_free(ppp->pp_synp);
4830 ppp_next = ppp->pp_next;
4831 vim_free(ppp);
4832 }
4833
4834 if (!success)
4835 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004836 vim_free(syn_opt_arg.cont_list);
4837 vim_free(syn_opt_arg.cont_in_list);
4838 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004839 if (not_enough)
4840 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
4841 else if (illegal || rest == NULL)
4842 EMSG2(_(e_invarg2), arg);
4843 }
4844}
4845
4846/*
4847 * A simple syntax group ID comparison function suitable for use in qsort()
4848 */
4849 static int
4850#ifdef __BORLANDC__
4851_RTLENTRYF
4852#endif
4853syn_compare_stub(v1, v2)
4854 const void *v1;
4855 const void *v2;
4856{
4857 const short *s1 = v1;
4858 const short *s2 = v2;
4859
4860 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
4861}
4862
4863/*
4864 * Combines lists of syntax clusters.
4865 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
4866 */
4867 static void
4868syn_combine_list(clstr1, clstr2, list_op)
4869 short **clstr1;
4870 short **clstr2;
4871 int list_op;
4872{
4873 int count1 = 0;
4874 int count2 = 0;
4875 short *g1;
4876 short *g2;
4877 short *clstr = NULL;
4878 int count;
4879 int round;
4880
4881 /*
4882 * Handle degenerate cases.
4883 */
4884 if (*clstr2 == NULL)
4885 return;
4886 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
4887 {
4888 if (list_op == CLUSTER_REPLACE)
4889 vim_free(*clstr1);
4890 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
4891 *clstr1 = *clstr2;
4892 else
4893 vim_free(*clstr2);
4894 return;
4895 }
4896
4897 for (g1 = *clstr1; *g1; g1++)
4898 ++count1;
4899 for (g2 = *clstr2; *g2; g2++)
4900 ++count2;
4901
4902 /*
4903 * For speed purposes, sort both lists.
4904 */
4905 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
4906 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
4907
4908 /*
4909 * We proceed in two passes; in round 1, we count the elements to place
4910 * in the new list, and in round 2, we allocate and populate the new
4911 * list. For speed, we use a mergesort-like method, adding the smaller
4912 * of the current elements in each list to the new list.
4913 */
4914 for (round = 1; round <= 2; round++)
4915 {
4916 g1 = *clstr1;
4917 g2 = *clstr2;
4918 count = 0;
4919
4920 /*
4921 * First, loop through the lists until one of them is empty.
4922 */
4923 while (*g1 && *g2)
4924 {
4925 /*
4926 * We always want to add from the first list.
4927 */
4928 if (*g1 < *g2)
4929 {
4930 if (round == 2)
4931 clstr[count] = *g1;
4932 count++;
4933 g1++;
4934 continue;
4935 }
4936 /*
4937 * We only want to add from the second list if we're adding the
4938 * lists.
4939 */
4940 if (list_op == CLUSTER_ADD)
4941 {
4942 if (round == 2)
4943 clstr[count] = *g2;
4944 count++;
4945 }
4946 if (*g1 == *g2)
4947 g1++;
4948 g2++;
4949 }
4950
4951 /*
4952 * Now add the leftovers from whichever list didn't get finished
4953 * first. As before, we only want to add from the second list if
4954 * we're adding the lists.
4955 */
4956 for (; *g1; g1++, count++)
4957 if (round == 2)
4958 clstr[count] = *g1;
4959 if (list_op == CLUSTER_ADD)
4960 for (; *g2; g2++, count++)
4961 if (round == 2)
4962 clstr[count] = *g2;
4963
4964 if (round == 1)
4965 {
4966 /*
4967 * If the group ended up empty, we don't need to allocate any
4968 * space for it.
4969 */
4970 if (count == 0)
4971 {
4972 clstr = NULL;
4973 break;
4974 }
4975 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
4976 if (clstr == NULL)
4977 break;
4978 clstr[count] = 0;
4979 }
4980 }
4981
4982 /*
4983 * Finally, put the new list in place.
4984 */
4985 vim_free(*clstr1);
4986 vim_free(*clstr2);
4987 *clstr1 = clstr;
4988}
4989
4990/*
4991 * Lookup a syntax cluster name and return it's ID.
4992 * If it is not found, 0 is returned.
4993 */
4994 static int
4995syn_scl_name2id(name)
4996 char_u *name;
4997{
4998 int i;
4999 char_u *name_u;
5000
5001 /* Avoid using stricmp() too much, it's slow on some systems */
5002 name_u = vim_strsave_up(name);
5003 if (name_u == NULL)
5004 return 0;
5005 for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
5006 if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
5007 && STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
5008 break;
5009 vim_free(name_u);
5010 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5011}
5012
5013/*
5014 * Like syn_scl_name2id(), but take a pointer + length argument.
5015 */
5016 static int
5017syn_scl_namen2id(linep, len)
5018 char_u *linep;
5019 int len;
5020{
5021 char_u *name;
5022 int id = 0;
5023
5024 name = vim_strnsave(linep, len);
5025 if (name != NULL)
5026 {
5027 id = syn_scl_name2id(name);
5028 vim_free(name);
5029 }
5030 return id;
5031}
5032
5033/*
5034 * Find syntax cluster name in the table and return it's ID.
5035 * The argument is a pointer to the name and the length of the name.
5036 * If it doesn't exist yet, a new entry is created.
5037 * Return 0 for failure.
5038 */
5039 static int
5040syn_check_cluster(pp, len)
5041 char_u *pp;
5042 int len;
5043{
5044 int id;
5045 char_u *name;
5046
5047 name = vim_strnsave(pp, len);
5048 if (name == NULL)
5049 return 0;
5050
5051 id = syn_scl_name2id(name);
5052 if (id == 0) /* doesn't exist yet */
5053 id = syn_add_cluster(name);
5054 else
5055 vim_free(name);
5056 return id;
5057}
5058
5059/*
5060 * Add new syntax cluster and return it's ID.
5061 * "name" must be an allocated string, it will be consumed.
5062 * Return 0 for failure.
5063 */
5064 static int
5065syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005066 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005067{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005068 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005069
5070 /*
5071 * First call for this growarray: init growing array.
5072 */
5073 if (curbuf->b_syn_clusters.ga_data == NULL)
5074 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00005075 curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005076 curbuf->b_syn_clusters.ga_growsize = 10;
5077 }
5078
5079 /*
5080 * Make room for at least one other cluster entry.
5081 */
5082 if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5083 {
5084 vim_free(name);
5085 return 0;
5086 }
5087 len = curbuf->b_syn_clusters.ga_len;
5088
Bram Moolenaar217ad922005-03-20 22:37:15 +00005089 vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005090 SYN_CLSTR(curbuf)[len].scl_name = name;
5091 SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5092 SYN_CLSTR(curbuf)[len].scl_list = NULL;
5093 ++curbuf->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005094
Bram Moolenaar217ad922005-03-20 22:37:15 +00005095 if (STRICMP(name, "Spell") == 0)
5096 curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
5097
Bram Moolenaar071d4272004-06-13 20:20:40 +00005098 return len + SYNID_CLUSTER;
5099}
5100
5101/*
5102 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5103 * [add={groupname},..] [remove={groupname},..]".
5104 */
5105/* ARGSUSED */
5106 static void
5107syn_cmd_cluster(eap, syncing)
5108 exarg_T *eap;
5109 int syncing; /* not used */
5110{
5111 char_u *arg = eap->arg;
5112 char_u *group_name_end;
5113 char_u *rest;
5114 int scl_id;
5115 short *clstr_list;
5116 int got_clstr = FALSE;
5117 int opt_len;
5118 int list_op;
5119
5120 eap->nextcmd = find_nextcmd(arg);
5121 if (eap->skip)
5122 return;
5123
5124 rest = get_group_name(arg, &group_name_end);
5125
5126 if (rest != NULL)
5127 {
5128 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005129 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005130
5131 for (;;)
5132 {
5133 if (STRNICMP(rest, "add", 3) == 0
5134 && (vim_iswhite(rest[3]) || rest[3] == '='))
5135 {
5136 opt_len = 3;
5137 list_op = CLUSTER_ADD;
5138 }
5139 else if (STRNICMP(rest, "remove", 6) == 0
5140 && (vim_iswhite(rest[6]) || rest[6] == '='))
5141 {
5142 opt_len = 6;
5143 list_op = CLUSTER_SUBTRACT;
5144 }
5145 else if (STRNICMP(rest, "contains", 8) == 0
5146 && (vim_iswhite(rest[8]) || rest[8] == '='))
5147 {
5148 opt_len = 8;
5149 list_op = CLUSTER_REPLACE;
5150 }
5151 else
5152 break;
5153
5154 clstr_list = NULL;
5155 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5156 {
5157 EMSG2(_(e_invarg2), rest);
5158 break;
5159 }
5160 syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5161 &clstr_list, list_op);
5162 got_clstr = TRUE;
5163 }
5164
5165 if (got_clstr)
5166 {
5167 redraw_curbuf_later(NOT_VALID);
5168 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5169 }
5170 }
5171
5172 if (!got_clstr)
5173 EMSG(_("E400: No cluster specified"));
5174 if (rest == NULL || !ends_excmd(*rest))
5175 EMSG2(_(e_invarg2), arg);
5176}
5177
5178/*
5179 * On first call for current buffer: Init growing array.
5180 */
5181 static void
5182init_syn_patterns()
5183{
5184 curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5185 curbuf->b_syn_patterns.ga_growsize = 10;
5186}
5187
5188/*
5189 * Get one pattern for a ":syntax match" or ":syntax region" command.
5190 * Stores the pattern and program in a synpat_T.
5191 * Returns a pointer to the next argument, or NULL in case of an error.
5192 */
5193 static char_u *
5194get_syn_pattern(arg, ci)
5195 char_u *arg;
5196 synpat_T *ci;
5197{
5198 char_u *end;
5199 int *p;
5200 int idx;
5201 char_u *cpo_save;
5202
5203 /* need at least three chars */
5204 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5205 return NULL;
5206
5207 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5208 if (*end != *arg) /* end delimiter not found */
5209 {
5210 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5211 return NULL;
5212 }
5213 /* store the pattern and compiled regexp program */
5214 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5215 return NULL;
5216
5217 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5218 cpo_save = p_cpo;
5219 p_cpo = (char_u *)"";
5220 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5221 p_cpo = cpo_save;
5222
5223 if (ci->sp_prog == NULL)
5224 return NULL;
5225 ci->sp_ic = curbuf->b_syn_ic;
5226
5227 /*
5228 * Check for a match, highlight or region offset.
5229 */
5230 ++end;
5231 do
5232 {
5233 for (idx = SPO_COUNT; --idx >= 0; )
5234 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5235 break;
5236 if (idx >= 0)
5237 {
5238 p = &(ci->sp_offsets[idx]);
5239 if (idx != SPO_LC_OFF)
5240 switch (end[3])
5241 {
5242 case 's': break;
5243 case 'b': break;
5244 case 'e': idx += SPO_COUNT; break;
5245 default: idx = -1; break;
5246 }
5247 if (idx >= 0)
5248 {
5249 ci->sp_off_flags |= (1 << idx);
5250 if (idx == SPO_LC_OFF) /* lc=99 */
5251 {
5252 end += 3;
5253 *p = getdigits(&end);
5254
5255 /* "lc=" offset automatically sets "ms=" offset */
5256 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5257 {
5258 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5259 ci->sp_offsets[SPO_MS_OFF] = *p;
5260 }
5261 }
5262 else /* yy=x+99 */
5263 {
5264 end += 4;
5265 if (*end == '+')
5266 {
5267 ++end;
5268 *p = getdigits(&end); /* positive offset */
5269 }
5270 else if (*end == '-')
5271 {
5272 ++end;
5273 *p = -getdigits(&end); /* negative offset */
5274 }
5275 }
5276 if (*end != ',')
5277 break;
5278 ++end;
5279 }
5280 }
5281 } while (idx >= 0);
5282
5283 if (!ends_excmd(*end) && !vim_iswhite(*end))
5284 {
5285 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5286 return NULL;
5287 }
5288 return skipwhite(end);
5289}
5290
5291/*
5292 * Handle ":syntax sync .." command.
5293 */
5294/* ARGSUSED */
5295 static void
5296syn_cmd_sync(eap, syncing)
5297 exarg_T *eap;
5298 int syncing; /* not used */
5299{
5300 char_u *arg_start = eap->arg;
5301 char_u *arg_end;
5302 char_u *key = NULL;
5303 char_u *next_arg;
5304 int illegal = FALSE;
5305 int finished = FALSE;
5306 long n;
5307 char_u *cpo_save;
5308
5309 if (ends_excmd(*arg_start))
5310 {
5311 syn_cmd_list(eap, TRUE);
5312 return;
5313 }
5314
5315 while (!ends_excmd(*arg_start))
5316 {
5317 arg_end = skiptowhite(arg_start);
5318 next_arg = skipwhite(arg_end);
5319 vim_free(key);
5320 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5321 if (STRCMP(key, "CCOMMENT") == 0)
5322 {
5323 if (!eap->skip)
5324 curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5325 if (!ends_excmd(*next_arg))
5326 {
5327 arg_end = skiptowhite(next_arg);
5328 if (!eap->skip)
5329 curbuf->b_syn_sync_id = syn_check_group(next_arg,
5330 (int)(arg_end - next_arg));
5331 next_arg = skipwhite(arg_end);
5332 }
5333 else if (!eap->skip)
5334 curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5335 }
5336 else if ( STRNCMP(key, "LINES", 5) == 0
5337 || STRNCMP(key, "MINLINES", 8) == 0
5338 || STRNCMP(key, "MAXLINES", 8) == 0
5339 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5340 {
5341 if (key[4] == 'S')
5342 arg_end = key + 6;
5343 else if (key[0] == 'L')
5344 arg_end = key + 11;
5345 else
5346 arg_end = key + 9;
5347 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5348 {
5349 illegal = TRUE;
5350 break;
5351 }
5352 n = getdigits(&arg_end);
5353 if (!eap->skip)
5354 {
5355 if (key[4] == 'B')
5356 curbuf->b_syn_sync_linebreaks = n;
5357 else if (key[1] == 'A')
5358 curbuf->b_syn_sync_maxlines = n;
5359 else
5360 curbuf->b_syn_sync_minlines = n;
5361 }
5362 }
5363 else if (STRCMP(key, "FROMSTART") == 0)
5364 {
5365 if (!eap->skip)
5366 {
5367 curbuf->b_syn_sync_minlines = MAXLNUM;
5368 curbuf->b_syn_sync_maxlines = 0;
5369 }
5370 }
5371 else if (STRCMP(key, "LINECONT") == 0)
5372 {
5373 if (curbuf->b_syn_linecont_pat != NULL)
5374 {
5375 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5376 finished = TRUE;
5377 break;
5378 }
5379 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5380 if (*arg_end != *next_arg) /* end delimiter not found */
5381 {
5382 illegal = TRUE;
5383 break;
5384 }
5385
5386 if (!eap->skip)
5387 {
5388 /* store the pattern and compiled regexp program */
5389 if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5390 (int)(arg_end - next_arg - 1))) == NULL)
5391 {
5392 finished = TRUE;
5393 break;
5394 }
5395 curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5396
5397 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5398 cpo_save = p_cpo;
5399 p_cpo = (char_u *)"";
5400 curbuf->b_syn_linecont_prog =
5401 vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5402 p_cpo = cpo_save;
5403
5404 if (curbuf->b_syn_linecont_prog == NULL)
5405 {
5406 vim_free(curbuf->b_syn_linecont_pat);
5407 curbuf->b_syn_linecont_pat = NULL;
5408 finished = TRUE;
5409 break;
5410 }
5411 }
5412 next_arg = skipwhite(arg_end + 1);
5413 }
5414 else
5415 {
5416 eap->arg = next_arg;
5417 if (STRCMP(key, "MATCH") == 0)
5418 syn_cmd_match(eap, TRUE);
5419 else if (STRCMP(key, "REGION") == 0)
5420 syn_cmd_region(eap, TRUE);
5421 else if (STRCMP(key, "CLEAR") == 0)
5422 syn_cmd_clear(eap, TRUE);
5423 else
5424 illegal = TRUE;
5425 finished = TRUE;
5426 break;
5427 }
5428 arg_start = next_arg;
5429 }
5430 vim_free(key);
5431 if (illegal)
5432 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5433 else if (!finished)
5434 {
5435 eap->nextcmd = check_nextcmd(arg_start);
5436 redraw_curbuf_later(NOT_VALID);
5437 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5438 }
5439}
5440
5441/*
5442 * Convert a line of highlight group names into a list of group ID numbers.
5443 * "arg" should point to the "contains" or "nextgroup" keyword.
5444 * "arg" is advanced to after the last group name.
5445 * Careful: the argument is modified (NULs added).
5446 * returns FAIL for some error, OK for success.
5447 */
5448 static int
5449get_id_list(arg, keylen, list)
5450 char_u **arg;
5451 int keylen; /* length of keyword */
5452 short **list; /* where to store the resulting list, if not
5453 NULL, the list is silently skipped! */
5454{
5455 char_u *p = NULL;
5456 char_u *end;
5457 int round;
5458 int count;
5459 int total_count = 0;
5460 short *retval = NULL;
5461 char_u *name;
5462 regmatch_T regmatch;
5463 int id;
5464 int i;
5465 int failed = FALSE;
5466
5467 /*
5468 * We parse the list twice:
5469 * round == 1: count the number of items, allocate the array.
5470 * round == 2: fill the array with the items.
5471 * In round 1 new groups may be added, causing the number of items to
5472 * grow when a regexp is used. In that case round 1 is done once again.
5473 */
5474 for (round = 1; round <= 2; ++round)
5475 {
5476 /*
5477 * skip "contains"
5478 */
5479 p = skipwhite(*arg + keylen);
5480 if (*p != '=')
5481 {
5482 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5483 break;
5484 }
5485 p = skipwhite(p + 1);
5486 if (ends_excmd(*p))
5487 {
5488 EMSG2(_("E406: Empty argument: %s"), *arg);
5489 break;
5490 }
5491
5492 /*
5493 * parse the arguments after "contains"
5494 */
5495 count = 0;
5496 while (!ends_excmd(*p))
5497 {
5498 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5499 ;
5500 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5501 if (name == NULL)
5502 {
5503 failed = TRUE;
5504 break;
5505 }
5506 STRNCPY(name + 1, p, end - p);
5507 name[end - p + 1] = NUL;
5508 if ( STRCMP(name + 1, "ALLBUT") == 0
5509 || STRCMP(name + 1, "ALL") == 0
5510 || STRCMP(name + 1, "TOP") == 0
5511 || STRCMP(name + 1, "CONTAINED") == 0)
5512 {
5513 if (TOUPPER_ASC(**arg) != 'C')
5514 {
5515 EMSG2(_("E407: %s not allowed here"), name + 1);
5516 failed = TRUE;
5517 vim_free(name);
5518 break;
5519 }
5520 if (count != 0)
5521 {
5522 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5523 failed = TRUE;
5524 vim_free(name);
5525 break;
5526 }
5527 if (name[1] == 'A')
5528 id = SYNID_ALLBUT;
5529 else if (name[1] == 'T')
5530 id = SYNID_TOP;
5531 else
5532 id = SYNID_CONTAINED;
5533 id += current_syn_inc_tag;
5534 }
5535 else if (name[1] == '@')
5536 {
5537 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5538 }
5539 else
5540 {
5541 /*
5542 * Handle full group name.
5543 */
5544 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5545 id = syn_check_group(name + 1, (int)(end - p));
5546 else
5547 {
5548 /*
5549 * Handle match of regexp with group names.
5550 */
5551 *name = '^';
5552 STRCAT(name, "$");
5553 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5554 if (regmatch.regprog == NULL)
5555 {
5556 failed = TRUE;
5557 vim_free(name);
5558 break;
5559 }
5560
5561 regmatch.rm_ic = TRUE;
5562 id = 0;
5563 for (i = highlight_ga.ga_len; --i >= 0; )
5564 {
5565 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5566 (colnr_T)0))
5567 {
5568 if (round == 2)
5569 {
5570 /* Got more items than expected; can happen
5571 * when adding items that match:
5572 * "contains=a.*b,axb".
5573 * Go back to first round */
5574 if (count >= total_count)
5575 {
5576 vim_free(retval);
5577 round = 1;
5578 }
5579 else
5580 retval[count] = i + 1;
5581 }
5582 ++count;
5583 id = -1; /* remember that we found one */
5584 }
5585 }
5586 vim_free(regmatch.regprog);
5587 }
5588 }
5589 vim_free(name);
5590 if (id == 0)
5591 {
5592 EMSG2(_("E409: Unknown group name: %s"), p);
5593 failed = TRUE;
5594 break;
5595 }
5596 if (id > 0)
5597 {
5598 if (round == 2)
5599 {
5600 /* Got more items than expected, go back to first round */
5601 if (count >= total_count)
5602 {
5603 vim_free(retval);
5604 round = 1;
5605 }
5606 else
5607 retval[count] = id;
5608 }
5609 ++count;
5610 }
5611 p = skipwhite(end);
5612 if (*p != ',')
5613 break;
5614 p = skipwhite(p + 1); /* skip comma in between arguments */
5615 }
5616 if (failed)
5617 break;
5618 if (round == 1)
5619 {
5620 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5621 if (retval == NULL)
5622 break;
5623 retval[count] = 0; /* zero means end of the list */
5624 total_count = count;
5625 }
5626 }
5627
5628 *arg = p;
5629 if (failed || retval == NULL)
5630 {
5631 vim_free(retval);
5632 return FAIL;
5633 }
5634
5635 if (*list == NULL)
5636 *list = retval;
5637 else
5638 vim_free(retval); /* list already found, don't overwrite it */
5639
5640 return OK;
5641}
5642
5643/*
5644 * Make a copy of an ID list.
5645 */
5646 static short *
5647copy_id_list(list)
5648 short *list;
5649{
5650 int len;
5651 int count;
5652 short *retval;
5653
5654 if (list == NULL)
5655 return NULL;
5656
5657 for (count = 0; list[count]; ++count)
5658 ;
5659 len = (count + 1) * sizeof(short);
5660 retval = (short *)alloc((unsigned)len);
5661 if (retval != NULL)
5662 mch_memmove(retval, list, (size_t)len);
5663
5664 return retval;
5665}
5666
5667/*
5668 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5669 * "cur_si" can be NULL if not checking the "containedin" list.
5670 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5671 * the current item.
5672 * This function is called very often, keep it fast!!
5673 */
5674 static int
5675in_id_list(cur_si, list, ssp, contained)
5676 stateitem_T *cur_si; /* current item or NULL */
5677 short *list; /* id list */
5678 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
5679 int contained; /* group id is contained */
5680{
5681 int retval;
5682 short *scl_list;
5683 short item;
5684 short id = ssp->id;
5685 static int depth = 0;
5686 int r;
5687
5688 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00005689 if (cur_si != NULL && ssp->cont_in_list != NULL
5690 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005691 {
5692 /* Ignore transparent items without a contains argument. Double check
5693 * that we don't go back past the first one. */
5694 while ((cur_si->si_flags & HL_TRANS_CONT)
5695 && cur_si > (stateitem_T *)(current_state.ga_data))
5696 --cur_si;
5697 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
5698 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5699 &(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5700 SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5701 return TRUE;
5702 }
5703
5704 if (list == NULL)
5705 return FALSE;
5706
5707 /*
5708 * If list is ID_LIST_ALL, we are in a transparent item that isn't
5709 * inside anything. Only allow not-contained groups.
5710 */
5711 if (list == ID_LIST_ALL)
5712 return !contained;
5713
5714 /*
5715 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5716 * contains list. We also require that "id" is at the same ":syn include"
5717 * level as the list.
5718 */
5719 item = *list;
5720 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5721 {
5722 if (item < SYNID_TOP)
5723 {
5724 /* ALL or ALLBUT: accept all groups in the same file */
5725 if (item - SYNID_ALLBUT != ssp->inc_tag)
5726 return FALSE;
5727 }
5728 else if (item < SYNID_CONTAINED)
5729 {
5730 /* TOP: accept all not-contained groups in the same file */
5731 if (item - SYNID_TOP != ssp->inc_tag || contained)
5732 return FALSE;
5733 }
5734 else
5735 {
5736 /* CONTAINED: accept all contained groups in the same file */
5737 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5738 return FALSE;
5739 }
5740 item = *++list;
5741 retval = FALSE;
5742 }
5743 else
5744 retval = TRUE;
5745
5746 /*
5747 * Return "retval" if id is in the contains list.
5748 */
5749 while (item != 0)
5750 {
5751 if (item == id)
5752 return retval;
5753 if (item >= SYNID_CLUSTER)
5754 {
5755 scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5756 /* restrict recursiveness to 30 to avoid an endless loop for a
5757 * cluster that includes itself (indirectly) */
5758 if (scl_list != NULL && depth < 30)
5759 {
5760 ++depth;
5761 r = in_id_list(NULL, scl_list, ssp, contained);
5762 --depth;
5763 if (r)
5764 return retval;
5765 }
5766 }
5767 item = *++list;
5768 }
5769 return !retval;
5770}
5771
5772struct subcommand
5773{
5774 char *name; /* subcommand name */
5775 void (*func)__ARGS((exarg_T *, int)); /* function to call */
5776};
5777
5778static struct subcommand subcommands[] =
5779{
5780 {"case", syn_cmd_case},
5781 {"clear", syn_cmd_clear},
5782 {"cluster", syn_cmd_cluster},
5783 {"enable", syn_cmd_enable},
5784 {"include", syn_cmd_include},
5785 {"keyword", syn_cmd_keyword},
5786 {"list", syn_cmd_list},
5787 {"manual", syn_cmd_manual},
5788 {"match", syn_cmd_match},
5789 {"on", syn_cmd_on},
5790 {"off", syn_cmd_off},
5791 {"region", syn_cmd_region},
5792 {"reset", syn_cmd_reset},
5793 {"sync", syn_cmd_sync},
5794 {"", syn_cmd_list},
5795 {NULL, NULL}
5796};
5797
5798/*
5799 * ":syntax".
5800 * This searches the subcommands[] table for the subcommand name, and calls a
5801 * syntax_subcommand() function to do the rest.
5802 */
5803 void
5804ex_syntax(eap)
5805 exarg_T *eap;
5806{
5807 char_u *arg = eap->arg;
5808 char_u *subcmd_end;
5809 char_u *subcmd_name;
5810 int i;
5811
5812 syn_cmdlinep = eap->cmdlinep;
5813
5814 /* isolate subcommand name */
5815 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5816 ;
5817 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5818 if (subcmd_name != NULL)
5819 {
5820 if (eap->skip) /* skip error messages for all subcommands */
5821 ++emsg_skip;
5822 for (i = 0; ; ++i)
5823 {
5824 if (subcommands[i].name == NULL)
5825 {
5826 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5827 break;
5828 }
5829 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5830 {
5831 eap->arg = skipwhite(subcmd_end);
5832 (subcommands[i].func)(eap, FALSE);
5833 break;
5834 }
5835 }
5836 vim_free(subcmd_name);
5837 if (eap->skip)
5838 --emsg_skip;
5839 }
5840}
5841
5842 int
5843syntax_present(buf)
5844 buf_T *buf;
5845{
5846 return (buf->b_syn_patterns.ga_len != 0
5847 || buf->b_syn_clusters.ga_len != 0
Bram Moolenaardad6b692005-01-25 22:14:34 +00005848 || curbuf->b_keywtab.ht_used > 0
5849 || curbuf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005850}
5851
5852#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5853
5854static enum
5855{
5856 EXP_SUBCMD, /* expand ":syn" sub-commands */
5857 EXP_CASE /* expand ":syn case" arguments */
5858} expand_what;
5859
5860
5861/*
5862 * Handle command line completion for :syntax command.
5863 */
5864 void
5865set_context_in_syntax_cmd(xp, arg)
5866 expand_T *xp;
5867 char_u *arg;
5868{
5869 char_u *p;
5870
5871 /* Default: expand subcommands */
5872 xp->xp_context = EXPAND_SYNTAX;
5873 expand_what = EXP_SUBCMD;
5874 xp->xp_pattern = arg;
5875 include_link = FALSE;
5876 include_default = FALSE;
5877
5878 /* (part of) subcommand already typed */
5879 if (*arg != NUL)
5880 {
5881 p = skiptowhite(arg);
5882 if (*p != NUL) /* past first word */
5883 {
5884 xp->xp_pattern = skipwhite(p);
5885 if (*skiptowhite(xp->xp_pattern) != NUL)
5886 xp->xp_context = EXPAND_NOTHING;
5887 else if (STRNICMP(arg, "case", p - arg) == 0)
5888 expand_what = EXP_CASE;
5889 else if ( STRNICMP(arg, "keyword", p - arg) == 0
5890 || STRNICMP(arg, "region", p - arg) == 0
5891 || STRNICMP(arg, "match", p - arg) == 0
5892 || STRNICMP(arg, "list", p - arg) == 0)
5893 xp->xp_context = EXPAND_HIGHLIGHT;
5894 else
5895 xp->xp_context = EXPAND_NOTHING;
5896 }
5897 }
5898}
5899
5900static char *(case_args[]) = {"match", "ignore", NULL};
5901
5902/*
5903 * Function given to ExpandGeneric() to obtain the list syntax names for
5904 * expansion.
5905 */
5906/*ARGSUSED*/
5907 char_u *
5908get_syntax_name(xp, idx)
5909 expand_T *xp;
5910 int idx;
5911{
5912 if (expand_what == EXP_SUBCMD)
5913 return (char_u *)subcommands[idx].name;
5914 return (char_u *)case_args[idx];
5915}
5916
5917#endif /* FEAT_CMDL_COMPL */
5918
5919#if defined(FEAT_EVAL) || defined(FEAT_PRINTER) || defined(PROTO)
5920/*
5921 * Function called for expression evaluation: get syntax ID at file position.
5922 */
5923 int
5924syn_get_id(lnum, col, trans)
5925 long lnum;
5926 long col;
5927 int trans; /* remove transparancy */
5928{
5929 /* When the position is not after the current position and in the same
5930 * line of the same buffer, need to restart parsing. */
5931 if (curwin->w_buffer != syn_buf
5932 || lnum != current_lnum
5933 || col < (long)current_col)
5934 syntax_start(curwin, lnum);
5935
Bram Moolenaar217ad922005-03-20 22:37:15 +00005936 (void)get_syntax_attr((colnr_T)col, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005937
5938 return (trans ? current_trans_id : current_id);
5939}
5940#endif
5941
5942#if defined(FEAT_FOLDING) || defined(PROTO)
5943/*
5944 * Function called to get folding level for line "lnum" in window "wp".
5945 */
5946 int
5947syn_get_foldlevel(wp, lnum)
5948 win_T *wp;
5949 long lnum;
5950{
5951 int level = 0;
5952 int i;
5953
5954 /* Return quickly when there are no fold items at all. */
5955 if (wp->w_buffer->b_syn_folditems != 0)
5956 {
5957 syntax_start(wp, lnum);
5958
5959 for (i = 0; i < current_state.ga_len; ++i)
5960 if (CUR_STATE(i).si_flags & HL_FOLD)
5961 ++level;
5962 }
5963 if (level > wp->w_p_fdn)
5964 level = wp->w_p_fdn;
5965 return level;
5966}
5967#endif
5968
5969#endif /* FEAT_SYN_HL */
5970
5971
5972/**************************************
5973 * Highlighting stuff *
5974 **************************************/
5975
5976/*
5977 * The default highlight groups. These are compiled-in for fast startup and
5978 * they still work when the runtime files can't be found.
5979 * When making changes here, also change runtime/colors/default.vim!
5980 */
5981static char *(highlight_init_both[]) =
5982 {
5983#ifdef FEAT_GUI
5984 "Cursor guibg=fg guifg=bg",
5985 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
5986#endif
5987 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
5988 "IncSearch term=reverse cterm=reverse gui=reverse",
5989 "ModeMsg term=bold cterm=bold gui=bold",
5990 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue",
5991 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold",
5992 "StatusLineNC term=reverse cterm=reverse gui=reverse",
5993 "VertSplit term=reverse cterm=reverse gui=reverse",
5994 "Visual term=reverse cterm=reverse gui=reverse guifg=Grey guibg=fg",
5995 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold",
5996 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red",
5997 NULL
5998 };
5999
6000static char *(highlight_init_light[]) =
6001 {
6002 "Directory term=bold ctermfg=DarkBlue guifg=Blue",
6003 "LineNr term=underline ctermfg=Brown guifg=Brown",
6004 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6005 "Normal gui=NONE",
6006 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6007 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
Bram Moolenaar217ad922005-03-20 22:37:15 +00006008 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl",
6009 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl",
6010 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006011 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue",
6012 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta",
6013 "WarningMsg term=standout ctermfg=DarkRed guifg=Red",
6014 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6015 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
6016 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6017 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6018 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue",
6019 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta",
6020 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
6021 NULL
6022 };
6023
6024static char *(highlight_init_dark[]) =
6025 {
6026 "Directory term=bold ctermfg=LightCyan guifg=Cyan",
6027 "LineNr term=underline ctermfg=Yellow guifg=Yellow",
6028 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen",
6029 "Normal gui=NONE",
6030 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green",
6031 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6032 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan",
Bram Moolenaar217ad922005-03-20 22:37:15 +00006033 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl",
6034 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl",
6035 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006036 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta",
6037 "WarningMsg term=standout ctermfg=LightRed guifg=Red",
6038 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6039 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
6040 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6041 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6042 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue",
6043 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta",
6044 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
6045 NULL
6046 };
6047
6048 void
6049init_highlight(both, reset)
6050 int both; /* include groups where 'bg' doesn't matter */
6051 int reset; /* clear group first */
6052{
6053 int i;
6054 char **pp;
6055 static int had_both = FALSE;
6056#ifdef FEAT_EVAL
6057 char_u *p;
6058
6059 /*
6060 * Try finding the color scheme file. Used when a color file was loaded
6061 * and 'background' or 't_Co' is changed.
6062 */
6063 p = get_var_value((char_u *)"g:colors_name");
6064 if (p != NULL && load_colors(p) == OK)
6065 return;
6066#endif
6067
6068 /*
6069 * Didn't use a color file, use the compiled-in colors.
6070 */
6071 if (both)
6072 {
6073 had_both = TRUE;
6074 pp = highlight_init_both;
6075 for (i = 0; pp[i] != NULL; ++i)
6076 do_highlight((char_u *)pp[i], reset, TRUE);
6077 }
6078 else if (!had_both)
6079 /* Don't do anything before the call with both == TRUE from main().
6080 * Not everything has been setup then, and that call will overrule
6081 * everything anyway. */
6082 return;
6083
6084 if (*p_bg == 'l')
6085 pp = highlight_init_light;
6086 else
6087 pp = highlight_init_dark;
6088 for (i = 0; pp[i] != NULL; ++i)
6089 do_highlight((char_u *)pp[i], reset, TRUE);
6090
6091#ifdef FEAT_SYN_HL
6092 /*
6093 * If syntax highlighting is enabled load the highlighting for it.
6094 */
6095 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006096 {
6097 static int recursive = 0;
6098
6099 if (recursive >= 5)
6100 EMSG(_("E679: recursive loop loading syncolor.vim"));
6101 else
6102 {
6103 ++recursive;
6104 (void)cmd_runtime((char_u *)"syntax/syncolor.vim", TRUE);
6105 --recursive;
6106 }
6107 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006108#endif
6109}
6110
6111/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006112 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006113 * Return OK for success, FAIL for failure.
6114 */
6115 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006116load_colors(name)
6117 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006118{
6119 char_u *buf;
6120 int retval = FAIL;
6121 static int recursive = FALSE;
6122
6123 /* When being called recursively, this is probably because setting
6124 * 'background' caused the highlighting to be reloaded. This means it is
6125 * working, thus we should return OK. */
6126 if (recursive)
6127 return OK;
6128
6129 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006130 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006131 if (buf != NULL)
6132 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006133 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006134 retval = cmd_runtime(buf, FALSE);
6135 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006136#ifdef FEAT_AUTOCMD
6137 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6138#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006139 }
6140 recursive = FALSE;
6141
6142 return retval;
6143}
6144
6145/*
6146 * Handle the ":highlight .." command.
6147 * When using ":hi clear" this is called recursively for each group with
6148 * "forceit" and "init" both TRUE.
6149 */
6150 void
6151do_highlight(line, forceit, init)
6152 char_u *line;
6153 int forceit;
6154 int init; /* TRUE when called for initializing */
6155{
6156 char_u *name_end;
6157 char_u *p;
6158 char_u *linep;
6159 char_u *key_start;
6160 char_u *arg_start;
6161 char_u *key = NULL, *arg = NULL;
6162 long i;
6163 int off;
6164 int len;
6165 int attr;
6166 int id;
6167 int idx;
6168 int dodefault = FALSE;
6169 int doclear = FALSE;
6170 int dolink = FALSE;
6171 int error = FALSE;
6172 int color;
6173 int is_normal_group = FALSE; /* "Normal" group */
6174#ifdef FEAT_GUI_X11
6175 int is_menu_group = FALSE; /* "Menu" group */
6176 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6177 int is_tooltip_group = FALSE; /* "Tooltip" group */
6178 int do_colors = FALSE; /* need to update colors? */
6179#else
6180# define is_menu_group 0
6181# define is_tooltip_group 0
6182#endif
6183
6184 /*
6185 * If no argument, list current highlighting.
6186 */
6187 if (ends_excmd(*line))
6188 {
6189 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6190 /* TODO: only call when the group has attributes set */
6191 highlight_list_one((int)i);
6192 return;
6193 }
6194
6195 /*
6196 * Isolate the name.
6197 */
6198 name_end = skiptowhite(line);
6199 linep = skipwhite(name_end);
6200
6201 /*
6202 * Check for "default" argument.
6203 */
6204 if (STRNCMP(line, "default", name_end - line) == 0)
6205 {
6206 dodefault = TRUE;
6207 line = linep;
6208 name_end = skiptowhite(line);
6209 linep = skipwhite(name_end);
6210 }
6211
6212 /*
6213 * Check for "clear" or "link" argument.
6214 */
6215 if (STRNCMP(line, "clear", name_end - line) == 0)
6216 doclear = TRUE;
6217 if (STRNCMP(line, "link", name_end - line) == 0)
6218 dolink = TRUE;
6219
6220 /*
6221 * ":highlight {group-name}": list highlighting for one group.
6222 */
6223 if (!doclear && !dolink && ends_excmd(*linep))
6224 {
6225 id = syn_namen2id(line, (int)(name_end - line));
6226 if (id == 0)
6227 EMSG2(_("E411: highlight group not found: %s"), line);
6228 else
6229 highlight_list_one(id);
6230 return;
6231 }
6232
6233 /*
6234 * Handle ":highlight link {from} {to}" command.
6235 */
6236 if (dolink)
6237 {
6238 char_u *from_start = linep;
6239 char_u *from_end;
6240 char_u *to_start;
6241 char_u *to_end;
6242 int from_id;
6243 int to_id;
6244
6245 from_end = skiptowhite(from_start);
6246 to_start = skipwhite(from_end);
6247 to_end = skiptowhite(to_start);
6248
6249 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6250 {
6251 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6252 from_start);
6253 return;
6254 }
6255
6256 if (!ends_excmd(*skipwhite(to_end)))
6257 {
6258 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6259 return;
6260 }
6261
6262 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6263 if (STRNCMP(to_start, "NONE", 4) == 0)
6264 to_id = 0;
6265 else
6266 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6267
6268 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6269 {
6270 /*
6271 * Don't allow a link when there already is some highlighting
6272 * for the group, unless '!' is used
6273 */
6274 if (to_id > 0 && !forceit && !init
6275 && hl_has_settings(from_id - 1, dodefault))
6276 {
6277 if (sourcing_name == NULL && !dodefault)
6278 EMSG(_("E414: group has settings, highlight link ignored"));
6279 }
6280 else
6281 {
6282 if (!init)
6283 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6284 HL_TABLE()[from_id - 1].sg_link = to_id;
6285 redraw_all_later(NOT_VALID);
6286 }
6287 }
6288
6289 /* Only call highlight_changed() once, after sourcing a syntax file */
6290 need_highlight_changed = TRUE;
6291
6292 return;
6293 }
6294
6295 if (doclear)
6296 {
6297 /*
6298 * ":highlight clear [group]" command.
6299 */
6300 line = linep;
6301 if (ends_excmd(*line))
6302 {
6303#ifdef FEAT_GUI
6304 /* First, we do not destroy the old values, but allocate the new
6305 * ones and update the display. THEN we destroy the old values.
6306 * If we destroy the old values first, then the old values
6307 * (such as GuiFont's or GuiFontset's) will still be displayed but
6308 * invalid because they were free'd.
6309 */
6310 if (gui.in_use)
6311 {
6312# ifdef FEAT_BEVAL_TIP
6313 gui_init_tooltip_font();
6314# endif
6315# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6316 gui_init_menu_font();
6317# endif
6318 }
6319# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6320 gui_mch_def_colors();
6321# endif
6322# ifdef FEAT_GUI_X11
6323# ifdef FEAT_MENU
6324
6325 /* This only needs to be done when there is no Menu highlight
6326 * group defined by default, which IS currently the case.
6327 */
6328 gui_mch_new_menu_colors();
6329# endif
6330 if (gui.in_use)
6331 {
6332 gui_new_scrollbar_colors();
6333# ifdef FEAT_BEVAL
6334 gui_mch_new_tooltip_colors();
6335# endif
6336# ifdef FEAT_MENU
6337 gui_mch_new_menu_font();
6338# endif
6339 }
6340# endif
6341
6342 /* Ok, we're done allocating the new default graphics items.
6343 * The screen should already be refreshed at this point.
6344 * It is now Ok to clear out the old data.
6345 */
6346#endif
6347#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006348 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006349#endif
6350 restore_cterm_colors();
6351
6352 /*
6353 * Clear all default highlight groups and load the defaults.
6354 */
6355 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6356 highlight_clear(idx);
6357 init_highlight(TRUE, TRUE);
6358#ifdef FEAT_GUI
6359 if (gui.in_use)
6360 highlight_gui_started();
6361#endif
6362 highlight_changed();
6363 redraw_later_clear();
6364 return;
6365 }
6366 name_end = skiptowhite(line);
6367 linep = skipwhite(name_end);
6368 }
6369
6370 /*
6371 * Find the group name in the table. If it does not exist yet, add it.
6372 */
6373 id = syn_check_group(line, (int)(name_end - line));
6374 if (id == 0) /* failed (out of memory) */
6375 return;
6376 idx = id - 1; /* index is ID minus one */
6377
6378 /* Return if "default" was used and the group already has settings. */
6379 if (dodefault && hl_has_settings(idx, TRUE))
6380 return;
6381
6382 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6383 is_normal_group = TRUE;
6384#ifdef FEAT_GUI_X11
6385 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6386 is_menu_group = TRUE;
6387 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6388 is_scrollbar_group = TRUE;
6389 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6390 is_tooltip_group = TRUE;
6391#endif
6392
6393 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6394 if (doclear || (forceit && init))
6395 {
6396 highlight_clear(idx);
6397 if (!doclear)
6398 HL_TABLE()[idx].sg_set = 0;
6399 }
6400
6401 if (!doclear)
6402 while (!ends_excmd(*linep))
6403 {
6404 key_start = linep;
6405 if (*linep == '=')
6406 {
6407 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6408 error = TRUE;
6409 break;
6410 }
6411
6412 /*
6413 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6414 * "guibg").
6415 */
6416 while (*linep && !vim_iswhite(*linep) && *linep != '=')
6417 ++linep;
6418 vim_free(key);
6419 key = vim_strnsave_up(key_start, (int)(linep - key_start));
6420 if (key == NULL)
6421 {
6422 error = TRUE;
6423 break;
6424 }
6425 linep = skipwhite(linep);
6426
6427 if (STRCMP(key, "NONE") == 0)
6428 {
6429 if (!init || HL_TABLE()[idx].sg_set == 0)
6430 {
6431 if (!init)
6432 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6433 highlight_clear(idx);
6434 }
6435 continue;
6436 }
6437
6438 /*
6439 * Check for the equal sign.
6440 */
6441 if (*linep != '=')
6442 {
6443 EMSG2(_("E416: missing equal sign: %s"), key_start);
6444 error = TRUE;
6445 break;
6446 }
6447 ++linep;
6448
6449 /*
6450 * Isolate the argument.
6451 */
6452 linep = skipwhite(linep);
6453 if (*linep == '\'') /* guifg='color name' */
6454 {
6455 arg_start = ++linep;
6456 linep = vim_strchr(linep, '\'');
6457 if (linep == NULL)
6458 {
6459 EMSG2(_(e_invarg2), key_start);
6460 error = TRUE;
6461 break;
6462 }
6463 }
6464 else
6465 {
6466 arg_start = linep;
6467 linep = skiptowhite(linep);
6468 }
6469 if (linep == arg_start)
6470 {
6471 EMSG2(_("E417: missing argument: %s"), key_start);
6472 error = TRUE;
6473 break;
6474 }
6475 vim_free(arg);
6476 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6477 if (arg == NULL)
6478 {
6479 error = TRUE;
6480 break;
6481 }
6482 if (*linep == '\'')
6483 ++linep;
6484
6485 /*
6486 * Store the argument.
6487 */
6488 if ( STRCMP(key, "TERM") == 0
6489 || STRCMP(key, "CTERM") == 0
6490 || STRCMP(key, "GUI") == 0)
6491 {
6492 attr = 0;
6493 off = 0;
6494 while (arg[off] != NUL)
6495 {
6496 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6497 {
6498 len = (int)STRLEN(hl_name_table[i]);
6499 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6500 {
6501 attr |= hl_attr_table[i];
6502 off += len;
6503 break;
6504 }
6505 }
6506 if (i < 0)
6507 {
6508 EMSG2(_("E418: Illegal value: %s"), arg);
6509 error = TRUE;
6510 break;
6511 }
6512 if (arg[off] == ',') /* another one follows */
6513 ++off;
6514 }
6515 if (error)
6516 break;
6517 if (*key == 'T')
6518 {
6519 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6520 {
6521 if (!init)
6522 HL_TABLE()[idx].sg_set |= SG_TERM;
6523 HL_TABLE()[idx].sg_term = attr;
6524 }
6525 }
6526 else if (*key == 'C')
6527 {
6528 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6529 {
6530 if (!init)
6531 HL_TABLE()[idx].sg_set |= SG_CTERM;
6532 HL_TABLE()[idx].sg_cterm = attr;
6533 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6534 }
6535 }
6536#ifdef FEAT_GUI
6537 else
6538 {
6539 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6540 {
6541 if (!init)
6542 HL_TABLE()[idx].sg_set |= SG_GUI;
6543 HL_TABLE()[idx].sg_gui = attr;
6544 }
6545 }
6546#endif
6547 }
6548 else if (STRCMP(key, "FONT") == 0)
6549 {
6550 /* in non-GUI fonts are simply ignored */
6551#ifdef FEAT_GUI
6552 if (!gui.shell_created)
6553 {
6554 /* GUI not started yet, always accept the name. */
6555 vim_free(HL_TABLE()[idx].sg_font_name);
6556 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6557 }
6558 else
6559 {
6560 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6561# ifdef FEAT_XFONTSET
6562 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6563# endif
6564 /* First, save the current font/fontset.
6565 * Then try to allocate the font/fontset.
6566 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6567 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6568 */
6569
6570 HL_TABLE()[idx].sg_font = NOFONT;
6571# ifdef FEAT_XFONTSET
6572 HL_TABLE()[idx].sg_fontset = NOFONTSET;
6573# endif
6574 hl_do_font(idx, arg, is_normal_group, is_menu_group,
6575 is_tooltip_group);
6576
6577# ifdef FEAT_XFONTSET
6578 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6579 {
6580 /* New fontset was accepted. Free the old one, if there was
6581 * one.
6582 */
6583 gui_mch_free_fontset(temp_sg_fontset);
6584 vim_free(HL_TABLE()[idx].sg_font_name);
6585 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6586 }
6587 else
6588 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6589# endif
6590 if (HL_TABLE()[idx].sg_font != NOFONT)
6591 {
6592 /* New font was accepted. Free the old one, if there was
6593 * one.
6594 */
6595 gui_mch_free_font(temp_sg_font);
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_font = temp_sg_font;
6601 }
6602#endif
6603 }
6604 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6605 {
6606 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6607 {
6608 if (!init)
6609 HL_TABLE()[idx].sg_set |= SG_CTERM;
6610
6611 /* When setting the foreground color, and previously the "bold"
6612 * flag was set for a light color, reset it now */
6613 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6614 {
6615 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6616 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6617 }
6618
6619 if (VIM_ISDIGIT(*arg))
6620 color = atoi((char *)arg);
6621 else if (STRICMP(arg, "fg") == 0)
6622 {
6623 if (cterm_normal_fg_color)
6624 color = cterm_normal_fg_color - 1;
6625 else
6626 {
6627 EMSG(_("E419: FG color unknown"));
6628 error = TRUE;
6629 break;
6630 }
6631 }
6632 else if (STRICMP(arg, "bg") == 0)
6633 {
6634 if (cterm_normal_bg_color > 0)
6635 color = cterm_normal_bg_color - 1;
6636 else
6637 {
6638 EMSG(_("E420: BG color unknown"));
6639 error = TRUE;
6640 break;
6641 }
6642 }
6643 else
6644 {
6645 static char *(color_names[28]) = {
6646 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6647 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6648 "Gray", "Grey",
6649 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6650 "Blue", "LightBlue", "Green", "LightGreen",
6651 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6652 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6653 static int color_numbers_16[28] = {0, 1, 2, 3,
6654 4, 5, 6, 6,
6655 7, 7,
6656 7, 7, 8, 8,
6657 9, 9, 10, 10,
6658 11, 11, 12, 12, 13,
6659 13, 14, 14, 15, -1};
6660 /* for xterm with 88 colors... */
6661 static int color_numbers_88[28] = {0, 4, 2, 6,
6662 1, 5, 32, 72,
6663 84, 84,
6664 7, 7, 82, 82,
6665 12, 43, 10, 61,
6666 14, 63, 9, 74, 13,
6667 75, 11, 78, 15, -1};
6668 /* for xterm with 256 colors... */
6669 static int color_numbers_256[28] = {0, 4, 2, 6,
6670 1, 5, 130, 130,
6671 248, 248,
6672 7, 7, 242, 242,
6673 12, 81, 10, 121,
6674 14, 159, 9, 224, 13,
6675 225, 11, 229, 15, -1};
6676 /* for terminals with less than 16 colors... */
6677 static int color_numbers_8[28] = {0, 4, 2, 6,
6678 1, 5, 3, 3,
6679 7, 7,
6680 7, 7, 0+8, 0+8,
6681 4+8, 4+8, 2+8, 2+8,
6682 6+8, 6+8, 1+8, 1+8, 5+8,
6683 5+8, 3+8, 3+8, 7+8, -1};
6684#if defined(__QNXNTO__)
6685 static int *color_numbers_8_qansi = color_numbers_8;
6686 /* On qnx, the 8 & 16 color arrays are the same */
6687 if (STRNCMP(T_NAME, "qansi", 5) == 0)
6688 color_numbers_8_qansi = color_numbers_16;
6689#endif
6690
6691 /* reduce calls to STRICMP a bit, it can be slow */
6692 off = TOUPPER_ASC(*arg);
6693 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6694 if (off == color_names[i][0]
6695 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6696 break;
6697 if (i < 0)
6698 {
6699 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6700 error = TRUE;
6701 break;
6702 }
6703
6704 /* Use the _16 table to check if its a valid color name. */
6705 color = color_numbers_16[i];
6706 if (color >= 0)
6707 {
6708 if (t_colors == 8)
6709 {
6710 /* t_Co is 8: use the 8 colors table */
6711#if defined(__QNXNTO__)
6712 color = color_numbers_8_qansi[i];
6713#else
6714 color = color_numbers_8[i];
6715#endif
6716 if (key[5] == 'F')
6717 {
6718 /* set/reset bold attribute to get light foreground
6719 * colors (on some terminals, e.g. "linux") */
6720 if (color & 8)
6721 {
6722 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
6723 HL_TABLE()[idx].sg_cterm_bold = TRUE;
6724 }
6725 else
6726 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6727 }
6728 color &= 7; /* truncate to 8 colors */
6729 }
6730 else if (t_colors == 16 || t_colors == 88
6731 || t_colors == 256)
6732 {
6733 /*
6734 * Guess: if the termcap entry ends in 'm', it is
6735 * probably an xterm-like terminal. Use the changed
6736 * order for colors.
6737 */
6738 if (*T_CAF != NUL)
6739 p = T_CAF;
6740 else
6741 p = T_CSF;
6742 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
6743 switch (t_colors)
6744 {
6745 case 16:
6746 color = color_numbers_8[i];
6747 break;
6748 case 88:
6749 color = color_numbers_88[i];
6750 break;
6751 case 256:
6752 color = color_numbers_256[i];
6753 break;
6754 }
6755 }
6756 }
6757 }
6758 /* Add one to the argument, to avoid zero */
6759 if (key[5] == 'F')
6760 {
6761 HL_TABLE()[idx].sg_cterm_fg = color + 1;
6762 if (is_normal_group)
6763 {
6764 cterm_normal_fg_color = color + 1;
6765 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
6766#ifdef FEAT_GUI
6767 /* Don't do this if the GUI is used. */
6768 if (!gui.in_use && !gui.starting)
6769#endif
6770 {
6771 must_redraw = CLEAR;
6772 if (termcap_active)
6773 term_fg_color(color);
6774 }
6775 }
6776 }
6777 else
6778 {
6779 HL_TABLE()[idx].sg_cterm_bg = color + 1;
6780 if (is_normal_group)
6781 {
6782 cterm_normal_bg_color = color + 1;
6783#ifdef FEAT_GUI
6784 /* Don't mess with 'background' if the GUI is used. */
6785 if (!gui.in_use && !gui.starting)
6786#endif
6787 {
6788 must_redraw = CLEAR;
6789 if (termcap_active)
6790 term_bg_color(color);
6791 if (t_colors < 16)
6792 i = (color == 0 || color == 4);
6793 else
6794 i = (color < 7 || color == 8);
6795 /* Set the 'background' option if the value is wrong. */
6796 if (i != (*p_bg == 'd'))
6797 set_option_value((char_u *)"bg", 0L,
6798 i ? (char_u *)"dark" : (char_u *)"light", 0);
6799 }
6800 }
6801 }
6802 }
6803 }
6804 else if (STRCMP(key, "GUIFG") == 0)
6805 {
6806#ifdef FEAT_GUI /* in non-GUI guifg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006807 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006808 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006809 if (!init)
6810 HL_TABLE()[idx].sg_set |= SG_GUI;
6811
6812 i = color_name2handle(arg);
6813 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6814 {
6815 HL_TABLE()[idx].sg_gui_fg = i;
6816 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
6817 if (STRCMP(arg, "NONE"))
6818 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
6819 else
6820 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006821# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006822 if (is_menu_group)
6823 gui.menu_fg_pixel = i;
6824 if (is_scrollbar_group)
6825 gui.scroll_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006826# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006827 if (is_tooltip_group)
6828 gui.tooltip_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006829# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006830 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006831# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006832 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006833 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006834#endif
6835 }
6836 else if (STRCMP(key, "GUIBG") == 0)
6837 {
6838#ifdef FEAT_GUI /* in non-GUI guibg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006839 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006840 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006841 if (!init)
6842 HL_TABLE()[idx].sg_set |= SG_GUI;
6843
6844 i = color_name2handle(arg);
6845 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6846 {
6847 HL_TABLE()[idx].sg_gui_bg = i;
6848 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
6849 if (STRCMP(arg, "NONE") != 0)
6850 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
6851 else
6852 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006853# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006854 if (is_menu_group)
6855 gui.menu_bg_pixel = i;
6856 if (is_scrollbar_group)
6857 gui.scroll_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006858# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006859 if (is_tooltip_group)
6860 gui.tooltip_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006861# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006862 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006863# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006864 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006865 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006866#endif
6867 }
6868 else if (STRCMP(key, "GUISP") == 0)
6869 {
6870#ifdef FEAT_GUI /* in non-GUI guisp colors are simply ignored */
6871 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6872 {
6873 if (!init)
6874 HL_TABLE()[idx].sg_set |= SG_GUI;
6875
6876 i = color_name2handle(arg);
6877 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6878 {
6879 HL_TABLE()[idx].sg_gui_sp = i;
6880 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
6881 if (STRCMP(arg, "NONE") != 0)
6882 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
6883 else
6884 HL_TABLE()[idx].sg_gui_sp_name = NULL;
6885 }
6886 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006887#endif
6888 }
6889 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
6890 {
6891 char_u buf[100];
6892 char_u *tname;
6893
6894 if (!init)
6895 HL_TABLE()[idx].sg_set |= SG_TERM;
6896
6897 /*
6898 * The "start" and "stop" arguments can be a literal escape
6899 * sequence, or a comma seperated list of terminal codes.
6900 */
6901 if (STRNCMP(arg, "t_", 2) == 0)
6902 {
6903 off = 0;
6904 buf[0] = 0;
6905 while (arg[off] != NUL)
6906 {
6907 /* Isolate one termcap name */
6908 for (len = 0; arg[off + len] &&
6909 arg[off + len] != ','; ++len)
6910 ;
6911 tname = vim_strnsave(arg + off, len);
6912 if (tname == NULL) /* out of memory */
6913 {
6914 error = TRUE;
6915 break;
6916 }
6917 /* lookup the escape sequence for the item */
6918 p = get_term_code(tname);
6919 vim_free(tname);
6920 if (p == NULL) /* ignore non-existing things */
6921 p = (char_u *)"";
6922
6923 /* Append it to the already found stuff */
6924 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
6925 {
6926 EMSG2(_("E422: terminal code too long: %s"), arg);
6927 error = TRUE;
6928 break;
6929 }
6930 STRCAT(buf, p);
6931
6932 /* Advance to the next item */
6933 off += len;
6934 if (arg[off] == ',') /* another one follows */
6935 ++off;
6936 }
6937 }
6938 else
6939 {
6940 /*
6941 * Copy characters from arg[] to buf[], translating <> codes.
6942 */
6943 for (p = arg, off = 0; off < 100 && *p; )
6944 {
6945 len = trans_special(&p, buf + off, FALSE);
6946 if (len) /* recognized special char */
6947 off += len;
6948 else /* copy as normal char */
6949 buf[off++] = *p++;
6950 }
6951 buf[off] = NUL;
6952 }
6953 if (error)
6954 break;
6955
6956 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
6957 p = NULL;
6958 else
6959 p = vim_strsave(buf);
6960 if (key[2] == 'A')
6961 {
6962 vim_free(HL_TABLE()[idx].sg_start);
6963 HL_TABLE()[idx].sg_start = p;
6964 }
6965 else
6966 {
6967 vim_free(HL_TABLE()[idx].sg_stop);
6968 HL_TABLE()[idx].sg_stop = p;
6969 }
6970 }
6971 else
6972 {
6973 EMSG2(_("E423: Illegal argument: %s"), key_start);
6974 error = TRUE;
6975 break;
6976 }
6977
6978 /*
6979 * When highlighting has been given for a group, don't link it.
6980 */
6981 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
6982 HL_TABLE()[idx].sg_link = 0;
6983
6984 /*
6985 * Continue with next argument.
6986 */
6987 linep = skipwhite(linep);
6988 }
6989
6990 /*
6991 * If there is an error, and it's a new entry, remove it from the table.
6992 */
6993 if (error && idx == highlight_ga.ga_len)
6994 syn_unadd_group();
6995 else
6996 {
6997 if (is_normal_group)
6998 {
6999 HL_TABLE()[idx].sg_term_attr = 0;
7000 HL_TABLE()[idx].sg_cterm_attr = 0;
7001#ifdef FEAT_GUI
7002 HL_TABLE()[idx].sg_gui_attr = 0;
7003 /*
7004 * Need to update all groups, because they might be using "bg"
7005 * and/or "fg", which have been changed now.
7006 */
7007 if (gui.in_use)
7008 highlight_gui_started();
7009#endif
7010 }
7011#ifdef FEAT_GUI_X11
7012# ifdef FEAT_MENU
7013 else if (is_menu_group)
7014 {
7015 if (gui.in_use && do_colors)
7016 gui_mch_new_menu_colors();
7017 }
7018# endif
7019 else if (is_scrollbar_group)
7020 {
7021 if (gui.in_use && do_colors)
7022 gui_new_scrollbar_colors();
7023 }
7024# ifdef FEAT_BEVAL
7025 else if (is_tooltip_group)
7026 {
7027 if (gui.in_use && do_colors)
7028 gui_mch_new_tooltip_colors();
7029 }
7030# endif
7031#endif
7032 else
7033 set_hl_attr(idx);
7034 redraw_all_later(NOT_VALID);
7035 }
7036 vim_free(key);
7037 vim_free(arg);
7038
7039 /* Only call highlight_changed() once, after sourcing a syntax file */
7040 need_highlight_changed = TRUE;
7041}
7042
7043/*
7044 * Reset the cterm colors to what they were before Vim was started, if
7045 * possible. Otherwise reset them to zero.
7046 */
7047 void
7048restore_cterm_colors()
7049{
7050#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7051 /* Since t_me has been set, this probably means that the user
7052 * wants to use this as default colors. Need to reset default
7053 * background/foreground colors. */
7054 mch_set_normal_colors();
7055#else
7056 cterm_normal_fg_color = 0;
7057 cterm_normal_fg_bold = 0;
7058 cterm_normal_bg_color = 0;
7059#endif
7060}
7061
7062/*
7063 * Return TRUE if highlight group "idx" has any settings.
7064 * When "check_link" is TRUE also check for an existing link.
7065 */
7066 static int
7067hl_has_settings(idx, check_link)
7068 int idx;
7069 int check_link;
7070{
7071 return ( HL_TABLE()[idx].sg_term_attr != 0
7072 || HL_TABLE()[idx].sg_cterm_attr != 0
7073#ifdef FEAT_GUI
7074 || HL_TABLE()[idx].sg_gui_attr != 0
7075#endif
7076 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7077}
7078
7079/*
7080 * Clear highlighting for one group.
7081 */
7082 static void
7083highlight_clear(idx)
7084 int idx;
7085{
7086 HL_TABLE()[idx].sg_term = 0;
7087 vim_free(HL_TABLE()[idx].sg_start);
7088 HL_TABLE()[idx].sg_start = NULL;
7089 vim_free(HL_TABLE()[idx].sg_stop);
7090 HL_TABLE()[idx].sg_stop = NULL;
7091 HL_TABLE()[idx].sg_term_attr = 0;
7092 HL_TABLE()[idx].sg_cterm = 0;
7093 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7094 HL_TABLE()[idx].sg_cterm_fg = 0;
7095 HL_TABLE()[idx].sg_cterm_bg = 0;
7096 HL_TABLE()[idx].sg_cterm_attr = 0;
7097#ifdef FEAT_GUI /* in non-GUI fonts are simply ignored */
7098 HL_TABLE()[idx].sg_gui = 0;
7099 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7100 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7101 HL_TABLE()[idx].sg_gui_fg_name = NULL;
7102 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7103 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7104 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007105 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7106 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7107 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007108 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7109 HL_TABLE()[idx].sg_font = NOFONT;
7110# ifdef FEAT_XFONTSET
7111 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7112 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7113# endif
7114 vim_free(HL_TABLE()[idx].sg_font_name);
7115 HL_TABLE()[idx].sg_font_name = NULL;
7116 HL_TABLE()[idx].sg_gui_attr = 0;
7117#endif
7118}
7119
7120#if defined(FEAT_GUI) || defined(PROTO)
7121/*
7122 * Set the normal foreground and background colors according to the "Normal"
7123 * highlighighting group. For X11 also set "Menu", "Scrollbar", and
7124 * "Tooltip" colors.
7125 */
7126 void
7127set_normal_colors()
7128{
7129 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007130 &gui.norm_pixel, &gui.back_pixel,
7131 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007132 {
7133 gui_mch_new_colors();
7134 must_redraw = CLEAR;
7135 }
7136#ifdef FEAT_GUI_X11
7137 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007138 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7139 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007140 {
7141# ifdef FEAT_MENU
7142 gui_mch_new_menu_colors();
7143# endif
7144 must_redraw = CLEAR;
7145 }
7146# ifdef FEAT_BEVAL
7147 if (set_group_colors((char_u *)"Tooltip",
7148 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7149 FALSE, FALSE, TRUE))
7150 {
7151# ifdef FEAT_TOOLBAR
7152 gui_mch_new_tooltip_colors();
7153# endif
7154 must_redraw = CLEAR;
7155 }
7156#endif
7157 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007158 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7159 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007160 {
7161 gui_new_scrollbar_colors();
7162 must_redraw = CLEAR;
7163 }
7164#endif
7165}
7166
7167/*
7168 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7169 */
7170 static int
7171set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7172 char_u *name;
7173 guicolor_T *fgp;
7174 guicolor_T *bgp;
7175 int do_menu;
7176 int use_norm;
7177 int do_tooltip;
7178{
7179 int idx;
7180
7181 idx = syn_name2id(name) - 1;
7182 if (idx >= 0)
7183 {
7184 gui_do_one_color(idx, do_menu, do_tooltip);
7185
7186 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7187 *fgp = HL_TABLE()[idx].sg_gui_fg;
7188 else if (use_norm)
7189 *fgp = gui.def_norm_pixel;
7190 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7191 *bgp = HL_TABLE()[idx].sg_gui_bg;
7192 else if (use_norm)
7193 *bgp = gui.def_back_pixel;
7194 return TRUE;
7195 }
7196 return FALSE;
7197}
7198
7199/*
7200 * Get the font of the "Normal" group.
7201 * Returns "" when it's not found or not set.
7202 */
7203 char_u *
7204hl_get_font_name()
7205{
7206 int id;
7207 char_u *s;
7208
7209 id = syn_name2id((char_u *)"Normal");
7210 if (id > 0)
7211 {
7212 s = HL_TABLE()[id - 1].sg_font_name;
7213 if (s != NULL)
7214 return s;
7215 }
7216 return (char_u *)"";
7217}
7218
7219/*
7220 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7221 * actually chosen to be used.
7222 */
7223 void
7224hl_set_font_name(font_name)
7225 char_u *font_name;
7226{
7227 int id;
7228
7229 id = syn_name2id((char_u *)"Normal");
7230 if (id > 0)
7231 {
7232 vim_free(HL_TABLE()[id - 1].sg_font_name);
7233 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7234 }
7235}
7236
7237/*
7238 * Set background color for "Normal" group. Called by gui_set_bg_color()
7239 * when the color is known.
7240 */
7241 void
7242hl_set_bg_color_name(name)
7243 char_u *name; /* must have been allocated */
7244{
7245 int id;
7246
7247 if (name != NULL)
7248 {
7249 id = syn_name2id((char_u *)"Normal");
7250 if (id > 0)
7251 {
7252 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7253 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7254 }
7255 }
7256}
7257
7258/*
7259 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7260 * when the color is known.
7261 */
7262 void
7263hl_set_fg_color_name(name)
7264 char_u *name; /* must have been allocated */
7265{
7266 int id;
7267
7268 if (name != NULL)
7269 {
7270 id = syn_name2id((char_u *)"Normal");
7271 if (id > 0)
7272 {
7273 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7274 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7275 }
7276 }
7277}
7278
7279/*
7280 * Return the handle for a color name.
7281 * Returns INVALCOLOR when failed.
7282 */
7283 static guicolor_T
7284color_name2handle(name)
7285 char_u *name;
7286{
7287 if (STRCMP(name, "NONE") == 0)
7288 return INVALCOLOR;
7289
7290 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7291 return gui.norm_pixel;
7292 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7293 return gui.back_pixel;
7294
7295 return gui_get_color(name);
7296}
7297
7298/*
7299 * Return the handle for a font name.
7300 * Returns NOFONT when failed.
7301 */
7302 static GuiFont
7303font_name2handle(name)
7304 char_u *name;
7305{
7306 if (STRCMP(name, "NONE") == 0)
7307 return NOFONT;
7308
7309 return gui_mch_get_font(name, TRUE);
7310}
7311
7312# ifdef FEAT_XFONTSET
7313/*
7314 * Return the handle for a fontset name.
7315 * Returns NOFONTSET when failed.
7316 */
7317 static GuiFontset
7318fontset_name2handle(name, fixed_width)
7319 char_u *name;
7320 int fixed_width;
7321{
7322 if (STRCMP(name, "NONE") == 0)
7323 return NOFONTSET;
7324
7325 return gui_mch_get_fontset(name, TRUE, fixed_width);
7326}
7327# endif
7328
7329/*
7330 * Get the font or fontset for one highlight group.
7331 */
7332/*ARGSUSED*/
7333 static void
7334hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7335 int idx;
7336 char_u *arg;
7337 int do_normal; /* set normal font */
7338 int do_menu; /* set menu font */
7339 int do_tooltip; /* set tooltip font */
7340{
7341# ifdef FEAT_XFONTSET
7342 /* If 'guifontset' is not empty, first try using the name as a
7343 * fontset. If that doesn't work, use it as a font name. */
7344 if (*p_guifontset != NUL
7345# ifdef FONTSET_ALWAYS
7346 || do_menu
7347# endif
7348# ifdef FEAT_BEVAL_TIP
7349 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
7350 || do_tooltip
7351# endif
7352 )
7353 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7354# ifdef FONTSET_ALWAYS
7355 || do_menu
7356# endif
7357# ifdef FEAT_BEVAL_TIP
7358 || do_tooltip
7359# endif
7360 );
7361 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7362 {
7363 /* If it worked and it's the Normal group, use it as the
7364 * normal fontset. Same for the Menu group. */
7365 if (do_normal)
7366 gui_init_font(arg, TRUE);
7367# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7368 if (do_menu)
7369 {
7370# ifdef FONTSET_ALWAYS
7371 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7372# else
7373 /* YIKES! This is a bug waiting to crash the program */
7374 gui.menu_font = HL_TABLE()[idx].sg_fontset;
7375# endif
7376 gui_mch_new_menu_font();
7377 }
7378# ifdef FEAT_BEVAL
7379 if (do_tooltip)
7380 {
7381 /* The Athena widget set cannot currently handle switching between
7382 * displaying a single font and a fontset.
7383 * If the XtNinternational resource is set to True at widget
7384 * creation, then a fontset is always used, othwise an
7385 * XFontStruct is used.
7386 */
7387 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7388 gui_mch_new_tooltip_font();
7389 }
7390# endif
7391# endif
7392 }
7393 else
7394# endif
7395 {
7396 HL_TABLE()[idx].sg_font = font_name2handle(arg);
7397 /* If it worked and it's the Normal group, use it as the
7398 * normal font. Same for the Menu group. */
7399 if (HL_TABLE()[idx].sg_font != NOFONT)
7400 {
7401 if (do_normal)
7402 gui_init_font(arg, FALSE);
7403#ifndef FONTSET_ALWAYS
7404# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7405 if (do_menu)
7406 {
7407 gui.menu_font = HL_TABLE()[idx].sg_font;
7408 gui_mch_new_menu_font();
7409 }
7410# endif
7411#endif
7412 }
7413 }
7414}
7415
7416#endif /* FEAT_GUI */
7417
7418/*
7419 * Table with the specifications for an attribute number.
7420 * Note that this table is used by ALL buffers. This is required because the
7421 * GUI can redraw at any time for any buffer.
7422 */
7423garray_T term_attr_table = {0, 0, 0, 0, NULL};
7424
7425#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7426
7427garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
7428
7429#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7430
7431#ifdef FEAT_GUI
7432garray_T gui_attr_table = {0, 0, 0, 0, NULL};
7433
7434#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7435#endif
7436
7437/*
7438 * Return the attr number for a set of colors and font.
7439 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7440 * if the combination is new.
7441 * Return 0 for error (no more room).
7442 */
7443 static int
7444get_attr_entry(table, aep)
7445 garray_T *table;
7446 attrentry_T *aep;
7447{
7448 int i;
7449 attrentry_T *gap;
7450 static int recursive = FALSE;
7451
7452 /*
7453 * Init the table, in case it wasn't done yet.
7454 */
7455 table->ga_itemsize = sizeof(attrentry_T);
7456 table->ga_growsize = 7;
7457
7458 /*
7459 * Try to find an entry with the same specifications.
7460 */
7461 for (i = 0; i < table->ga_len; ++i)
7462 {
7463 gap = &(((attrentry_T *)table->ga_data)[i]);
7464 if ( aep->ae_attr == gap->ae_attr
7465 && (
7466#ifdef FEAT_GUI
7467 (table == &gui_attr_table
7468 && (aep->ae_u.gui.fg_color == gap->ae_u.gui.fg_color
7469 && aep->ae_u.gui.bg_color == gap->ae_u.gui.bg_color
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007470 && aep->ae_u.gui.sp_color == gap->ae_u.gui.sp_color
Bram Moolenaar071d4272004-06-13 20:20:40 +00007471 && aep->ae_u.gui.font == gap->ae_u.gui.font
7472# ifdef FEAT_XFONTSET
7473 && aep->ae_u.gui.fontset == gap->ae_u.gui.fontset
7474# endif
7475 ))
7476 ||
7477#endif
7478 (table == &term_attr_table
7479 && (aep->ae_u.term.start == NULL) ==
7480 (gap->ae_u.term.start == NULL)
7481 && (aep->ae_u.term.start == NULL
7482 || STRCMP(aep->ae_u.term.start,
7483 gap->ae_u.term.start) == 0)
7484 && (aep->ae_u.term.stop == NULL) ==
7485 (gap->ae_u.term.stop == NULL)
7486 && (aep->ae_u.term.stop == NULL
7487 || STRCMP(aep->ae_u.term.stop,
7488 gap->ae_u.term.stop) == 0))
7489 || (table == &cterm_attr_table
7490 && aep->ae_u.cterm.fg_color == gap->ae_u.cterm.fg_color
7491 && aep->ae_u.cterm.bg_color == gap->ae_u.cterm.bg_color)
7492 ))
7493
7494 return i + ATTR_OFF;
7495 }
7496
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +00007497 if (table->ga_len + ATTR_OFF >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007498 {
7499 /*
7500 * Running out of attribute entries! remove all attributes, and
7501 * compute new ones for all groups.
7502 * When called recursively, we are really out of numbers.
7503 */
7504 if (recursive)
7505 {
7506 EMSG(_("E424: Too many different highlighting attributes in use"));
7507 return 0;
7508 }
7509 recursive = TRUE;
7510
7511#ifdef FEAT_GUI
7512 ga_clear(&gui_attr_table);
7513#endif
7514 ga_clear(&term_attr_table);
7515 ga_clear(&cterm_attr_table);
7516 must_redraw = CLEAR;
7517
7518 for (i = 0; i < highlight_ga.ga_len; ++i)
7519 set_hl_attr(i);
7520
7521 recursive = FALSE;
7522 }
7523
7524 /*
7525 * This is a new combination of colors and font, add an entry.
7526 */
7527 if (ga_grow(table, 1) == FAIL)
7528 return 0;
7529
7530 gap = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7531 vim_memset(gap, 0, sizeof(attrentry_T));
7532 gap->ae_attr = aep->ae_attr;
7533#ifdef FEAT_GUI
7534 if (table == &gui_attr_table)
7535 {
7536 gap->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7537 gap->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007538 gap->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007539 gap->ae_u.gui.font = aep->ae_u.gui.font;
7540# ifdef FEAT_XFONTSET
7541 gap->ae_u.gui.fontset = aep->ae_u.gui.fontset;
7542# endif
7543 }
7544#endif
7545 if (table == &term_attr_table)
7546 {
7547 if (aep->ae_u.term.start == NULL)
7548 gap->ae_u.term.start = NULL;
7549 else
7550 gap->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
7551 if (aep->ae_u.term.stop == NULL)
7552 gap->ae_u.term.stop = NULL;
7553 else
7554 gap->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
7555 }
7556 else if (table == &cterm_attr_table)
7557 {
7558 gap->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7559 gap->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
7560 }
7561 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007562 return (table->ga_len - 1 + ATTR_OFF);
7563}
7564
Bram Moolenaar217ad922005-03-20 22:37:15 +00007565#if defined(FEAT_SYN_HL) || defined(PROTO)
7566/*
7567 * Combine the spelling attributes with other attributes. "spell_attr"
7568 * overrules "char_attr".
7569 * This creates a new group when required.
7570 * Since we expect there to be few spelling mistakes we don't cache the
7571 * result.
7572 * Return the resulting attributes.
7573 */
7574 int
7575hl_combine_attr(char_attr, spell_attr)
7576 int char_attr;
7577 int spell_attr;
7578{
7579 attrentry_T *char_aep = NULL;
7580 attrentry_T *spell_aep;
7581 attrentry_T new_en;
7582
7583 if (char_attr == 0)
7584 return spell_attr;
7585 if (char_attr <= HL_ALL && spell_attr <= HL_ALL)
7586 return char_attr | spell_attr;
7587#ifdef FEAT_GUI
7588 if (gui.in_use)
7589 {
7590 if (char_attr > HL_ALL)
7591 char_aep = syn_gui_attr2entry(char_attr);
7592 if (char_aep != NULL)
7593 new_en = *char_aep;
7594 else
7595 {
7596 vim_memset(&new_en, 0, sizeof(new_en));
7597 if (char_attr <= HL_ALL)
7598 new_en.ae_attr = char_attr;
7599 }
7600
7601 if (spell_attr <= HL_ALL)
7602 new_en.ae_attr |= spell_attr;
7603 else
7604 {
7605 spell_aep = syn_gui_attr2entry(spell_attr);
7606 if (spell_aep != NULL)
7607 {
7608 new_en.ae_attr |= spell_aep->ae_attr;
7609 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7610 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7611 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7612 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7613 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7614 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7615 if (spell_aep->ae_u.gui.font != NOFONT)
7616 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7617# ifdef FEAT_XFONTSET
7618 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7619 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7620# endif
7621 }
7622 }
7623 return get_attr_entry(&gui_attr_table, &new_en);
7624 }
7625#endif
7626
7627 if (t_colors > 1)
7628 {
7629 if (char_attr > HL_ALL)
7630 char_aep = syn_cterm_attr2entry(char_attr);
7631 if (char_aep != NULL)
7632 new_en = *char_aep;
7633 else
7634 {
7635 vim_memset(&new_en, 0, sizeof(new_en));
7636 if (char_attr <= HL_ALL)
7637 new_en.ae_attr = char_attr;
7638 }
7639
7640 if (spell_attr <= HL_ALL)
7641 new_en.ae_attr |= spell_attr;
7642 else
7643 {
7644 spell_aep = syn_cterm_attr2entry(spell_attr);
7645 if (spell_aep != NULL)
7646 {
7647 new_en.ae_attr |= spell_aep->ae_attr;
7648 if (spell_aep->ae_u.cterm.fg_color > 0)
7649 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
7650 if (spell_aep->ae_u.cterm.bg_color > 0)
7651 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
7652 }
7653 }
7654 return get_attr_entry(&cterm_attr_table, &new_en);
7655 }
7656
7657 if (char_attr > HL_ALL)
7658 char_aep = syn_term_attr2entry(char_attr);
7659 if (char_aep != NULL)
7660 new_en = *char_aep;
7661 else
7662 {
7663 vim_memset(&new_en, 0, sizeof(new_en));
7664 if (char_attr <= HL_ALL)
7665 new_en.ae_attr = char_attr;
7666 }
7667
7668 if (spell_attr <= HL_ALL)
7669 new_en.ae_attr |= spell_attr;
7670 else
7671 {
7672 spell_aep = syn_cterm_attr2entry(spell_attr);
7673 if (spell_aep != NULL)
7674 {
7675 new_en.ae_attr |= spell_aep->ae_attr;
7676 if (spell_aep->ae_u.term.start != NULL)
7677 {
7678 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
7679 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
7680 }
7681 }
7682 }
7683 return get_attr_entry(&term_attr_table, &new_en);
7684}
7685#endif
7686
Bram Moolenaar071d4272004-06-13 20:20:40 +00007687#ifdef FEAT_GUI
7688
7689 attrentry_T *
7690syn_gui_attr2entry(attr)
7691 int attr;
7692{
7693 attr -= ATTR_OFF;
7694 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
7695 return NULL;
7696 return &(GUI_ATTR_ENTRY(attr));
7697}
7698
7699#endif /* FEAT_GUI */
7700
7701 attrentry_T *
7702syn_term_attr2entry(attr)
7703 int attr;
7704{
7705 attr -= ATTR_OFF;
7706 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
7707 return NULL;
7708 return &(TERM_ATTR_ENTRY(attr));
7709}
7710
7711 attrentry_T *
7712syn_cterm_attr2entry(attr)
7713 int attr;
7714{
7715 attr -= ATTR_OFF;
7716 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
7717 return NULL;
7718 return &(CTERM_ATTR_ENTRY(attr));
7719}
7720
7721#define LIST_ATTR 1
7722#define LIST_STRING 2
7723#define LIST_INT 3
7724
7725 static void
7726highlight_list_one(id)
7727 int id;
7728{
7729 struct hl_group *sgp;
7730 int didh = FALSE;
7731
7732 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
7733
7734 didh = highlight_list_arg(id, didh, LIST_ATTR,
7735 sgp->sg_term, NULL, "term");
7736 didh = highlight_list_arg(id, didh, LIST_STRING,
7737 0, sgp->sg_start, "start");
7738 didh = highlight_list_arg(id, didh, LIST_STRING,
7739 0, sgp->sg_stop, "stop");
7740
7741 didh = highlight_list_arg(id, didh, LIST_ATTR,
7742 sgp->sg_cterm, NULL, "cterm");
7743 didh = highlight_list_arg(id, didh, LIST_INT,
7744 sgp->sg_cterm_fg, NULL, "ctermfg");
7745 didh = highlight_list_arg(id, didh, LIST_INT,
7746 sgp->sg_cterm_bg, NULL, "ctermbg");
7747
7748#ifdef FEAT_GUI
7749 didh = highlight_list_arg(id, didh, LIST_ATTR,
7750 sgp->sg_gui, NULL, "gui");
7751 didh = highlight_list_arg(id, didh, LIST_STRING,
7752 0, sgp->sg_gui_fg_name, "guifg");
7753 didh = highlight_list_arg(id, didh, LIST_STRING,
7754 0, sgp->sg_gui_bg_name, "guibg");
7755 didh = highlight_list_arg(id, didh, LIST_STRING,
7756 0, sgp->sg_font_name, "font");
7757#endif
7758
7759 if (sgp->sg_link)
7760 {
7761 (void)syn_list_header(didh, 9999, id);
7762 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
7763 msg_putchar(' ');
7764 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
7765 }
7766}
7767
7768 static int
7769highlight_list_arg(id, didh, type, iarg, sarg, name)
7770 int id;
7771 int didh;
7772 int type;
7773 int iarg;
7774 char_u *sarg;
7775 char *name;
7776{
7777 char_u buf[100];
7778 char_u *ts;
7779 int i;
7780
7781 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
7782 {
7783 ts = buf;
7784 if (type == LIST_INT)
7785 sprintf((char *)buf, "%d", iarg - 1);
7786 else if (type == LIST_STRING)
7787 ts = sarg;
7788 else /* type == LIST_ATTR */
7789 {
7790 buf[0] = NUL;
7791 for (i = 0; hl_attr_table[i] != 0; ++i)
7792 {
7793 if (iarg & hl_attr_table[i])
7794 {
7795 if (buf[0] != NUL)
7796 STRCAT(buf, ",");
7797 STRCAT(buf, hl_name_table[i]);
7798 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
7799 }
7800 }
7801 }
7802
7803 (void)syn_list_header(didh,
7804 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
7805 didh = TRUE;
7806
7807 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
7808 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
7809 msg_outtrans(ts);
7810 }
7811 return didh;
7812}
7813
7814#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
7815/*
7816 * Return "1" if highlight group "id" has attribute "flag".
7817 * Return NULL otherwise.
7818 */
7819 char_u *
7820highlight_has_attr(id, flag, modec)
7821 int id;
7822 int flag;
7823 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
7824{
7825 int attr;
7826
7827 if (id <= 0 || id > highlight_ga.ga_len)
7828 return NULL;
7829
7830#ifdef FEAT_GUI
7831 if (modec == 'g')
7832 attr = HL_TABLE()[id - 1].sg_gui;
7833 else
7834#endif
7835 if (modec == 'c')
7836 attr = HL_TABLE()[id - 1].sg_cterm;
7837 else
7838 attr = HL_TABLE()[id - 1].sg_term;
7839
7840 if (attr & flag)
7841 return (char_u *)"1";
7842 return NULL;
7843}
7844#endif
7845
7846#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
7847/*
7848 * Return color name of highlight group "id".
7849 */
7850 char_u *
7851highlight_color(id, what, modec)
7852 int id;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007853 char_u *what; /* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
7855{
7856 static char_u name[20];
7857 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007858 int fg = FALSE;
7859# ifdef FEAT_GUI
7860 int sp = FALSE;
7861# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007862
7863 if (id <= 0 || id > highlight_ga.ga_len)
7864 return NULL;
7865
7866 if (TOLOWER_ASC(what[0]) == 'f')
7867 fg = TRUE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007868# ifdef FEAT_GUI
7869 else if (TOLOWER_ASC(what[0]) == 's')
7870 sp = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007871 if (modec == 'g')
7872 {
7873 /* return #RRGGBB form (only possible when GUI is running) */
7874 if (gui.in_use && what[1] && what[2] == '#')
7875 {
7876 guicolor_T color;
7877 long_u rgb;
7878 static char_u buf[10];
7879
7880 if (fg)
7881 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007882 else if (sp)
7883 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007884 else
7885 color = HL_TABLE()[id - 1].sg_gui_bg;
7886 if (color == INVALCOLOR)
7887 return NULL;
7888 rgb = gui_mch_get_rgb(color);
7889 sprintf((char *)buf, "#%02x%02x%02x",
7890 (unsigned)(rgb >> 16),
7891 (unsigned)(rgb >> 8) & 255,
7892 (unsigned)rgb & 255);
7893 return buf;
7894 }
7895 if (fg)
7896 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007897 if (sp)
7898 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007899 return (HL_TABLE()[id - 1].sg_gui_bg_name);
7900 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007901# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007902 if (modec == 'c')
7903 {
7904 if (fg)
7905 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
7906 else
7907 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
7908 sprintf((char *)name, "%d", n);
7909 return name;
7910 }
7911 /* term doesn't have color */
7912 return NULL;
7913}
7914#endif
7915
7916#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
7917 || defined(PROTO)
7918/*
7919 * Return color name of highlight group "id" as RGB value.
7920 */
7921 long_u
7922highlight_gui_color_rgb(id, fg)
7923 int id;
7924 int fg; /* TRUE = fg, FALSE = bg */
7925{
7926 guicolor_T color;
7927
7928 if (id <= 0 || id > highlight_ga.ga_len)
7929 return 0L;
7930
7931 if (fg)
7932 color = HL_TABLE()[id - 1].sg_gui_fg;
7933 else
7934 color = HL_TABLE()[id - 1].sg_gui_bg;
7935
7936 if (color == INVALCOLOR)
7937 return 0L;
7938
7939 return gui_mch_get_rgb(color);
7940}
7941#endif
7942
7943/*
7944 * Output the syntax list header.
7945 * Return TRUE when started a new line.
7946 */
7947 static int
7948syn_list_header(did_header, outlen, id)
7949 int did_header; /* did header already */
7950 int outlen; /* length of string that comes */
7951 int id; /* highlight group id */
7952{
7953 int endcol = 19;
7954 int newline = TRUE;
7955
7956 if (!did_header)
7957 {
7958 msg_putchar('\n');
7959 msg_outtrans(HL_TABLE()[id - 1].sg_name);
7960 endcol = 15;
7961 }
7962 else if (msg_col + outlen + 1 >= Columns)
7963 msg_putchar('\n');
7964 else
7965 {
7966 if (msg_col >= endcol) /* wrap around is like starting a new line */
7967 newline = FALSE;
7968 }
7969
7970 if (msg_col >= endcol) /* output at least one space */
7971 endcol = msg_col + 1;
7972 if (Columns <= endcol) /* avoid hang for tiny window */
7973 endcol = Columns - 1;
7974
7975 msg_advance(endcol);
7976
7977 /* Show "xxx" with the attributes. */
7978 if (!did_header)
7979 {
7980 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
7981 msg_putchar(' ');
7982 }
7983
7984 return newline;
7985}
7986
7987/*
7988 * Set the attribute numbers for a highlight group.
7989 * Called after one of the attributes has changed.
7990 */
7991 static void
7992set_hl_attr(idx)
7993 int idx; /* index in array */
7994{
7995 attrentry_T at_en;
7996 struct hl_group *sgp = HL_TABLE() + idx;
7997
7998 /* The "Normal" group doesn't need an attribute number */
7999 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8000 return;
8001
8002#ifdef FEAT_GUI
8003 /*
8004 * For the GUI mode: If there are other than "normal" highlighting
8005 * attributes, need to allocate an attr number.
8006 */
8007 if (sgp->sg_gui_fg == INVALCOLOR
8008 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008009 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008010 && sgp->sg_font == NOFONT
8011# ifdef FEAT_XFONTSET
8012 && sgp->sg_fontset == NOFONTSET
8013# endif
8014 )
8015 {
8016 sgp->sg_gui_attr = sgp->sg_gui;
8017 }
8018 else
8019 {
8020 at_en.ae_attr = sgp->sg_gui;
8021 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8022 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008023 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008024 at_en.ae_u.gui.font = sgp->sg_font;
8025# ifdef FEAT_XFONTSET
8026 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8027# endif
8028 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8029 }
8030#endif
8031 /*
8032 * For the term mode: If there are other than "normal" highlighting
8033 * attributes, need to allocate an attr number.
8034 */
8035 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8036 sgp->sg_term_attr = sgp->sg_term;
8037 else
8038 {
8039 at_en.ae_attr = sgp->sg_term;
8040 at_en.ae_u.term.start = sgp->sg_start;
8041 at_en.ae_u.term.stop = sgp->sg_stop;
8042 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8043 }
8044
8045 /*
8046 * For the color term mode: If there are other than "normal"
8047 * highlighting attributes, need to allocate an attr number.
8048 */
8049 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8050 sgp->sg_cterm_attr = sgp->sg_cterm;
8051 else
8052 {
8053 at_en.ae_attr = sgp->sg_cterm;
8054 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8055 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8056 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8057 }
8058}
8059
8060/*
8061 * Lookup a highlight group name and return it's ID.
8062 * If it is not found, 0 is returned.
8063 */
8064 int
8065syn_name2id(name)
8066 char_u *name;
8067{
8068 int i;
8069 char_u name_u[200];
8070
8071 /* Avoid using stricmp() too much, it's slow on some systems */
8072 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8073 * don't deserve to be found! */
8074 STRNCPY(name_u, name, 199);
8075 name_u[199] = NUL;
8076 vim_strup(name_u);
8077 for (i = highlight_ga.ga_len; --i >= 0; )
8078 if (HL_TABLE()[i].sg_name_u != NULL
8079 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8080 break;
8081 return i + 1;
8082}
8083
8084#if defined(FEAT_EVAL) || defined(PROTO)
8085/*
8086 * Return TRUE if highlight group "name" exists.
8087 */
8088 int
8089highlight_exists(name)
8090 char_u *name;
8091{
8092 return (syn_name2id(name) > 0);
8093}
8094#endif
8095
8096/*
8097 * Like syn_name2id(), but take a pointer + length argument.
8098 */
8099 int
8100syn_namen2id(linep, len)
8101 char_u *linep;
8102 int len;
8103{
8104 char_u *name;
8105 int id = 0;
8106
8107 name = vim_strnsave(linep, len);
8108 if (name != NULL)
8109 {
8110 id = syn_name2id(name);
8111 vim_free(name);
8112 }
8113 return id;
8114}
8115
8116/*
8117 * Find highlight group name in the table and return it's ID.
8118 * The argument is a pointer to the name and the length of the name.
8119 * If it doesn't exist yet, a new entry is created.
8120 * Return 0 for failure.
8121 */
8122 int
8123syn_check_group(pp, len)
8124 char_u *pp;
8125 int len;
8126{
8127 int id;
8128 char_u *name;
8129
8130 name = vim_strnsave(pp, len);
8131 if (name == NULL)
8132 return 0;
8133
8134 id = syn_name2id(name);
8135 if (id == 0) /* doesn't exist yet */
8136 id = syn_add_group(name);
8137 else
8138 vim_free(name);
8139 return id;
8140}
8141
8142/*
8143 * Add new highlight group and return it's ID.
8144 * "name" must be an allocated string, it will be consumed.
8145 * Return 0 for failure.
8146 */
8147 static int
8148syn_add_group(name)
8149 char_u *name;
8150{
8151 char_u *p;
8152
8153 /* Check that the name is ASCII letters, digits and underscore. */
8154 for (p = name; *p != NUL; ++p)
8155 {
8156 if (!vim_isprintc(*p))
8157 {
8158 EMSG(_("E669: Unprintable character in group name"));
8159 return 0;
8160 }
8161 else if (!ASCII_ISALNUM(*p) && *p != '_')
8162 {
8163 /* This is an error, but since there previously was no check only
8164 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008165 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008166 MSG(_("W18: Invalid character in group name"));
8167 break;
8168 }
8169 }
8170
8171 /*
8172 * First call for this growarray: init growing array.
8173 */
8174 if (highlight_ga.ga_data == NULL)
8175 {
8176 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8177 highlight_ga.ga_growsize = 10;
8178 }
8179
8180 /*
8181 * Make room for at least one other syntax_highlight entry.
8182 */
8183 if (ga_grow(&highlight_ga, 1) == FAIL)
8184 {
8185 vim_free(name);
8186 return 0;
8187 }
8188
8189 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8190 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8191 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8192#ifdef FEAT_GUI
8193 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8194 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008195 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008196#endif
8197 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008198
8199 return highlight_ga.ga_len; /* ID is index plus one */
8200}
8201
8202/*
8203 * When, just after calling syn_add_group(), an error is discovered, this
8204 * function deletes the new name.
8205 */
8206 static void
8207syn_unadd_group()
8208{
8209 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008210 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8211 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8212}
8213
8214/*
8215 * Translate a group ID to highlight attributes.
8216 */
8217 int
8218syn_id2attr(hl_id)
8219 int hl_id;
8220{
8221 int attr;
8222 struct hl_group *sgp;
8223
8224 hl_id = syn_get_final_id(hl_id);
8225 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8226
8227#ifdef FEAT_GUI
8228 /*
8229 * Only use GUI attr when the GUI is being used.
8230 */
8231 if (gui.in_use)
8232 attr = sgp->sg_gui_attr;
8233 else
8234#endif
8235 if (t_colors > 1)
8236 attr = sgp->sg_cterm_attr;
8237 else
8238 attr = sgp->sg_term_attr;
8239
8240 return attr;
8241}
8242
8243#ifdef FEAT_GUI
8244/*
8245 * Get the GUI colors and attributes for a group ID.
8246 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8247 */
8248 int
8249syn_id2colors(hl_id, fgp, bgp)
8250 int hl_id;
8251 guicolor_T *fgp;
8252 guicolor_T *bgp;
8253{
8254 struct hl_group *sgp;
8255
8256 hl_id = syn_get_final_id(hl_id);
8257 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8258
8259 *fgp = sgp->sg_gui_fg;
8260 *bgp = sgp->sg_gui_bg;
8261 return sgp->sg_gui;
8262}
8263#endif
8264
8265/*
8266 * Translate a group ID to the final group ID (following links).
8267 */
8268 int
8269syn_get_final_id(hl_id)
8270 int hl_id;
8271{
8272 int count;
8273 struct hl_group *sgp;
8274
8275 if (hl_id > highlight_ga.ga_len || hl_id < 1)
8276 return 0; /* Can be called from eval!! */
8277
8278 /*
8279 * Follow links until there is no more.
8280 * Look out for loops! Break after 100 links.
8281 */
8282 for (count = 100; --count >= 0; )
8283 {
8284 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8285 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8286 break;
8287 hl_id = sgp->sg_link;
8288 }
8289
8290 return hl_id;
8291}
8292
8293#ifdef FEAT_GUI
8294/*
8295 * Call this function just after the GUI has started.
8296 * It finds the font and color handles for the highlighting groups.
8297 */
8298 void
8299highlight_gui_started()
8300{
8301 int idx;
8302
8303 /* First get the colors from the "Normal" and "Menu" group, if set */
8304 set_normal_colors();
8305
8306 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8307 gui_do_one_color(idx, FALSE, FALSE);
8308
8309 highlight_changed();
8310}
8311
8312 static void
8313gui_do_one_color(idx, do_menu, do_tooltip)
8314 int idx;
8315 int do_menu; /* TRUE: might set the menu font */
8316 int do_tooltip; /* TRUE: might set the tooltip font */
8317{
8318 int didit = FALSE;
8319
8320 if (HL_TABLE()[idx].sg_font_name != NULL)
8321 {
8322 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8323 do_tooltip);
8324 didit = TRUE;
8325 }
8326 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8327 {
8328 HL_TABLE()[idx].sg_gui_fg =
8329 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8330 didit = TRUE;
8331 }
8332 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8333 {
8334 HL_TABLE()[idx].sg_gui_bg =
8335 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8336 didit = TRUE;
8337 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008338 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8339 {
8340 HL_TABLE()[idx].sg_gui_sp =
8341 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8342 didit = TRUE;
8343 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008344 if (didit) /* need to get a new attr number */
8345 set_hl_attr(idx);
8346}
8347
8348#endif
8349
8350/*
8351 * Translate the 'highlight' option into attributes in highlight_attr[] and
8352 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
8353 * corresponding highlights to use on top of HLF_SNC is computed.
8354 * Called only when the 'highlight' option has been changed and upon first
8355 * screen redraw after any :highlight command.
8356 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
8357 */
8358 int
8359highlight_changed()
8360{
8361 int hlf;
8362 int i;
8363 char_u *p;
8364 int attr;
8365 char_u *end;
8366 int id;
8367#ifdef USER_HIGHLIGHT
8368 char_u userhl[10];
8369# ifdef FEAT_STL_OPT
8370 int id_SNC = -1;
8371 int id_S = -1;
8372 int hlcnt;
8373# endif
8374#endif
8375 static int hl_flags[HLF_COUNT] = HL_FLAGS;
8376
8377 need_highlight_changed = FALSE;
8378
8379 /*
8380 * Clear all attributes.
8381 */
8382 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8383 highlight_attr[hlf] = 0;
8384
8385 /*
8386 * First set all attributes to their default value.
8387 * Then use the attributes from the 'highlight' option.
8388 */
8389 for (i = 0; i < 2; ++i)
8390 {
8391 if (i)
8392 p = p_hl;
8393 else
8394 p = get_highlight_default();
8395 if (p == NULL) /* just in case */
8396 continue;
8397
8398 while (*p)
8399 {
8400 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8401 if (hl_flags[hlf] == *p)
8402 break;
8403 ++p;
8404 if (hlf == (int)HLF_COUNT || *p == NUL)
8405 return FAIL;
8406
8407 /*
8408 * Allow several hl_flags to be combined, like "bu" for
8409 * bold-underlined.
8410 */
8411 attr = 0;
8412 for ( ; *p && *p != ','; ++p) /* parse upto comma */
8413 {
8414 if (vim_iswhite(*p)) /* ignore white space */
8415 continue;
8416
8417 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
8418 return FAIL;
8419
8420 switch (*p)
8421 {
8422 case 'b': attr |= HL_BOLD;
8423 break;
8424 case 'i': attr |= HL_ITALIC;
8425 break;
8426 case '-':
8427 case 'n': /* no highlighting */
8428 break;
8429 case 'r': attr |= HL_INVERSE;
8430 break;
8431 case 's': attr |= HL_STANDOUT;
8432 break;
8433 case 'u': attr |= HL_UNDERLINE;
8434 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008435 case 'c': attr |= HL_UNDERCURL;
8436 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008437 case ':': ++p; /* highlight group name */
8438 if (attr || *p == NUL) /* no combinations */
8439 return FAIL;
8440 end = vim_strchr(p, ',');
8441 if (end == NULL)
8442 end = p + STRLEN(p);
8443 id = syn_check_group(p, (int)(end - p));
8444 if (id == 0)
8445 return FAIL;
8446 attr = syn_id2attr(id);
8447 p = end - 1;
8448#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8449 if (hlf == (int)HLF_SNC)
8450 id_SNC = syn_get_final_id(id);
8451 else if (hlf == (int)HLF_S)
8452 id_S = syn_get_final_id(id);
8453#endif
8454 break;
8455 default: return FAIL;
8456 }
8457 }
8458 highlight_attr[hlf] = attr;
8459
8460 p = skip_to_option_part(p); /* skip comma and spaces */
8461 }
8462 }
8463
8464#ifdef USER_HIGHLIGHT
8465 /* Setup the user highlights
8466 *
8467 * Temporarily utilize 10 more hl entries. Have to be in there
8468 * simultaneously in case of table overflows in get_attr_entry()
8469 */
8470# ifdef FEAT_STL_OPT
8471 if (ga_grow(&highlight_ga, 10) == FAIL)
8472 return FAIL;
8473 hlcnt = highlight_ga.ga_len;
8474 if (id_S == 0)
8475 { /* Make sure id_S is always valid to simplify code below */
8476 memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8477 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8478 id_S = hlcnt + 10;
8479 }
8480# endif
8481 for (i = 0; i < 9; i++)
8482 {
8483 sprintf((char *)userhl, "User%d", i + 1);
8484 id = syn_name2id(userhl);
8485 if (id == 0)
8486 {
8487 highlight_user[i] = 0;
8488# ifdef FEAT_STL_OPT
8489 highlight_stlnc[i] = 0;
8490# endif
8491 }
8492 else
8493 {
8494# ifdef FEAT_STL_OPT
8495 struct hl_group *hlt = HL_TABLE();
8496# endif
8497
8498 highlight_user[i] = syn_id2attr(id);
8499# ifdef FEAT_STL_OPT
8500 if (id_SNC == 0)
8501 {
8502 memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8503 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8504 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8505# ifdef FEAT_GUI
8506 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8507# endif
8508 }
8509 else
8510 mch_memmove(&hlt[hlcnt + i],
8511 &hlt[id_SNC - 1],
8512 sizeof(struct hl_group));
8513 hlt[hlcnt + i].sg_link = 0;
8514
8515 /* Apply difference between UserX and HLF_S to HLF_SNC */
8516 hlt[hlcnt + i].sg_term ^=
8517 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8518 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8519 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8520 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8521 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8522 hlt[hlcnt + i].sg_cterm ^=
8523 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8524 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8525 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8526 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8527 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8528# ifdef FEAT_GUI
8529 hlt[hlcnt + i].sg_gui ^=
8530 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8531 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8532 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8533 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8534 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008535 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8536 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008537 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8538 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8539# ifdef FEAT_XFONTSET
8540 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8541 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8542# endif
8543# endif
8544 highlight_ga.ga_len = hlcnt + i + 1;
8545 set_hl_attr(hlcnt + i); /* At long last we can apply */
8546 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8547# endif
8548 }
8549 }
8550# ifdef FEAT_STL_OPT
8551 highlight_ga.ga_len = hlcnt;
8552# endif
8553
8554#endif /* USER_HIGHLIGHT */
8555
8556 return OK;
8557}
8558
8559#ifdef FEAT_CMDL_COMPL
8560
8561static void highlight_list __ARGS((void));
8562static void highlight_list_two __ARGS((int cnt, int attr));
8563
8564/*
8565 * Handle command line completion for :highlight command.
8566 */
8567 void
8568set_context_in_highlight_cmd(xp, arg)
8569 expand_T *xp;
8570 char_u *arg;
8571{
8572 char_u *p;
8573
8574 /* Default: expand group names */
8575 xp->xp_context = EXPAND_HIGHLIGHT;
8576 xp->xp_pattern = arg;
8577 include_link = TRUE;
8578 include_default = TRUE;
8579
8580 /* (part of) subcommand already typed */
8581 if (*arg != NUL)
8582 {
8583 p = skiptowhite(arg);
8584 if (*p != NUL) /* past "default" or group name */
8585 {
8586 include_default = FALSE;
8587 if (STRNCMP("default", arg, p - arg) == 0)
8588 {
8589 arg = skipwhite(p);
8590 xp->xp_pattern = arg;
8591 p = skiptowhite(arg);
8592 }
8593 if (*p != NUL) /* past group name */
8594 {
8595 include_link = FALSE;
8596 if (arg[1] == 'i' && arg[0] == 'N')
8597 highlight_list();
8598 if (STRNCMP("link", arg, p - arg) == 0
8599 || STRNCMP("clear", arg, p - arg) == 0)
8600 {
8601 xp->xp_pattern = skipwhite(p);
8602 p = skiptowhite(xp->xp_pattern);
8603 if (*p != NUL) /* past first group name */
8604 {
8605 xp->xp_pattern = skipwhite(p);
8606 p = skiptowhite(xp->xp_pattern);
8607 }
8608 }
8609 if (*p != NUL) /* past group name(s) */
8610 xp->xp_context = EXPAND_NOTHING;
8611 }
8612 }
8613 }
8614}
8615
8616/*
8617 * List highlighting matches in a nice way.
8618 */
8619 static void
8620highlight_list()
8621{
8622 int i;
8623
8624 for (i = 10; --i >= 0; )
8625 highlight_list_two(i, hl_attr(HLF_D));
8626 for (i = 40; --i >= 0; )
8627 highlight_list_two(99, 0);
8628}
8629
8630 static void
8631highlight_list_two(cnt, attr)
8632 int cnt;
8633 int attr;
8634{
8635 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
8636 msg_clr_eos();
8637 out_flush();
8638 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
8639}
8640
8641#endif /* FEAT_CMDL_COMPL */
8642
8643#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
8644 || defined(FEAT_SIGNS) || defined(PROTO)
8645/*
8646 * Function given to ExpandGeneric() to obtain the list of group names.
8647 * Also used for synIDattr() function.
8648 */
8649/*ARGSUSED*/
8650 char_u *
8651get_highlight_name(xp, idx)
8652 expand_T *xp;
8653 int idx;
8654{
8655 if (idx == highlight_ga.ga_len
8656#ifdef FEAT_CMDL_COMPL
8657 && include_link
8658#endif
8659 )
8660 return (char_u *)"link";
8661 if (idx == highlight_ga.ga_len + 1
8662#ifdef FEAT_CMDL_COMPL
8663 && include_link
8664#endif
8665 )
8666 return (char_u *)"clear";
8667 if (idx == highlight_ga.ga_len + 2
8668#ifdef FEAT_CMDL_COMPL
8669 && include_default
8670#endif
8671 )
8672 return (char_u *)"default";
8673 if (idx < 0 || idx >= highlight_ga.ga_len)
8674 return NULL;
8675 return HL_TABLE()[idx].sg_name;
8676}
8677#endif
8678
8679#ifdef FEAT_GUI
8680/*
8681 * Free all the highlight group fonts.
8682 * Used when quitting for systems which need it.
8683 */
8684 void
8685free_highlight_fonts()
8686{
8687 int idx;
8688
8689 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8690 {
8691 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8692 HL_TABLE()[idx].sg_font = NOFONT;
8693# ifdef FEAT_XFONTSET
8694 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8695 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8696# endif
8697 }
8698
8699 gui_mch_free_font(gui.norm_font);
8700# ifdef FEAT_XFONTSET
8701 gui_mch_free_fontset(gui.fontset);
8702# endif
8703# ifndef HAVE_GTK2
8704 gui_mch_free_font(gui.bold_font);
8705 gui_mch_free_font(gui.ital_font);
8706 gui_mch_free_font(gui.boldital_font);
8707# endif
8708}
8709#endif
8710
8711/**************************************
8712 * End of Highlighting stuff *
8713 **************************************/