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