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