blob: 1c093a0be3f732c910c5b61e2779f8239855989b [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
2977 * is a matchgroup. */
2978 len = STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
2979 if (col > len)
2980 result->col = len;
2981 else
2982 result->col = col;
2983 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002984}
2985
2986/*
2987 * Add offset to matched text for start of match or highlight.
2988 * Avoid resulting column to become negative.
2989 */
2990 static void
2991syn_add_start_off(result, regmatch, spp, idx, extra)
2992 lpos_T *result; /* returned position */
2993 regmmatch_T *regmatch; /* start/end of match */
2994 synpat_T *spp;
2995 int idx;
2996 int extra; /* extra chars for offset to end */
2997{
2998 int col;
2999
3000 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3001 {
3002 result->lnum = regmatch->endpos[0].lnum;
3003 col = regmatch->endpos[0].col + extra;
3004 }
3005 else
3006 {
3007 result->lnum = regmatch->startpos[0].lnum;
3008 col = regmatch->startpos[0].col;
3009 }
3010 col += spp->sp_offsets[idx];
3011 if (col < 0)
3012 result->col = 0;
3013 else
3014 result->col = col;
3015}
3016
3017/*
3018 * Get current line in syntax buffer.
3019 */
3020 static char_u *
3021syn_getcurline()
3022{
3023 return ml_get_buf(syn_buf, current_lnum, FALSE);
3024}
3025
3026/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003027 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003028 * Returns TRUE when there is a match.
3029 */
3030 static int
3031syn_regexec(rmp, lnum, col)
3032 regmmatch_T *rmp;
3033 linenr_T lnum;
3034 colnr_T col;
3035{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003036 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003037 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col) > 0)
3038 {
3039 rmp->startpos[0].lnum += lnum;
3040 rmp->endpos[0].lnum += lnum;
3041 return TRUE;
3042 }
3043 return FALSE;
3044}
3045
3046/*
3047 * Check one position in a line for a matching keyword.
3048 * The caller must check if a keyword can start at startcol.
3049 * Return it's ID if found, 0 otherwise.
3050 */
3051 static int
3052check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
3053 char_u *line;
3054 int startcol; /* position in line to check for keyword */
3055 int *endcolp; /* return: character after found keyword */
3056 long *flagsp; /* return: flags of matching keyword */
3057 short **next_listp; /* return: next_list of matching keyword */
3058 stateitem_T *cur_si; /* item at the top of the stack */
3059{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003060 keyentry_T *kp;
3061 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003062 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003063 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003064 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003065 hashtab_T *ht;
3066 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067
3068 /* Find first character after the keyword. First character was already
3069 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003070 kwp = line + startcol;
3071 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003072 do
3073 {
3074#ifdef FEAT_MBYTE
3075 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003076 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003077 else
3078#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003079 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003080 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003081 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003082
Bram Moolenaardad6b692005-01-25 22:14:34 +00003083 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003084 return 0;
3085
3086 /*
3087 * Must make a copy of the keyword, so we can add a NUL and make it
3088 * lowercase.
3089 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003090 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003091
3092 /*
3093 * Try twice:
3094 * 1. matching case
3095 * 2. ignoring case
3096 */
3097 for (round = 1; round <= 2; ++round)
3098 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003099 ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
3100 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003101 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003102 if (round == 2) /* ignore case */
3103 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003104
3105 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003106 * Find keywords that match. There can be several with different
3107 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003108 * When current_next_list is non-zero accept only that group, otherwise:
3109 * Accept a not-contained keyword at toplevel.
3110 * Accept a keyword at other levels only if it is in the contains list.
3111 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003112 hi = hash_find(ht, keyword);
3113 if (!HASHITEM_EMPTY(hi))
3114 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003115 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003116 if (current_next_list != 0
3117 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3118 : (cur_si == NULL
3119 ? !(kp->flags & HL_CONTAINED)
3120 : in_id_list(cur_si, cur_si->si_cont_list,
3121 &kp->k_syn, kp->flags & HL_CONTAINED)))
3122 {
3123 *endcolp = startcol + kwlen;
3124 *flagsp = kp->flags;
3125 *next_listp = kp->next_list;
3126 return kp->k_syn.id;
3127 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003128 }
3129 }
3130 return 0;
3131}
3132
3133/*
3134 * Handle ":syntax case" command.
3135 */
3136/* ARGSUSED */
3137 static void
3138syn_cmd_case(eap, syncing)
3139 exarg_T *eap;
3140 int syncing; /* not used */
3141{
3142 char_u *arg = eap->arg;
3143 char_u *next;
3144
3145 eap->nextcmd = find_nextcmd(arg);
3146 if (eap->skip)
3147 return;
3148
3149 next = skiptowhite(arg);
3150 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3151 curbuf->b_syn_ic = FALSE;
3152 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3153 curbuf->b_syn_ic = TRUE;
3154 else
3155 EMSG2(_("E390: Illegal argument: %s"), arg);
3156}
3157
3158/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003159 * Handle ":syntax spell" command.
3160 */
3161/* ARGSUSED */
3162 static void
3163syn_cmd_spell(eap, syncing)
3164 exarg_T *eap;
3165 int syncing; /* not used */
3166{
3167 char_u *arg = eap->arg;
3168 char_u *next;
3169
3170 eap->nextcmd = find_nextcmd(arg);
3171 if (eap->skip)
3172 return;
3173
3174 next = skiptowhite(arg);
3175 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
3176 curbuf->b_syn_spell = SYNSPL_TOP;
3177 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
3178 curbuf->b_syn_spell = SYNSPL_NOTOP;
3179 else if (STRNICMP(arg, "default", 4) == 0 && next - arg == 4)
3180 curbuf->b_syn_spell = SYNSPL_DEFAULT;
3181 else
3182 EMSG2(_("E390: Illegal argument: %s"), arg);
3183}
3184
3185/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003186 * Clear all syntax info for one buffer.
3187 */
3188 void
3189syntax_clear(buf)
3190 buf_T *buf;
3191{
3192 int i;
3193
Bram Moolenaar5b8d8fd2005-08-16 23:01:50 +00003194 buf->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003195 buf->b_syn_ic = FALSE; /* Use case, by default */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003196 buf->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003197 buf->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003198
3199 /* free the keywords */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003200 clear_keywtab(&buf->b_keywtab);
3201 clear_keywtab(&buf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003202
3203 /* free the syntax patterns */
3204 for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3205 syn_clear_pattern(buf, i);
3206 ga_clear(&buf->b_syn_patterns);
3207
3208 /* free the syntax clusters */
3209 for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3210 syn_clear_cluster(buf, i);
3211 ga_clear(&buf->b_syn_clusters);
Bram Moolenaar75c50c42005-06-04 22:06:24 +00003212 buf->b_spell_cluster_id = 0;
3213 buf->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003214
3215 buf->b_syn_sync_flags = 0;
3216 buf->b_syn_sync_minlines = 0;
3217 buf->b_syn_sync_maxlines = 0;
3218 buf->b_syn_sync_linebreaks = 0;
3219
3220 vim_free(buf->b_syn_linecont_prog);
3221 buf->b_syn_linecont_prog = NULL;
3222 vim_free(buf->b_syn_linecont_pat);
3223 buf->b_syn_linecont_pat = NULL;
3224#ifdef FEAT_FOLDING
3225 buf->b_syn_folditems = 0;
3226#endif
3227
3228 /* free the stored states */
3229 syn_stack_free_all(buf);
3230 invalidate_current_state();
3231}
3232
3233/*
3234 * Clear syncing info for one buffer.
3235 */
3236 static void
3237syntax_sync_clear()
3238{
3239 int i;
3240
3241 /* free the syntax patterns */
3242 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3243 if (SYN_ITEMS(curbuf)[i].sp_syncing)
3244 syn_remove_pattern(curbuf, i);
3245
3246 curbuf->b_syn_sync_flags = 0;
3247 curbuf->b_syn_sync_minlines = 0;
3248 curbuf->b_syn_sync_maxlines = 0;
3249 curbuf->b_syn_sync_linebreaks = 0;
3250
3251 vim_free(curbuf->b_syn_linecont_prog);
3252 curbuf->b_syn_linecont_prog = NULL;
3253 vim_free(curbuf->b_syn_linecont_pat);
3254 curbuf->b_syn_linecont_pat = NULL;
3255
3256 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3257}
3258
3259/*
3260 * Remove one pattern from the buffer's pattern list.
3261 */
3262 static void
3263syn_remove_pattern(buf, idx)
3264 buf_T *buf;
3265 int idx;
3266{
3267 synpat_T *spp;
3268
3269 spp = &(SYN_ITEMS(buf)[idx]);
3270#ifdef FEAT_FOLDING
3271 if (spp->sp_flags & HL_FOLD)
3272 --buf->b_syn_folditems;
3273#endif
3274 syn_clear_pattern(buf, idx);
3275 mch_memmove(spp, spp + 1,
3276 sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3277 --buf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003278}
3279
3280/*
3281 * Clear and free one syntax pattern. When clearing all, must be called from
3282 * last to first!
3283 */
3284 static void
3285syn_clear_pattern(buf, i)
3286 buf_T *buf;
3287 int i;
3288{
3289 vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3290 vim_free(SYN_ITEMS(buf)[i].sp_prog);
3291 /* Only free sp_cont_list and sp_next_list of first start pattern */
3292 if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3293 {
3294 vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3295 vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3296 }
3297}
3298
3299/*
3300 * Clear and free one syntax cluster.
3301 */
3302 static void
3303syn_clear_cluster(buf, i)
3304 buf_T *buf;
3305 int i;
3306{
3307 vim_free(SYN_CLSTR(buf)[i].scl_name);
3308 vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3309 vim_free(SYN_CLSTR(buf)[i].scl_list);
3310}
3311
3312/*
3313 * Handle ":syntax clear" command.
3314 */
3315 static void
3316syn_cmd_clear(eap, syncing)
3317 exarg_T *eap;
3318 int syncing;
3319{
3320 char_u *arg = eap->arg;
3321 char_u *arg_end;
3322 int id;
3323
3324 eap->nextcmd = find_nextcmd(arg);
3325 if (eap->skip)
3326 return;
3327
3328 /*
3329 * We have to disable this within ":syn include @group filename",
3330 * because otherwise @group would get deleted.
3331 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3332 * clear".
3333 */
3334 if (curbuf->b_syn_topgrp != 0)
3335 return;
3336
3337 if (ends_excmd(*arg))
3338 {
3339 /*
3340 * No argument: Clear all syntax items.
3341 */
3342 if (syncing)
3343 syntax_sync_clear();
3344 else
3345 {
3346 syntax_clear(curbuf);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003347 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348 }
3349 }
3350 else
3351 {
3352 /*
3353 * Clear the group IDs that are in the argument.
3354 */
3355 while (!ends_excmd(*arg))
3356 {
3357 arg_end = skiptowhite(arg);
3358 if (*arg == '@')
3359 {
3360 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3361 if (id == 0)
3362 {
3363 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3364 break;
3365 }
3366 else
3367 {
3368 /*
3369 * We can't physically delete a cluster without changing
3370 * the IDs of other clusters, so we do the next best thing
3371 * and make it empty.
3372 */
3373 short scl_id = id - SYNID_CLUSTER;
3374
3375 vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3376 SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3377 }
3378 }
3379 else
3380 {
3381 id = syn_namen2id(arg, (int)(arg_end - arg));
3382 if (id == 0)
3383 {
3384 EMSG2(_(e_nogroup), arg);
3385 break;
3386 }
3387 else
3388 syn_clear_one(id, syncing);
3389 }
3390 arg = skipwhite(arg_end);
3391 }
3392 }
3393 redraw_curbuf_later(NOT_VALID);
3394 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3395}
3396
3397/*
3398 * Clear one syntax group for the current buffer.
3399 */
3400 static void
3401syn_clear_one(id, syncing)
3402 int id;
3403 int syncing;
3404{
3405 synpat_T *spp;
3406 int idx;
3407
3408 /* Clear keywords only when not ":syn sync clear group-name" */
3409 if (!syncing)
3410 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003411 (void)syn_clear_keyword(id, &curbuf->b_keywtab);
3412 (void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003413 }
3414
3415 /* clear the patterns for "id" */
3416 for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3417 {
3418 spp = &(SYN_ITEMS(curbuf)[idx]);
3419 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3420 continue;
3421 syn_remove_pattern(curbuf, idx);
3422 }
3423}
3424
3425/*
3426 * Handle ":syntax on" command.
3427 */
3428/* ARGSUSED */
3429 static void
3430syn_cmd_on(eap, syncing)
3431 exarg_T *eap;
3432 int syncing; /* not used */
3433{
3434 syn_cmd_onoff(eap, "syntax");
3435}
3436
3437/*
3438 * Handle ":syntax enable" command.
3439 */
3440/* ARGSUSED */
3441 static void
3442syn_cmd_enable(eap, syncing)
3443 exarg_T *eap;
3444 int syncing; /* not used */
3445{
3446 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3447 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003448 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003449}
3450
3451/*
3452 * Handle ":syntax reset" command.
3453 */
3454/* ARGSUSED */
3455 static void
3456syn_cmd_reset(eap, syncing)
3457 exarg_T *eap;
3458 int syncing; /* not used */
3459{
3460 eap->nextcmd = check_nextcmd(eap->arg);
3461 if (!eap->skip)
3462 {
3463 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3464 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003465 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003466 }
3467}
3468
3469/*
3470 * Handle ":syntax manual" command.
3471 */
3472/* ARGSUSED */
3473 static void
3474syn_cmd_manual(eap, syncing)
3475 exarg_T *eap;
3476 int syncing; /* not used */
3477{
3478 syn_cmd_onoff(eap, "manual");
3479}
3480
3481/*
3482 * Handle ":syntax off" command.
3483 */
3484/* ARGSUSED */
3485 static void
3486syn_cmd_off(eap, syncing)
3487 exarg_T *eap;
3488 int syncing; /* not used */
3489{
3490 syn_cmd_onoff(eap, "nosyntax");
3491}
3492
3493 static void
3494syn_cmd_onoff(eap, name)
3495 exarg_T *eap;
3496 char *name;
3497{
3498 char_u buf[100];
3499
3500 eap->nextcmd = check_nextcmd(eap->arg);
3501 if (!eap->skip)
3502 {
3503 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003504 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003505 do_cmdline_cmd(buf);
3506 }
3507}
3508
3509/*
3510 * Handle ":syntax [list]" command: list current syntax words.
3511 */
3512 static void
3513syn_cmd_list(eap, syncing)
3514 exarg_T *eap;
3515 int syncing; /* when TRUE: list syncing items */
3516{
3517 char_u *arg = eap->arg;
3518 int id;
3519 char_u *arg_end;
3520
3521 eap->nextcmd = find_nextcmd(arg);
3522 if (eap->skip)
3523 return;
3524
3525 if (!syntax_present(curbuf))
3526 {
3527 MSG(_("No Syntax items defined for this buffer"));
3528 return;
3529 }
3530
3531 if (syncing)
3532 {
3533 if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3534 {
3535 MSG_PUTS(_("syncing on C-style comments"));
3536 syn_lines_msg();
3537 syn_match_msg();
3538 return;
3539 }
3540 else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3541 {
3542 if (curbuf->b_syn_sync_minlines == 0)
3543 MSG_PUTS(_("no syncing"));
3544 else
3545 {
3546 MSG_PUTS(_("syncing starts "));
3547 msg_outnum(curbuf->b_syn_sync_minlines);
3548 MSG_PUTS(_(" lines before top line"));
3549 syn_match_msg();
3550 }
3551 return;
3552 }
3553 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3554 if (curbuf->b_syn_sync_minlines > 0
3555 || curbuf->b_syn_sync_maxlines > 0
3556 || curbuf->b_syn_sync_linebreaks > 0)
3557 {
3558 MSG_PUTS(_("\nsyncing on items"));
3559 syn_lines_msg();
3560 syn_match_msg();
3561 }
3562 }
3563 else
3564 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3565 if (ends_excmd(*arg))
3566 {
3567 /*
3568 * No argument: List all group IDs and all syntax clusters.
3569 */
3570 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3571 syn_list_one(id, syncing, FALSE);
3572 for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3573 syn_list_cluster(id);
3574 }
3575 else
3576 {
3577 /*
3578 * List the group IDs and syntax clusters that are in the argument.
3579 */
3580 while (!ends_excmd(*arg) && !got_int)
3581 {
3582 arg_end = skiptowhite(arg);
3583 if (*arg == '@')
3584 {
3585 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3586 if (id == 0)
3587 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3588 else
3589 syn_list_cluster(id - SYNID_CLUSTER);
3590 }
3591 else
3592 {
3593 id = syn_namen2id(arg, (int)(arg_end - arg));
3594 if (id == 0)
3595 EMSG2(_(e_nogroup), arg);
3596 else
3597 syn_list_one(id, syncing, TRUE);
3598 }
3599 arg = skipwhite(arg_end);
3600 }
3601 }
3602 eap->nextcmd = check_nextcmd(arg);
3603}
3604
3605 static void
3606syn_lines_msg()
3607{
3608 if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3609 {
3610 MSG_PUTS("; ");
3611 if (curbuf->b_syn_sync_minlines > 0)
3612 {
3613 MSG_PUTS(_("minimal "));
3614 msg_outnum(curbuf->b_syn_sync_minlines);
3615 if (curbuf->b_syn_sync_maxlines)
3616 MSG_PUTS(", ");
3617 }
3618 if (curbuf->b_syn_sync_maxlines > 0)
3619 {
3620 MSG_PUTS(_("maximal "));
3621 msg_outnum(curbuf->b_syn_sync_maxlines);
3622 }
3623 MSG_PUTS(_(" lines before top line"));
3624 }
3625}
3626
3627 static void
3628syn_match_msg()
3629{
3630 if (curbuf->b_syn_sync_linebreaks > 0)
3631 {
3632 MSG_PUTS(_("; match "));
3633 msg_outnum(curbuf->b_syn_sync_linebreaks);
3634 MSG_PUTS(_(" line breaks"));
3635 }
3636}
3637
3638static int last_matchgroup;
3639
3640struct name_list
3641{
3642 int flag;
3643 char *name;
3644};
3645
3646static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3647
3648/*
3649 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3650 */
3651 static void
3652syn_list_one(id, syncing, link_only)
3653 int id;
3654 int syncing; /* when TRUE: list syncing items */
3655 int link_only; /* when TRUE; list link-only too */
3656{
3657 int attr;
3658 int idx;
3659 int did_header = FALSE;
3660 synpat_T *spp;
3661 static struct name_list namelist1[] =
3662 {
3663 {HL_DISPLAY, "display"},
3664 {HL_CONTAINED, "contained"},
3665 {HL_ONELINE, "oneline"},
3666 {HL_KEEPEND, "keepend"},
3667 {HL_EXTEND, "extend"},
3668 {HL_EXCLUDENL, "excludenl"},
3669 {HL_TRANSP, "transparent"},
3670 {HL_FOLD, "fold"},
3671 {0, NULL}
3672 };
3673 static struct name_list namelist2[] =
3674 {
3675 {HL_SKIPWHITE, "skipwhite"},
3676 {HL_SKIPNL, "skipnl"},
3677 {HL_SKIPEMPTY, "skipempty"},
3678 {0, NULL}
3679 };
3680
3681 attr = hl_attr(HLF_D); /* highlight like directories */
3682
3683 /* list the keywords for "id" */
3684 if (!syncing)
3685 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003686 did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3687 did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003688 did_header, attr);
3689 }
3690
3691 /* list the patterns for "id" */
3692 for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3693 {
3694 spp = &(SYN_ITEMS(curbuf)[idx]);
3695 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3696 continue;
3697
3698 (void)syn_list_header(did_header, 999, id);
3699 did_header = TRUE;
3700 last_matchgroup = 0;
3701 if (spp->sp_type == SPTYPE_MATCH)
3702 {
3703 put_pattern("match", ' ', spp, attr);
3704 msg_putchar(' ');
3705 }
3706 else if (spp->sp_type == SPTYPE_START)
3707 {
3708 while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3709 put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3710 if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3711 put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3712 while (idx < curbuf->b_syn_patterns.ga_len
3713 && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3714 put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3715 --idx;
3716 msg_putchar(' ');
3717 }
3718 syn_list_flags(namelist1, spp->sp_flags, attr);
3719
3720 if (spp->sp_cont_list != NULL)
3721 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3722
3723 if (spp->sp_syn.cont_in_list != NULL)
3724 put_id_list((char_u *)"containedin",
3725 spp->sp_syn.cont_in_list, attr);
3726
3727 if (spp->sp_next_list != NULL)
3728 {
3729 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3730 syn_list_flags(namelist2, spp->sp_flags, attr);
3731 }
3732 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3733 {
3734 if (spp->sp_flags & HL_SYNC_HERE)
3735 msg_puts_attr((char_u *)"grouphere", attr);
3736 else
3737 msg_puts_attr((char_u *)"groupthere", attr);
3738 msg_putchar(' ');
3739 if (spp->sp_sync_idx >= 0)
3740 msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3741 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3742 else
3743 MSG_PUTS("NONE");
3744 msg_putchar(' ');
3745 }
3746 }
3747
3748 /* list the link, if there is one */
3749 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3750 {
3751 (void)syn_list_header(did_header, 999, id);
3752 msg_puts_attr((char_u *)"links to", attr);
3753 msg_putchar(' ');
3754 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3755 }
3756}
3757
3758 static void
3759syn_list_flags(nl, flags, attr)
3760 struct name_list *nl;
3761 int flags;
3762 int attr;
3763{
3764 int i;
3765
3766 for (i = 0; nl[i].flag != 0; ++i)
3767 if (flags & nl[i].flag)
3768 {
3769 msg_puts_attr((char_u *)nl[i].name, attr);
3770 msg_putchar(' ');
3771 }
3772}
3773
3774/*
3775 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3776 */
3777 static void
3778syn_list_cluster(id)
3779 int id;
3780{
3781 int endcol = 15;
3782
3783 /* slight hack: roughly duplicate the guts of syn_list_header() */
3784 msg_putchar('\n');
3785 msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3786
3787 if (msg_col >= endcol) /* output at least one space */
3788 endcol = msg_col + 1;
3789 if (Columns <= endcol) /* avoid hang for tiny window */
3790 endcol = Columns - 1;
3791
3792 msg_advance(endcol);
3793 if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3794 {
3795 put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3796 hl_attr(HLF_D));
3797 }
3798 else
3799 {
3800 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3801 msg_puts((char_u *)"=NONE");
3802 }
3803}
3804
3805 static void
3806put_id_list(name, list, attr)
3807 char_u *name;
3808 short *list;
3809 int attr;
3810{
3811 short *p;
3812
3813 msg_puts_attr(name, attr);
3814 msg_putchar('=');
3815 for (p = list; *p; ++p)
3816 {
3817 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3818 {
3819 if (p[1])
3820 MSG_PUTS("ALLBUT");
3821 else
3822 MSG_PUTS("ALL");
3823 }
3824 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3825 {
3826 MSG_PUTS("TOP");
3827 }
3828 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3829 {
3830 MSG_PUTS("CONTAINED");
3831 }
3832 else if (*p >= SYNID_CLUSTER)
3833 {
3834 short scl_id = *p - SYNID_CLUSTER;
3835
3836 msg_putchar('@');
3837 msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3838 }
3839 else
3840 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3841 if (p[1])
3842 msg_putchar(',');
3843 }
3844 msg_putchar(' ');
3845}
3846
3847 static void
3848put_pattern(s, c, spp, attr)
3849 char *s;
3850 int c;
3851 synpat_T *spp;
3852 int attr;
3853{
3854 long n;
3855 int mask;
3856 int first;
3857 static char *sepchars = "/+=-#@\"|'^&";
3858 int i;
3859
3860 /* May have to write "matchgroup=group" */
3861 if (last_matchgroup != spp->sp_syn_match_id)
3862 {
3863 last_matchgroup = spp->sp_syn_match_id;
3864 msg_puts_attr((char_u *)"matchgroup", attr);
3865 msg_putchar('=');
3866 if (last_matchgroup == 0)
3867 msg_outtrans((char_u *)"NONE");
3868 else
3869 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3870 msg_putchar(' ');
3871 }
3872
3873 /* output the name of the pattern and an '=' or ' ' */
3874 msg_puts_attr((char_u *)s, attr);
3875 msg_putchar(c);
3876
3877 /* output the pattern, in between a char that is not in the pattern */
3878 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3879 if (sepchars[++i] == NUL)
3880 {
3881 i = 0; /* no good char found, just use the first one */
3882 break;
3883 }
3884 msg_putchar(sepchars[i]);
3885 msg_outtrans(spp->sp_pattern);
3886 msg_putchar(sepchars[i]);
3887
3888 /* output any pattern options */
3889 first = TRUE;
3890 for (i = 0; i < SPO_COUNT; ++i)
3891 {
3892 mask = (1 << i);
3893 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3894 {
3895 if (!first)
3896 msg_putchar(','); /* separate with commas */
3897 msg_puts((char_u *)spo_name_tab[i]);
3898 n = spp->sp_offsets[i];
3899 if (i != SPO_LC_OFF)
3900 {
3901 if (spp->sp_off_flags & mask)
3902 msg_putchar('s');
3903 else
3904 msg_putchar('e');
3905 if (n > 0)
3906 msg_putchar('+');
3907 }
3908 if (n || i == SPO_LC_OFF)
3909 msg_outnum(n);
3910 first = FALSE;
3911 }
3912 }
3913 msg_putchar(' ');
3914}
3915
3916/*
3917 * List or clear the keywords for one syntax group.
3918 * Return TRUE if the header has been printed.
3919 */
3920 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00003921syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003922 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003923 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003924 int did_header; /* header has already been printed */
3925 int attr;
3926{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003927 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003928 hashitem_T *hi;
3929 keyentry_T *kp;
3930 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003931 int prev_contained = 0;
3932 short *prev_next_list = NULL;
3933 short *prev_cont_in_list = NULL;
3934 int prev_skipnl = 0;
3935 int prev_skipwhite = 0;
3936 int prev_skipempty = 0;
3937
Bram Moolenaar071d4272004-06-13 20:20:40 +00003938 /*
3939 * Unfortunately, this list of keywords is not sorted on alphabet but on
3940 * hash value...
3941 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003942 todo = ht->ht_used;
3943 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003944 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003945 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003946 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003947 --todo;
3948 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003949 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003950 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003951 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003952 if (prev_contained != (kp->flags & HL_CONTAINED)
3953 || prev_skipnl != (kp->flags & HL_SKIPNL)
3954 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
3955 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
3956 || prev_cont_in_list != kp->k_syn.cont_in_list
3957 || prev_next_list != kp->next_list)
3958 outlen = 9999;
3959 else
3960 outlen = (int)STRLEN(kp->keyword);
3961 /* output "contained" and "nextgroup" on each line */
3962 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003963 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003964 prev_contained = 0;
3965 prev_next_list = NULL;
3966 prev_cont_in_list = NULL;
3967 prev_skipnl = 0;
3968 prev_skipwhite = 0;
3969 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003971 did_header = TRUE;
3972 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003973 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003974 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003975 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00003976 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003977 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003978 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003979 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003980 put_id_list((char_u *)"containedin",
3981 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003982 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00003983 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003984 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003985 if (kp->next_list != prev_next_list)
3986 {
3987 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
3988 msg_putchar(' ');
3989 prev_next_list = kp->next_list;
3990 if (kp->flags & HL_SKIPNL)
3991 {
3992 msg_puts_attr((char_u *)"skipnl", attr);
3993 msg_putchar(' ');
3994 prev_skipnl = (kp->flags & HL_SKIPNL);
3995 }
3996 if (kp->flags & HL_SKIPWHITE)
3997 {
3998 msg_puts_attr((char_u *)"skipwhite", attr);
3999 msg_putchar(' ');
4000 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4001 }
4002 if (kp->flags & HL_SKIPEMPTY)
4003 {
4004 msg_puts_attr((char_u *)"skipempty", attr);
4005 msg_putchar(' ');
4006 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4007 }
4008 }
4009 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004011 }
4012 }
4013 }
4014
4015 return did_header;
4016}
4017
4018 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004019syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004020 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004021 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004022{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004023 hashitem_T *hi;
4024 keyentry_T *kp;
4025 keyentry_T *kp_prev;
4026 keyentry_T *kp_next;
4027 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004028
Bram Moolenaardad6b692005-01-25 22:14:34 +00004029 hash_lock(ht);
4030 todo = ht->ht_used;
4031 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004032 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004033 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004034 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004035 --todo;
4036 kp_prev = NULL;
4037 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004038 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004039 if (kp->k_syn.id == id)
4040 {
4041 kp_next = kp->ke_next;
4042 if (kp_prev == NULL)
4043 {
4044 if (kp_next == NULL)
4045 hash_remove(ht, hi);
4046 else
4047 hi->hi_key = KE2HIKEY(kp_next);
4048 }
4049 else
4050 kp_prev->ke_next = kp_next;
4051 vim_free(kp->next_list);
4052 vim_free(kp->k_syn.cont_in_list);
4053 vim_free(kp);
4054 kp = kp_next;
4055 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004056 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004057 {
4058 kp_prev = kp;
4059 kp = kp->ke_next;
4060 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061 }
4062 }
4063 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004064 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004065}
4066
4067/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004068 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069 */
4070 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004071clear_keywtab(ht)
4072 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004073{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004074 hashitem_T *hi;
4075 int todo;
4076 keyentry_T *kp;
4077 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078
Bram Moolenaardad6b692005-01-25 22:14:34 +00004079 todo = ht->ht_used;
4080 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004081 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004082 if (!HASHITEM_EMPTY(hi))
4083 {
4084 --todo;
4085 kp = HI2KE(hi);
4086 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004087 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004088 kp_next = kp->ke_next;
4089 vim_free(kp->next_list);
4090 vim_free(kp->k_syn.cont_in_list);
4091 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004092 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004093 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004094 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004095 hash_clear(ht);
4096 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004097}
4098
4099/*
4100 * Add a keyword to the list of keywords.
4101 */
4102 static void
4103add_keyword(name, id, flags, cont_in_list, next_list)
4104 char_u *name; /* name of keyword */
4105 int id; /* group ID for this keyword */
4106 int flags; /* flags for this keyword */
4107 short *cont_in_list; /* containedin for this keyword */
4108 short *next_list; /* nextgroup for this keyword */
4109{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004110 keyentry_T *kp;
4111 hashtab_T *ht;
4112 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004113 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004114 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004115 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116
4117 if (curbuf->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004118 name_ic = str_foldcase(name, (int)STRLEN(name),
4119 name_folded, MAXKEYWLEN + 1);
4120 else
4121 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004122 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4123 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004125 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004126 kp->k_syn.id = id;
4127 kp->k_syn.inc_tag = current_syn_inc_tag;
4128 kp->flags = flags;
4129 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004130 if (cont_in_list != NULL)
4131 curbuf->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004132 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004133
4134 if (curbuf->b_syn_ic)
Bram Moolenaardad6b692005-01-25 22:14:34 +00004135 ht = &curbuf->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004136 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004137 ht = &curbuf->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004138
Bram Moolenaardad6b692005-01-25 22:14:34 +00004139 hash = hash_hash(kp->keyword);
4140 hi = hash_lookup(ht, kp->keyword, hash);
4141 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004142 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004143 /* new keyword, add to hashtable */
4144 kp->ke_next = NULL;
4145 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004147 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004148 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004149 /* keyword already exists, prepend to list */
4150 kp->ke_next = HI2KE(hi);
4151 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004152 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004153}
4154
4155/*
4156 * Get the start and end of the group name argument.
4157 * Return a pointer to the first argument.
4158 * Return NULL if the end of the command was found instead of further args.
4159 */
4160 static char_u *
4161get_group_name(arg, name_end)
4162 char_u *arg; /* start of the argument */
4163 char_u **name_end; /* pointer to end of the name */
4164{
4165 char_u *rest;
4166
4167 *name_end = skiptowhite(arg);
4168 rest = skipwhite(*name_end);
4169
4170 /*
4171 * Check if there are enough arguments. The first argument may be a
4172 * pattern, where '|' is allowed, so only check for NUL.
4173 */
4174 if (ends_excmd(*arg) || *rest == NUL)
4175 return NULL;
4176 return rest;
4177}
4178
4179/*
4180 * Check for syntax command option arguments.
4181 * This can be called at any place in the list of arguments, and just picks
4182 * out the arguments that are known. Can be called several times in a row to
4183 * collect all options in between other arguments.
4184 * Return a pointer to the next argument (which isn't an option).
4185 * Return NULL for any error;
4186 */
4187 static char_u *
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004188get_syn_options(arg, opt)
4189 char_u *arg; /* next argument to be checked */
4190 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004191{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004192 char_u *gname_start, *gname;
4193 int syn_id;
4194 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004195 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004196 int i;
4197 int fidx;
4198 static struct flag
4199 {
4200 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004201 int argtype;
4202 int flags;
4203 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4204 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4205 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4206 {"eExXtTeEnNdD", 0, HL_EXTEND},
4207 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4208 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4209 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4210 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4211 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4212 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4213 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4214 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4215 {"fFoOlLdD", 0, HL_FOLD},
4216 {"cCoOnNtTaAiInNsS", 1, 0},
4217 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4218 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004219 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004220 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221
4222 if (arg == NULL) /* already detected error */
4223 return NULL;
4224
Bram Moolenaar071d4272004-06-13 20:20:40 +00004225 for (;;)
4226 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004227 /*
4228 * This is used very often when a large number of keywords is defined.
4229 * Need to skip quickly when no option name is found.
4230 * Also avoid tolower(), it's slow.
4231 */
4232 if (strchr(first_letters, *arg) == NULL)
4233 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004234
4235 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4236 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004237 p = flagtab[fidx].name;
4238 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4239 if (arg[len] != p[i] && arg[len] != p[i + 1])
4240 break;
4241 if (p[i] == NUL && (vim_iswhite(arg[len])
4242 || (flagtab[fidx].argtype > 0
4243 ? arg[len] == '='
4244 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004246 if (opt->keyword
4247 && (flagtab[fidx].flags == HL_DISPLAY
4248 || flagtab[fidx].flags == HL_FOLD
4249 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004250 /* treat "display", "fold" and "extend" as a keyword */
4251 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004252 break;
4253 }
4254 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004255 if (fidx < 0) /* no match found */
4256 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004257
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004258 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004259 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004260 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004261 {
4262 EMSG(_("E395: contains argument not accepted here"));
4263 return NULL;
4264 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004265 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266 return NULL;
4267 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004268 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004270#if 0 /* cannot happen */
4271 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272 {
4273 EMSG(_("E396: containedin argument not accepted here"));
4274 return NULL;
4275 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004276#endif
4277 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 return NULL;
4279 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004280 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004282 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004283 return NULL;
4284 }
4285 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004286 {
4287 opt->flags |= flagtab[fidx].flags;
4288 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004290 if (flagtab[fidx].flags == HL_SYNC_HERE
4291 || flagtab[fidx].flags == HL_SYNC_THERE)
4292 {
4293 if (opt->sync_idx == NULL)
4294 {
4295 EMSG(_("E393: group[t]here not accepted here"));
4296 return NULL;
4297 }
4298 gname_start = arg;
4299 arg = skiptowhite(arg);
4300 if (gname_start == arg)
4301 return NULL;
4302 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4303 if (gname == NULL)
4304 return NULL;
4305 if (STRCMP(gname, "NONE") == 0)
4306 *opt->sync_idx = NONE_IDX;
4307 else
4308 {
4309 syn_id = syn_name2id(gname);
4310 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4311 if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4312 && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4313 {
4314 *opt->sync_idx = i;
4315 break;
4316 }
4317 if (i < 0)
4318 {
4319 EMSG2(_("E394: Didn't find region item for %s"), gname);
4320 vim_free(gname);
4321 return NULL;
4322 }
4323 }
4324
4325 vim_free(gname);
4326 arg = skipwhite(arg);
4327 }
4328#ifdef FEAT_FOLDING
4329 else if (flagtab[fidx].flags == HL_FOLD
4330 && foldmethodIsSyntax(curwin))
4331 /* Need to update folds later. */
4332 foldUpdateAll(curwin);
4333#endif
4334 }
4335 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004336
4337 return arg;
4338}
4339
4340/*
4341 * Adjustments to syntax item when declared in a ":syn include"'d file.
4342 * Set the contained flag, and if the item is not already contained, add it
4343 * to the specified top-level group, if any.
4344 */
4345 static void
4346syn_incl_toplevel(id, flagsp)
4347 int id;
4348 int *flagsp;
4349{
4350 if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4351 return;
4352 *flagsp |= HL_CONTAINED;
4353 if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4354 {
4355 /* We have to alloc this, because syn_combine_list() will free it. */
4356 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4357 int tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4358
4359 if (grp_list != NULL)
4360 {
4361 grp_list[0] = id;
4362 grp_list[1] = 0;
4363 syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4364 CLUSTER_ADD);
4365 }
4366 }
4367}
4368
4369/*
4370 * Handle ":syntax include [@{group-name}] filename" command.
4371 */
4372/* ARGSUSED */
4373 static void
4374syn_cmd_include(eap, syncing)
4375 exarg_T *eap;
4376 int syncing; /* not used */
4377{
4378 char_u *arg = eap->arg;
4379 int sgl_id = 1;
4380 char_u *group_name_end;
4381 char_u *rest;
4382 char_u *errormsg = NULL;
4383 int prev_toplvl_grp;
4384 int prev_syn_inc_tag;
4385 int source = FALSE;
4386
4387 eap->nextcmd = find_nextcmd(arg);
4388 if (eap->skip)
4389 return;
4390
4391 if (arg[0] == '@')
4392 {
4393 ++arg;
4394 rest = get_group_name(arg, &group_name_end);
4395 if (rest == NULL)
4396 {
4397 EMSG((char_u *)_("E397: Filename required"));
4398 return;
4399 }
4400 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4401 /* separate_nextcmd() and expand_filename() depend on this */
4402 eap->arg = rest;
4403 }
4404
4405 /*
4406 * Everything that's left, up to the next command, should be the
4407 * filename to include.
4408 */
4409 eap->argt |= (XFILE | NOSPC);
4410 separate_nextcmd(eap);
4411 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4412 {
4413 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4414 * file. Need to expand the file name first. In other cases
4415 * ":runtime!" is used. */
4416 source = TRUE;
4417 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4418 {
4419 if (errormsg != NULL)
4420 EMSG(errormsg);
4421 return;
4422 }
4423 }
4424
4425 /*
4426 * Save and restore the existing top-level grouplist id and ":syn
4427 * include" tag around the actual inclusion.
4428 */
4429 prev_syn_inc_tag = current_syn_inc_tag;
4430 current_syn_inc_tag = ++running_syn_inc_tag;
4431 prev_toplvl_grp = curbuf->b_syn_topgrp;
4432 curbuf->b_syn_topgrp = sgl_id;
4433 if (source ? do_source(eap->arg, FALSE, FALSE) == FAIL
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00004434 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004435 EMSG2(_(e_notopen), eap->arg);
4436 curbuf->b_syn_topgrp = prev_toplvl_grp;
4437 current_syn_inc_tag = prev_syn_inc_tag;
4438}
4439
4440/*
4441 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4442 */
4443/* ARGSUSED */
4444 static void
4445syn_cmd_keyword(eap, syncing)
4446 exarg_T *eap;
4447 int syncing; /* not used */
4448{
4449 char_u *arg = eap->arg;
4450 char_u *group_name_end;
4451 int syn_id;
4452 char_u *rest;
4453 char_u *keyword_copy;
4454 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004455 char_u *kw;
4456 syn_opt_arg_T syn_opt_arg;
4457 int cnt;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004458
4459 rest = get_group_name(arg, &group_name_end);
4460
4461 if (rest != NULL)
4462 {
4463 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4464
4465 /* allocate a buffer, for removing the backslashes in the keyword */
4466 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4467 if (keyword_copy != NULL)
4468 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004469 syn_opt_arg.flags = 0;
4470 syn_opt_arg.keyword = TRUE;
4471 syn_opt_arg.sync_idx = NULL;
4472 syn_opt_arg.has_cont_list = FALSE;
4473 syn_opt_arg.cont_in_list = NULL;
4474 syn_opt_arg.next_list = NULL;
4475
Bram Moolenaar071d4272004-06-13 20:20:40 +00004476 /*
4477 * The options given apply to ALL keywords, so all options must be
4478 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004479 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004480 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004481 cnt = 0;
4482 p = keyword_copy;
4483 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004484 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004485 rest = get_syn_options(rest, &syn_opt_arg);
4486 if (rest == NULL || ends_excmd(*rest))
4487 break;
4488 /* Copy the keyword, removing backslashes, and add a NUL. */
4489 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004490 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004491 if (*rest == '\\' && rest[1] != NUL)
4492 ++rest;
4493 *p++ = *rest++;
4494 }
4495 *p++ = NUL;
4496 ++cnt;
4497 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004498
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004499 if (!eap->skip)
4500 {
4501 /* Adjust flags for use of ":syn include". */
4502 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4503
4504 /*
4505 * 2: Add an entry for each keyword.
4506 */
4507 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4508 {
4509 for (p = vim_strchr(kw, '['); ; )
4510 {
4511 if (p != NULL)
4512 *p = NUL;
4513 add_keyword(kw, syn_id, syn_opt_arg.flags,
4514 syn_opt_arg.cont_in_list,
4515 syn_opt_arg.next_list);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004516 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004517 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004518 if (p[1] == NUL)
4519 {
4520 EMSG2(_("E747: Missing ']': %s"), kw);
4521 kw = p + 2; /* skip over the NUL */
4522 break;
4523 }
4524 if (p[1] == ']')
4525 {
4526 kw = p + 1; /* skip over the "]" */
4527 break;
4528 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004529#ifdef FEAT_MBYTE
4530 if (has_mbyte)
4531 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004532 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004533
4534 mch_memmove(p, p + 1, l);
4535 p += l;
4536 }
4537 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004538#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004539 {
4540 p[0] = p[1];
4541 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004542 }
4543 }
4544 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004545 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004546
Bram Moolenaar071d4272004-06-13 20:20:40 +00004547 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004548 vim_free(syn_opt_arg.cont_in_list);
4549 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004550 }
4551 }
4552
4553 if (rest != NULL)
4554 eap->nextcmd = check_nextcmd(rest);
4555 else
4556 EMSG2(_(e_invarg2), arg);
4557
Bram Moolenaar071d4272004-06-13 20:20:40 +00004558 redraw_curbuf_later(NOT_VALID);
4559 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4560}
4561
4562/*
4563 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4564 *
4565 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4566 */
4567 static void
4568syn_cmd_match(eap, syncing)
4569 exarg_T *eap;
4570 int syncing; /* TRUE for ":syntax sync match .. " */
4571{
4572 char_u *arg = eap->arg;
4573 char_u *group_name_end;
4574 char_u *rest;
4575 synpat_T item; /* the item found in the line */
4576 int syn_id;
4577 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004578 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579 int sync_idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580
4581 /* Isolate the group name, check for validity */
4582 rest = get_group_name(arg, &group_name_end);
4583
4584 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004585 syn_opt_arg.flags = 0;
4586 syn_opt_arg.keyword = FALSE;
4587 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4588 syn_opt_arg.has_cont_list = TRUE;
4589 syn_opt_arg.cont_list = NULL;
4590 syn_opt_arg.cont_in_list = NULL;
4591 syn_opt_arg.next_list = NULL;
4592 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004593
4594 /* get the pattern. */
4595 init_syn_patterns();
4596 vim_memset(&item, 0, sizeof(item));
4597 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004598 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4599 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004600
4601 /* Get options after the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004602 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004603
4604 if (rest != NULL) /* all arguments are valid */
4605 {
4606 /*
4607 * Check for trailing command and illegal trailing arguments.
4608 */
4609 eap->nextcmd = check_nextcmd(rest);
4610 if (!ends_excmd(*rest) || eap->skip)
4611 rest = NULL;
4612 else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4613 && (syn_id = syn_check_group(arg,
4614 (int)(group_name_end - arg))) != 0)
4615 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004616 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004617 /*
4618 * Store the pattern in the syn_items list
4619 */
4620 idx = curbuf->b_syn_patterns.ga_len;
4621 SYN_ITEMS(curbuf)[idx] = item;
4622 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4623 SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4624 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4625 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004626 SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004627 SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004628 SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4629 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4630 syn_opt_arg.cont_in_list;
4631 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004632 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004633 SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004635
4636 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004637 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638 curbuf->b_syn_sync_flags |= SF_MATCH;
4639#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004640 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004641 ++curbuf->b_syn_folditems;
4642#endif
4643
4644 redraw_curbuf_later(NOT_VALID);
4645 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4646 return; /* don't free the progs and patterns now */
4647 }
4648 }
4649
4650 /*
4651 * Something failed, free the allocated memory.
4652 */
4653 vim_free(item.sp_prog);
4654 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004655 vim_free(syn_opt_arg.cont_list);
4656 vim_free(syn_opt_arg.cont_in_list);
4657 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004658
4659 if (rest == NULL)
4660 EMSG2(_(e_invarg2), arg);
4661}
4662
4663/*
4664 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4665 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4666 */
4667 static void
4668syn_cmd_region(eap, syncing)
4669 exarg_T *eap;
4670 int syncing; /* TRUE for ":syntax sync region .." */
4671{
4672 char_u *arg = eap->arg;
4673 char_u *group_name_end;
4674 char_u *rest; /* next arg, NULL on error */
4675 char_u *key_end;
4676 char_u *key = NULL;
4677 char_u *p;
4678 int item;
4679#define ITEM_START 0
4680#define ITEM_SKIP 1
4681#define ITEM_END 2
4682#define ITEM_MATCHGROUP 3
4683 struct pat_ptr
4684 {
4685 synpat_T *pp_synp; /* pointer to syn_pattern */
4686 int pp_matchgroup_id; /* matchgroup ID */
4687 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4688 } *(pat_ptrs[3]);
4689 /* patterns found in the line */
4690 struct pat_ptr *ppp;
4691 struct pat_ptr *ppp_next;
4692 int pat_count = 0; /* nr of syn_patterns found */
4693 int syn_id;
4694 int matchgroup_id = 0;
4695 int not_enough = FALSE; /* not enough arguments */
4696 int illegal = FALSE; /* illegal arguments */
4697 int success = FALSE;
4698 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004699 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700
4701 /* Isolate the group name, check for validity */
4702 rest = get_group_name(arg, &group_name_end);
4703
4704 pat_ptrs[0] = NULL;
4705 pat_ptrs[1] = NULL;
4706 pat_ptrs[2] = NULL;
4707
4708 init_syn_patterns();
4709
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004710 syn_opt_arg.flags = 0;
4711 syn_opt_arg.keyword = FALSE;
4712 syn_opt_arg.sync_idx = NULL;
4713 syn_opt_arg.has_cont_list = TRUE;
4714 syn_opt_arg.cont_list = NULL;
4715 syn_opt_arg.cont_in_list = NULL;
4716 syn_opt_arg.next_list = NULL;
4717
Bram Moolenaar071d4272004-06-13 20:20:40 +00004718 /*
4719 * get the options, patterns and matchgroup.
4720 */
4721 while (rest != NULL && !ends_excmd(*rest))
4722 {
4723 /* Check for option arguments */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004724 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004725 if (rest == NULL || ends_excmd(*rest))
4726 break;
4727
4728 /* must be a pattern or matchgroup then */
4729 key_end = rest;
4730 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4731 ++key_end;
4732 vim_free(key);
4733 key = vim_strnsave_up(rest, (int)(key_end - rest));
4734 if (key == NULL) /* out of memory */
4735 {
4736 rest = NULL;
4737 break;
4738 }
4739 if (STRCMP(key, "MATCHGROUP") == 0)
4740 item = ITEM_MATCHGROUP;
4741 else if (STRCMP(key, "START") == 0)
4742 item = ITEM_START;
4743 else if (STRCMP(key, "END") == 0)
4744 item = ITEM_END;
4745 else if (STRCMP(key, "SKIP") == 0)
4746 {
4747 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4748 {
4749 illegal = TRUE;
4750 break;
4751 }
4752 item = ITEM_SKIP;
4753 }
4754 else
4755 break;
4756 rest = skipwhite(key_end);
4757 if (*rest != '=')
4758 {
4759 rest = NULL;
4760 EMSG2(_("E398: Missing '=': %s"), arg);
4761 break;
4762 }
4763 rest = skipwhite(rest + 1);
4764 if (*rest == NUL)
4765 {
4766 not_enough = TRUE;
4767 break;
4768 }
4769
4770 if (item == ITEM_MATCHGROUP)
4771 {
4772 p = skiptowhite(rest);
4773 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4774 matchgroup_id = 0;
4775 else
4776 {
4777 matchgroup_id = syn_check_group(rest, (int)(p - rest));
4778 if (matchgroup_id == 0)
4779 {
4780 illegal = TRUE;
4781 break;
4782 }
4783 }
4784 rest = skipwhite(p);
4785 }
4786 else
4787 {
4788 /*
4789 * Allocate room for a syn_pattern, and link it in the list of
4790 * syn_patterns for this item, at the start (because the list is
4791 * used from end to start).
4792 */
4793 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4794 if (ppp == NULL)
4795 {
4796 rest = NULL;
4797 break;
4798 }
4799 ppp->pp_next = pat_ptrs[item];
4800 pat_ptrs[item] = ppp;
4801 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4802 if (ppp->pp_synp == NULL)
4803 {
4804 rest = NULL;
4805 break;
4806 }
4807
4808 /*
4809 * Get the syntax pattern and the following offset(s).
4810 */
4811 /* Enable the appropriate \z specials. */
4812 if (item == ITEM_START)
4813 reg_do_extmatch = REX_SET;
4814 else if (item == ITEM_SKIP || item == ITEM_END)
4815 reg_do_extmatch = REX_USE;
4816 rest = get_syn_pattern(rest, ppp->pp_synp);
4817 reg_do_extmatch = 0;
4818 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004819 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004820 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4821 ppp->pp_matchgroup_id = matchgroup_id;
4822 ++pat_count;
4823 }
4824 }
4825 vim_free(key);
4826 if (illegal || not_enough)
4827 rest = NULL;
4828
4829 /*
4830 * Must have a "start" and "end" pattern.
4831 */
4832 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4833 pat_ptrs[ITEM_END] == NULL))
4834 {
4835 not_enough = TRUE;
4836 rest = NULL;
4837 }
4838
4839 if (rest != NULL)
4840 {
4841 /*
4842 * Check for trailing garbage or command.
4843 * If OK, add the item.
4844 */
4845 eap->nextcmd = check_nextcmd(rest);
4846 if (!ends_excmd(*rest) || eap->skip)
4847 rest = NULL;
4848 else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4849 && (syn_id = syn_check_group(arg,
4850 (int)(group_name_end - arg))) != 0)
4851 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004852 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853 /*
4854 * Store the start/skip/end in the syn_items list
4855 */
4856 idx = curbuf->b_syn_patterns.ga_len;
4857 for (item = ITEM_START; item <= ITEM_END; ++item)
4858 {
4859 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4860 {
4861 SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4862 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4863 SYN_ITEMS(curbuf)[idx].sp_type =
4864 (item == ITEM_START) ? SPTYPE_START :
4865 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004866 SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004867 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4868 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4869 SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4870 ppp->pp_matchgroup_id;
4871 if (item == ITEM_START)
4872 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004873 SYN_ITEMS(curbuf)[idx].sp_cont_list =
4874 syn_opt_arg.cont_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004875 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004876 syn_opt_arg.cont_in_list;
4877 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004878 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004879 SYN_ITEMS(curbuf)[idx].sp_next_list =
4880 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004881 }
4882 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004883 ++idx;
4884#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004885 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004886 ++curbuf->b_syn_folditems;
4887#endif
4888 }
4889 }
4890
4891 redraw_curbuf_later(NOT_VALID);
4892 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4893 success = TRUE; /* don't free the progs and patterns now */
4894 }
4895 }
4896
4897 /*
4898 * Free the allocated memory.
4899 */
4900 for (item = ITEM_START; item <= ITEM_END; ++item)
4901 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4902 {
4903 if (!success)
4904 {
4905 vim_free(ppp->pp_synp->sp_prog);
4906 vim_free(ppp->pp_synp->sp_pattern);
4907 }
4908 vim_free(ppp->pp_synp);
4909 ppp_next = ppp->pp_next;
4910 vim_free(ppp);
4911 }
4912
4913 if (!success)
4914 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004915 vim_free(syn_opt_arg.cont_list);
4916 vim_free(syn_opt_arg.cont_in_list);
4917 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918 if (not_enough)
4919 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
4920 else if (illegal || rest == NULL)
4921 EMSG2(_(e_invarg2), arg);
4922 }
4923}
4924
4925/*
4926 * A simple syntax group ID comparison function suitable for use in qsort()
4927 */
4928 static int
4929#ifdef __BORLANDC__
4930_RTLENTRYF
4931#endif
4932syn_compare_stub(v1, v2)
4933 const void *v1;
4934 const void *v2;
4935{
4936 const short *s1 = v1;
4937 const short *s2 = v2;
4938
4939 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
4940}
4941
4942/*
4943 * Combines lists of syntax clusters.
4944 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
4945 */
4946 static void
4947syn_combine_list(clstr1, clstr2, list_op)
4948 short **clstr1;
4949 short **clstr2;
4950 int list_op;
4951{
4952 int count1 = 0;
4953 int count2 = 0;
4954 short *g1;
4955 short *g2;
4956 short *clstr = NULL;
4957 int count;
4958 int round;
4959
4960 /*
4961 * Handle degenerate cases.
4962 */
4963 if (*clstr2 == NULL)
4964 return;
4965 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
4966 {
4967 if (list_op == CLUSTER_REPLACE)
4968 vim_free(*clstr1);
4969 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
4970 *clstr1 = *clstr2;
4971 else
4972 vim_free(*clstr2);
4973 return;
4974 }
4975
4976 for (g1 = *clstr1; *g1; g1++)
4977 ++count1;
4978 for (g2 = *clstr2; *g2; g2++)
4979 ++count2;
4980
4981 /*
4982 * For speed purposes, sort both lists.
4983 */
4984 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
4985 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
4986
4987 /*
4988 * We proceed in two passes; in round 1, we count the elements to place
4989 * in the new list, and in round 2, we allocate and populate the new
4990 * list. For speed, we use a mergesort-like method, adding the smaller
4991 * of the current elements in each list to the new list.
4992 */
4993 for (round = 1; round <= 2; round++)
4994 {
4995 g1 = *clstr1;
4996 g2 = *clstr2;
4997 count = 0;
4998
4999 /*
5000 * First, loop through the lists until one of them is empty.
5001 */
5002 while (*g1 && *g2)
5003 {
5004 /*
5005 * We always want to add from the first list.
5006 */
5007 if (*g1 < *g2)
5008 {
5009 if (round == 2)
5010 clstr[count] = *g1;
5011 count++;
5012 g1++;
5013 continue;
5014 }
5015 /*
5016 * We only want to add from the second list if we're adding the
5017 * lists.
5018 */
5019 if (list_op == CLUSTER_ADD)
5020 {
5021 if (round == 2)
5022 clstr[count] = *g2;
5023 count++;
5024 }
5025 if (*g1 == *g2)
5026 g1++;
5027 g2++;
5028 }
5029
5030 /*
5031 * Now add the leftovers from whichever list didn't get finished
5032 * first. As before, we only want to add from the second list if
5033 * we're adding the lists.
5034 */
5035 for (; *g1; g1++, count++)
5036 if (round == 2)
5037 clstr[count] = *g1;
5038 if (list_op == CLUSTER_ADD)
5039 for (; *g2; g2++, count++)
5040 if (round == 2)
5041 clstr[count] = *g2;
5042
5043 if (round == 1)
5044 {
5045 /*
5046 * If the group ended up empty, we don't need to allocate any
5047 * space for it.
5048 */
5049 if (count == 0)
5050 {
5051 clstr = NULL;
5052 break;
5053 }
5054 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5055 if (clstr == NULL)
5056 break;
5057 clstr[count] = 0;
5058 }
5059 }
5060
5061 /*
5062 * Finally, put the new list in place.
5063 */
5064 vim_free(*clstr1);
5065 vim_free(*clstr2);
5066 *clstr1 = clstr;
5067}
5068
5069/*
5070 * Lookup a syntax cluster name and return it's ID.
5071 * If it is not found, 0 is returned.
5072 */
5073 static int
5074syn_scl_name2id(name)
5075 char_u *name;
5076{
5077 int i;
5078 char_u *name_u;
5079
5080 /* Avoid using stricmp() too much, it's slow on some systems */
5081 name_u = vim_strsave_up(name);
5082 if (name_u == NULL)
5083 return 0;
5084 for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
5085 if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
5086 && STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
5087 break;
5088 vim_free(name_u);
5089 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5090}
5091
5092/*
5093 * Like syn_scl_name2id(), but take a pointer + length argument.
5094 */
5095 static int
5096syn_scl_namen2id(linep, len)
5097 char_u *linep;
5098 int len;
5099{
5100 char_u *name;
5101 int id = 0;
5102
5103 name = vim_strnsave(linep, len);
5104 if (name != NULL)
5105 {
5106 id = syn_scl_name2id(name);
5107 vim_free(name);
5108 }
5109 return id;
5110}
5111
5112/*
5113 * Find syntax cluster name in the table and return it's ID.
5114 * The argument is a pointer to the name and the length of the name.
5115 * If it doesn't exist yet, a new entry is created.
5116 * Return 0 for failure.
5117 */
5118 static int
5119syn_check_cluster(pp, len)
5120 char_u *pp;
5121 int len;
5122{
5123 int id;
5124 char_u *name;
5125
5126 name = vim_strnsave(pp, len);
5127 if (name == NULL)
5128 return 0;
5129
5130 id = syn_scl_name2id(name);
5131 if (id == 0) /* doesn't exist yet */
5132 id = syn_add_cluster(name);
5133 else
5134 vim_free(name);
5135 return id;
5136}
5137
5138/*
5139 * Add new syntax cluster and return it's ID.
5140 * "name" must be an allocated string, it will be consumed.
5141 * Return 0 for failure.
5142 */
5143 static int
5144syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005145 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005146{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005147 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005148
5149 /*
5150 * First call for this growarray: init growing array.
5151 */
5152 if (curbuf->b_syn_clusters.ga_data == NULL)
5153 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00005154 curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005155 curbuf->b_syn_clusters.ga_growsize = 10;
5156 }
5157
5158 /*
5159 * Make room for at least one other cluster entry.
5160 */
5161 if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5162 {
5163 vim_free(name);
5164 return 0;
5165 }
5166 len = curbuf->b_syn_clusters.ga_len;
5167
Bram Moolenaar217ad922005-03-20 22:37:15 +00005168 vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005169 SYN_CLSTR(curbuf)[len].scl_name = name;
5170 SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5171 SYN_CLSTR(curbuf)[len].scl_list = NULL;
5172 ++curbuf->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005173
Bram Moolenaar217ad922005-03-20 22:37:15 +00005174 if (STRICMP(name, "Spell") == 0)
5175 curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005176 if (STRICMP(name, "NoSpell") == 0)
5177 curbuf->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005178
Bram Moolenaar071d4272004-06-13 20:20:40 +00005179 return len + SYNID_CLUSTER;
5180}
5181
5182/*
5183 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5184 * [add={groupname},..] [remove={groupname},..]".
5185 */
5186/* ARGSUSED */
5187 static void
5188syn_cmd_cluster(eap, syncing)
5189 exarg_T *eap;
5190 int syncing; /* not used */
5191{
5192 char_u *arg = eap->arg;
5193 char_u *group_name_end;
5194 char_u *rest;
5195 int scl_id;
5196 short *clstr_list;
5197 int got_clstr = FALSE;
5198 int opt_len;
5199 int list_op;
5200
5201 eap->nextcmd = find_nextcmd(arg);
5202 if (eap->skip)
5203 return;
5204
5205 rest = get_group_name(arg, &group_name_end);
5206
5207 if (rest != NULL)
5208 {
5209 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005210 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005211
5212 for (;;)
5213 {
5214 if (STRNICMP(rest, "add", 3) == 0
5215 && (vim_iswhite(rest[3]) || rest[3] == '='))
5216 {
5217 opt_len = 3;
5218 list_op = CLUSTER_ADD;
5219 }
5220 else if (STRNICMP(rest, "remove", 6) == 0
5221 && (vim_iswhite(rest[6]) || rest[6] == '='))
5222 {
5223 opt_len = 6;
5224 list_op = CLUSTER_SUBTRACT;
5225 }
5226 else if (STRNICMP(rest, "contains", 8) == 0
5227 && (vim_iswhite(rest[8]) || rest[8] == '='))
5228 {
5229 opt_len = 8;
5230 list_op = CLUSTER_REPLACE;
5231 }
5232 else
5233 break;
5234
5235 clstr_list = NULL;
5236 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5237 {
5238 EMSG2(_(e_invarg2), rest);
5239 break;
5240 }
5241 syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5242 &clstr_list, list_op);
5243 got_clstr = TRUE;
5244 }
5245
5246 if (got_clstr)
5247 {
5248 redraw_curbuf_later(NOT_VALID);
5249 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5250 }
5251 }
5252
5253 if (!got_clstr)
5254 EMSG(_("E400: No cluster specified"));
5255 if (rest == NULL || !ends_excmd(*rest))
5256 EMSG2(_(e_invarg2), arg);
5257}
5258
5259/*
5260 * On first call for current buffer: Init growing array.
5261 */
5262 static void
5263init_syn_patterns()
5264{
5265 curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5266 curbuf->b_syn_patterns.ga_growsize = 10;
5267}
5268
5269/*
5270 * Get one pattern for a ":syntax match" or ":syntax region" command.
5271 * Stores the pattern and program in a synpat_T.
5272 * Returns a pointer to the next argument, or NULL in case of an error.
5273 */
5274 static char_u *
5275get_syn_pattern(arg, ci)
5276 char_u *arg;
5277 synpat_T *ci;
5278{
5279 char_u *end;
5280 int *p;
5281 int idx;
5282 char_u *cpo_save;
5283
5284 /* need at least three chars */
5285 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5286 return NULL;
5287
5288 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5289 if (*end != *arg) /* end delimiter not found */
5290 {
5291 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5292 return NULL;
5293 }
5294 /* store the pattern and compiled regexp program */
5295 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5296 return NULL;
5297
5298 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5299 cpo_save = p_cpo;
5300 p_cpo = (char_u *)"";
5301 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5302 p_cpo = cpo_save;
5303
5304 if (ci->sp_prog == NULL)
5305 return NULL;
5306 ci->sp_ic = curbuf->b_syn_ic;
5307
5308 /*
5309 * Check for a match, highlight or region offset.
5310 */
5311 ++end;
5312 do
5313 {
5314 for (idx = SPO_COUNT; --idx >= 0; )
5315 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5316 break;
5317 if (idx >= 0)
5318 {
5319 p = &(ci->sp_offsets[idx]);
5320 if (idx != SPO_LC_OFF)
5321 switch (end[3])
5322 {
5323 case 's': break;
5324 case 'b': break;
5325 case 'e': idx += SPO_COUNT; break;
5326 default: idx = -1; break;
5327 }
5328 if (idx >= 0)
5329 {
5330 ci->sp_off_flags |= (1 << idx);
5331 if (idx == SPO_LC_OFF) /* lc=99 */
5332 {
5333 end += 3;
5334 *p = getdigits(&end);
5335
5336 /* "lc=" offset automatically sets "ms=" offset */
5337 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5338 {
5339 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5340 ci->sp_offsets[SPO_MS_OFF] = *p;
5341 }
5342 }
5343 else /* yy=x+99 */
5344 {
5345 end += 4;
5346 if (*end == '+')
5347 {
5348 ++end;
5349 *p = getdigits(&end); /* positive offset */
5350 }
5351 else if (*end == '-')
5352 {
5353 ++end;
5354 *p = -getdigits(&end); /* negative offset */
5355 }
5356 }
5357 if (*end != ',')
5358 break;
5359 ++end;
5360 }
5361 }
5362 } while (idx >= 0);
5363
5364 if (!ends_excmd(*end) && !vim_iswhite(*end))
5365 {
5366 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5367 return NULL;
5368 }
5369 return skipwhite(end);
5370}
5371
5372/*
5373 * Handle ":syntax sync .." command.
5374 */
5375/* ARGSUSED */
5376 static void
5377syn_cmd_sync(eap, syncing)
5378 exarg_T *eap;
5379 int syncing; /* not used */
5380{
5381 char_u *arg_start = eap->arg;
5382 char_u *arg_end;
5383 char_u *key = NULL;
5384 char_u *next_arg;
5385 int illegal = FALSE;
5386 int finished = FALSE;
5387 long n;
5388 char_u *cpo_save;
5389
5390 if (ends_excmd(*arg_start))
5391 {
5392 syn_cmd_list(eap, TRUE);
5393 return;
5394 }
5395
5396 while (!ends_excmd(*arg_start))
5397 {
5398 arg_end = skiptowhite(arg_start);
5399 next_arg = skipwhite(arg_end);
5400 vim_free(key);
5401 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5402 if (STRCMP(key, "CCOMMENT") == 0)
5403 {
5404 if (!eap->skip)
5405 curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5406 if (!ends_excmd(*next_arg))
5407 {
5408 arg_end = skiptowhite(next_arg);
5409 if (!eap->skip)
5410 curbuf->b_syn_sync_id = syn_check_group(next_arg,
5411 (int)(arg_end - next_arg));
5412 next_arg = skipwhite(arg_end);
5413 }
5414 else if (!eap->skip)
5415 curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5416 }
5417 else if ( STRNCMP(key, "LINES", 5) == 0
5418 || STRNCMP(key, "MINLINES", 8) == 0
5419 || STRNCMP(key, "MAXLINES", 8) == 0
5420 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5421 {
5422 if (key[4] == 'S')
5423 arg_end = key + 6;
5424 else if (key[0] == 'L')
5425 arg_end = key + 11;
5426 else
5427 arg_end = key + 9;
5428 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5429 {
5430 illegal = TRUE;
5431 break;
5432 }
5433 n = getdigits(&arg_end);
5434 if (!eap->skip)
5435 {
5436 if (key[4] == 'B')
5437 curbuf->b_syn_sync_linebreaks = n;
5438 else if (key[1] == 'A')
5439 curbuf->b_syn_sync_maxlines = n;
5440 else
5441 curbuf->b_syn_sync_minlines = n;
5442 }
5443 }
5444 else if (STRCMP(key, "FROMSTART") == 0)
5445 {
5446 if (!eap->skip)
5447 {
5448 curbuf->b_syn_sync_minlines = MAXLNUM;
5449 curbuf->b_syn_sync_maxlines = 0;
5450 }
5451 }
5452 else if (STRCMP(key, "LINECONT") == 0)
5453 {
5454 if (curbuf->b_syn_linecont_pat != NULL)
5455 {
5456 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5457 finished = TRUE;
5458 break;
5459 }
5460 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5461 if (*arg_end != *next_arg) /* end delimiter not found */
5462 {
5463 illegal = TRUE;
5464 break;
5465 }
5466
5467 if (!eap->skip)
5468 {
5469 /* store the pattern and compiled regexp program */
5470 if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5471 (int)(arg_end - next_arg - 1))) == NULL)
5472 {
5473 finished = TRUE;
5474 break;
5475 }
5476 curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5477
5478 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5479 cpo_save = p_cpo;
5480 p_cpo = (char_u *)"";
5481 curbuf->b_syn_linecont_prog =
5482 vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5483 p_cpo = cpo_save;
5484
5485 if (curbuf->b_syn_linecont_prog == NULL)
5486 {
5487 vim_free(curbuf->b_syn_linecont_pat);
5488 curbuf->b_syn_linecont_pat = NULL;
5489 finished = TRUE;
5490 break;
5491 }
5492 }
5493 next_arg = skipwhite(arg_end + 1);
5494 }
5495 else
5496 {
5497 eap->arg = next_arg;
5498 if (STRCMP(key, "MATCH") == 0)
5499 syn_cmd_match(eap, TRUE);
5500 else if (STRCMP(key, "REGION") == 0)
5501 syn_cmd_region(eap, TRUE);
5502 else if (STRCMP(key, "CLEAR") == 0)
5503 syn_cmd_clear(eap, TRUE);
5504 else
5505 illegal = TRUE;
5506 finished = TRUE;
5507 break;
5508 }
5509 arg_start = next_arg;
5510 }
5511 vim_free(key);
5512 if (illegal)
5513 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5514 else if (!finished)
5515 {
5516 eap->nextcmd = check_nextcmd(arg_start);
5517 redraw_curbuf_later(NOT_VALID);
5518 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5519 }
5520}
5521
5522/*
5523 * Convert a line of highlight group names into a list of group ID numbers.
5524 * "arg" should point to the "contains" or "nextgroup" keyword.
5525 * "arg" is advanced to after the last group name.
5526 * Careful: the argument is modified (NULs added).
5527 * returns FAIL for some error, OK for success.
5528 */
5529 static int
5530get_id_list(arg, keylen, list)
5531 char_u **arg;
5532 int keylen; /* length of keyword */
5533 short **list; /* where to store the resulting list, if not
5534 NULL, the list is silently skipped! */
5535{
5536 char_u *p = NULL;
5537 char_u *end;
5538 int round;
5539 int count;
5540 int total_count = 0;
5541 short *retval = NULL;
5542 char_u *name;
5543 regmatch_T regmatch;
5544 int id;
5545 int i;
5546 int failed = FALSE;
5547
5548 /*
5549 * We parse the list twice:
5550 * round == 1: count the number of items, allocate the array.
5551 * round == 2: fill the array with the items.
5552 * In round 1 new groups may be added, causing the number of items to
5553 * grow when a regexp is used. In that case round 1 is done once again.
5554 */
5555 for (round = 1; round <= 2; ++round)
5556 {
5557 /*
5558 * skip "contains"
5559 */
5560 p = skipwhite(*arg + keylen);
5561 if (*p != '=')
5562 {
5563 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5564 break;
5565 }
5566 p = skipwhite(p + 1);
5567 if (ends_excmd(*p))
5568 {
5569 EMSG2(_("E406: Empty argument: %s"), *arg);
5570 break;
5571 }
5572
5573 /*
5574 * parse the arguments after "contains"
5575 */
5576 count = 0;
5577 while (!ends_excmd(*p))
5578 {
5579 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5580 ;
5581 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5582 if (name == NULL)
5583 {
5584 failed = TRUE;
5585 break;
5586 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005587 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005588 if ( STRCMP(name + 1, "ALLBUT") == 0
5589 || STRCMP(name + 1, "ALL") == 0
5590 || STRCMP(name + 1, "TOP") == 0
5591 || STRCMP(name + 1, "CONTAINED") == 0)
5592 {
5593 if (TOUPPER_ASC(**arg) != 'C')
5594 {
5595 EMSG2(_("E407: %s not allowed here"), name + 1);
5596 failed = TRUE;
5597 vim_free(name);
5598 break;
5599 }
5600 if (count != 0)
5601 {
5602 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5603 failed = TRUE;
5604 vim_free(name);
5605 break;
5606 }
5607 if (name[1] == 'A')
5608 id = SYNID_ALLBUT;
5609 else if (name[1] == 'T')
5610 id = SYNID_TOP;
5611 else
5612 id = SYNID_CONTAINED;
5613 id += current_syn_inc_tag;
5614 }
5615 else if (name[1] == '@')
5616 {
5617 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5618 }
5619 else
5620 {
5621 /*
5622 * Handle full group name.
5623 */
5624 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5625 id = syn_check_group(name + 1, (int)(end - p));
5626 else
5627 {
5628 /*
5629 * Handle match of regexp with group names.
5630 */
5631 *name = '^';
5632 STRCAT(name, "$");
5633 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5634 if (regmatch.regprog == NULL)
5635 {
5636 failed = TRUE;
5637 vim_free(name);
5638 break;
5639 }
5640
5641 regmatch.rm_ic = TRUE;
5642 id = 0;
5643 for (i = highlight_ga.ga_len; --i >= 0; )
5644 {
5645 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5646 (colnr_T)0))
5647 {
5648 if (round == 2)
5649 {
5650 /* Got more items than expected; can happen
5651 * when adding items that match:
5652 * "contains=a.*b,axb".
5653 * Go back to first round */
5654 if (count >= total_count)
5655 {
5656 vim_free(retval);
5657 round = 1;
5658 }
5659 else
5660 retval[count] = i + 1;
5661 }
5662 ++count;
5663 id = -1; /* remember that we found one */
5664 }
5665 }
5666 vim_free(regmatch.regprog);
5667 }
5668 }
5669 vim_free(name);
5670 if (id == 0)
5671 {
5672 EMSG2(_("E409: Unknown group name: %s"), p);
5673 failed = TRUE;
5674 break;
5675 }
5676 if (id > 0)
5677 {
5678 if (round == 2)
5679 {
5680 /* Got more items than expected, go back to first round */
5681 if (count >= total_count)
5682 {
5683 vim_free(retval);
5684 round = 1;
5685 }
5686 else
5687 retval[count] = id;
5688 }
5689 ++count;
5690 }
5691 p = skipwhite(end);
5692 if (*p != ',')
5693 break;
5694 p = skipwhite(p + 1); /* skip comma in between arguments */
5695 }
5696 if (failed)
5697 break;
5698 if (round == 1)
5699 {
5700 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5701 if (retval == NULL)
5702 break;
5703 retval[count] = 0; /* zero means end of the list */
5704 total_count = count;
5705 }
5706 }
5707
5708 *arg = p;
5709 if (failed || retval == NULL)
5710 {
5711 vim_free(retval);
5712 return FAIL;
5713 }
5714
5715 if (*list == NULL)
5716 *list = retval;
5717 else
5718 vim_free(retval); /* list already found, don't overwrite it */
5719
5720 return OK;
5721}
5722
5723/*
5724 * Make a copy of an ID list.
5725 */
5726 static short *
5727copy_id_list(list)
5728 short *list;
5729{
5730 int len;
5731 int count;
5732 short *retval;
5733
5734 if (list == NULL)
5735 return NULL;
5736
5737 for (count = 0; list[count]; ++count)
5738 ;
5739 len = (count + 1) * sizeof(short);
5740 retval = (short *)alloc((unsigned)len);
5741 if (retval != NULL)
5742 mch_memmove(retval, list, (size_t)len);
5743
5744 return retval;
5745}
5746
5747/*
5748 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5749 * "cur_si" can be NULL if not checking the "containedin" list.
5750 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5751 * the current item.
5752 * This function is called very often, keep it fast!!
5753 */
5754 static int
5755in_id_list(cur_si, list, ssp, contained)
5756 stateitem_T *cur_si; /* current item or NULL */
5757 short *list; /* id list */
5758 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
5759 int contained; /* group id is contained */
5760{
5761 int retval;
5762 short *scl_list;
5763 short item;
5764 short id = ssp->id;
5765 static int depth = 0;
5766 int r;
5767
5768 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00005769 if (cur_si != NULL && ssp->cont_in_list != NULL
5770 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005771 {
5772 /* Ignore transparent items without a contains argument. Double check
5773 * that we don't go back past the first one. */
5774 while ((cur_si->si_flags & HL_TRANS_CONT)
5775 && cur_si > (stateitem_T *)(current_state.ga_data))
5776 --cur_si;
5777 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
5778 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5779 &(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5780 SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5781 return TRUE;
5782 }
5783
5784 if (list == NULL)
5785 return FALSE;
5786
5787 /*
5788 * If list is ID_LIST_ALL, we are in a transparent item that isn't
5789 * inside anything. Only allow not-contained groups.
5790 */
5791 if (list == ID_LIST_ALL)
5792 return !contained;
5793
5794 /*
5795 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5796 * contains list. We also require that "id" is at the same ":syn include"
5797 * level as the list.
5798 */
5799 item = *list;
5800 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5801 {
5802 if (item < SYNID_TOP)
5803 {
5804 /* ALL or ALLBUT: accept all groups in the same file */
5805 if (item - SYNID_ALLBUT != ssp->inc_tag)
5806 return FALSE;
5807 }
5808 else if (item < SYNID_CONTAINED)
5809 {
5810 /* TOP: accept all not-contained groups in the same file */
5811 if (item - SYNID_TOP != ssp->inc_tag || contained)
5812 return FALSE;
5813 }
5814 else
5815 {
5816 /* CONTAINED: accept all contained groups in the same file */
5817 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5818 return FALSE;
5819 }
5820 item = *++list;
5821 retval = FALSE;
5822 }
5823 else
5824 retval = TRUE;
5825
5826 /*
5827 * Return "retval" if id is in the contains list.
5828 */
5829 while (item != 0)
5830 {
5831 if (item == id)
5832 return retval;
5833 if (item >= SYNID_CLUSTER)
5834 {
5835 scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5836 /* restrict recursiveness to 30 to avoid an endless loop for a
5837 * cluster that includes itself (indirectly) */
5838 if (scl_list != NULL && depth < 30)
5839 {
5840 ++depth;
5841 r = in_id_list(NULL, scl_list, ssp, contained);
5842 --depth;
5843 if (r)
5844 return retval;
5845 }
5846 }
5847 item = *++list;
5848 }
5849 return !retval;
5850}
5851
5852struct subcommand
5853{
5854 char *name; /* subcommand name */
5855 void (*func)__ARGS((exarg_T *, int)); /* function to call */
5856};
5857
5858static struct subcommand subcommands[] =
5859{
5860 {"case", syn_cmd_case},
5861 {"clear", syn_cmd_clear},
5862 {"cluster", syn_cmd_cluster},
5863 {"enable", syn_cmd_enable},
5864 {"include", syn_cmd_include},
5865 {"keyword", syn_cmd_keyword},
5866 {"list", syn_cmd_list},
5867 {"manual", syn_cmd_manual},
5868 {"match", syn_cmd_match},
5869 {"on", syn_cmd_on},
5870 {"off", syn_cmd_off},
5871 {"region", syn_cmd_region},
5872 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005873 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00005874 {"sync", syn_cmd_sync},
5875 {"", syn_cmd_list},
5876 {NULL, NULL}
5877};
5878
5879/*
5880 * ":syntax".
5881 * This searches the subcommands[] table for the subcommand name, and calls a
5882 * syntax_subcommand() function to do the rest.
5883 */
5884 void
5885ex_syntax(eap)
5886 exarg_T *eap;
5887{
5888 char_u *arg = eap->arg;
5889 char_u *subcmd_end;
5890 char_u *subcmd_name;
5891 int i;
5892
5893 syn_cmdlinep = eap->cmdlinep;
5894
5895 /* isolate subcommand name */
5896 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5897 ;
5898 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5899 if (subcmd_name != NULL)
5900 {
5901 if (eap->skip) /* skip error messages for all subcommands */
5902 ++emsg_skip;
5903 for (i = 0; ; ++i)
5904 {
5905 if (subcommands[i].name == NULL)
5906 {
5907 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5908 break;
5909 }
5910 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5911 {
5912 eap->arg = skipwhite(subcmd_end);
5913 (subcommands[i].func)(eap, FALSE);
5914 break;
5915 }
5916 }
5917 vim_free(subcmd_name);
5918 if (eap->skip)
5919 --emsg_skip;
5920 }
5921}
5922
5923 int
5924syntax_present(buf)
5925 buf_T *buf;
5926{
5927 return (buf->b_syn_patterns.ga_len != 0
5928 || buf->b_syn_clusters.ga_len != 0
Bram Moolenaardad6b692005-01-25 22:14:34 +00005929 || curbuf->b_keywtab.ht_used > 0
5930 || curbuf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005931}
5932
5933#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5934
5935static enum
5936{
5937 EXP_SUBCMD, /* expand ":syn" sub-commands */
5938 EXP_CASE /* expand ":syn case" arguments */
5939} expand_what;
5940
5941
5942/*
5943 * Handle command line completion for :syntax command.
5944 */
5945 void
5946set_context_in_syntax_cmd(xp, arg)
5947 expand_T *xp;
5948 char_u *arg;
5949{
5950 char_u *p;
5951
5952 /* Default: expand subcommands */
5953 xp->xp_context = EXPAND_SYNTAX;
5954 expand_what = EXP_SUBCMD;
5955 xp->xp_pattern = arg;
5956 include_link = FALSE;
5957 include_default = FALSE;
5958
5959 /* (part of) subcommand already typed */
5960 if (*arg != NUL)
5961 {
5962 p = skiptowhite(arg);
5963 if (*p != NUL) /* past first word */
5964 {
5965 xp->xp_pattern = skipwhite(p);
5966 if (*skiptowhite(xp->xp_pattern) != NUL)
5967 xp->xp_context = EXPAND_NOTHING;
5968 else if (STRNICMP(arg, "case", p - arg) == 0)
5969 expand_what = EXP_CASE;
5970 else if ( STRNICMP(arg, "keyword", p - arg) == 0
5971 || STRNICMP(arg, "region", p - arg) == 0
5972 || STRNICMP(arg, "match", p - arg) == 0
5973 || STRNICMP(arg, "list", p - arg) == 0)
5974 xp->xp_context = EXPAND_HIGHLIGHT;
5975 else
5976 xp->xp_context = EXPAND_NOTHING;
5977 }
5978 }
5979}
5980
5981static char *(case_args[]) = {"match", "ignore", NULL};
5982
5983/*
5984 * Function given to ExpandGeneric() to obtain the list syntax names for
5985 * expansion.
5986 */
5987/*ARGSUSED*/
5988 char_u *
5989get_syntax_name(xp, idx)
5990 expand_T *xp;
5991 int idx;
5992{
5993 if (expand_what == EXP_SUBCMD)
5994 return (char_u *)subcommands[idx].name;
5995 return (char_u *)case_args[idx];
5996}
5997
5998#endif /* FEAT_CMDL_COMPL */
5999
Bram Moolenaar071d4272004-06-13 20:20:40 +00006000/*
6001 * Function called for expression evaluation: get syntax ID at file position.
6002 */
6003 int
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006004syn_get_id(wp, lnum, col, trans, spellp)
6005 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006006 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006007 colnr_T col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006008 int trans; /* remove transparancy */
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006009 int *spellp; /* return: can do spell checking */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006010{
6011 /* When the position is not after the current position and in the same
6012 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006013 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006014 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006015 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006016 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006017
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006018 (void)get_syntax_attr(col, spellp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006019
6020 return (trans ? current_trans_id : current_id);
6021}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006022
6023#if defined(FEAT_FOLDING) || defined(PROTO)
6024/*
6025 * Function called to get folding level for line "lnum" in window "wp".
6026 */
6027 int
6028syn_get_foldlevel(wp, lnum)
6029 win_T *wp;
6030 long lnum;
6031{
6032 int level = 0;
6033 int i;
6034
6035 /* Return quickly when there are no fold items at all. */
6036 if (wp->w_buffer->b_syn_folditems != 0)
6037 {
6038 syntax_start(wp, lnum);
6039
6040 for (i = 0; i < current_state.ga_len; ++i)
6041 if (CUR_STATE(i).si_flags & HL_FOLD)
6042 ++level;
6043 }
6044 if (level > wp->w_p_fdn)
6045 level = wp->w_p_fdn;
6046 return level;
6047}
6048#endif
6049
6050#endif /* FEAT_SYN_HL */
6051
6052
6053/**************************************
6054 * Highlighting stuff *
6055 **************************************/
6056
6057/*
6058 * The default highlight groups. These are compiled-in for fast startup and
6059 * they still work when the runtime files can't be found.
6060 * When making changes here, also change runtime/colors/default.vim!
6061 */
6062static char *(highlight_init_both[]) =
6063 {
6064#ifdef FEAT_GUI
6065 "Cursor guibg=fg guifg=bg",
6066 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
6067#endif
6068 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White",
6069 "IncSearch term=reverse cterm=reverse gui=reverse",
6070 "ModeMsg term=bold cterm=bold gui=bold",
6071 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue",
6072 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold",
6073 "StatusLineNC term=reverse cterm=reverse gui=reverse",
6074 "VertSplit term=reverse cterm=reverse gui=reverse",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006075 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold",
6076 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red",
Bram Moolenaar1c7715d2005-10-03 22:02:18 +00006077 "PmenuThumb cterm=reverse gui=reverse",
6078 "PmenuSbar ctermbg=Grey guibg=Grey",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006079 NULL
6080 };
6081
6082static char *(highlight_init_light[]) =
6083 {
6084 "Directory term=bold ctermfg=DarkBlue guifg=Blue",
6085 "LineNr term=underline ctermfg=Brown guifg=Brown",
6086 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6087 "Normal gui=NONE",
6088 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen",
6089 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE",
Bram Moolenaar217ad922005-03-20 22:37:15 +00006090 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl",
Bram Moolenaar0d9c26d2005-07-02 23:19:16 +00006091 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl",
Bram Moolenaar217ad922005-03-20 22:37:15 +00006092 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl",
6093 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl",
Bram Moolenaar1c7715d2005-10-03 22:02:18 +00006094 "Pmenu ctermbg=LightMagenta guibg=LightMagenta",
6095 "PmenuSel ctermbg=LightGrey guibg=Grey",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006096 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue",
6097 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta",
6098 "WarningMsg term=standout ctermfg=DarkRed guifg=Red",
6099 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6100 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue",
6101 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
6102 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue",
Bram Moolenaarf75a9632005-09-13 21:20:47 +00006103 "Visual term=reverse ctermbg=Magenta guibg=LightGrey",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006104 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue",
6105 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta",
6106 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan",
6107 NULL
6108 };
6109
6110static char *(highlight_init_dark[]) =
6111 {
6112 "Directory term=bold ctermfg=LightCyan guifg=Cyan",
6113 "LineNr term=underline ctermfg=Yellow guifg=Yellow",
6114 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen",
6115 "Normal gui=NONE",
6116 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green",
6117 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6118 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan",
Bram Moolenaar217ad922005-03-20 22:37:15 +00006119 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl",
Bram Moolenaar0d9c26d2005-07-02 23:19:16 +00006120 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl",
Bram Moolenaar217ad922005-03-20 22:37:15 +00006121 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl",
6122 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl",
Bram Moolenaar1c7715d2005-10-03 22:02:18 +00006123 "Pmenu ctermbg=Magenta guibg=Magenta",
6124 "PmenuSel ctermbg=Grey guibg=Grey",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006125 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta",
6126 "WarningMsg term=standout ctermfg=LightRed guifg=Red",
6127 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black",
6128 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan",
6129 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
6130 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan",
Bram Moolenaarf75a9632005-09-13 21:20:47 +00006131 "Visual term=reverse ctermbg=Magenta guibg=DarkGrey",
Bram Moolenaar071d4272004-06-13 20:20:40 +00006132 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue",
6133 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta",
6134 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan",
6135 NULL
6136 };
6137
6138 void
6139init_highlight(both, reset)
6140 int both; /* include groups where 'bg' doesn't matter */
6141 int reset; /* clear group first */
6142{
6143 int i;
6144 char **pp;
6145 static int had_both = FALSE;
6146#ifdef FEAT_EVAL
6147 char_u *p;
6148
6149 /*
6150 * Try finding the color scheme file. Used when a color file was loaded
6151 * and 'background' or 't_Co' is changed.
6152 */
6153 p = get_var_value((char_u *)"g:colors_name");
6154 if (p != NULL && load_colors(p) == OK)
6155 return;
6156#endif
6157
6158 /*
6159 * Didn't use a color file, use the compiled-in colors.
6160 */
6161 if (both)
6162 {
6163 had_both = TRUE;
6164 pp = highlight_init_both;
6165 for (i = 0; pp[i] != NULL; ++i)
6166 do_highlight((char_u *)pp[i], reset, TRUE);
6167 }
6168 else if (!had_both)
6169 /* Don't do anything before the call with both == TRUE from main().
6170 * Not everything has been setup then, and that call will overrule
6171 * everything anyway. */
6172 return;
6173
6174 if (*p_bg == 'l')
6175 pp = highlight_init_light;
6176 else
6177 pp = highlight_init_dark;
6178 for (i = 0; pp[i] != NULL; ++i)
6179 do_highlight((char_u *)pp[i], reset, TRUE);
6180
Bram Moolenaarab194812005-09-14 21:40:12 +00006181 /* Magenta background looks ugly, but grey may not work for 8 colors.
6182 * Thus let it depend on the number of colors available. */
6183 if (t_colors > 8)
6184 do_highlight((char_u *)(*p_bg == 'l' ? "Visual ctermbg=LightGrey"
6185 : "Visual ctermbg=DarkGrey"), reset, TRUE);
6186
Bram Moolenaar071d4272004-06-13 20:20:40 +00006187#ifdef FEAT_SYN_HL
6188 /*
6189 * If syntax highlighting is enabled load the highlighting for it.
6190 */
6191 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006192 {
6193 static int recursive = 0;
6194
6195 if (recursive >= 5)
6196 EMSG(_("E679: recursive loop loading syncolor.vim"));
6197 else
6198 {
6199 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006200 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006201 --recursive;
6202 }
6203 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006204#endif
6205}
6206
6207/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006208 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006209 * Return OK for success, FAIL for failure.
6210 */
6211 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006212load_colors(name)
6213 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006214{
6215 char_u *buf;
6216 int retval = FAIL;
6217 static int recursive = FALSE;
6218
6219 /* When being called recursively, this is probably because setting
6220 * 'background' caused the highlighting to be reloaded. This means it is
6221 * working, thus we should return OK. */
6222 if (recursive)
6223 return OK;
6224
6225 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006226 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006227 if (buf != NULL)
6228 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006229 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006230 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006231 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006232#ifdef FEAT_AUTOCMD
6233 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6234#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006235 }
6236 recursive = FALSE;
6237
6238 return retval;
6239}
6240
6241/*
6242 * Handle the ":highlight .." command.
6243 * When using ":hi clear" this is called recursively for each group with
6244 * "forceit" and "init" both TRUE.
6245 */
6246 void
6247do_highlight(line, forceit, init)
6248 char_u *line;
6249 int forceit;
6250 int init; /* TRUE when called for initializing */
6251{
6252 char_u *name_end;
6253 char_u *p;
6254 char_u *linep;
6255 char_u *key_start;
6256 char_u *arg_start;
6257 char_u *key = NULL, *arg = NULL;
6258 long i;
6259 int off;
6260 int len;
6261 int attr;
6262 int id;
6263 int idx;
6264 int dodefault = FALSE;
6265 int doclear = FALSE;
6266 int dolink = FALSE;
6267 int error = FALSE;
6268 int color;
6269 int is_normal_group = FALSE; /* "Normal" group */
6270#ifdef FEAT_GUI_X11
6271 int is_menu_group = FALSE; /* "Menu" group */
6272 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6273 int is_tooltip_group = FALSE; /* "Tooltip" group */
6274 int do_colors = FALSE; /* need to update colors? */
6275#else
6276# define is_menu_group 0
6277# define is_tooltip_group 0
6278#endif
6279
6280 /*
6281 * If no argument, list current highlighting.
6282 */
6283 if (ends_excmd(*line))
6284 {
6285 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6286 /* TODO: only call when the group has attributes set */
6287 highlight_list_one((int)i);
6288 return;
6289 }
6290
6291 /*
6292 * Isolate the name.
6293 */
6294 name_end = skiptowhite(line);
6295 linep = skipwhite(name_end);
6296
6297 /*
6298 * Check for "default" argument.
6299 */
6300 if (STRNCMP(line, "default", name_end - line) == 0)
6301 {
6302 dodefault = TRUE;
6303 line = linep;
6304 name_end = skiptowhite(line);
6305 linep = skipwhite(name_end);
6306 }
6307
6308 /*
6309 * Check for "clear" or "link" argument.
6310 */
6311 if (STRNCMP(line, "clear", name_end - line) == 0)
6312 doclear = TRUE;
6313 if (STRNCMP(line, "link", name_end - line) == 0)
6314 dolink = TRUE;
6315
6316 /*
6317 * ":highlight {group-name}": list highlighting for one group.
6318 */
6319 if (!doclear && !dolink && ends_excmd(*linep))
6320 {
6321 id = syn_namen2id(line, (int)(name_end - line));
6322 if (id == 0)
6323 EMSG2(_("E411: highlight group not found: %s"), line);
6324 else
6325 highlight_list_one(id);
6326 return;
6327 }
6328
6329 /*
6330 * Handle ":highlight link {from} {to}" command.
6331 */
6332 if (dolink)
6333 {
6334 char_u *from_start = linep;
6335 char_u *from_end;
6336 char_u *to_start;
6337 char_u *to_end;
6338 int from_id;
6339 int to_id;
6340
6341 from_end = skiptowhite(from_start);
6342 to_start = skipwhite(from_end);
6343 to_end = skiptowhite(to_start);
6344
6345 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6346 {
6347 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6348 from_start);
6349 return;
6350 }
6351
6352 if (!ends_excmd(*skipwhite(to_end)))
6353 {
6354 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6355 return;
6356 }
6357
6358 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6359 if (STRNCMP(to_start, "NONE", 4) == 0)
6360 to_id = 0;
6361 else
6362 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6363
6364 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6365 {
6366 /*
6367 * Don't allow a link when there already is some highlighting
6368 * for the group, unless '!' is used
6369 */
6370 if (to_id > 0 && !forceit && !init
6371 && hl_has_settings(from_id - 1, dodefault))
6372 {
6373 if (sourcing_name == NULL && !dodefault)
6374 EMSG(_("E414: group has settings, highlight link ignored"));
6375 }
6376 else
6377 {
6378 if (!init)
6379 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6380 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006381#ifdef FEAT_EVAL
6382 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6383#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006384 redraw_all_later(NOT_VALID);
6385 }
6386 }
6387
6388 /* Only call highlight_changed() once, after sourcing a syntax file */
6389 need_highlight_changed = TRUE;
6390
6391 return;
6392 }
6393
6394 if (doclear)
6395 {
6396 /*
6397 * ":highlight clear [group]" command.
6398 */
6399 line = linep;
6400 if (ends_excmd(*line))
6401 {
6402#ifdef FEAT_GUI
6403 /* First, we do not destroy the old values, but allocate the new
6404 * ones and update the display. THEN we destroy the old values.
6405 * If we destroy the old values first, then the old values
6406 * (such as GuiFont's or GuiFontset's) will still be displayed but
6407 * invalid because they were free'd.
6408 */
6409 if (gui.in_use)
6410 {
6411# ifdef FEAT_BEVAL_TIP
6412 gui_init_tooltip_font();
6413# endif
6414# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6415 gui_init_menu_font();
6416# endif
6417 }
6418# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6419 gui_mch_def_colors();
6420# endif
6421# ifdef FEAT_GUI_X11
6422# ifdef FEAT_MENU
6423
6424 /* This only needs to be done when there is no Menu highlight
6425 * group defined by default, which IS currently the case.
6426 */
6427 gui_mch_new_menu_colors();
6428# endif
6429 if (gui.in_use)
6430 {
6431 gui_new_scrollbar_colors();
6432# ifdef FEAT_BEVAL
6433 gui_mch_new_tooltip_colors();
6434# endif
6435# ifdef FEAT_MENU
6436 gui_mch_new_menu_font();
6437# endif
6438 }
6439# endif
6440
6441 /* Ok, we're done allocating the new default graphics items.
6442 * The screen should already be refreshed at this point.
6443 * It is now Ok to clear out the old data.
6444 */
6445#endif
6446#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006447 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006448#endif
6449 restore_cterm_colors();
6450
6451 /*
6452 * Clear all default highlight groups and load the defaults.
6453 */
6454 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6455 highlight_clear(idx);
6456 init_highlight(TRUE, TRUE);
6457#ifdef FEAT_GUI
6458 if (gui.in_use)
6459 highlight_gui_started();
6460#endif
6461 highlight_changed();
6462 redraw_later_clear();
6463 return;
6464 }
6465 name_end = skiptowhite(line);
6466 linep = skipwhite(name_end);
6467 }
6468
6469 /*
6470 * Find the group name in the table. If it does not exist yet, add it.
6471 */
6472 id = syn_check_group(line, (int)(name_end - line));
6473 if (id == 0) /* failed (out of memory) */
6474 return;
6475 idx = id - 1; /* index is ID minus one */
6476
6477 /* Return if "default" was used and the group already has settings. */
6478 if (dodefault && hl_has_settings(idx, TRUE))
6479 return;
6480
6481 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6482 is_normal_group = TRUE;
6483#ifdef FEAT_GUI_X11
6484 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6485 is_menu_group = TRUE;
6486 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6487 is_scrollbar_group = TRUE;
6488 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6489 is_tooltip_group = TRUE;
6490#endif
6491
6492 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6493 if (doclear || (forceit && init))
6494 {
6495 highlight_clear(idx);
6496 if (!doclear)
6497 HL_TABLE()[idx].sg_set = 0;
6498 }
6499
6500 if (!doclear)
6501 while (!ends_excmd(*linep))
6502 {
6503 key_start = linep;
6504 if (*linep == '=')
6505 {
6506 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6507 error = TRUE;
6508 break;
6509 }
6510
6511 /*
6512 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6513 * "guibg").
6514 */
6515 while (*linep && !vim_iswhite(*linep) && *linep != '=')
6516 ++linep;
6517 vim_free(key);
6518 key = vim_strnsave_up(key_start, (int)(linep - key_start));
6519 if (key == NULL)
6520 {
6521 error = TRUE;
6522 break;
6523 }
6524 linep = skipwhite(linep);
6525
6526 if (STRCMP(key, "NONE") == 0)
6527 {
6528 if (!init || HL_TABLE()[idx].sg_set == 0)
6529 {
6530 if (!init)
6531 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6532 highlight_clear(idx);
6533 }
6534 continue;
6535 }
6536
6537 /*
6538 * Check for the equal sign.
6539 */
6540 if (*linep != '=')
6541 {
6542 EMSG2(_("E416: missing equal sign: %s"), key_start);
6543 error = TRUE;
6544 break;
6545 }
6546 ++linep;
6547
6548 /*
6549 * Isolate the argument.
6550 */
6551 linep = skipwhite(linep);
6552 if (*linep == '\'') /* guifg='color name' */
6553 {
6554 arg_start = ++linep;
6555 linep = vim_strchr(linep, '\'');
6556 if (linep == NULL)
6557 {
6558 EMSG2(_(e_invarg2), key_start);
6559 error = TRUE;
6560 break;
6561 }
6562 }
6563 else
6564 {
6565 arg_start = linep;
6566 linep = skiptowhite(linep);
6567 }
6568 if (linep == arg_start)
6569 {
6570 EMSG2(_("E417: missing argument: %s"), key_start);
6571 error = TRUE;
6572 break;
6573 }
6574 vim_free(arg);
6575 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6576 if (arg == NULL)
6577 {
6578 error = TRUE;
6579 break;
6580 }
6581 if (*linep == '\'')
6582 ++linep;
6583
6584 /*
6585 * Store the argument.
6586 */
6587 if ( STRCMP(key, "TERM") == 0
6588 || STRCMP(key, "CTERM") == 0
6589 || STRCMP(key, "GUI") == 0)
6590 {
6591 attr = 0;
6592 off = 0;
6593 while (arg[off] != NUL)
6594 {
6595 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6596 {
6597 len = (int)STRLEN(hl_name_table[i]);
6598 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6599 {
6600 attr |= hl_attr_table[i];
6601 off += len;
6602 break;
6603 }
6604 }
6605 if (i < 0)
6606 {
6607 EMSG2(_("E418: Illegal value: %s"), arg);
6608 error = TRUE;
6609 break;
6610 }
6611 if (arg[off] == ',') /* another one follows */
6612 ++off;
6613 }
6614 if (error)
6615 break;
6616 if (*key == 'T')
6617 {
6618 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6619 {
6620 if (!init)
6621 HL_TABLE()[idx].sg_set |= SG_TERM;
6622 HL_TABLE()[idx].sg_term = attr;
6623 }
6624 }
6625 else if (*key == 'C')
6626 {
6627 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6628 {
6629 if (!init)
6630 HL_TABLE()[idx].sg_set |= SG_CTERM;
6631 HL_TABLE()[idx].sg_cterm = attr;
6632 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6633 }
6634 }
6635#ifdef FEAT_GUI
6636 else
6637 {
6638 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6639 {
6640 if (!init)
6641 HL_TABLE()[idx].sg_set |= SG_GUI;
6642 HL_TABLE()[idx].sg_gui = attr;
6643 }
6644 }
6645#endif
6646 }
6647 else if (STRCMP(key, "FONT") == 0)
6648 {
6649 /* in non-GUI fonts are simply ignored */
6650#ifdef FEAT_GUI
6651 if (!gui.shell_created)
6652 {
6653 /* GUI not started yet, always accept the name. */
6654 vim_free(HL_TABLE()[idx].sg_font_name);
6655 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6656 }
6657 else
6658 {
6659 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6660# ifdef FEAT_XFONTSET
6661 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6662# endif
6663 /* First, save the current font/fontset.
6664 * Then try to allocate the font/fontset.
6665 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6666 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6667 */
6668
6669 HL_TABLE()[idx].sg_font = NOFONT;
6670# ifdef FEAT_XFONTSET
6671 HL_TABLE()[idx].sg_fontset = NOFONTSET;
6672# endif
6673 hl_do_font(idx, arg, is_normal_group, is_menu_group,
6674 is_tooltip_group);
6675
6676# ifdef FEAT_XFONTSET
6677 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6678 {
6679 /* New fontset was accepted. Free the old one, if there was
6680 * one.
6681 */
6682 gui_mch_free_fontset(temp_sg_fontset);
6683 vim_free(HL_TABLE()[idx].sg_font_name);
6684 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6685 }
6686 else
6687 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6688# endif
6689 if (HL_TABLE()[idx].sg_font != NOFONT)
6690 {
6691 /* New font was accepted. Free the old one, if there was
6692 * one.
6693 */
6694 gui_mch_free_font(temp_sg_font);
6695 vim_free(HL_TABLE()[idx].sg_font_name);
6696 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6697 }
6698 else
6699 HL_TABLE()[idx].sg_font = temp_sg_font;
6700 }
6701#endif
6702 }
6703 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6704 {
6705 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6706 {
6707 if (!init)
6708 HL_TABLE()[idx].sg_set |= SG_CTERM;
6709
6710 /* When setting the foreground color, and previously the "bold"
6711 * flag was set for a light color, reset it now */
6712 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6713 {
6714 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6715 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6716 }
6717
6718 if (VIM_ISDIGIT(*arg))
6719 color = atoi((char *)arg);
6720 else if (STRICMP(arg, "fg") == 0)
6721 {
6722 if (cterm_normal_fg_color)
6723 color = cterm_normal_fg_color - 1;
6724 else
6725 {
6726 EMSG(_("E419: FG color unknown"));
6727 error = TRUE;
6728 break;
6729 }
6730 }
6731 else if (STRICMP(arg, "bg") == 0)
6732 {
6733 if (cterm_normal_bg_color > 0)
6734 color = cterm_normal_bg_color - 1;
6735 else
6736 {
6737 EMSG(_("E420: BG color unknown"));
6738 error = TRUE;
6739 break;
6740 }
6741 }
6742 else
6743 {
6744 static char *(color_names[28]) = {
6745 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6746 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6747 "Gray", "Grey",
6748 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6749 "Blue", "LightBlue", "Green", "LightGreen",
6750 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6751 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6752 static int color_numbers_16[28] = {0, 1, 2, 3,
6753 4, 5, 6, 6,
6754 7, 7,
6755 7, 7, 8, 8,
6756 9, 9, 10, 10,
6757 11, 11, 12, 12, 13,
6758 13, 14, 14, 15, -1};
6759 /* for xterm with 88 colors... */
6760 static int color_numbers_88[28] = {0, 4, 2, 6,
6761 1, 5, 32, 72,
6762 84, 84,
6763 7, 7, 82, 82,
6764 12, 43, 10, 61,
6765 14, 63, 9, 74, 13,
6766 75, 11, 78, 15, -1};
6767 /* for xterm with 256 colors... */
6768 static int color_numbers_256[28] = {0, 4, 2, 6,
6769 1, 5, 130, 130,
6770 248, 248,
6771 7, 7, 242, 242,
6772 12, 81, 10, 121,
6773 14, 159, 9, 224, 13,
6774 225, 11, 229, 15, -1};
6775 /* for terminals with less than 16 colors... */
6776 static int color_numbers_8[28] = {0, 4, 2, 6,
6777 1, 5, 3, 3,
6778 7, 7,
6779 7, 7, 0+8, 0+8,
6780 4+8, 4+8, 2+8, 2+8,
6781 6+8, 6+8, 1+8, 1+8, 5+8,
6782 5+8, 3+8, 3+8, 7+8, -1};
6783#if defined(__QNXNTO__)
6784 static int *color_numbers_8_qansi = color_numbers_8;
6785 /* On qnx, the 8 & 16 color arrays are the same */
6786 if (STRNCMP(T_NAME, "qansi", 5) == 0)
6787 color_numbers_8_qansi = color_numbers_16;
6788#endif
6789
6790 /* reduce calls to STRICMP a bit, it can be slow */
6791 off = TOUPPER_ASC(*arg);
6792 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6793 if (off == color_names[i][0]
6794 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6795 break;
6796 if (i < 0)
6797 {
6798 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6799 error = TRUE;
6800 break;
6801 }
6802
6803 /* Use the _16 table to check if its a valid color name. */
6804 color = color_numbers_16[i];
6805 if (color >= 0)
6806 {
6807 if (t_colors == 8)
6808 {
6809 /* t_Co is 8: use the 8 colors table */
6810#if defined(__QNXNTO__)
6811 color = color_numbers_8_qansi[i];
6812#else
6813 color = color_numbers_8[i];
6814#endif
6815 if (key[5] == 'F')
6816 {
6817 /* set/reset bold attribute to get light foreground
6818 * colors (on some terminals, e.g. "linux") */
6819 if (color & 8)
6820 {
6821 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
6822 HL_TABLE()[idx].sg_cterm_bold = TRUE;
6823 }
6824 else
6825 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6826 }
6827 color &= 7; /* truncate to 8 colors */
6828 }
6829 else if (t_colors == 16 || t_colors == 88
6830 || t_colors == 256)
6831 {
6832 /*
6833 * Guess: if the termcap entry ends in 'm', it is
6834 * probably an xterm-like terminal. Use the changed
6835 * order for colors.
6836 */
6837 if (*T_CAF != NUL)
6838 p = T_CAF;
6839 else
6840 p = T_CSF;
6841 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
6842 switch (t_colors)
6843 {
6844 case 16:
6845 color = color_numbers_8[i];
6846 break;
6847 case 88:
6848 color = color_numbers_88[i];
6849 break;
6850 case 256:
6851 color = color_numbers_256[i];
6852 break;
6853 }
6854 }
6855 }
6856 }
6857 /* Add one to the argument, to avoid zero */
6858 if (key[5] == 'F')
6859 {
6860 HL_TABLE()[idx].sg_cterm_fg = color + 1;
6861 if (is_normal_group)
6862 {
6863 cterm_normal_fg_color = color + 1;
6864 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
6865#ifdef FEAT_GUI
6866 /* Don't do this if the GUI is used. */
6867 if (!gui.in_use && !gui.starting)
6868#endif
6869 {
6870 must_redraw = CLEAR;
6871 if (termcap_active)
6872 term_fg_color(color);
6873 }
6874 }
6875 }
6876 else
6877 {
6878 HL_TABLE()[idx].sg_cterm_bg = color + 1;
6879 if (is_normal_group)
6880 {
6881 cterm_normal_bg_color = color + 1;
6882#ifdef FEAT_GUI
6883 /* Don't mess with 'background' if the GUI is used. */
6884 if (!gui.in_use && !gui.starting)
6885#endif
6886 {
6887 must_redraw = CLEAR;
6888 if (termcap_active)
6889 term_bg_color(color);
6890 if (t_colors < 16)
6891 i = (color == 0 || color == 4);
6892 else
6893 i = (color < 7 || color == 8);
6894 /* Set the 'background' option if the value is wrong. */
6895 if (i != (*p_bg == 'd'))
6896 set_option_value((char_u *)"bg", 0L,
6897 i ? (char_u *)"dark" : (char_u *)"light", 0);
6898 }
6899 }
6900 }
6901 }
6902 }
6903 else if (STRCMP(key, "GUIFG") == 0)
6904 {
6905#ifdef FEAT_GUI /* in non-GUI guifg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006906 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006907 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006908 if (!init)
6909 HL_TABLE()[idx].sg_set |= SG_GUI;
6910
6911 i = color_name2handle(arg);
6912 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6913 {
6914 HL_TABLE()[idx].sg_gui_fg = i;
6915 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
6916 if (STRCMP(arg, "NONE"))
6917 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
6918 else
6919 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006920# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006921 if (is_menu_group)
6922 gui.menu_fg_pixel = i;
6923 if (is_scrollbar_group)
6924 gui.scroll_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006925# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006926 if (is_tooltip_group)
6927 gui.tooltip_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006928# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006929 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006930# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006931 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006932 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006933#endif
6934 }
6935 else if (STRCMP(key, "GUIBG") == 0)
6936 {
6937#ifdef FEAT_GUI /* in non-GUI guibg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006938 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006939 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006940 if (!init)
6941 HL_TABLE()[idx].sg_set |= SG_GUI;
6942
6943 i = color_name2handle(arg);
6944 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6945 {
6946 HL_TABLE()[idx].sg_gui_bg = i;
6947 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
6948 if (STRCMP(arg, "NONE") != 0)
6949 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
6950 else
6951 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006952# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006953 if (is_menu_group)
6954 gui.menu_bg_pixel = i;
6955 if (is_scrollbar_group)
6956 gui.scroll_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006957# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006958 if (is_tooltip_group)
6959 gui.tooltip_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006960# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006961 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006962# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006963 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006964 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00006965#endif
6966 }
6967 else if (STRCMP(key, "GUISP") == 0)
6968 {
6969#ifdef FEAT_GUI /* in non-GUI guisp colors are simply ignored */
6970 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6971 {
6972 if (!init)
6973 HL_TABLE()[idx].sg_set |= SG_GUI;
6974
6975 i = color_name2handle(arg);
6976 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
6977 {
6978 HL_TABLE()[idx].sg_gui_sp = i;
6979 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
6980 if (STRCMP(arg, "NONE") != 0)
6981 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
6982 else
6983 HL_TABLE()[idx].sg_gui_sp_name = NULL;
6984 }
6985 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006986#endif
6987 }
6988 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
6989 {
6990 char_u buf[100];
6991 char_u *tname;
6992
6993 if (!init)
6994 HL_TABLE()[idx].sg_set |= SG_TERM;
6995
6996 /*
6997 * The "start" and "stop" arguments can be a literal escape
6998 * sequence, or a comma seperated list of terminal codes.
6999 */
7000 if (STRNCMP(arg, "t_", 2) == 0)
7001 {
7002 off = 0;
7003 buf[0] = 0;
7004 while (arg[off] != NUL)
7005 {
7006 /* Isolate one termcap name */
7007 for (len = 0; arg[off + len] &&
7008 arg[off + len] != ','; ++len)
7009 ;
7010 tname = vim_strnsave(arg + off, len);
7011 if (tname == NULL) /* out of memory */
7012 {
7013 error = TRUE;
7014 break;
7015 }
7016 /* lookup the escape sequence for the item */
7017 p = get_term_code(tname);
7018 vim_free(tname);
7019 if (p == NULL) /* ignore non-existing things */
7020 p = (char_u *)"";
7021
7022 /* Append it to the already found stuff */
7023 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7024 {
7025 EMSG2(_("E422: terminal code too long: %s"), arg);
7026 error = TRUE;
7027 break;
7028 }
7029 STRCAT(buf, p);
7030
7031 /* Advance to the next item */
7032 off += len;
7033 if (arg[off] == ',') /* another one follows */
7034 ++off;
7035 }
7036 }
7037 else
7038 {
7039 /*
7040 * Copy characters from arg[] to buf[], translating <> codes.
7041 */
7042 for (p = arg, off = 0; off < 100 && *p; )
7043 {
7044 len = trans_special(&p, buf + off, FALSE);
7045 if (len) /* recognized special char */
7046 off += len;
7047 else /* copy as normal char */
7048 buf[off++] = *p++;
7049 }
7050 buf[off] = NUL;
7051 }
7052 if (error)
7053 break;
7054
7055 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7056 p = NULL;
7057 else
7058 p = vim_strsave(buf);
7059 if (key[2] == 'A')
7060 {
7061 vim_free(HL_TABLE()[idx].sg_start);
7062 HL_TABLE()[idx].sg_start = p;
7063 }
7064 else
7065 {
7066 vim_free(HL_TABLE()[idx].sg_stop);
7067 HL_TABLE()[idx].sg_stop = p;
7068 }
7069 }
7070 else
7071 {
7072 EMSG2(_("E423: Illegal argument: %s"), key_start);
7073 error = TRUE;
7074 break;
7075 }
7076
7077 /*
7078 * When highlighting has been given for a group, don't link it.
7079 */
7080 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7081 HL_TABLE()[idx].sg_link = 0;
7082
7083 /*
7084 * Continue with next argument.
7085 */
7086 linep = skipwhite(linep);
7087 }
7088
7089 /*
7090 * If there is an error, and it's a new entry, remove it from the table.
7091 */
7092 if (error && idx == highlight_ga.ga_len)
7093 syn_unadd_group();
7094 else
7095 {
7096 if (is_normal_group)
7097 {
7098 HL_TABLE()[idx].sg_term_attr = 0;
7099 HL_TABLE()[idx].sg_cterm_attr = 0;
7100#ifdef FEAT_GUI
7101 HL_TABLE()[idx].sg_gui_attr = 0;
7102 /*
7103 * Need to update all groups, because they might be using "bg"
7104 * and/or "fg", which have been changed now.
7105 */
7106 if (gui.in_use)
7107 highlight_gui_started();
7108#endif
7109 }
7110#ifdef FEAT_GUI_X11
7111# ifdef FEAT_MENU
7112 else if (is_menu_group)
7113 {
7114 if (gui.in_use && do_colors)
7115 gui_mch_new_menu_colors();
7116 }
7117# endif
7118 else if (is_scrollbar_group)
7119 {
7120 if (gui.in_use && do_colors)
7121 gui_new_scrollbar_colors();
7122 }
7123# ifdef FEAT_BEVAL
7124 else if (is_tooltip_group)
7125 {
7126 if (gui.in_use && do_colors)
7127 gui_mch_new_tooltip_colors();
7128 }
7129# endif
7130#endif
7131 else
7132 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007133#ifdef FEAT_EVAL
7134 HL_TABLE()[idx].sg_scriptID = current_SID;
7135#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007136 redraw_all_later(NOT_VALID);
7137 }
7138 vim_free(key);
7139 vim_free(arg);
7140
7141 /* Only call highlight_changed() once, after sourcing a syntax file */
7142 need_highlight_changed = TRUE;
7143}
7144
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007145#if defined(EXITFREE) || defined(PROTO)
7146 void
7147free_highlight()
7148{
7149 int i;
7150
7151 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007152 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007153 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007154 vim_free(HL_TABLE()[i].sg_name);
7155 vim_free(HL_TABLE()[i].sg_name_u);
7156 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007157 ga_clear(&highlight_ga);
7158}
7159#endif
7160
Bram Moolenaar071d4272004-06-13 20:20:40 +00007161/*
7162 * Reset the cterm colors to what they were before Vim was started, if
7163 * possible. Otherwise reset them to zero.
7164 */
7165 void
7166restore_cterm_colors()
7167{
7168#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7169 /* Since t_me has been set, this probably means that the user
7170 * wants to use this as default colors. Need to reset default
7171 * background/foreground colors. */
7172 mch_set_normal_colors();
7173#else
7174 cterm_normal_fg_color = 0;
7175 cterm_normal_fg_bold = 0;
7176 cterm_normal_bg_color = 0;
7177#endif
7178}
7179
7180/*
7181 * Return TRUE if highlight group "idx" has any settings.
7182 * When "check_link" is TRUE also check for an existing link.
7183 */
7184 static int
7185hl_has_settings(idx, check_link)
7186 int idx;
7187 int check_link;
7188{
7189 return ( HL_TABLE()[idx].sg_term_attr != 0
7190 || HL_TABLE()[idx].sg_cterm_attr != 0
7191#ifdef FEAT_GUI
7192 || HL_TABLE()[idx].sg_gui_attr != 0
7193#endif
7194 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7195}
7196
7197/*
7198 * Clear highlighting for one group.
7199 */
7200 static void
7201highlight_clear(idx)
7202 int idx;
7203{
7204 HL_TABLE()[idx].sg_term = 0;
7205 vim_free(HL_TABLE()[idx].sg_start);
7206 HL_TABLE()[idx].sg_start = NULL;
7207 vim_free(HL_TABLE()[idx].sg_stop);
7208 HL_TABLE()[idx].sg_stop = NULL;
7209 HL_TABLE()[idx].sg_term_attr = 0;
7210 HL_TABLE()[idx].sg_cterm = 0;
7211 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7212 HL_TABLE()[idx].sg_cterm_fg = 0;
7213 HL_TABLE()[idx].sg_cterm_bg = 0;
7214 HL_TABLE()[idx].sg_cterm_attr = 0;
7215#ifdef FEAT_GUI /* in non-GUI fonts are simply ignored */
7216 HL_TABLE()[idx].sg_gui = 0;
7217 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7218 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7219 HL_TABLE()[idx].sg_gui_fg_name = NULL;
7220 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7221 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7222 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007223 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7224 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7225 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007226 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7227 HL_TABLE()[idx].sg_font = NOFONT;
7228# ifdef FEAT_XFONTSET
7229 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7230 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7231# endif
7232 vim_free(HL_TABLE()[idx].sg_font_name);
7233 HL_TABLE()[idx].sg_font_name = NULL;
7234 HL_TABLE()[idx].sg_gui_attr = 0;
7235#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007236#ifdef FEAT_EVAL
7237 /* Clear the script ID only when there is no link, since that is not
7238 * cleared. */
7239 if (HL_TABLE()[idx].sg_link == 0)
7240 HL_TABLE()[idx].sg_scriptID = 0;
7241#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007242}
7243
7244#if defined(FEAT_GUI) || defined(PROTO)
7245/*
7246 * Set the normal foreground and background colors according to the "Normal"
7247 * highlighighting group. For X11 also set "Menu", "Scrollbar", and
7248 * "Tooltip" colors.
7249 */
7250 void
7251set_normal_colors()
7252{
7253 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007254 &gui.norm_pixel, &gui.back_pixel,
7255 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007256 {
7257 gui_mch_new_colors();
7258 must_redraw = CLEAR;
7259 }
7260#ifdef FEAT_GUI_X11
7261 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007262 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7263 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007264 {
7265# ifdef FEAT_MENU
7266 gui_mch_new_menu_colors();
7267# endif
7268 must_redraw = CLEAR;
7269 }
7270# ifdef FEAT_BEVAL
7271 if (set_group_colors((char_u *)"Tooltip",
7272 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7273 FALSE, FALSE, TRUE))
7274 {
7275# ifdef FEAT_TOOLBAR
7276 gui_mch_new_tooltip_colors();
7277# endif
7278 must_redraw = CLEAR;
7279 }
7280#endif
7281 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007282 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7283 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007284 {
7285 gui_new_scrollbar_colors();
7286 must_redraw = CLEAR;
7287 }
7288#endif
7289}
7290
7291/*
7292 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7293 */
7294 static int
7295set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7296 char_u *name;
7297 guicolor_T *fgp;
7298 guicolor_T *bgp;
7299 int do_menu;
7300 int use_norm;
7301 int do_tooltip;
7302{
7303 int idx;
7304
7305 idx = syn_name2id(name) - 1;
7306 if (idx >= 0)
7307 {
7308 gui_do_one_color(idx, do_menu, do_tooltip);
7309
7310 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7311 *fgp = HL_TABLE()[idx].sg_gui_fg;
7312 else if (use_norm)
7313 *fgp = gui.def_norm_pixel;
7314 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7315 *bgp = HL_TABLE()[idx].sg_gui_bg;
7316 else if (use_norm)
7317 *bgp = gui.def_back_pixel;
7318 return TRUE;
7319 }
7320 return FALSE;
7321}
7322
7323/*
7324 * Get the font of the "Normal" group.
7325 * Returns "" when it's not found or not set.
7326 */
7327 char_u *
7328hl_get_font_name()
7329{
7330 int id;
7331 char_u *s;
7332
7333 id = syn_name2id((char_u *)"Normal");
7334 if (id > 0)
7335 {
7336 s = HL_TABLE()[id - 1].sg_font_name;
7337 if (s != NULL)
7338 return s;
7339 }
7340 return (char_u *)"";
7341}
7342
7343/*
7344 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7345 * actually chosen to be used.
7346 */
7347 void
7348hl_set_font_name(font_name)
7349 char_u *font_name;
7350{
7351 int id;
7352
7353 id = syn_name2id((char_u *)"Normal");
7354 if (id > 0)
7355 {
7356 vim_free(HL_TABLE()[id - 1].sg_font_name);
7357 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7358 }
7359}
7360
7361/*
7362 * Set background color for "Normal" group. Called by gui_set_bg_color()
7363 * when the color is known.
7364 */
7365 void
7366hl_set_bg_color_name(name)
7367 char_u *name; /* must have been allocated */
7368{
7369 int id;
7370
7371 if (name != NULL)
7372 {
7373 id = syn_name2id((char_u *)"Normal");
7374 if (id > 0)
7375 {
7376 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7377 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7378 }
7379 }
7380}
7381
7382/*
7383 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7384 * when the color is known.
7385 */
7386 void
7387hl_set_fg_color_name(name)
7388 char_u *name; /* must have been allocated */
7389{
7390 int id;
7391
7392 if (name != NULL)
7393 {
7394 id = syn_name2id((char_u *)"Normal");
7395 if (id > 0)
7396 {
7397 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7398 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7399 }
7400 }
7401}
7402
7403/*
7404 * Return the handle for a color name.
7405 * Returns INVALCOLOR when failed.
7406 */
7407 static guicolor_T
7408color_name2handle(name)
7409 char_u *name;
7410{
7411 if (STRCMP(name, "NONE") == 0)
7412 return INVALCOLOR;
7413
7414 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7415 return gui.norm_pixel;
7416 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7417 return gui.back_pixel;
7418
7419 return gui_get_color(name);
7420}
7421
7422/*
7423 * Return the handle for a font name.
7424 * Returns NOFONT when failed.
7425 */
7426 static GuiFont
7427font_name2handle(name)
7428 char_u *name;
7429{
7430 if (STRCMP(name, "NONE") == 0)
7431 return NOFONT;
7432
7433 return gui_mch_get_font(name, TRUE);
7434}
7435
7436# ifdef FEAT_XFONTSET
7437/*
7438 * Return the handle for a fontset name.
7439 * Returns NOFONTSET when failed.
7440 */
7441 static GuiFontset
7442fontset_name2handle(name, fixed_width)
7443 char_u *name;
7444 int fixed_width;
7445{
7446 if (STRCMP(name, "NONE") == 0)
7447 return NOFONTSET;
7448
7449 return gui_mch_get_fontset(name, TRUE, fixed_width);
7450}
7451# endif
7452
7453/*
7454 * Get the font or fontset for one highlight group.
7455 */
7456/*ARGSUSED*/
7457 static void
7458hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7459 int idx;
7460 char_u *arg;
7461 int do_normal; /* set normal font */
7462 int do_menu; /* set menu font */
7463 int do_tooltip; /* set tooltip font */
7464{
7465# ifdef FEAT_XFONTSET
7466 /* If 'guifontset' is not empty, first try using the name as a
7467 * fontset. If that doesn't work, use it as a font name. */
7468 if (*p_guifontset != NUL
7469# ifdef FONTSET_ALWAYS
7470 || do_menu
7471# endif
7472# ifdef FEAT_BEVAL_TIP
7473 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
7474 || do_tooltip
7475# endif
7476 )
7477 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7478# ifdef FONTSET_ALWAYS
7479 || do_menu
7480# endif
7481# ifdef FEAT_BEVAL_TIP
7482 || do_tooltip
7483# endif
7484 );
7485 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7486 {
7487 /* If it worked and it's the Normal group, use it as the
7488 * normal fontset. Same for the Menu group. */
7489 if (do_normal)
7490 gui_init_font(arg, TRUE);
7491# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7492 if (do_menu)
7493 {
7494# ifdef FONTSET_ALWAYS
7495 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7496# else
7497 /* YIKES! This is a bug waiting to crash the program */
7498 gui.menu_font = HL_TABLE()[idx].sg_fontset;
7499# endif
7500 gui_mch_new_menu_font();
7501 }
7502# ifdef FEAT_BEVAL
7503 if (do_tooltip)
7504 {
7505 /* The Athena widget set cannot currently handle switching between
7506 * displaying a single font and a fontset.
7507 * If the XtNinternational resource is set to True at widget
7508 * creation, then a fontset is always used, othwise an
7509 * XFontStruct is used.
7510 */
7511 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7512 gui_mch_new_tooltip_font();
7513 }
7514# endif
7515# endif
7516 }
7517 else
7518# endif
7519 {
7520 HL_TABLE()[idx].sg_font = font_name2handle(arg);
7521 /* If it worked and it's the Normal group, use it as the
7522 * normal font. Same for the Menu group. */
7523 if (HL_TABLE()[idx].sg_font != NOFONT)
7524 {
7525 if (do_normal)
7526 gui_init_font(arg, FALSE);
7527#ifndef FONTSET_ALWAYS
7528# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7529 if (do_menu)
7530 {
7531 gui.menu_font = HL_TABLE()[idx].sg_font;
7532 gui_mch_new_menu_font();
7533 }
7534# endif
7535#endif
7536 }
7537 }
7538}
7539
7540#endif /* FEAT_GUI */
7541
7542/*
7543 * Table with the specifications for an attribute number.
7544 * Note that this table is used by ALL buffers. This is required because the
7545 * GUI can redraw at any time for any buffer.
7546 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007547static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007548
7549#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7550
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007551static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007552
7553#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7554
7555#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007556static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007557
7558#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7559#endif
7560
7561/*
7562 * Return the attr number for a set of colors and font.
7563 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7564 * if the combination is new.
7565 * Return 0 for error (no more room).
7566 */
7567 static int
7568get_attr_entry(table, aep)
7569 garray_T *table;
7570 attrentry_T *aep;
7571{
7572 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007573 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007574 static int recursive = FALSE;
7575
7576 /*
7577 * Init the table, in case it wasn't done yet.
7578 */
7579 table->ga_itemsize = sizeof(attrentry_T);
7580 table->ga_growsize = 7;
7581
7582 /*
7583 * Try to find an entry with the same specifications.
7584 */
7585 for (i = 0; i < table->ga_len; ++i)
7586 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007587 taep = &(((attrentry_T *)table->ga_data)[i]);
7588 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00007589 && (
7590#ifdef FEAT_GUI
7591 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007592 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
7593 && aep->ae_u.gui.bg_color
7594 == taep->ae_u.gui.bg_color
7595 && aep->ae_u.gui.sp_color
7596 == taep->ae_u.gui.sp_color
7597 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00007598# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007599 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00007600# endif
7601 ))
7602 ||
7603#endif
7604 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007605 && (aep->ae_u.term.start == NULL)
7606 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007607 && (aep->ae_u.term.start == NULL
7608 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007609 taep->ae_u.term.start) == 0)
7610 && (aep->ae_u.term.stop == NULL)
7611 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007612 && (aep->ae_u.term.stop == NULL
7613 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007614 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007615 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007616 && aep->ae_u.cterm.fg_color
7617 == taep->ae_u.cterm.fg_color
7618 && aep->ae_u.cterm.bg_color
7619 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007620 ))
7621
7622 return i + ATTR_OFF;
7623 }
7624
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00007625 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007626 {
7627 /*
7628 * Running out of attribute entries! remove all attributes, and
7629 * compute new ones for all groups.
7630 * When called recursively, we are really out of numbers.
7631 */
7632 if (recursive)
7633 {
7634 EMSG(_("E424: Too many different highlighting attributes in use"));
7635 return 0;
7636 }
7637 recursive = TRUE;
7638
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007639 clear_hl_tables();
7640
Bram Moolenaar071d4272004-06-13 20:20:40 +00007641 must_redraw = CLEAR;
7642
7643 for (i = 0; i < highlight_ga.ga_len; ++i)
7644 set_hl_attr(i);
7645
7646 recursive = FALSE;
7647 }
7648
7649 /*
7650 * This is a new combination of colors and font, add an entry.
7651 */
7652 if (ga_grow(table, 1) == FAIL)
7653 return 0;
7654
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007655 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7656 vim_memset(taep, 0, sizeof(attrentry_T));
7657 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007658#ifdef FEAT_GUI
7659 if (table == &gui_attr_table)
7660 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007661 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7662 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
7663 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
7664 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007665# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007666 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007667# endif
7668 }
7669#endif
7670 if (table == &term_attr_table)
7671 {
7672 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007673 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007674 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007675 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007676 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007677 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007678 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007679 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007680 }
7681 else if (table == &cterm_attr_table)
7682 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007683 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7684 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007685 }
7686 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007687 return (table->ga_len - 1 + ATTR_OFF);
7688}
7689
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007690/*
7691 * Clear all highlight tables.
7692 */
7693 void
7694clear_hl_tables()
7695{
7696 int i;
7697 attrentry_T *taep;
7698
7699#ifdef FEAT_GUI
7700 ga_clear(&gui_attr_table);
7701#endif
7702 for (i = 0; i < term_attr_table.ga_len; ++i)
7703 {
7704 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
7705 vim_free(taep->ae_u.term.start);
7706 vim_free(taep->ae_u.term.stop);
7707 }
7708 ga_clear(&term_attr_table);
7709 ga_clear(&cterm_attr_table);
7710}
7711
Bram Moolenaar217ad922005-03-20 22:37:15 +00007712#if defined(FEAT_SYN_HL) || defined(PROTO)
7713/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00007714 * Combine special attributes (e.g., for spelling) with other attributes
7715 * (e.g., for syntax highlighting).
7716 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00007717 * This creates a new group when required.
7718 * Since we expect there to be few spelling mistakes we don't cache the
7719 * result.
7720 * Return the resulting attributes.
7721 */
7722 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00007723hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00007724 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00007725 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007726{
7727 attrentry_T *char_aep = NULL;
7728 attrentry_T *spell_aep;
7729 attrentry_T new_en;
7730
7731 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00007732 return prim_attr;
7733 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
7734 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007735#ifdef FEAT_GUI
7736 if (gui.in_use)
7737 {
7738 if (char_attr > HL_ALL)
7739 char_aep = syn_gui_attr2entry(char_attr);
7740 if (char_aep != NULL)
7741 new_en = *char_aep;
7742 else
7743 {
7744 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00007745 new_en.ae_u.gui.fg_color = INVALCOLOR;
7746 new_en.ae_u.gui.bg_color = INVALCOLOR;
7747 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007748 if (char_attr <= HL_ALL)
7749 new_en.ae_attr = char_attr;
7750 }
7751
Bram Moolenaar30abd282005-06-22 22:35:10 +00007752 if (prim_attr <= HL_ALL)
7753 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007754 else
7755 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00007756 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00007757 if (spell_aep != NULL)
7758 {
7759 new_en.ae_attr |= spell_aep->ae_attr;
7760 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7761 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7762 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7763 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7764 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7765 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7766 if (spell_aep->ae_u.gui.font != NOFONT)
7767 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7768# ifdef FEAT_XFONTSET
7769 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7770 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7771# endif
7772 }
7773 }
7774 return get_attr_entry(&gui_attr_table, &new_en);
7775 }
7776#endif
7777
7778 if (t_colors > 1)
7779 {
7780 if (char_attr > HL_ALL)
7781 char_aep = syn_cterm_attr2entry(char_attr);
7782 if (char_aep != NULL)
7783 new_en = *char_aep;
7784 else
7785 {
7786 vim_memset(&new_en, 0, sizeof(new_en));
7787 if (char_attr <= HL_ALL)
7788 new_en.ae_attr = char_attr;
7789 }
7790
Bram Moolenaar30abd282005-06-22 22:35:10 +00007791 if (prim_attr <= HL_ALL)
7792 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007793 else
7794 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00007795 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00007796 if (spell_aep != NULL)
7797 {
7798 new_en.ae_attr |= spell_aep->ae_attr;
7799 if (spell_aep->ae_u.cterm.fg_color > 0)
7800 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
7801 if (spell_aep->ae_u.cterm.bg_color > 0)
7802 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
7803 }
7804 }
7805 return get_attr_entry(&cterm_attr_table, &new_en);
7806 }
7807
7808 if (char_attr > HL_ALL)
7809 char_aep = syn_term_attr2entry(char_attr);
7810 if (char_aep != NULL)
7811 new_en = *char_aep;
7812 else
7813 {
7814 vim_memset(&new_en, 0, sizeof(new_en));
7815 if (char_attr <= HL_ALL)
7816 new_en.ae_attr = char_attr;
7817 }
7818
Bram Moolenaar30abd282005-06-22 22:35:10 +00007819 if (prim_attr <= HL_ALL)
7820 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007821 else
7822 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00007823 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00007824 if (spell_aep != NULL)
7825 {
7826 new_en.ae_attr |= spell_aep->ae_attr;
7827 if (spell_aep->ae_u.term.start != NULL)
7828 {
7829 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
7830 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
7831 }
7832 }
7833 }
7834 return get_attr_entry(&term_attr_table, &new_en);
7835}
7836#endif
7837
Bram Moolenaar071d4272004-06-13 20:20:40 +00007838#ifdef FEAT_GUI
7839
7840 attrentry_T *
7841syn_gui_attr2entry(attr)
7842 int attr;
7843{
7844 attr -= ATTR_OFF;
7845 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
7846 return NULL;
7847 return &(GUI_ATTR_ENTRY(attr));
7848}
7849
7850#endif /* FEAT_GUI */
7851
7852 attrentry_T *
7853syn_term_attr2entry(attr)
7854 int attr;
7855{
7856 attr -= ATTR_OFF;
7857 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
7858 return NULL;
7859 return &(TERM_ATTR_ENTRY(attr));
7860}
7861
7862 attrentry_T *
7863syn_cterm_attr2entry(attr)
7864 int attr;
7865{
7866 attr -= ATTR_OFF;
7867 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
7868 return NULL;
7869 return &(CTERM_ATTR_ENTRY(attr));
7870}
7871
7872#define LIST_ATTR 1
7873#define LIST_STRING 2
7874#define LIST_INT 3
7875
7876 static void
7877highlight_list_one(id)
7878 int id;
7879{
7880 struct hl_group *sgp;
7881 int didh = FALSE;
7882
7883 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
7884
7885 didh = highlight_list_arg(id, didh, LIST_ATTR,
7886 sgp->sg_term, NULL, "term");
7887 didh = highlight_list_arg(id, didh, LIST_STRING,
7888 0, sgp->sg_start, "start");
7889 didh = highlight_list_arg(id, didh, LIST_STRING,
7890 0, sgp->sg_stop, "stop");
7891
7892 didh = highlight_list_arg(id, didh, LIST_ATTR,
7893 sgp->sg_cterm, NULL, "cterm");
7894 didh = highlight_list_arg(id, didh, LIST_INT,
7895 sgp->sg_cterm_fg, NULL, "ctermfg");
7896 didh = highlight_list_arg(id, didh, LIST_INT,
7897 sgp->sg_cterm_bg, NULL, "ctermbg");
7898
7899#ifdef FEAT_GUI
7900 didh = highlight_list_arg(id, didh, LIST_ATTR,
7901 sgp->sg_gui, NULL, "gui");
7902 didh = highlight_list_arg(id, didh, LIST_STRING,
7903 0, sgp->sg_gui_fg_name, "guifg");
7904 didh = highlight_list_arg(id, didh, LIST_STRING,
7905 0, sgp->sg_gui_bg_name, "guibg");
7906 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00007907 0, sgp->sg_gui_sp_name, "guisp");
7908 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00007909 0, sgp->sg_font_name, "font");
7910#endif
7911
Bram Moolenaar661b1822005-07-28 22:36:45 +00007912 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007913 {
7914 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007915 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007916 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
7917 msg_putchar(' ');
7918 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
7919 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00007920
7921#ifdef FEAT_EVAL
7922 if (didh && p_verbose > 0)
7923 last_set_msg(sgp->sg_scriptID);
7924#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007925}
7926
7927 static int
7928highlight_list_arg(id, didh, type, iarg, sarg, name)
7929 int id;
7930 int didh;
7931 int type;
7932 int iarg;
7933 char_u *sarg;
7934 char *name;
7935{
7936 char_u buf[100];
7937 char_u *ts;
7938 int i;
7939
Bram Moolenaar661b1822005-07-28 22:36:45 +00007940 if (got_int)
7941 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007942 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
7943 {
7944 ts = buf;
7945 if (type == LIST_INT)
7946 sprintf((char *)buf, "%d", iarg - 1);
7947 else if (type == LIST_STRING)
7948 ts = sarg;
7949 else /* type == LIST_ATTR */
7950 {
7951 buf[0] = NUL;
7952 for (i = 0; hl_attr_table[i] != 0; ++i)
7953 {
7954 if (iarg & hl_attr_table[i])
7955 {
7956 if (buf[0] != NUL)
7957 STRCAT(buf, ",");
7958 STRCAT(buf, hl_name_table[i]);
7959 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
7960 }
7961 }
7962 }
7963
7964 (void)syn_list_header(didh,
7965 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
7966 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007967 if (!got_int)
7968 {
7969 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
7970 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
7971 msg_outtrans(ts);
7972 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007973 }
7974 return didh;
7975}
7976
7977#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
7978/*
7979 * Return "1" if highlight group "id" has attribute "flag".
7980 * Return NULL otherwise.
7981 */
7982 char_u *
7983highlight_has_attr(id, flag, modec)
7984 int id;
7985 int flag;
7986 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
7987{
7988 int attr;
7989
7990 if (id <= 0 || id > highlight_ga.ga_len)
7991 return NULL;
7992
7993#ifdef FEAT_GUI
7994 if (modec == 'g')
7995 attr = HL_TABLE()[id - 1].sg_gui;
7996 else
7997#endif
7998 if (modec == 'c')
7999 attr = HL_TABLE()[id - 1].sg_cterm;
8000 else
8001 attr = HL_TABLE()[id - 1].sg_term;
8002
8003 if (attr & flag)
8004 return (char_u *)"1";
8005 return NULL;
8006}
8007#endif
8008
8009#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8010/*
8011 * Return color name of highlight group "id".
8012 */
8013 char_u *
8014highlight_color(id, what, modec)
8015 int id;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008016 char_u *what; /* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008017 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8018{
8019 static char_u name[20];
8020 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008021 int fg = FALSE;
8022# ifdef FEAT_GUI
8023 int sp = FALSE;
8024# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008025
8026 if (id <= 0 || id > highlight_ga.ga_len)
8027 return NULL;
8028
8029 if (TOLOWER_ASC(what[0]) == 'f')
8030 fg = TRUE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008031# ifdef FEAT_GUI
8032 else if (TOLOWER_ASC(what[0]) == 's')
8033 sp = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008034 if (modec == 'g')
8035 {
8036 /* return #RRGGBB form (only possible when GUI is running) */
8037 if (gui.in_use && what[1] && what[2] == '#')
8038 {
8039 guicolor_T color;
8040 long_u rgb;
8041 static char_u buf[10];
8042
8043 if (fg)
8044 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008045 else if (sp)
8046 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008047 else
8048 color = HL_TABLE()[id - 1].sg_gui_bg;
8049 if (color == INVALCOLOR)
8050 return NULL;
8051 rgb = gui_mch_get_rgb(color);
8052 sprintf((char *)buf, "#%02x%02x%02x",
8053 (unsigned)(rgb >> 16),
8054 (unsigned)(rgb >> 8) & 255,
8055 (unsigned)rgb & 255);
8056 return buf;
8057 }
8058 if (fg)
8059 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008060 if (sp)
8061 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008062 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8063 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008064# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008065 if (modec == 'c')
8066 {
8067 if (fg)
8068 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8069 else
8070 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8071 sprintf((char *)name, "%d", n);
8072 return name;
8073 }
8074 /* term doesn't have color */
8075 return NULL;
8076}
8077#endif
8078
8079#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8080 || defined(PROTO)
8081/*
8082 * Return color name of highlight group "id" as RGB value.
8083 */
8084 long_u
8085highlight_gui_color_rgb(id, fg)
8086 int id;
8087 int fg; /* TRUE = fg, FALSE = bg */
8088{
8089 guicolor_T color;
8090
8091 if (id <= 0 || id > highlight_ga.ga_len)
8092 return 0L;
8093
8094 if (fg)
8095 color = HL_TABLE()[id - 1].sg_gui_fg;
8096 else
8097 color = HL_TABLE()[id - 1].sg_gui_bg;
8098
8099 if (color == INVALCOLOR)
8100 return 0L;
8101
8102 return gui_mch_get_rgb(color);
8103}
8104#endif
8105
8106/*
8107 * Output the syntax list header.
8108 * Return TRUE when started a new line.
8109 */
8110 static int
8111syn_list_header(did_header, outlen, id)
8112 int did_header; /* did header already */
8113 int outlen; /* length of string that comes */
8114 int id; /* highlight group id */
8115{
8116 int endcol = 19;
8117 int newline = TRUE;
8118
8119 if (!did_header)
8120 {
8121 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008122 if (got_int)
8123 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008124 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8125 endcol = 15;
8126 }
8127 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008128 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008129 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008130 if (got_int)
8131 return TRUE;
8132 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008133 else
8134 {
8135 if (msg_col >= endcol) /* wrap around is like starting a new line */
8136 newline = FALSE;
8137 }
8138
8139 if (msg_col >= endcol) /* output at least one space */
8140 endcol = msg_col + 1;
8141 if (Columns <= endcol) /* avoid hang for tiny window */
8142 endcol = Columns - 1;
8143
8144 msg_advance(endcol);
8145
8146 /* Show "xxx" with the attributes. */
8147 if (!did_header)
8148 {
8149 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8150 msg_putchar(' ');
8151 }
8152
8153 return newline;
8154}
8155
8156/*
8157 * Set the attribute numbers for a highlight group.
8158 * Called after one of the attributes has changed.
8159 */
8160 static void
8161set_hl_attr(idx)
8162 int idx; /* index in array */
8163{
8164 attrentry_T at_en;
8165 struct hl_group *sgp = HL_TABLE() + idx;
8166
8167 /* The "Normal" group doesn't need an attribute number */
8168 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8169 return;
8170
8171#ifdef FEAT_GUI
8172 /*
8173 * For the GUI mode: If there are other than "normal" highlighting
8174 * attributes, need to allocate an attr number.
8175 */
8176 if (sgp->sg_gui_fg == INVALCOLOR
8177 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008178 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008179 && sgp->sg_font == NOFONT
8180# ifdef FEAT_XFONTSET
8181 && sgp->sg_fontset == NOFONTSET
8182# endif
8183 )
8184 {
8185 sgp->sg_gui_attr = sgp->sg_gui;
8186 }
8187 else
8188 {
8189 at_en.ae_attr = sgp->sg_gui;
8190 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8191 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008192 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008193 at_en.ae_u.gui.font = sgp->sg_font;
8194# ifdef FEAT_XFONTSET
8195 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8196# endif
8197 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8198 }
8199#endif
8200 /*
8201 * For the term mode: If there are other than "normal" highlighting
8202 * attributes, need to allocate an attr number.
8203 */
8204 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8205 sgp->sg_term_attr = sgp->sg_term;
8206 else
8207 {
8208 at_en.ae_attr = sgp->sg_term;
8209 at_en.ae_u.term.start = sgp->sg_start;
8210 at_en.ae_u.term.stop = sgp->sg_stop;
8211 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8212 }
8213
8214 /*
8215 * For the color term mode: If there are other than "normal"
8216 * highlighting attributes, need to allocate an attr number.
8217 */
8218 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8219 sgp->sg_cterm_attr = sgp->sg_cterm;
8220 else
8221 {
8222 at_en.ae_attr = sgp->sg_cterm;
8223 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8224 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8225 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8226 }
8227}
8228
8229/*
8230 * Lookup a highlight group name and return it's ID.
8231 * If it is not found, 0 is returned.
8232 */
8233 int
8234syn_name2id(name)
8235 char_u *name;
8236{
8237 int i;
8238 char_u name_u[200];
8239
8240 /* Avoid using stricmp() too much, it's slow on some systems */
8241 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8242 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008243 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008244 vim_strup(name_u);
8245 for (i = highlight_ga.ga_len; --i >= 0; )
8246 if (HL_TABLE()[i].sg_name_u != NULL
8247 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8248 break;
8249 return i + 1;
8250}
8251
8252#if defined(FEAT_EVAL) || defined(PROTO)
8253/*
8254 * Return TRUE if highlight group "name" exists.
8255 */
8256 int
8257highlight_exists(name)
8258 char_u *name;
8259{
8260 return (syn_name2id(name) > 0);
8261}
8262#endif
8263
8264/*
8265 * Like syn_name2id(), but take a pointer + length argument.
8266 */
8267 int
8268syn_namen2id(linep, len)
8269 char_u *linep;
8270 int len;
8271{
8272 char_u *name;
8273 int id = 0;
8274
8275 name = vim_strnsave(linep, len);
8276 if (name != NULL)
8277 {
8278 id = syn_name2id(name);
8279 vim_free(name);
8280 }
8281 return id;
8282}
8283
8284/*
8285 * Find highlight group name in the table and return it's ID.
8286 * The argument is a pointer to the name and the length of the name.
8287 * If it doesn't exist yet, a new entry is created.
8288 * Return 0 for failure.
8289 */
8290 int
8291syn_check_group(pp, len)
8292 char_u *pp;
8293 int len;
8294{
8295 int id;
8296 char_u *name;
8297
8298 name = vim_strnsave(pp, len);
8299 if (name == NULL)
8300 return 0;
8301
8302 id = syn_name2id(name);
8303 if (id == 0) /* doesn't exist yet */
8304 id = syn_add_group(name);
8305 else
8306 vim_free(name);
8307 return id;
8308}
8309
8310/*
8311 * Add new highlight group and return it's ID.
8312 * "name" must be an allocated string, it will be consumed.
8313 * Return 0 for failure.
8314 */
8315 static int
8316syn_add_group(name)
8317 char_u *name;
8318{
8319 char_u *p;
8320
8321 /* Check that the name is ASCII letters, digits and underscore. */
8322 for (p = name; *p != NUL; ++p)
8323 {
8324 if (!vim_isprintc(*p))
8325 {
8326 EMSG(_("E669: Unprintable character in group name"));
8327 return 0;
8328 }
8329 else if (!ASCII_ISALNUM(*p) && *p != '_')
8330 {
8331 /* This is an error, but since there previously was no check only
8332 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008333 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008334 MSG(_("W18: Invalid character in group name"));
8335 break;
8336 }
8337 }
8338
8339 /*
8340 * First call for this growarray: init growing array.
8341 */
8342 if (highlight_ga.ga_data == NULL)
8343 {
8344 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8345 highlight_ga.ga_growsize = 10;
8346 }
8347
8348 /*
8349 * Make room for at least one other syntax_highlight entry.
8350 */
8351 if (ga_grow(&highlight_ga, 1) == FAIL)
8352 {
8353 vim_free(name);
8354 return 0;
8355 }
8356
8357 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8358 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8359 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8360#ifdef FEAT_GUI
8361 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8362 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008363 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008364#endif
8365 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008366
8367 return highlight_ga.ga_len; /* ID is index plus one */
8368}
8369
8370/*
8371 * When, just after calling syn_add_group(), an error is discovered, this
8372 * function deletes the new name.
8373 */
8374 static void
8375syn_unadd_group()
8376{
8377 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008378 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8379 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8380}
8381
8382/*
8383 * Translate a group ID to highlight attributes.
8384 */
8385 int
8386syn_id2attr(hl_id)
8387 int hl_id;
8388{
8389 int attr;
8390 struct hl_group *sgp;
8391
8392 hl_id = syn_get_final_id(hl_id);
8393 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8394
8395#ifdef FEAT_GUI
8396 /*
8397 * Only use GUI attr when the GUI is being used.
8398 */
8399 if (gui.in_use)
8400 attr = sgp->sg_gui_attr;
8401 else
8402#endif
8403 if (t_colors > 1)
8404 attr = sgp->sg_cterm_attr;
8405 else
8406 attr = sgp->sg_term_attr;
8407
8408 return attr;
8409}
8410
8411#ifdef FEAT_GUI
8412/*
8413 * Get the GUI colors and attributes for a group ID.
8414 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8415 */
8416 int
8417syn_id2colors(hl_id, fgp, bgp)
8418 int hl_id;
8419 guicolor_T *fgp;
8420 guicolor_T *bgp;
8421{
8422 struct hl_group *sgp;
8423
8424 hl_id = syn_get_final_id(hl_id);
8425 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8426
8427 *fgp = sgp->sg_gui_fg;
8428 *bgp = sgp->sg_gui_bg;
8429 return sgp->sg_gui;
8430}
8431#endif
8432
8433/*
8434 * Translate a group ID to the final group ID (following links).
8435 */
8436 int
8437syn_get_final_id(hl_id)
8438 int hl_id;
8439{
8440 int count;
8441 struct hl_group *sgp;
8442
8443 if (hl_id > highlight_ga.ga_len || hl_id < 1)
8444 return 0; /* Can be called from eval!! */
8445
8446 /*
8447 * Follow links until there is no more.
8448 * Look out for loops! Break after 100 links.
8449 */
8450 for (count = 100; --count >= 0; )
8451 {
8452 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8453 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8454 break;
8455 hl_id = sgp->sg_link;
8456 }
8457
8458 return hl_id;
8459}
8460
8461#ifdef FEAT_GUI
8462/*
8463 * Call this function just after the GUI has started.
8464 * It finds the font and color handles for the highlighting groups.
8465 */
8466 void
8467highlight_gui_started()
8468{
8469 int idx;
8470
8471 /* First get the colors from the "Normal" and "Menu" group, if set */
8472 set_normal_colors();
8473
8474 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8475 gui_do_one_color(idx, FALSE, FALSE);
8476
8477 highlight_changed();
8478}
8479
8480 static void
8481gui_do_one_color(idx, do_menu, do_tooltip)
8482 int idx;
8483 int do_menu; /* TRUE: might set the menu font */
8484 int do_tooltip; /* TRUE: might set the tooltip font */
8485{
8486 int didit = FALSE;
8487
8488 if (HL_TABLE()[idx].sg_font_name != NULL)
8489 {
8490 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8491 do_tooltip);
8492 didit = TRUE;
8493 }
8494 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8495 {
8496 HL_TABLE()[idx].sg_gui_fg =
8497 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8498 didit = TRUE;
8499 }
8500 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8501 {
8502 HL_TABLE()[idx].sg_gui_bg =
8503 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8504 didit = TRUE;
8505 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008506 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8507 {
8508 HL_TABLE()[idx].sg_gui_sp =
8509 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8510 didit = TRUE;
8511 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008512 if (didit) /* need to get a new attr number */
8513 set_hl_attr(idx);
8514}
8515
8516#endif
8517
8518/*
8519 * Translate the 'highlight' option into attributes in highlight_attr[] and
8520 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
8521 * corresponding highlights to use on top of HLF_SNC is computed.
8522 * Called only when the 'highlight' option has been changed and upon first
8523 * screen redraw after any :highlight command.
8524 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
8525 */
8526 int
8527highlight_changed()
8528{
8529 int hlf;
8530 int i;
8531 char_u *p;
8532 int attr;
8533 char_u *end;
8534 int id;
8535#ifdef USER_HIGHLIGHT
8536 char_u userhl[10];
8537# ifdef FEAT_STL_OPT
8538 int id_SNC = -1;
8539 int id_S = -1;
8540 int hlcnt;
8541# endif
8542#endif
8543 static int hl_flags[HLF_COUNT] = HL_FLAGS;
8544
8545 need_highlight_changed = FALSE;
8546
8547 /*
8548 * Clear all attributes.
8549 */
8550 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8551 highlight_attr[hlf] = 0;
8552
8553 /*
8554 * First set all attributes to their default value.
8555 * Then use the attributes from the 'highlight' option.
8556 */
8557 for (i = 0; i < 2; ++i)
8558 {
8559 if (i)
8560 p = p_hl;
8561 else
8562 p = get_highlight_default();
8563 if (p == NULL) /* just in case */
8564 continue;
8565
8566 while (*p)
8567 {
8568 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8569 if (hl_flags[hlf] == *p)
8570 break;
8571 ++p;
8572 if (hlf == (int)HLF_COUNT || *p == NUL)
8573 return FAIL;
8574
8575 /*
8576 * Allow several hl_flags to be combined, like "bu" for
8577 * bold-underlined.
8578 */
8579 attr = 0;
8580 for ( ; *p && *p != ','; ++p) /* parse upto comma */
8581 {
8582 if (vim_iswhite(*p)) /* ignore white space */
8583 continue;
8584
8585 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
8586 return FAIL;
8587
8588 switch (*p)
8589 {
8590 case 'b': attr |= HL_BOLD;
8591 break;
8592 case 'i': attr |= HL_ITALIC;
8593 break;
8594 case '-':
8595 case 'n': /* no highlighting */
8596 break;
8597 case 'r': attr |= HL_INVERSE;
8598 break;
8599 case 's': attr |= HL_STANDOUT;
8600 break;
8601 case 'u': attr |= HL_UNDERLINE;
8602 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008603 case 'c': attr |= HL_UNDERCURL;
8604 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008605 case ':': ++p; /* highlight group name */
8606 if (attr || *p == NUL) /* no combinations */
8607 return FAIL;
8608 end = vim_strchr(p, ',');
8609 if (end == NULL)
8610 end = p + STRLEN(p);
8611 id = syn_check_group(p, (int)(end - p));
8612 if (id == 0)
8613 return FAIL;
8614 attr = syn_id2attr(id);
8615 p = end - 1;
8616#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8617 if (hlf == (int)HLF_SNC)
8618 id_SNC = syn_get_final_id(id);
8619 else if (hlf == (int)HLF_S)
8620 id_S = syn_get_final_id(id);
8621#endif
8622 break;
8623 default: return FAIL;
8624 }
8625 }
8626 highlight_attr[hlf] = attr;
8627
8628 p = skip_to_option_part(p); /* skip comma and spaces */
8629 }
8630 }
8631
8632#ifdef USER_HIGHLIGHT
8633 /* Setup the user highlights
8634 *
8635 * Temporarily utilize 10 more hl entries. Have to be in there
8636 * simultaneously in case of table overflows in get_attr_entry()
8637 */
8638# ifdef FEAT_STL_OPT
8639 if (ga_grow(&highlight_ga, 10) == FAIL)
8640 return FAIL;
8641 hlcnt = highlight_ga.ga_len;
8642 if (id_S == 0)
8643 { /* Make sure id_S is always valid to simplify code below */
8644 memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8645 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8646 id_S = hlcnt + 10;
8647 }
8648# endif
8649 for (i = 0; i < 9; i++)
8650 {
8651 sprintf((char *)userhl, "User%d", i + 1);
8652 id = syn_name2id(userhl);
8653 if (id == 0)
8654 {
8655 highlight_user[i] = 0;
8656# ifdef FEAT_STL_OPT
8657 highlight_stlnc[i] = 0;
8658# endif
8659 }
8660 else
8661 {
8662# ifdef FEAT_STL_OPT
8663 struct hl_group *hlt = HL_TABLE();
8664# endif
8665
8666 highlight_user[i] = syn_id2attr(id);
8667# ifdef FEAT_STL_OPT
8668 if (id_SNC == 0)
8669 {
8670 memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8671 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8672 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8673# ifdef FEAT_GUI
8674 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8675# endif
8676 }
8677 else
8678 mch_memmove(&hlt[hlcnt + i],
8679 &hlt[id_SNC - 1],
8680 sizeof(struct hl_group));
8681 hlt[hlcnt + i].sg_link = 0;
8682
8683 /* Apply difference between UserX and HLF_S to HLF_SNC */
8684 hlt[hlcnt + i].sg_term ^=
8685 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8686 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8687 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8688 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8689 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8690 hlt[hlcnt + i].sg_cterm ^=
8691 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8692 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8693 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8694 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8695 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8696# ifdef FEAT_GUI
8697 hlt[hlcnt + i].sg_gui ^=
8698 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8699 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8700 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8701 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8702 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008703 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8704 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008705 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8706 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8707# ifdef FEAT_XFONTSET
8708 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8709 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8710# endif
8711# endif
8712 highlight_ga.ga_len = hlcnt + i + 1;
8713 set_hl_attr(hlcnt + i); /* At long last we can apply */
8714 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8715# endif
8716 }
8717 }
8718# ifdef FEAT_STL_OPT
8719 highlight_ga.ga_len = hlcnt;
8720# endif
8721
8722#endif /* USER_HIGHLIGHT */
8723
8724 return OK;
8725}
8726
8727#ifdef FEAT_CMDL_COMPL
8728
8729static void highlight_list __ARGS((void));
8730static void highlight_list_two __ARGS((int cnt, int attr));
8731
8732/*
8733 * Handle command line completion for :highlight command.
8734 */
8735 void
8736set_context_in_highlight_cmd(xp, arg)
8737 expand_T *xp;
8738 char_u *arg;
8739{
8740 char_u *p;
8741
8742 /* Default: expand group names */
8743 xp->xp_context = EXPAND_HIGHLIGHT;
8744 xp->xp_pattern = arg;
8745 include_link = TRUE;
8746 include_default = TRUE;
8747
8748 /* (part of) subcommand already typed */
8749 if (*arg != NUL)
8750 {
8751 p = skiptowhite(arg);
8752 if (*p != NUL) /* past "default" or group name */
8753 {
8754 include_default = FALSE;
8755 if (STRNCMP("default", arg, p - arg) == 0)
8756 {
8757 arg = skipwhite(p);
8758 xp->xp_pattern = arg;
8759 p = skiptowhite(arg);
8760 }
8761 if (*p != NUL) /* past group name */
8762 {
8763 include_link = FALSE;
8764 if (arg[1] == 'i' && arg[0] == 'N')
8765 highlight_list();
8766 if (STRNCMP("link", arg, p - arg) == 0
8767 || STRNCMP("clear", arg, p - arg) == 0)
8768 {
8769 xp->xp_pattern = skipwhite(p);
8770 p = skiptowhite(xp->xp_pattern);
8771 if (*p != NUL) /* past first group name */
8772 {
8773 xp->xp_pattern = skipwhite(p);
8774 p = skiptowhite(xp->xp_pattern);
8775 }
8776 }
8777 if (*p != NUL) /* past group name(s) */
8778 xp->xp_context = EXPAND_NOTHING;
8779 }
8780 }
8781 }
8782}
8783
8784/*
8785 * List highlighting matches in a nice way.
8786 */
8787 static void
8788highlight_list()
8789{
8790 int i;
8791
8792 for (i = 10; --i >= 0; )
8793 highlight_list_two(i, hl_attr(HLF_D));
8794 for (i = 40; --i >= 0; )
8795 highlight_list_two(99, 0);
8796}
8797
8798 static void
8799highlight_list_two(cnt, attr)
8800 int cnt;
8801 int attr;
8802{
8803 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
8804 msg_clr_eos();
8805 out_flush();
8806 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
8807}
8808
8809#endif /* FEAT_CMDL_COMPL */
8810
8811#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
8812 || defined(FEAT_SIGNS) || defined(PROTO)
8813/*
8814 * Function given to ExpandGeneric() to obtain the list of group names.
8815 * Also used for synIDattr() function.
8816 */
8817/*ARGSUSED*/
8818 char_u *
8819get_highlight_name(xp, idx)
8820 expand_T *xp;
8821 int idx;
8822{
8823 if (idx == highlight_ga.ga_len
8824#ifdef FEAT_CMDL_COMPL
8825 && include_link
8826#endif
8827 )
8828 return (char_u *)"link";
8829 if (idx == highlight_ga.ga_len + 1
8830#ifdef FEAT_CMDL_COMPL
8831 && include_link
8832#endif
8833 )
8834 return (char_u *)"clear";
8835 if (idx == highlight_ga.ga_len + 2
8836#ifdef FEAT_CMDL_COMPL
8837 && include_default
8838#endif
8839 )
8840 return (char_u *)"default";
8841 if (idx < 0 || idx >= highlight_ga.ga_len)
8842 return NULL;
8843 return HL_TABLE()[idx].sg_name;
8844}
8845#endif
8846
8847#ifdef FEAT_GUI
8848/*
8849 * Free all the highlight group fonts.
8850 * Used when quitting for systems which need it.
8851 */
8852 void
8853free_highlight_fonts()
8854{
8855 int idx;
8856
8857 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8858 {
8859 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8860 HL_TABLE()[idx].sg_font = NOFONT;
8861# ifdef FEAT_XFONTSET
8862 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8863 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8864# endif
8865 }
8866
8867 gui_mch_free_font(gui.norm_font);
8868# ifdef FEAT_XFONTSET
8869 gui_mch_free_fontset(gui.fontset);
8870# endif
8871# ifndef HAVE_GTK2
8872 gui_mch_free_font(gui.bold_font);
8873 gui_mch_free_font(gui.ital_font);
8874 gui_mch_free_font(gui.boldital_font);
8875# endif
8876}
8877#endif
8878
8879/**************************************
8880 * End of Highlighting stuff *
8881 **************************************/