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