blob: c023486c3a6cbd90a54534e6a159e5a91c08f3f9 [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{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000310 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000311 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 */
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000554 if (syn_buf->b_sst_len <= Rows)
555 dist = 999999;
556 else
557 dist = syn_buf->b_ml.ml_line_count / (syn_buf->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000558 prev = syn_stack_find_entry(current_lnum);
559 while (current_lnum < lnum)
560 {
561 syn_start_line();
562 (void)syn_finish_line(FALSE);
563 ++current_lnum;
564
565 /* If we parsed at least "minlines" lines or started at a valid
566 * state, the current state is considered valid. */
567 if (current_lnum >= first_stored)
568 {
569 /* Check if the saved state entry is for the current line and is
570 * equal to the current state. If so, then validate all saved
571 * states that depended on a change before the parsed line. */
572 if (prev == NULL)
573 sp = syn_buf->b_sst_first;
574 else
575 sp = prev->sst_next;
576 if (sp != NULL
577 && sp->sst_lnum == current_lnum
578 && syn_stack_equal(sp))
579 {
580 parsed_lnum = current_lnum;
581 prev = sp;
582 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
583 {
584 if (sp->sst_lnum <= lnum)
585 /* valid state before desired line, use this one */
586 prev = sp;
587 else if (sp->sst_change_lnum == 0)
588 /* past saved states depending on change, break here. */
589 break;
590 sp->sst_change_lnum = 0;
591 sp = sp->sst_next;
592 }
593 load_current_state(prev);
594 }
595 /* Store the state at this line when it's the first one, the line
596 * where we start parsing, or some distance from the previously
597 * saved state. But only when parsed at least 'minlines'. */
598 else if (prev == NULL
599 || current_lnum == lnum
600 || current_lnum >= prev->sst_lnum + dist)
601 prev = store_current_state(prev);
602 }
603
604 /* This can take a long time: break when CTRL-C pressed. The current
605 * state will be wrong then. */
606 line_breakcheck();
607 if (got_int)
608 {
609 current_lnum = lnum;
610 break;
611 }
612 }
613
614 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000615}
616
617/*
618 * We cannot simply discard growarrays full of state_items or buf_states; we
619 * have to manually release their extmatch pointers first.
620 */
621 static void
622clear_syn_state(p)
623 synstate_T *p;
624{
625 int i;
626 garray_T *gap;
627
628 if (p->sst_stacksize > SST_FIX_STATES)
629 {
630 gap = &(p->sst_union.sst_ga);
631 for (i = 0; i < gap->ga_len; i++)
632 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
633 ga_clear(gap);
634 }
635 else
636 {
637 for (i = 0; i < p->sst_stacksize; i++)
638 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
639 }
640}
641
642/*
643 * Cleanup the current_state stack.
644 */
645 static void
646clear_current_state()
647{
648 int i;
649 stateitem_T *sip;
650
651 sip = (stateitem_T *)(current_state.ga_data);
652 for (i = 0; i < current_state.ga_len; i++)
653 unref_extmatch(sip[i].si_extmatch);
654 ga_clear(&current_state);
655}
656
657/*
658 * Try to find a synchronisation point for line "lnum".
659 *
660 * This sets current_lnum and the current state. One of three methods is
661 * used:
662 * 1. Search backwards for the end of a C-comment.
663 * 2. Search backwards for given sync patterns.
664 * 3. Simply start on a given number of lines above "lnum".
665 */
666 static void
667syn_sync(wp, start_lnum, last_valid)
668 win_T *wp;
669 linenr_T start_lnum;
670 synstate_T *last_valid;
671{
672 buf_T *curbuf_save;
673 win_T *curwin_save;
674 pos_T cursor_save;
675 int idx;
676 linenr_T lnum;
677 linenr_T end_lnum;
678 linenr_T break_lnum;
679 int had_sync_point;
680 stateitem_T *cur_si;
681 synpat_T *spp;
682 char_u *line;
683 int found_flags = 0;
684 int found_match_idx = 0;
685 linenr_T found_current_lnum = 0;
686 int found_current_col= 0;
687 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000688 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000689
690 /*
691 * Clear any current state that might be hanging around.
692 */
693 invalidate_current_state();
694
695 /*
696 * Start at least "minlines" back. Default starting point for parsing is
697 * there.
698 * Start further back, to avoid that scrolling backwards will result in
699 * resyncing for every line. Now it resyncs only one out of N lines,
700 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
701 * Watch out for overflow when minlines is MAXLNUM.
702 */
703 if (syn_buf->b_syn_sync_minlines > start_lnum)
704 start_lnum = 1;
705 else
706 {
707 if (syn_buf->b_syn_sync_minlines == 1)
708 lnum = 1;
709 else if (syn_buf->b_syn_sync_minlines < 10)
710 lnum = syn_buf->b_syn_sync_minlines * 2;
711 else
712 lnum = syn_buf->b_syn_sync_minlines * 3 / 2;
713 if (syn_buf->b_syn_sync_maxlines != 0
714 && lnum > syn_buf->b_syn_sync_maxlines)
715 lnum = syn_buf->b_syn_sync_maxlines;
716 if (lnum >= start_lnum)
717 start_lnum = 1;
718 else
719 start_lnum -= lnum;
720 }
721 current_lnum = start_lnum;
722
723 /*
724 * 1. Search backwards for the end of a C-style comment.
725 */
726 if (syn_buf->b_syn_sync_flags & SF_CCOMMENT)
727 {
728 /* Need to make syn_buf the current buffer for a moment, to be able to
729 * use find_start_comment(). */
730 curwin_save = curwin;
731 curwin = wp;
732 curbuf_save = curbuf;
733 curbuf = syn_buf;
734
735 /*
736 * Skip lines that end in a backslash.
737 */
738 for ( ; start_lnum > 1; --start_lnum)
739 {
740 line = ml_get(start_lnum - 1);
741 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
742 break;
743 }
744 current_lnum = start_lnum;
745
746 /* set cursor to start of search */
747 cursor_save = wp->w_cursor;
748 wp->w_cursor.lnum = start_lnum;
749 wp->w_cursor.col = 0;
750
751 /*
752 * If the line is inside a comment, need to find the syntax item that
753 * defines the comment.
754 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
755 */
756 if (find_start_comment((int)syn_buf->b_syn_sync_maxlines) != NULL)
757 {
758 for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
759 if (SYN_ITEMS(syn_buf)[idx].sp_syn.id == syn_buf->b_syn_sync_id
760 && SYN_ITEMS(syn_buf)[idx].sp_type == SPTYPE_START)
761 {
762 validate_current_state();
763 if (push_current_state(idx) == OK)
764 update_si_attr(current_state.ga_len - 1);
765 break;
766 }
767 }
768
769 /* restore cursor and buffer */
770 wp->w_cursor = cursor_save;
771 curwin = curwin_save;
772 curbuf = curbuf_save;
773 }
774
775 /*
776 * 2. Search backwards for given sync patterns.
777 */
778 else if (syn_buf->b_syn_sync_flags & SF_MATCH)
779 {
780 if (syn_buf->b_syn_sync_maxlines != 0
781 && start_lnum > syn_buf->b_syn_sync_maxlines)
782 break_lnum = start_lnum - syn_buf->b_syn_sync_maxlines;
783 else
784 break_lnum = 0;
785
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000786 found_m_endpos.lnum = 0;
787 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000788 end_lnum = start_lnum;
789 lnum = start_lnum;
790 while (--lnum > break_lnum)
791 {
792 /* This can take a long time: break when CTRL-C pressed. */
793 line_breakcheck();
794 if (got_int)
795 {
796 invalidate_current_state();
797 current_lnum = start_lnum;
798 break;
799 }
800
801 /* Check if we have run into a valid saved state stack now. */
802 if (last_valid != NULL && lnum == last_valid->sst_lnum)
803 {
804 load_current_state(last_valid);
805 break;
806 }
807
808 /*
809 * Check if the previous line has the line-continuation pattern.
810 */
811 if (lnum > 1 && syn_match_linecont(lnum - 1))
812 continue;
813
814 /*
815 * Start with nothing on the state stack
816 */
817 validate_current_state();
818
819 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
820 {
821 syn_start_line();
822 for (;;)
823 {
824 had_sync_point = syn_finish_line(TRUE);
825 /*
826 * When a sync point has been found, remember where, and
827 * continue to look for another one, further on in the line.
828 */
829 if (had_sync_point && current_state.ga_len)
830 {
831 cur_si = &CUR_STATE(current_state.ga_len - 1);
832 if (cur_si->si_m_endpos.lnum > start_lnum)
833 {
834 /* ignore match that goes to after where started */
835 current_lnum = end_lnum;
836 break;
837 }
838 spp = &(SYN_ITEMS(syn_buf)[cur_si->si_idx]);
839 found_flags = spp->sp_flags;
840 found_match_idx = spp->sp_sync_idx;
841 found_current_lnum = current_lnum;
842 found_current_col = current_col;
843 found_m_endpos = cur_si->si_m_endpos;
844 /*
845 * Continue after the match (be aware of a zero-length
846 * match).
847 */
848 if (found_m_endpos.lnum > current_lnum)
849 {
850 current_lnum = found_m_endpos.lnum;
851 current_col = found_m_endpos.col;
852 if (current_lnum >= end_lnum)
853 break;
854 }
855 else if (found_m_endpos.col > current_col)
856 current_col = found_m_endpos.col;
857 else
858 ++current_col;
859
860 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000861 * an item that ends here, need to do that now. Be
862 * careful not to go past the NUL. */
863 prev_current_col = current_col;
864 if (syn_getcurline()[current_col] != NUL)
865 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000866 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000867 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000868 }
869 else
870 break;
871 }
872 }
873
874 /*
875 * If a sync point was encountered, break here.
876 */
877 if (found_flags)
878 {
879 /*
880 * Put the item that was specified by the sync point on the
881 * state stack. If there was no item specified, make the
882 * state stack empty.
883 */
884 clear_current_state();
885 if (found_match_idx >= 0
886 && push_current_state(found_match_idx) == OK)
887 update_si_attr(current_state.ga_len - 1);
888
889 /*
890 * When using "grouphere", continue from the sync point
891 * match, until the end of the line. Parsing starts at
892 * the next line.
893 * For "groupthere" the parsing starts at start_lnum.
894 */
895 if (found_flags & HL_SYNC_HERE)
896 {
897 if (current_state.ga_len)
898 {
899 cur_si = &CUR_STATE(current_state.ga_len - 1);
900 cur_si->si_h_startpos.lnum = found_current_lnum;
901 cur_si->si_h_startpos.col = found_current_col;
902 update_si_end(cur_si, (int)current_col, TRUE);
903 check_keepend();
904 }
905 current_col = found_m_endpos.col;
906 current_lnum = found_m_endpos.lnum;
907 (void)syn_finish_line(FALSE);
908 ++current_lnum;
909 }
910 else
911 current_lnum = start_lnum;
912
913 break;
914 }
915
916 end_lnum = lnum;
917 invalidate_current_state();
918 }
919
920 /* Ran into start of the file or exceeded maximum number of lines */
921 if (lnum <= break_lnum)
922 {
923 invalidate_current_state();
924 current_lnum = break_lnum + 1;
925 }
926 }
927
928 validate_current_state();
929}
930
931/*
932 * Return TRUE if the line-continuation pattern matches in line "lnum".
933 */
934 static int
935syn_match_linecont(lnum)
936 linenr_T lnum;
937{
938 regmmatch_T regmatch;
939
940 if (syn_buf->b_syn_linecont_prog != NULL)
941 {
942 regmatch.rmm_ic = syn_buf->b_syn_linecont_ic;
943 regmatch.regprog = syn_buf->b_syn_linecont_prog;
944 return syn_regexec(&regmatch, lnum, (colnr_T)0);
945 }
946 return FALSE;
947}
948
949/*
950 * Prepare the current state for the start of a line.
951 */
952 static void
953syn_start_line()
954{
955 current_finished = FALSE;
956 current_col = 0;
957
958 /*
959 * Need to update the end of a start/skip/end that continues from the
960 * previous line and regions that have "keepend".
961 */
962 if (current_state.ga_len > 0)
963 syn_update_ends(TRUE);
964
965 next_match_idx = -1;
966 ++current_line_id;
967}
968
969/*
970 * Check for items in the stack that need their end updated.
971 * When "startofline" is TRUE the last item is always updated.
972 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
973 */
974 static void
975syn_update_ends(startofline)
976 int startofline;
977{
978 stateitem_T *cur_si;
979 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +0000980 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000981
982 if (startofline)
983 {
984 /* Check for a match carried over from a previous line with a
985 * contained region. The match ends as soon as the region ends. */
986 for (i = 0; i < current_state.ga_len; ++i)
987 {
988 cur_si = &CUR_STATE(i);
989 if (cur_si->si_idx >= 0
990 && (SYN_ITEMS(syn_buf)[cur_si->si_idx]).sp_type
991 == SPTYPE_MATCH
992 && cur_si->si_m_endpos.lnum < current_lnum)
993 {
994 cur_si->si_flags |= HL_MATCHCONT;
995 cur_si->si_m_endpos.lnum = 0;
996 cur_si->si_m_endpos.col = 0;
997 cur_si->si_h_endpos = cur_si->si_m_endpos;
998 cur_si->si_ends = TRUE;
999 }
1000 }
1001 }
1002
1003 /*
1004 * Need to update the end of a start/skip/end that continues from the
1005 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001006 * influence contained items. If we've just removed "extend"
1007 * (startofline == 0) then we should update ends of normal regions
1008 * contained inside "keepend" because "extend" could have extended
1009 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001010 * Then check for items ending in column 0.
1011 */
1012 i = current_state.ga_len - 1;
1013 if (keepend_level >= 0)
1014 for ( ; i > keepend_level; --i)
1015 if (CUR_STATE(i).si_flags & HL_EXTEND)
1016 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001017
1018 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001019 for ( ; i < current_state.ga_len; ++i)
1020 {
1021 cur_si = &CUR_STATE(i);
1022 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001023 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024 || (i == current_state.ga_len - 1 && startofline))
1025 {
1026 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1027 cur_si->si_h_startpos.lnum = current_lnum;
1028
1029 if (!(cur_si->si_flags & HL_MATCHCONT))
1030 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001031
1032 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1033 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001034 }
1035 }
1036 check_keepend();
1037 check_state_ends();
1038}
1039
1040/****************************************
1041 * Handling of the state stack cache.
1042 */
1043
1044/*
1045 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1046 *
1047 * To speed up syntax highlighting, the state stack for the start of some
1048 * lines is cached. These entries can be used to start parsing at that point.
1049 *
1050 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1051 * valid entries. b_sst_first points to the first one, then follow sst_next.
1052 * The entries are sorted on line number. The first entry is often for line 2
1053 * (line 1 always starts with an empty stack).
1054 * There is also a list for free entries. This construction is used to avoid
1055 * having to allocate and free memory blocks too often.
1056 *
1057 * When making changes to the buffer, this is logged in b_mod_*. When calling
1058 * update_screen() to update the display, it will call
1059 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1060 * entries. The entries which are inside the changed area are removed,
1061 * because they must be recomputed. Entries below the changed have their line
1062 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1063 * set to indicate that a check must be made if the changed lines would change
1064 * the cached entry.
1065 *
1066 * When later displaying lines, an entry is stored for each line. Displayed
1067 * lines are likely to be displayed again, in which case the state at the
1068 * start of the line is needed.
1069 * For not displayed lines, an entry is stored for every so many lines. These
1070 * entries will be used e.g., when scrolling backwards. The distance between
1071 * entries depends on the number of lines in the buffer. For small buffers
1072 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1073 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1074 */
1075
1076/*
1077 * Free b_sst_array[] for buffer "buf".
1078 * Used when syntax items changed to force resyncing everywhere.
1079 */
1080 void
1081syn_stack_free_all(buf)
1082 buf_T *buf;
1083{
1084 synstate_T *p;
1085 win_T *wp;
1086
1087 if (buf->b_sst_array != NULL)
1088 {
1089 for (p = buf->b_sst_first; p != NULL; p = p->sst_next)
1090 clear_syn_state(p);
1091 vim_free(buf->b_sst_array);
1092 buf->b_sst_array = NULL;
1093 buf->b_sst_len = 0;
1094 }
1095#ifdef FEAT_FOLDING
1096 /* When using "syntax" fold method, must update all folds. */
1097 FOR_ALL_WINDOWS(wp)
1098 {
1099 if (wp->w_buffer == buf && foldmethodIsSyntax(wp))
1100 foldUpdateAll(wp);
1101 }
1102#endif
1103}
1104
1105/*
1106 * Allocate the syntax state stack for syn_buf when needed.
1107 * If the number of entries in b_sst_array[] is much too big or a bit too
1108 * small, reallocate it.
1109 * Also used to allocate b_sst_array[] for the first time.
1110 */
1111 static void
1112syn_stack_alloc()
1113{
1114 long len;
1115 synstate_T *to, *from;
1116 synstate_T *sstp;
1117
1118 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1119 if (len < SST_MIN_ENTRIES)
1120 len = SST_MIN_ENTRIES;
1121 else if (len > SST_MAX_ENTRIES)
1122 len = SST_MAX_ENTRIES;
1123 if (syn_buf->b_sst_len > len * 2 || syn_buf->b_sst_len < len)
1124 {
1125 /* Allocate 50% too much, to avoid reallocating too often. */
1126 len = syn_buf->b_ml.ml_line_count;
1127 len = (len + len / 2) / SST_DIST + Rows * 2;
1128 if (len < SST_MIN_ENTRIES)
1129 len = SST_MIN_ENTRIES;
1130 else if (len > SST_MAX_ENTRIES)
1131 len = SST_MAX_ENTRIES;
1132
1133 if (syn_buf->b_sst_array != NULL)
1134 {
1135 /* When shrinking the array, cleanup the existing stack.
1136 * Make sure that all valid entries fit in the new array. */
1137 while (syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2 > len
1138 && syn_stack_cleanup())
1139 ;
1140 if (len < syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2)
1141 len = syn_buf->b_sst_len - syn_buf->b_sst_freecount + 2;
1142 }
1143
1144 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1145 if (sstp == NULL) /* out of memory! */
1146 return;
1147
1148 to = sstp - 1;
1149 if (syn_buf->b_sst_array != NULL)
1150 {
1151 /* Move the states from the old array to the new one. */
1152 for (from = syn_buf->b_sst_first; from != NULL;
1153 from = from->sst_next)
1154 {
1155 ++to;
1156 *to = *from;
1157 to->sst_next = to + 1;
1158 }
1159 }
1160 if (to != sstp - 1)
1161 {
1162 to->sst_next = NULL;
1163 syn_buf->b_sst_first = sstp;
1164 syn_buf->b_sst_freecount = len - (int)(to - sstp) - 1;
1165 }
1166 else
1167 {
1168 syn_buf->b_sst_first = NULL;
1169 syn_buf->b_sst_freecount = len;
1170 }
1171
1172 /* Create the list of free entries. */
1173 syn_buf->b_sst_firstfree = to + 1;
1174 while (++to < sstp + len)
1175 to->sst_next = to + 1;
1176 (sstp + len - 1)->sst_next = NULL;
1177
1178 vim_free(syn_buf->b_sst_array);
1179 syn_buf->b_sst_array = sstp;
1180 syn_buf->b_sst_len = len;
1181 }
1182}
1183
1184/*
1185 * Check for changes in a buffer to affect stored syntax states. Uses the
1186 * b_mod_* fields.
1187 * Called from update_screen(), before screen is being updated, once for each
1188 * displayed buffer.
1189 */
1190 void
1191syn_stack_apply_changes(buf)
1192 buf_T *buf;
1193{
1194 synstate_T *p, *prev, *np;
1195 linenr_T n;
1196
1197 if (buf->b_sst_array == NULL) /* nothing to do */
1198 return;
1199
1200 prev = NULL;
1201 for (p = buf->b_sst_first; p != NULL; )
1202 {
Bram Moolenaar1f8a5f02005-07-01 22:41:52 +00001203 if (p->sst_lnum + buf->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001204 {
1205 n = p->sst_lnum + buf->b_mod_xlines;
1206 if (n <= buf->b_mod_bot)
1207 {
1208 /* this state is inside the changed area, remove it */
1209 np = p->sst_next;
1210 if (prev == NULL)
1211 buf->b_sst_first = np;
1212 else
1213 prev->sst_next = np;
1214 syn_stack_free_entry(buf, p);
1215 p = np;
1216 continue;
1217 }
1218 /* This state is below the changed area. Remember the line
1219 * that needs to be parsed before this entry can be made valid
1220 * again. */
1221 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1222 {
1223 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1224 p->sst_change_lnum += buf->b_mod_xlines;
1225 else
1226 p->sst_change_lnum = buf->b_mod_top;
1227 }
1228 if (p->sst_change_lnum == 0
1229 || p->sst_change_lnum < buf->b_mod_bot)
1230 p->sst_change_lnum = buf->b_mod_bot;
1231
1232 p->sst_lnum = n;
1233 }
1234 prev = p;
1235 p = p->sst_next;
1236 }
1237}
1238
1239/*
1240 * Reduce the number of entries in the state stack for syn_buf.
1241 * Returns TRUE if at least one entry was freed.
1242 */
1243 static int
1244syn_stack_cleanup()
1245{
1246 synstate_T *p, *prev;
1247 disptick_T tick;
1248 int above;
1249 int dist;
1250 int retval = FALSE;
1251
1252 if (syn_buf->b_sst_array == NULL || syn_buf->b_sst_first == NULL)
1253 return retval;
1254
1255 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001256 if (syn_buf->b_sst_len <= Rows)
1257 dist = 999999;
1258 else
1259 dist = syn_buf->b_ml.ml_line_count / (syn_buf->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001260
1261 /*
1262 * Go throught the list to find the "tick" for the oldest entry that can
1263 * be removed. Set "above" when the "tick" for the oldest entry is above
1264 * "b_sst_lasttick" (the display tick wraps around).
1265 */
1266 tick = syn_buf->b_sst_lasttick;
1267 above = FALSE;
1268 prev = syn_buf->b_sst_first;
1269 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1270 {
1271 if (prev->sst_lnum + dist > p->sst_lnum)
1272 {
1273 if (p->sst_tick > syn_buf->b_sst_lasttick)
1274 {
1275 if (!above || p->sst_tick < tick)
1276 tick = p->sst_tick;
1277 above = TRUE;
1278 }
1279 else if (!above && p->sst_tick < tick)
1280 tick = p->sst_tick;
1281 }
1282 }
1283
1284 /*
1285 * Go through the list to make the entries for the oldest tick at an
1286 * interval of several lines.
1287 */
1288 prev = syn_buf->b_sst_first;
1289 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1290 {
1291 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1292 {
1293 /* Move this entry from used list to free list */
1294 prev->sst_next = p->sst_next;
1295 syn_stack_free_entry(syn_buf, p);
1296 p = prev;
1297 retval = TRUE;
1298 }
1299 }
1300 return retval;
1301}
1302
1303/*
1304 * Free the allocated memory for a syn_state item.
1305 * Move the entry into the free list.
1306 */
1307 static void
1308syn_stack_free_entry(buf, p)
1309 buf_T *buf;
1310 synstate_T *p;
1311{
1312 clear_syn_state(p);
1313 p->sst_next = buf->b_sst_firstfree;
1314 buf->b_sst_firstfree = p;
1315 ++buf->b_sst_freecount;
1316}
1317
1318/*
1319 * Find an entry in the list of state stacks at or before "lnum".
1320 * Returns NULL when there is no entry or the first entry is after "lnum".
1321 */
1322 static synstate_T *
1323syn_stack_find_entry(lnum)
1324 linenr_T lnum;
1325{
1326 synstate_T *p, *prev;
1327
1328 prev = NULL;
1329 for (p = syn_buf->b_sst_first; p != NULL; prev = p, p = p->sst_next)
1330 {
1331 if (p->sst_lnum == lnum)
1332 return p;
1333 if (p->sst_lnum > lnum)
1334 break;
1335 }
1336 return prev;
1337}
1338
1339/*
1340 * Try saving the current state in b_sst_array[].
1341 * The current state must be valid for the start of the current_lnum line!
1342 */
1343 static synstate_T *
1344store_current_state(sp)
1345 synstate_T *sp; /* at or before where state is to be saved or
1346 NULL */
1347{
1348 int i;
1349 synstate_T *p;
1350 bufstate_T *bp;
1351 stateitem_T *cur_si;
1352
1353 if (sp == NULL)
1354 sp = syn_stack_find_entry(current_lnum);
1355
1356 /*
1357 * If the current state contains a start or end pattern that continues
1358 * from the previous line, we can't use it. Don't store it then.
1359 */
1360 for (i = current_state.ga_len - 1; i >= 0; --i)
1361 {
1362 cur_si = &CUR_STATE(i);
1363 if (cur_si->si_h_startpos.lnum >= current_lnum
1364 || cur_si->si_m_endpos.lnum >= current_lnum
1365 || cur_si->si_h_endpos.lnum >= current_lnum
1366 || (cur_si->si_end_idx
1367 && cur_si->si_eoe_pos.lnum >= current_lnum))
1368 break;
1369 }
1370 if (i >= 0)
1371 {
1372 if (sp != NULL)
1373 {
1374 /* find "sp" in the list and remove it */
1375 if (syn_buf->b_sst_first == sp)
1376 /* it's the first entry */
1377 syn_buf->b_sst_first = sp->sst_next;
1378 else
1379 {
1380 /* find the entry just before this one to adjust sst_next */
1381 for (p = syn_buf->b_sst_first; p != NULL; p = p->sst_next)
1382 if (p->sst_next == sp)
1383 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001384 if (p != NULL) /* just in case */
1385 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001386 }
1387 syn_stack_free_entry(syn_buf, sp);
1388 sp = NULL;
1389 }
1390 }
1391 else if (sp == NULL || sp->sst_lnum != current_lnum)
1392 {
1393 /*
1394 * Add a new entry
1395 */
1396 /* If no free items, cleanup the array first. */
1397 if (syn_buf->b_sst_freecount == 0)
1398 {
1399 (void)syn_stack_cleanup();
1400 /* "sp" may have been moved to the freelist now */
1401 sp = syn_stack_find_entry(current_lnum);
1402 }
1403 /* Still no free items? Must be a strange problem... */
1404 if (syn_buf->b_sst_freecount == 0)
1405 sp = NULL;
1406 else
1407 {
1408 /* Take the first item from the free list and put it in the used
1409 * list, after *sp */
1410 p = syn_buf->b_sst_firstfree;
1411 syn_buf->b_sst_firstfree = p->sst_next;
1412 --syn_buf->b_sst_freecount;
1413 if (sp == NULL)
1414 {
1415 /* Insert in front of the list */
1416 p->sst_next = syn_buf->b_sst_first;
1417 syn_buf->b_sst_first = p;
1418 }
1419 else
1420 {
1421 /* insert in list after *sp */
1422 p->sst_next = sp->sst_next;
1423 sp->sst_next = p;
1424 }
1425 sp = p;
1426 sp->sst_stacksize = 0;
1427 sp->sst_lnum = current_lnum;
1428 }
1429 }
1430 if (sp != NULL)
1431 {
1432 /* When overwriting an existing state stack, clear it first */
1433 clear_syn_state(sp);
1434 sp->sst_stacksize = current_state.ga_len;
1435 if (current_state.ga_len > SST_FIX_STATES)
1436 {
1437 /* Need to clear it, might be something remaining from when the
1438 * length was less than SST_FIX_STATES. */
1439 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1440 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1441 sp->sst_stacksize = 0;
1442 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001443 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001444 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1445 }
1446 else
1447 bp = sp->sst_union.sst_stack;
1448 for (i = 0; i < sp->sst_stacksize; ++i)
1449 {
1450 bp[i].bs_idx = CUR_STATE(i).si_idx;
1451 bp[i].bs_flags = CUR_STATE(i).si_flags;
1452 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1453 }
1454 sp->sst_next_flags = current_next_flags;
1455 sp->sst_next_list = current_next_list;
1456 sp->sst_tick = display_tick;
1457 sp->sst_change_lnum = 0;
1458 }
1459 current_state_stored = TRUE;
1460 return sp;
1461}
1462
1463/*
1464 * Copy a state stack from "from" in b_sst_array[] to current_state;
1465 */
1466 static void
1467load_current_state(from)
1468 synstate_T *from;
1469{
1470 int i;
1471 bufstate_T *bp;
1472
1473 clear_current_state();
1474 validate_current_state();
1475 keepend_level = -1;
1476 if (from->sst_stacksize
1477 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1478 {
1479 if (from->sst_stacksize > SST_FIX_STATES)
1480 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1481 else
1482 bp = from->sst_union.sst_stack;
1483 for (i = 0; i < from->sst_stacksize; ++i)
1484 {
1485 CUR_STATE(i).si_idx = bp[i].bs_idx;
1486 CUR_STATE(i).si_flags = bp[i].bs_flags;
1487 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1488 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1489 keepend_level = i;
1490 CUR_STATE(i).si_ends = FALSE;
1491 CUR_STATE(i).si_m_lnum = 0;
1492 if (CUR_STATE(i).si_idx >= 0)
1493 CUR_STATE(i).si_next_list =
1494 (SYN_ITEMS(syn_buf)[CUR_STATE(i).si_idx]).sp_next_list;
1495 else
1496 CUR_STATE(i).si_next_list = NULL;
1497 update_si_attr(i);
1498 }
1499 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001500 }
1501 current_next_list = from->sst_next_list;
1502 current_next_flags = from->sst_next_flags;
1503 current_lnum = from->sst_lnum;
1504}
1505
1506/*
1507 * Compare saved state stack "*sp" with the current state.
1508 * Return TRUE when they are equal.
1509 */
1510 static int
1511syn_stack_equal(sp)
1512 synstate_T *sp;
1513{
1514 int i, j;
1515 bufstate_T *bp;
1516 reg_extmatch_T *six, *bsx;
1517
1518 /* First a quick check if the stacks have the same size end nextlist. */
1519 if (sp->sst_stacksize == current_state.ga_len
1520 && sp->sst_next_list == current_next_list)
1521 {
1522 /* Need to compare all states on both stacks. */
1523 if (sp->sst_stacksize > SST_FIX_STATES)
1524 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1525 else
1526 bp = sp->sst_union.sst_stack;
1527
1528 for (i = current_state.ga_len; --i >= 0; )
1529 {
1530 /* If the item has another index the state is different. */
1531 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1532 break;
1533 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1534 {
1535 /* When the extmatch pointers are different, the strings in
1536 * them can still be the same. Check if the extmatch
1537 * references are equal. */
1538 bsx = bp[i].bs_extmatch;
1539 six = CUR_STATE(i).si_extmatch;
1540 /* If one of the extmatch pointers is NULL the states are
1541 * different. */
1542 if (bsx == NULL || six == NULL)
1543 break;
1544 for (j = 0; j < NSUBEXP; ++j)
1545 {
1546 /* Check each referenced match string. They must all be
1547 * equal. */
1548 if (bsx->matches[j] != six->matches[j])
1549 {
1550 /* If the pointer is different it can still be the
1551 * same text. Compare the strings, ignore case when
1552 * the start item has the sp_ic flag set. */
1553 if (bsx->matches[j] == NULL
1554 || six->matches[j] == NULL)
1555 break;
1556 if ((SYN_ITEMS(syn_buf)[CUR_STATE(i).si_idx]).sp_ic
1557 ? MB_STRICMP(bsx->matches[j],
1558 six->matches[j]) != 0
1559 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1560 break;
1561 }
1562 }
1563 if (j != NSUBEXP)
1564 break;
1565 }
1566 }
1567 if (i < 0)
1568 return TRUE;
1569 }
1570 return FALSE;
1571}
1572
1573/*
1574 * We stop parsing syntax above line "lnum". If the stored state at or below
1575 * this line depended on a change before it, it now depends on the line below
1576 * the last parsed line.
1577 * The window looks like this:
1578 * line which changed
1579 * displayed line
1580 * displayed line
1581 * lnum -> line below window
1582 */
1583 void
1584syntax_end_parsing(lnum)
1585 linenr_T lnum;
1586{
1587 synstate_T *sp;
1588
1589 sp = syn_stack_find_entry(lnum);
1590 if (sp != NULL && sp->sst_lnum < lnum)
1591 sp = sp->sst_next;
1592
1593 if (sp != NULL && sp->sst_change_lnum != 0)
1594 sp->sst_change_lnum = lnum;
1595}
1596
1597/*
1598 * End of handling of the state stack.
1599 ****************************************/
1600
1601 static void
1602invalidate_current_state()
1603{
1604 clear_current_state();
1605 current_state.ga_itemsize = 0; /* mark current_state invalid */
1606 current_next_list = NULL;
1607 keepend_level = -1;
1608}
1609
1610 static void
1611validate_current_state()
1612{
1613 current_state.ga_itemsize = sizeof(stateitem_T);
1614 current_state.ga_growsize = 3;
1615}
1616
1617/*
1618 * Return TRUE if the syntax at start of lnum changed since last time.
1619 * This will only be called just after get_syntax_attr() for the previous
1620 * line, to check if the next line needs to be redrawn too.
1621 */
1622 int
1623syntax_check_changed(lnum)
1624 linenr_T lnum;
1625{
1626 int retval = TRUE;
1627 synstate_T *sp;
1628
Bram Moolenaar071d4272004-06-13 20:20:40 +00001629 /*
1630 * Check the state stack when:
1631 * - lnum is just below the previously syntaxed line.
1632 * - lnum is not before the lines with saved states.
1633 * - lnum is not past the lines with saved states.
1634 * - lnum is at or before the last changed line.
1635 */
1636 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1637 {
1638 sp = syn_stack_find_entry(lnum);
1639 if (sp != NULL && sp->sst_lnum == lnum)
1640 {
1641 /*
1642 * finish the previous line (needed when not all of the line was
1643 * drawn)
1644 */
1645 (void)syn_finish_line(FALSE);
1646
1647 /*
1648 * Compare the current state with the previously saved state of
1649 * the line.
1650 */
1651 if (syn_stack_equal(sp))
1652 retval = FALSE;
1653
1654 /*
1655 * Store the current state in b_sst_array[] for later use.
1656 */
1657 ++current_lnum;
1658 (void)store_current_state(NULL);
1659 }
1660 }
1661
Bram Moolenaar071d4272004-06-13 20:20:40 +00001662 return retval;
1663}
1664
1665/*
1666 * Finish the current line.
1667 * This doesn't return any attributes, it only gets the state at the end of
1668 * the line. It can start anywhere in the line, as long as the current state
1669 * is valid.
1670 */
1671 static int
1672syn_finish_line(syncing)
1673 int syncing; /* called for syncing */
1674{
1675 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001676 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001677
1678 if (!current_finished)
1679 {
1680 while (!current_finished)
1681 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00001682 (void)syn_current_attr(syncing, FALSE, NULL);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001683 /*
1684 * When syncing, and found some item, need to check the item.
1685 */
1686 if (syncing && current_state.ga_len)
1687 {
1688 /*
1689 * Check for match with sync item.
1690 */
1691 cur_si = &CUR_STATE(current_state.ga_len - 1);
1692 if (cur_si->si_idx >= 0
1693 && (SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags
1694 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1695 return TRUE;
1696
1697 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001698 * that ends here, need to do that now. Be careful not to go
1699 * past the NUL. */
1700 prev_current_col = current_col;
1701 if (syn_getcurline()[current_col] != NUL)
1702 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001703 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001704 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001705 }
1706 ++current_col;
1707 }
1708 }
1709 return FALSE;
1710}
1711
1712/*
1713 * Return highlight attributes for next character.
1714 * Must first call syntax_start() once for the line.
1715 * "col" is normally 0 for the first use in a line, and increments by one each
1716 * time. It's allowed to skip characters and to stop before the end of the
1717 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001718 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1719 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720 */
1721 int
Bram Moolenaar217ad922005-03-20 22:37:15 +00001722get_syntax_attr(col, can_spell)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001723 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001724 int *can_spell;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001725{
1726 int attr = 0;
1727
1728 /* check for out of memory situation */
1729 if (syn_buf->b_sst_array == NULL)
1730 return 0;
1731
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001732 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001733 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001734 {
1735 clear_current_state();
1736#ifdef FEAT_EVAL
1737 current_id = 0;
1738 current_trans_id = 0;
1739#endif
1740 return 0;
1741 }
1742
Bram Moolenaar071d4272004-06-13 20:20:40 +00001743 /* Make sure current_state is valid */
1744 if (INVALID_STATE(&current_state))
1745 validate_current_state();
1746
1747 /*
1748 * Skip from the current column to "col", get the attributes for "col".
1749 */
1750 while (current_col <= col)
1751 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00001752 attr = syn_current_attr(FALSE, TRUE, can_spell);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 ++current_col;
1754 }
1755
Bram Moolenaar071d4272004-06-13 20:20:40 +00001756 return attr;
1757}
1758
1759/*
1760 * Get syntax attributes for current_lnum, current_col.
1761 */
1762 static int
Bram Moolenaar217ad922005-03-20 22:37:15 +00001763syn_current_attr(syncing, displaying, can_spell)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001764 int syncing; /* When 1: called for syncing */
1765 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001766 int *can_spell; /* return: do spell checking */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001767{
1768 int syn_id;
1769 lpos_T endpos; /* was: char_u *endp; */
1770 lpos_T hl_startpos; /* was: int hl_startcol; */
1771 lpos_T hl_endpos;
1772 lpos_T eos_pos; /* end-of-start match (start region) */
1773 lpos_T eoe_pos; /* end-of-end pattern */
1774 int end_idx; /* group ID for end pattern */
1775 int idx;
1776 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001777 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001778 int startcol;
1779 int endcol;
1780 long flags;
1781 short *next_list;
1782 int found_match; /* found usable match */
1783 static int try_next_column = FALSE; /* must try in next col */
1784 int do_keywords;
1785 regmmatch_T regmatch;
1786 lpos_T pos;
1787 int lc_col;
1788 reg_extmatch_T *cur_extmatch = NULL;
1789 char_u *line; /* current line. NOTE: becomes invalid after
1790 looking for a pattern match! */
1791
1792 /* variables for zero-width matches that have a "nextgroup" argument */
1793 int keep_next_list;
1794 int zero_width_next_list = FALSE;
1795 garray_T zero_width_next_ga;
1796
1797 /*
1798 * No character, no attributes! Past end of line?
1799 * Do try matching with an empty line (could be the start of a region).
1800 */
1801 line = syn_getcurline();
1802 if (line[current_col] == NUL && current_col != 0)
1803 {
1804 /*
1805 * If we found a match after the last column, use it.
1806 */
1807 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1808 && next_match_col != MAXCOL)
1809 (void)push_next_match(NULL);
1810
1811 current_finished = TRUE;
1812 current_state_stored = FALSE;
1813 return 0;
1814 }
1815
1816 /* if the current or next character is NUL, we will finish the line now */
1817 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1818 {
1819 current_finished = TRUE;
1820 current_state_stored = FALSE;
1821 }
1822
1823 /*
1824 * When in the previous column there was a match but it could not be used
1825 * (empty match or already matched in this column) need to try again in
1826 * the next column.
1827 */
1828 if (try_next_column)
1829 {
1830 next_match_idx = -1;
1831 try_next_column = FALSE;
1832 }
1833
1834 /* Only check for keywords when not syncing and there are some. */
1835 do_keywords = !syncing
Bram Moolenaardad6b692005-01-25 22:14:34 +00001836 && (syn_buf->b_keywtab.ht_used > 0
1837 || syn_buf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001838
1839 /* Init the list of zero-width matches with a nextlist. This is used to
1840 * avoid matching the same item in the same position twice. */
1841 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1842
1843 /*
1844 * Repeat matching keywords and patterns, to find contained items at the
1845 * same column. This stops when there are no extra matches at the current
1846 * column.
1847 */
1848 do
1849 {
1850 found_match = FALSE;
1851 keep_next_list = FALSE;
1852 syn_id = 0;
1853
1854 /*
1855 * 1. Check for a current state.
1856 * Only when there is no current state, or if the current state may
1857 * contain other things, we need to check for keywords and patterns.
1858 * Always need to check for contained items if some item has the
1859 * "containedin" argument (takes extra time!).
1860 */
1861 if (current_state.ga_len)
1862 cur_si = &CUR_STATE(current_state.ga_len - 1);
1863 else
1864 cur_si = NULL;
1865
1866 if (syn_buf->b_syn_containedin || cur_si == NULL
1867 || cur_si->si_cont_list != NULL)
1868 {
1869 /*
1870 * 2. Check for keywords, if on a keyword char after a non-keyword
1871 * char. Don't do this when syncing.
1872 */
1873 if (do_keywords)
1874 {
1875 line = syn_getcurline();
1876 if (vim_iswordc_buf(line + current_col, syn_buf)
1877 && (current_col == 0
1878 || !vim_iswordc_buf(line + current_col - 1
1879#ifdef FEAT_MBYTE
1880 - (has_mbyte
1881 ? (*mb_head_off)(line, line + current_col - 1)
1882 : 0)
1883#endif
1884 , syn_buf)))
1885 {
1886 syn_id = check_keyword_id(line, (int)current_col,
1887 &endcol, &flags, &next_list, cur_si);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001888 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001889 {
1890 if (push_current_state(KEYWORD_IDX) == OK)
1891 {
1892 cur_si = &CUR_STATE(current_state.ga_len - 1);
1893 cur_si->si_m_startcol = current_col;
1894 cur_si->si_h_startpos.lnum = current_lnum;
1895 cur_si->si_h_startpos.col = 0; /* starts right away */
1896 cur_si->si_m_endpos.lnum = current_lnum;
1897 cur_si->si_m_endpos.col = endcol;
1898 cur_si->si_h_endpos.lnum = current_lnum;
1899 cur_si->si_h_endpos.col = endcol;
1900 cur_si->si_ends = TRUE;
1901 cur_si->si_end_idx = 0;
1902 cur_si->si_flags = flags;
1903 cur_si->si_id = syn_id;
1904 cur_si->si_trans_id = syn_id;
1905 if (flags & HL_TRANSP)
1906 {
1907 if (current_state.ga_len < 2)
1908 {
1909 cur_si->si_attr = 0;
1910 cur_si->si_trans_id = 0;
1911 }
1912 else
1913 {
1914 cur_si->si_attr = CUR_STATE(
1915 current_state.ga_len - 2).si_attr;
1916 cur_si->si_trans_id = CUR_STATE(
1917 current_state.ga_len - 2).si_trans_id;
1918 }
1919 }
1920 else
1921 cur_si->si_attr = syn_id2attr(syn_id);
1922 cur_si->si_cont_list = NULL;
1923 cur_si->si_next_list = next_list;
1924 check_keepend();
1925 }
1926 else
1927 vim_free(next_list);
1928 }
1929 }
1930 }
1931
1932 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001933 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00001934 */
1935 if (syn_id == 0 && syn_buf->b_syn_patterns.ga_len)
1936 {
1937 /*
1938 * If we didn't check for a match yet, or we are past it, check
1939 * for any match with a pattern.
1940 */
1941 if (next_match_idx < 0 || next_match_col < (int)current_col)
1942 {
1943 /*
1944 * Check all relevant patterns for a match at this
1945 * position. This is complicated, because matching with a
1946 * pattern takes quite a bit of time, thus we want to
1947 * avoid doing it when it's not needed.
1948 */
1949 next_match_idx = 0; /* no match in this line yet */
1950 next_match_col = MAXCOL;
1951 for (idx = syn_buf->b_syn_patterns.ga_len; --idx >= 0; )
1952 {
1953 spp = &(SYN_ITEMS(syn_buf)[idx]);
1954 if ( spp->sp_syncing == syncing
1955 && (displaying || !(spp->sp_flags & HL_DISPLAY))
1956 && (spp->sp_type == SPTYPE_MATCH
1957 || spp->sp_type == SPTYPE_START)
1958 && (current_next_list != NULL
1959 ? in_id_list(NULL, current_next_list,
1960 &spp->sp_syn, 0)
1961 : (cur_si == NULL
1962 ? !(spp->sp_flags & HL_CONTAINED)
1963 : in_id_list(cur_si,
1964 cur_si->si_cont_list, &spp->sp_syn,
1965 spp->sp_flags & HL_CONTAINED))))
1966 {
1967 /* If we already tried matching in this line, and
1968 * there isn't a match before next_match_col, skip
1969 * this item. */
1970 if (spp->sp_line_id == current_line_id
1971 && spp->sp_startcol >= next_match_col)
1972 continue;
1973 spp->sp_line_id = current_line_id;
1974
1975 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
1976 if (lc_col < 0)
1977 lc_col = 0;
1978
1979 regmatch.rmm_ic = spp->sp_ic;
1980 regmatch.regprog = spp->sp_prog;
1981 if (!syn_regexec(&regmatch, current_lnum,
1982 (colnr_T)lc_col))
1983 {
1984 /* no match in this line, try another one */
1985 spp->sp_startcol = MAXCOL;
1986 continue;
1987 }
1988
1989 /*
1990 * Compute the first column of the match.
1991 */
1992 syn_add_start_off(&pos, &regmatch,
1993 spp, SPO_MS_OFF, -1);
1994 if (pos.lnum > current_lnum)
1995 {
1996 /* must have used end of match in a next line,
1997 * we can't handle that */
1998 spp->sp_startcol = MAXCOL;
1999 continue;
2000 }
2001 startcol = pos.col;
2002
2003 /* remember the next column where this pattern
2004 * matches in the current line */
2005 spp->sp_startcol = startcol;
2006
2007 /*
2008 * If a previously found match starts at a lower
2009 * column number, don't use this one.
2010 */
2011 if (startcol >= next_match_col)
2012 continue;
2013
2014 /*
2015 * If we matched this pattern at this position
2016 * before, skip it. Must retry in the next
2017 * column, because it may match from there.
2018 */
2019 if (did_match_already(idx, &zero_width_next_ga))
2020 {
2021 try_next_column = TRUE;
2022 continue;
2023 }
2024
2025 endpos.lnum = regmatch.endpos[0].lnum;
2026 endpos.col = regmatch.endpos[0].col;
2027
2028 /* Compute the highlight start. */
2029 syn_add_start_off(&hl_startpos, &regmatch,
2030 spp, SPO_HS_OFF, -1);
2031
2032 /* Compute the region start. */
2033 /* Default is to use the end of the match. */
2034 syn_add_end_off(&eos_pos, &regmatch,
2035 spp, SPO_RS_OFF, 0);
2036
2037 /*
2038 * Grab the external submatches before they get
2039 * overwritten. Reference count doesn't change.
2040 */
2041 unref_extmatch(cur_extmatch);
2042 cur_extmatch = re_extmatch_out;
2043 re_extmatch_out = NULL;
2044
2045 flags = 0;
2046 eoe_pos.lnum = 0; /* avoid warning */
2047 eoe_pos.col = 0;
2048 end_idx = 0;
2049 hl_endpos.lnum = 0;
2050
2051 /*
2052 * For a "oneline" the end must be found in the
2053 * same line too. Search for it after the end of
2054 * the match with the start pattern. Set the
2055 * resulting end positions at the same time.
2056 */
2057 if (spp->sp_type == SPTYPE_START
2058 && (spp->sp_flags & HL_ONELINE))
2059 {
2060 lpos_T startpos;
2061
2062 startpos = endpos;
2063 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2064 &flags, &eoe_pos, &end_idx, cur_extmatch);
2065 if (endpos.lnum == 0)
2066 continue; /* not found */
2067 }
2068
2069 /*
2070 * For a "match" the size must be > 0 after the
2071 * end offset needs has been added. Except when
2072 * syncing.
2073 */
2074 else if (spp->sp_type == SPTYPE_MATCH)
2075 {
2076 syn_add_end_off(&hl_endpos, &regmatch, spp,
2077 SPO_HE_OFF, 0);
2078 syn_add_end_off(&endpos, &regmatch, spp,
2079 SPO_ME_OFF, 0);
2080 if (endpos.lnum == current_lnum
2081 && (int)endpos.col + syncing < startcol)
2082 {
2083 /*
2084 * If an empty string is matched, may need
2085 * to try matching again at next column.
2086 */
2087 if (regmatch.startpos[0].col
2088 == regmatch.endpos[0].col)
2089 try_next_column = TRUE;
2090 continue;
2091 }
2092 }
2093
2094 /*
2095 * keep the best match so far in next_match_*
2096 */
2097 /* Highlighting must start after startpos and end
2098 * before endpos. */
2099 if (hl_startpos.lnum == current_lnum
2100 && (int)hl_startpos.col < startcol)
2101 hl_startpos.col = startcol;
2102 limit_pos_zero(&hl_endpos, &endpos);
2103
2104 next_match_idx = idx;
2105 next_match_col = startcol;
2106 next_match_m_endpos = endpos;
2107 next_match_h_endpos = hl_endpos;
2108 next_match_h_startpos = hl_startpos;
2109 next_match_flags = flags;
2110 next_match_eos_pos = eos_pos;
2111 next_match_eoe_pos = eoe_pos;
2112 next_match_end_idx = end_idx;
2113 unref_extmatch(next_match_extmatch);
2114 next_match_extmatch = cur_extmatch;
2115 cur_extmatch = NULL;
2116 }
2117 }
2118 }
2119
2120 /*
2121 * If we found a match at the current column, use it.
2122 */
2123 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2124 {
2125 synpat_T *lspp;
2126
2127 /* When a zero-width item matched which has a nextgroup,
2128 * don't push the item but set nextgroup. */
2129 lspp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2130 if (next_match_m_endpos.lnum == current_lnum
2131 && next_match_m_endpos.col == current_col
2132 && lspp->sp_next_list != NULL)
2133 {
2134 current_next_list = lspp->sp_next_list;
2135 current_next_flags = lspp->sp_flags;
2136 keep_next_list = TRUE;
2137 zero_width_next_list = TRUE;
2138
2139 /* Add the index to a list, so that we can check
2140 * later that we don't match it again (and cause an
2141 * endless loop). */
2142 if (ga_grow(&zero_width_next_ga, 1) == OK)
2143 {
2144 ((int *)(zero_width_next_ga.ga_data))
2145 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002146 }
2147 next_match_idx = -1;
2148 }
2149 else
2150 cur_si = push_next_match(cur_si);
2151 found_match = TRUE;
2152 }
2153 }
2154 }
2155
2156 /*
2157 * Handle searching for nextgroup match.
2158 */
2159 if (current_next_list != NULL && !keep_next_list)
2160 {
2161 /*
2162 * If a nextgroup was not found, continue looking for one if:
2163 * - this is an empty line and the "skipempty" option was given
2164 * - we are on white space and the "skipwhite" option was given
2165 */
2166 if (!found_match)
2167 {
2168 line = syn_getcurline();
2169 if (((current_next_flags & HL_SKIPWHITE)
2170 && vim_iswhite(line[current_col]))
2171 || ((current_next_flags & HL_SKIPEMPTY)
2172 && *line == NUL))
2173 break;
2174 }
2175
2176 /*
2177 * If a nextgroup was found: Use it, and continue looking for
2178 * contained matches.
2179 * If a nextgroup was not found: Continue looking for a normal
2180 * match.
2181 * When did set current_next_list for a zero-width item and no
2182 * match was found don't loop (would get stuck).
2183 */
2184 current_next_list = NULL;
2185 next_match_idx = -1;
2186 if (!zero_width_next_list)
2187 found_match = TRUE;
2188 }
2189
2190 } while (found_match);
2191
2192 /*
2193 * Use attributes from the current state, if within its highlighting.
2194 * If not, use attributes from the current-but-one state, etc.
2195 */
2196 current_attr = 0;
2197#ifdef FEAT_EVAL
2198 current_id = 0;
2199 current_trans_id = 0;
2200#endif
2201 if (cur_si != NULL)
2202 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002203#ifndef FEAT_EVAL
2204 int current_trans_id = 0;
2205#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002206 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2207 {
2208 sip = &CUR_STATE(idx);
2209 if ((current_lnum > sip->si_h_startpos.lnum
2210 || (current_lnum == sip->si_h_startpos.lnum
2211 && current_col >= sip->si_h_startpos.col))
2212 && (sip->si_h_endpos.lnum == 0
2213 || current_lnum < sip->si_h_endpos.lnum
2214 || (current_lnum == sip->si_h_endpos.lnum
2215 && current_col < sip->si_h_endpos.col)))
2216 {
2217 current_attr = sip->si_attr;
2218#ifdef FEAT_EVAL
2219 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002220#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002221 current_trans_id = sip->si_trans_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002222 break;
2223 }
2224 }
2225
Bram Moolenaar217ad922005-03-20 22:37:15 +00002226 if (can_spell != NULL)
2227 {
2228 struct sp_syn sps;
2229
2230 /*
2231 * set "can_spell" to TRUE if spell checking is supposed to be
2232 * done in the current item.
2233 */
Bram Moolenaar217ad922005-03-20 22:37:15 +00002234 if (syn_buf->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002235 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002236 /* There is no @Spell cluster: Do spelling for items without
2237 * @NoSpell cluster. */
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002238 if (syn_buf->b_nospell_cluster_id == 0 || current_trans_id == 0)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002239 *can_spell = (syn_buf->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002240 else
2241 {
2242 sps.inc_tag = 0;
2243 sps.id = syn_buf->b_nospell_cluster_id;
2244 sps.cont_in_list = NULL;
2245 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2246 }
2247 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002248 else
2249 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002250 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002251 * the @Spell cluster. But not when @NoSpell is also there.
2252 * At the toplevel only spell check when ":syn spell toplevel"
2253 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002254 if (current_trans_id == 0)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002255 *can_spell = (syn_buf->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002256 else
2257 {
2258 sps.inc_tag = 0;
2259 sps.id = syn_buf->b_spell_cluster_id;
2260 sps.cont_in_list = NULL;
2261 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2262
2263 if (syn_buf->b_nospell_cluster_id != 0)
2264 {
2265 sps.id = syn_buf->b_nospell_cluster_id;
2266 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2267 *can_spell = FALSE;
2268 }
2269 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002270 }
2271 }
2272
2273
Bram Moolenaar071d4272004-06-13 20:20:40 +00002274 /*
2275 * Check for end of current state (and the states before it) at the
2276 * next column. Don't do this for syncing, because we would miss a
2277 * single character match.
2278 * First check if the current state ends at the current column. It
2279 * may be for an empty match and a containing item might end in the
2280 * current column.
2281 */
2282 if (!syncing)
2283 {
2284 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002285 if (current_state.ga_len > 0
2286 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002287 {
2288 ++current_col;
2289 check_state_ends();
2290 --current_col;
2291 }
2292 }
2293 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002294 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002295 /* Default: Only do spelling when there is no @Spell cluster or when
2296 * ":syn spell toplevel" was used. */
2297 *can_spell = syn_buf->b_syn_spell == SYNSPL_DEFAULT
2298 ? (syn_buf->b_spell_cluster_id == 0)
2299 : (syn_buf->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002300
2301 /* nextgroup ends at end of line, unless "skipnl" or "skipemtpy" present */
2302 if (current_next_list != NULL
2303 && syn_getcurline()[current_col + 1] == NUL
2304 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2305 current_next_list = NULL;
2306
2307 if (zero_width_next_ga.ga_len > 0)
2308 ga_clear(&zero_width_next_ga);
2309
2310 /* No longer need external matches. But keep next_match_extmatch. */
2311 unref_extmatch(re_extmatch_out);
2312 re_extmatch_out = NULL;
2313 unref_extmatch(cur_extmatch);
2314
2315 return current_attr;
2316}
2317
2318
2319/*
2320 * Check if we already matched pattern "idx" at the current column.
2321 */
2322 static int
2323did_match_already(idx, gap)
2324 int idx;
2325 garray_T *gap;
2326{
2327 int i;
2328
2329 for (i = current_state.ga_len; --i >= 0; )
2330 if (CUR_STATE(i).si_m_startcol == (int)current_col
2331 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2332 && CUR_STATE(i).si_idx == idx)
2333 return TRUE;
2334
2335 /* Zero-width matches with a nextgroup argument are not put on the syntax
2336 * stack, and can only be matched once anyway. */
2337 for (i = gap->ga_len; --i >= 0; )
2338 if (((int *)(gap->ga_data))[i] == idx)
2339 return TRUE;
2340
2341 return FALSE;
2342}
2343
2344/*
2345 * Push the next match onto the stack.
2346 */
2347 static stateitem_T *
2348push_next_match(cur_si)
2349 stateitem_T *cur_si;
2350{
2351 synpat_T *spp;
2352
2353 spp = &(SYN_ITEMS(syn_buf)[next_match_idx]);
2354
2355 /*
2356 * Push the item in current_state stack;
2357 */
2358 if (push_current_state(next_match_idx) == OK)
2359 {
2360 /*
2361 * If it's a start-skip-end type that crosses lines, figure out how
2362 * much it continues in this line. Otherwise just fill in the length.
2363 */
2364 cur_si = &CUR_STATE(current_state.ga_len - 1);
2365 cur_si->si_h_startpos = next_match_h_startpos;
2366 cur_si->si_m_startcol = current_col;
2367 cur_si->si_m_lnum = current_lnum;
2368 cur_si->si_flags = spp->sp_flags;
2369 cur_si->si_next_list = spp->sp_next_list;
2370 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2371 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2372 {
2373 /* Try to find the end pattern in the current line */
2374 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2375 check_keepend();
2376 }
2377 else
2378 {
2379 cur_si->si_m_endpos = next_match_m_endpos;
2380 cur_si->si_h_endpos = next_match_h_endpos;
2381 cur_si->si_ends = TRUE;
2382 cur_si->si_flags |= next_match_flags;
2383 cur_si->si_eoe_pos = next_match_eoe_pos;
2384 cur_si->si_end_idx = next_match_end_idx;
2385 }
2386 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2387 keepend_level = current_state.ga_len - 1;
2388 check_keepend();
2389 update_si_attr(current_state.ga_len - 1);
2390
2391 /*
2392 * If the start pattern has another highlight group, push another item
2393 * on the stack for the start pattern.
2394 */
2395 if ( spp->sp_type == SPTYPE_START
2396 && spp->sp_syn_match_id != 0
2397 && push_current_state(next_match_idx) == OK)
2398 {
2399 cur_si = &CUR_STATE(current_state.ga_len - 1);
2400 cur_si->si_h_startpos = next_match_h_startpos;
2401 cur_si->si_m_startcol = current_col;
2402 cur_si->si_m_lnum = current_lnum;
2403 cur_si->si_m_endpos = next_match_eos_pos;
2404 cur_si->si_h_endpos = next_match_eos_pos;
2405 cur_si->si_ends = TRUE;
2406 cur_si->si_end_idx = 0;
2407 cur_si->si_flags = HL_MATCH;
2408 cur_si->si_next_list = NULL;
2409 check_keepend();
2410 update_si_attr(current_state.ga_len - 1);
2411 }
2412 }
2413
2414 next_match_idx = -1; /* try other match next time */
2415
2416 return cur_si;
2417}
2418
2419/*
2420 * Check for end of current state (and the states before it).
2421 */
2422 static void
2423check_state_ends()
2424{
2425 stateitem_T *cur_si;
2426 int had_extend = FALSE;
2427
2428 cur_si = &CUR_STATE(current_state.ga_len - 1);
2429 for (;;)
2430 {
2431 if (cur_si->si_ends
2432 && (cur_si->si_m_endpos.lnum < current_lnum
2433 || (cur_si->si_m_endpos.lnum == current_lnum
2434 && cur_si->si_m_endpos.col <= current_col)))
2435 {
2436 /*
2437 * If there is an end pattern group ID, highlight the end pattern
2438 * now. No need to pop the current item from the stack.
2439 * Only do this if the end pattern continues beyond the current
2440 * position.
2441 */
2442 if (cur_si->si_end_idx
2443 && (cur_si->si_eoe_pos.lnum > current_lnum
2444 || (cur_si->si_eoe_pos.lnum == current_lnum
2445 && cur_si->si_eoe_pos.col > current_col)))
2446 {
2447 cur_si->si_idx = cur_si->si_end_idx;
2448 cur_si->si_end_idx = 0;
2449 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2450 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2451 cur_si->si_flags |= HL_MATCH;
2452 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002453
2454 /* what matches next may be different now, clear it */
2455 next_match_idx = 0;
2456 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002457 break;
2458 }
2459 else
2460 {
2461 /* handle next_list, unless at end of line and no "skipnl" or
2462 * "skipempty" */
2463 current_next_list = cur_si->si_next_list;
2464 current_next_flags = cur_si->si_flags;
2465 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2466 && syn_getcurline()[current_col] == NUL)
2467 current_next_list = NULL;
2468
2469 /* When the ended item has "extend", another item with
2470 * "keepend" now needs to check for its end. */
2471 if (cur_si->si_flags & HL_EXTEND)
2472 had_extend = TRUE;
2473
2474 pop_current_state();
2475
2476 if (current_state.ga_len == 0)
2477 break;
2478
2479 if (had_extend)
2480 {
2481 syn_update_ends(FALSE);
2482 if (current_state.ga_len == 0)
2483 break;
2484 }
2485
2486 cur_si = &CUR_STATE(current_state.ga_len - 1);
2487
2488 /*
2489 * Only for a region the search for the end continues after
2490 * the end of the contained item. If the contained match
2491 * included the end-of-line, break here, the region continues.
2492 * Don't do this when:
2493 * - "keepend" is used for the contained item
2494 * - not at the end of the line (could be end="x$"me=e-1).
2495 * - "excludenl" is used (HL_HAS_EOL won't be set)
2496 */
2497 if (cur_si->si_idx >= 0
2498 && SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_type
2499 == SPTYPE_START
2500 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2501 {
2502 update_si_end(cur_si, (int)current_col, TRUE);
2503 check_keepend();
2504 if ((current_next_flags & HL_HAS_EOL)
2505 && keepend_level < 0
2506 && syn_getcurline()[current_col] == NUL)
2507 break;
2508 }
2509 }
2510 }
2511 else
2512 break;
2513 }
2514}
2515
2516/*
2517 * Update an entry in the current_state stack for a match or region. This
2518 * fills in si_attr, si_next_list and si_cont_list.
2519 */
2520 static void
2521update_si_attr(idx)
2522 int idx;
2523{
2524 stateitem_T *sip = &CUR_STATE(idx);
2525 synpat_T *spp;
2526
2527 spp = &(SYN_ITEMS(syn_buf)[sip->si_idx]);
2528 if (sip->si_flags & HL_MATCH)
2529 sip->si_id = spp->sp_syn_match_id;
2530 else
2531 sip->si_id = spp->sp_syn.id;
2532 sip->si_attr = syn_id2attr(sip->si_id);
2533 sip->si_trans_id = sip->si_id;
2534 if (sip->si_flags & HL_MATCH)
2535 sip->si_cont_list = NULL;
2536 else
2537 sip->si_cont_list = spp->sp_cont_list;
2538
2539 /*
2540 * For transparent items, take attr from outer item.
2541 * Also take cont_list, if there is none.
2542 * Don't do this for the matchgroup of a start or end pattern.
2543 */
2544 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2545 {
2546 if (idx == 0)
2547 {
2548 sip->si_attr = 0;
2549 sip->si_trans_id = 0;
2550 if (sip->si_cont_list == NULL)
2551 sip->si_cont_list = ID_LIST_ALL;
2552 }
2553 else
2554 {
2555 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2556 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002557 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2558 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002559 if (sip->si_cont_list == NULL)
2560 {
2561 sip->si_flags |= HL_TRANS_CONT;
2562 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2563 }
2564 }
2565 }
2566}
2567
2568/*
2569 * Check the current stack for patterns with "keepend" flag.
2570 * Propagate the match-end to contained items, until a "skipend" item is found.
2571 */
2572 static void
2573check_keepend()
2574{
2575 int i;
2576 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002577 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002578 stateitem_T *sip;
2579
2580 /*
2581 * This check can consume a lot of time; only do it from the level where
2582 * there really is a keepend.
2583 */
2584 if (keepend_level < 0)
2585 return;
2586
2587 /*
2588 * Find the last index of an "extend" item. "keepend" items before that
2589 * won't do anything. If there is no "extend" item "i" will be
2590 * "keepend_level" and all "keepend" items will work normally.
2591 */
2592 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2593 if (CUR_STATE(i).si_flags & HL_EXTEND)
2594 break;
2595
2596 maxpos.lnum = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002597 maxpos_h.lnum = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598 for ( ; i < current_state.ga_len; ++i)
2599 {
2600 sip = &CUR_STATE(i);
2601 if (maxpos.lnum != 0)
2602 {
2603 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002604 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002605 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2606 sip->si_ends = TRUE;
2607 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002608 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2609 {
2610 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002611 || maxpos.lnum > sip->si_m_endpos.lnum
2612 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002613 && maxpos.col > sip->si_m_endpos.col))
2614 maxpos = sip->si_m_endpos;
2615 if (maxpos_h.lnum == 0
2616 || maxpos_h.lnum > sip->si_h_endpos.lnum
2617 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2618 && maxpos_h.col > sip->si_h_endpos.col))
2619 maxpos_h = sip->si_h_endpos;
2620 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621 }
2622}
2623
2624/*
2625 * Update an entry in the current_state stack for a start-skip-end pattern.
2626 * This finds the end of the current item, if it's in the current line.
2627 *
2628 * Return the flags for the matched END.
2629 */
2630 static void
2631update_si_end(sip, startcol, force)
2632 stateitem_T *sip;
2633 int startcol; /* where to start searching for the end */
2634 int force; /* when TRUE overrule a previous end */
2635{
2636 lpos_T startpos;
2637 lpos_T endpos;
2638 lpos_T hl_endpos;
2639 lpos_T end_endpos;
2640 int end_idx;
2641
2642 /* Don't update when it's already done. Can be a match of an end pattern
2643 * that started in a previous line. Watch out: can also be a "keepend"
2644 * from a containing item. */
2645 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2646 return;
2647
2648 /*
2649 * We need to find the end of the region. It may continue in the next
2650 * line.
2651 */
2652 end_idx = 0;
2653 startpos.lnum = current_lnum;
2654 startpos.col = startcol;
2655 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2656 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2657
2658 if (endpos.lnum == 0)
2659 {
2660 /* No end pattern matched. */
2661 if (SYN_ITEMS(syn_buf)[sip->si_idx].sp_flags & HL_ONELINE)
2662 {
2663 /* a "oneline" never continues in the next line */
2664 sip->si_ends = TRUE;
2665 sip->si_m_endpos.lnum = current_lnum;
2666 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2667 }
2668 else
2669 {
2670 /* continues in the next line */
2671 sip->si_ends = FALSE;
2672 sip->si_m_endpos.lnum = 0;
2673 }
2674 sip->si_h_endpos = sip->si_m_endpos;
2675 }
2676 else
2677 {
2678 /* match within this line */
2679 sip->si_m_endpos = endpos;
2680 sip->si_h_endpos = hl_endpos;
2681 sip->si_eoe_pos = end_endpos;
2682 sip->si_ends = TRUE;
2683 sip->si_end_idx = end_idx;
2684 }
2685}
2686
2687/*
2688 * Add a new state to the current state stack.
2689 * It is cleared and the index set to "idx".
2690 * Return FAIL if it's not possible (out of memory).
2691 */
2692 static int
2693push_current_state(idx)
2694 int idx;
2695{
2696 if (ga_grow(&current_state, 1) == FAIL)
2697 return FAIL;
2698 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2699 CUR_STATE(current_state.ga_len).si_idx = idx;
2700 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002701 return OK;
2702}
2703
2704/*
2705 * Remove a state from the current_state stack.
2706 */
2707 static void
2708pop_current_state()
2709{
2710 if (current_state.ga_len)
2711 {
2712 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2713 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002714 }
2715 /* after the end of a pattern, try matching a keyword or pattern */
2716 next_match_idx = -1;
2717
2718 /* if first state with "keepend" is popped, reset keepend_level */
2719 if (keepend_level >= current_state.ga_len)
2720 keepend_level = -1;
2721}
2722
2723/*
2724 * Find the end of a start/skip/end syntax region after "startpos".
2725 * Only checks one line.
2726 * Also handles a match item that continued from a previous line.
2727 * If not found, the syntax item continues in the next line. m_endpos->lnum
2728 * will be 0.
2729 * If found, the end of the region and the end of the highlighting is
2730 * computed.
2731 */
2732 static void
2733find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2734 end_idx, start_ext)
2735 int idx; /* index of the pattern */
2736 lpos_T *startpos; /* where to start looking for an END match */
2737 lpos_T *m_endpos; /* return: end of match */
2738 lpos_T *hl_endpos; /* return: end of highlighting */
2739 long *flagsp; /* return: flags of matching END */
2740 lpos_T *end_endpos; /* return: end of end pattern match */
2741 int *end_idx; /* return: group ID for end pat. match, or 0 */
2742 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2743{
2744 colnr_T matchcol;
2745 synpat_T *spp, *spp_skip;
2746 int start_idx;
2747 int best_idx;
2748 regmmatch_T regmatch;
2749 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2750 lpos_T pos;
2751 char_u *line;
2752 int had_match = FALSE;
2753
2754 /*
2755 * Check for being called with a START pattern.
2756 * Can happen with a match that continues to the next line, because it
2757 * contained a region.
2758 */
2759 spp = &(SYN_ITEMS(syn_buf)[idx]);
2760 if (spp->sp_type != SPTYPE_START)
2761 {
2762 *hl_endpos = *startpos;
2763 return;
2764 }
2765
2766 /*
2767 * Find the SKIP or first END pattern after the last START pattern.
2768 */
2769 for (;;)
2770 {
2771 spp = &(SYN_ITEMS(syn_buf)[idx]);
2772 if (spp->sp_type != SPTYPE_START)
2773 break;
2774 ++idx;
2775 }
2776
2777 /*
2778 * Lookup the SKIP pattern (if present)
2779 */
2780 if (spp->sp_type == SPTYPE_SKIP)
2781 {
2782 spp_skip = spp;
2783 ++idx;
2784 }
2785 else
2786 spp_skip = NULL;
2787
2788 /* Setup external matches for syn_regexec(). */
2789 unref_extmatch(re_extmatch_in);
2790 re_extmatch_in = ref_extmatch(start_ext);
2791
2792 matchcol = startpos->col; /* start looking for a match at sstart */
2793 start_idx = idx; /* remember the first END pattern. */
2794 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2795 for (;;)
2796 {
2797 /*
2798 * Find end pattern that matches first after "matchcol".
2799 */
2800 best_idx = -1;
2801 for (idx = start_idx; idx < syn_buf->b_syn_patterns.ga_len; ++idx)
2802 {
2803 int lc_col = matchcol;
2804
2805 spp = &(SYN_ITEMS(syn_buf)[idx]);
2806 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2807 break;
2808 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2809 if (lc_col < 0)
2810 lc_col = 0;
2811
2812 regmatch.rmm_ic = spp->sp_ic;
2813 regmatch.regprog = spp->sp_prog;
2814 if (syn_regexec(&regmatch, startpos->lnum, lc_col))
2815 {
2816 if (best_idx == -1 || regmatch.startpos[0].col
2817 < best_regmatch.startpos[0].col)
2818 {
2819 best_idx = idx;
2820 best_regmatch.startpos[0] = regmatch.startpos[0];
2821 best_regmatch.endpos[0] = regmatch.endpos[0];
2822 }
2823 }
2824 }
2825
2826 /*
2827 * If all end patterns have been tried, and there is no match, the
2828 * item continues until end-of-line.
2829 */
2830 if (best_idx == -1)
2831 break;
2832
2833 /*
2834 * If the skip pattern matches before the end pattern,
2835 * continue searching after the skip pattern.
2836 */
2837 if (spp_skip != NULL)
2838 {
2839 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
2840
2841 if (lc_col < 0)
2842 lc_col = 0;
2843 regmatch.rmm_ic = spp_skip->sp_ic;
2844 regmatch.regprog = spp_skip->sp_prog;
2845 if (syn_regexec(&regmatch, startpos->lnum, lc_col)
2846 && regmatch.startpos[0].col
2847 <= best_regmatch.startpos[0].col)
2848 {
2849 /* Add offset to skip pattern match */
2850 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
2851
2852 /* If the skip pattern goes on to the next line, there is no
2853 * match with an end pattern in this line. */
2854 if (pos.lnum > startpos->lnum)
2855 break;
2856
2857 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
2858
2859 /* take care of an empty match or negative offset */
2860 if (pos.col <= matchcol)
2861 ++matchcol;
2862 else if (pos.col <= regmatch.endpos[0].col)
2863 matchcol = pos.col;
2864 else
2865 /* Be careful not to jump over the NUL at the end-of-line */
2866 for (matchcol = regmatch.endpos[0].col;
2867 line[matchcol] != NUL && matchcol < pos.col;
2868 ++matchcol)
2869 ;
2870
2871 /* if the skip pattern includes end-of-line, break here */
2872 if (line[matchcol] == NUL)
2873 break;
2874
2875 continue; /* start with first end pattern again */
2876 }
2877 }
2878
2879 /*
2880 * Match from start pattern to end pattern.
2881 * Correct for match and highlight offset of end pattern.
2882 */
2883 spp = &(SYN_ITEMS(syn_buf)[best_idx]);
2884 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
2885 /* can't end before the start */
2886 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
2887 m_endpos->col = startpos->col;
2888
2889 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
2890 /* can't end before the start */
2891 if (end_endpos->lnum == startpos->lnum
2892 && end_endpos->col < startpos->col)
2893 end_endpos->col = startpos->col;
2894 /* can't end after the match */
2895 limit_pos(end_endpos, m_endpos);
2896
2897 /*
2898 * If the end group is highlighted differently, adjust the pointers.
2899 */
2900 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
2901 {
2902 *end_idx = best_idx;
2903 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
2904 {
2905 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
2906 hl_endpos->col = best_regmatch.endpos[0].col;
2907 }
2908 else
2909 {
2910 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
2911 hl_endpos->col = best_regmatch.startpos[0].col;
2912 }
2913 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
2914
2915 /* can't end before the start */
2916 if (hl_endpos->lnum == startpos->lnum
2917 && hl_endpos->col < startpos->col)
2918 hl_endpos->col = startpos->col;
2919 limit_pos(hl_endpos, m_endpos);
2920
2921 /* now the match ends where the highlighting ends, it is turned
2922 * into the matchgroup for the end */
2923 *m_endpos = *hl_endpos;
2924 }
2925 else
2926 {
2927 *end_idx = 0;
2928 *hl_endpos = *end_endpos;
2929 }
2930
2931 *flagsp = spp->sp_flags;
2932
2933 had_match = TRUE;
2934 break;
2935 }
2936
2937 /* no match for an END pattern in this line */
2938 if (!had_match)
2939 m_endpos->lnum = 0;
2940
2941 /* Remove external matches. */
2942 unref_extmatch(re_extmatch_in);
2943 re_extmatch_in = NULL;
2944}
2945
2946/*
2947 * Limit "pos" not to be after "limit".
2948 */
2949 static void
2950limit_pos(pos, limit)
2951 lpos_T *pos;
2952 lpos_T *limit;
2953{
2954 if (pos->lnum > limit->lnum)
2955 *pos = *limit;
2956 else if (pos->lnum == limit->lnum && pos->col > limit->col)
2957 pos->col = limit->col;
2958}
2959
2960/*
2961 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
2962 */
2963 static void
2964limit_pos_zero(pos, limit)
2965 lpos_T *pos;
2966 lpos_T *limit;
2967{
2968 if (pos->lnum == 0)
2969 *pos = *limit;
2970 else
2971 limit_pos(pos, limit);
2972}
2973
2974/*
2975 * Add offset to matched text for end of match or highlight.
2976 */
2977 static void
2978syn_add_end_off(result, regmatch, spp, idx, extra)
2979 lpos_T *result; /* returned position */
2980 regmmatch_T *regmatch; /* start/end of match */
2981 synpat_T *spp; /* matched pattern */
2982 int idx; /* index of offset */
2983 int extra; /* extra chars for offset to start */
2984{
2985 int col;
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00002986 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002987
2988 if (spp->sp_off_flags & (1 << idx))
2989 {
2990 result->lnum = regmatch->startpos[0].lnum;
2991 col = regmatch->startpos[0].col + extra;
2992 }
2993 else
2994 {
2995 result->lnum = regmatch->endpos[0].lnum;
2996 col = regmatch->endpos[0].col;
2997 }
2998 col += spp->sp_offsets[idx];
2999 if (col < 0)
3000 result->col = 0;
3001 else
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003002 {
3003 /* Don't go past the end of the line. Matters for "rs=e+2" when there
Bram Moolenaar33aec762006-01-22 23:30:12 +00003004 * is a matchgroup. Watch out for match with last NL in the buffer. */
3005 if (result->lnum > syn_buf->b_ml.ml_line_count)
3006 len = 0;
3007 else
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003008 len = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003009 if (col > len)
3010 result->col = len;
3011 else
3012 result->col = col;
3013 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003014}
3015
3016/*
3017 * Add offset to matched text for start of match or highlight.
3018 * Avoid resulting column to become negative.
3019 */
3020 static void
3021syn_add_start_off(result, regmatch, spp, idx, extra)
3022 lpos_T *result; /* returned position */
3023 regmmatch_T *regmatch; /* start/end of match */
3024 synpat_T *spp;
3025 int idx;
3026 int extra; /* extra chars for offset to end */
3027{
3028 int col;
3029
3030 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3031 {
3032 result->lnum = regmatch->endpos[0].lnum;
3033 col = regmatch->endpos[0].col + extra;
3034 }
3035 else
3036 {
3037 result->lnum = regmatch->startpos[0].lnum;
3038 col = regmatch->startpos[0].col;
3039 }
3040 col += spp->sp_offsets[idx];
3041 if (col < 0)
3042 result->col = 0;
3043 else
3044 result->col = col;
3045}
3046
3047/*
3048 * Get current line in syntax buffer.
3049 */
3050 static char_u *
3051syn_getcurline()
3052{
3053 return ml_get_buf(syn_buf, current_lnum, FALSE);
3054}
3055
3056/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003057 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003058 * Returns TRUE when there is a match.
3059 */
3060 static int
3061syn_regexec(rmp, lnum, col)
3062 regmmatch_T *rmp;
3063 linenr_T lnum;
3064 colnr_T col;
3065{
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003066 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003067 if (vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col) > 0)
3068 {
3069 rmp->startpos[0].lnum += lnum;
3070 rmp->endpos[0].lnum += lnum;
3071 return TRUE;
3072 }
3073 return FALSE;
3074}
3075
3076/*
3077 * Check one position in a line for a matching keyword.
3078 * The caller must check if a keyword can start at startcol.
3079 * Return it's ID if found, 0 otherwise.
3080 */
3081 static int
3082check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si)
3083 char_u *line;
3084 int startcol; /* position in line to check for keyword */
3085 int *endcolp; /* return: character after found keyword */
3086 long *flagsp; /* return: flags of matching keyword */
3087 short **next_listp; /* return: next_list of matching keyword */
3088 stateitem_T *cur_si; /* item at the top of the stack */
3089{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003090 keyentry_T *kp;
3091 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003092 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003093 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003094 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003095 hashtab_T *ht;
3096 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003097
3098 /* Find first character after the keyword. First character was already
3099 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003100 kwp = line + startcol;
3101 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003102 do
3103 {
3104#ifdef FEAT_MBYTE
3105 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003106 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003107 else
3108#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003109 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003110 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00003111 while (vim_iswordc_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003112
Bram Moolenaardad6b692005-01-25 22:14:34 +00003113 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003114 return 0;
3115
3116 /*
3117 * Must make a copy of the keyword, so we can add a NUL and make it
3118 * lowercase.
3119 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003120 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003121
3122 /*
3123 * Try twice:
3124 * 1. matching case
3125 * 2. ignoring case
3126 */
3127 for (round = 1; round <= 2; ++round)
3128 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003129 ht = round == 1 ? &syn_buf->b_keywtab : &syn_buf->b_keywtab_ic;
3130 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003131 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003132 if (round == 2) /* ignore case */
3133 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003134
3135 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003136 * Find keywords that match. There can be several with different
3137 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003138 * When current_next_list is non-zero accept only that group, otherwise:
3139 * Accept a not-contained keyword at toplevel.
3140 * Accept a keyword at other levels only if it is in the contains list.
3141 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003142 hi = hash_find(ht, keyword);
3143 if (!HASHITEM_EMPTY(hi))
3144 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003145 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003146 if (current_next_list != 0
3147 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3148 : (cur_si == NULL
3149 ? !(kp->flags & HL_CONTAINED)
3150 : in_id_list(cur_si, cur_si->si_cont_list,
3151 &kp->k_syn, kp->flags & HL_CONTAINED)))
3152 {
3153 *endcolp = startcol + kwlen;
3154 *flagsp = kp->flags;
3155 *next_listp = kp->next_list;
3156 return kp->k_syn.id;
3157 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003158 }
3159 }
3160 return 0;
3161}
3162
3163/*
3164 * Handle ":syntax case" command.
3165 */
3166/* ARGSUSED */
3167 static void
3168syn_cmd_case(eap, syncing)
3169 exarg_T *eap;
3170 int syncing; /* not used */
3171{
3172 char_u *arg = eap->arg;
3173 char_u *next;
3174
3175 eap->nextcmd = find_nextcmd(arg);
3176 if (eap->skip)
3177 return;
3178
3179 next = skiptowhite(arg);
3180 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
3181 curbuf->b_syn_ic = FALSE;
3182 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
3183 curbuf->b_syn_ic = TRUE;
3184 else
3185 EMSG2(_("E390: Illegal argument: %s"), arg);
3186}
3187
3188/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003189 * Handle ":syntax spell" command.
3190 */
3191/* ARGSUSED */
3192 static void
3193syn_cmd_spell(eap, syncing)
3194 exarg_T *eap;
3195 int syncing; /* not used */
3196{
3197 char_u *arg = eap->arg;
3198 char_u *next;
3199
3200 eap->nextcmd = find_nextcmd(arg);
3201 if (eap->skip)
3202 return;
3203
3204 next = skiptowhite(arg);
3205 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
3206 curbuf->b_syn_spell = SYNSPL_TOP;
3207 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
3208 curbuf->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003209 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003210 curbuf->b_syn_spell = SYNSPL_DEFAULT;
3211 else
3212 EMSG2(_("E390: Illegal argument: %s"), arg);
3213}
3214
3215/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003216 * Clear all syntax info for one buffer.
3217 */
3218 void
3219syntax_clear(buf)
3220 buf_T *buf;
3221{
3222 int i;
3223
Bram Moolenaar5b8d8fd2005-08-16 23:01:50 +00003224 buf->b_syn_error = FALSE; /* clear previous error */
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003225 buf->b_syn_ic = FALSE; /* Use case, by default */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003226 buf->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
Bram Moolenaar54ee7752005-05-31 22:22:17 +00003227 buf->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003228
3229 /* free the keywords */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003230 clear_keywtab(&buf->b_keywtab);
3231 clear_keywtab(&buf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232
3233 /* free the syntax patterns */
3234 for (i = buf->b_syn_patterns.ga_len; --i >= 0; )
3235 syn_clear_pattern(buf, i);
3236 ga_clear(&buf->b_syn_patterns);
3237
3238 /* free the syntax clusters */
3239 for (i = buf->b_syn_clusters.ga_len; --i >= 0; )
3240 syn_clear_cluster(buf, i);
3241 ga_clear(&buf->b_syn_clusters);
Bram Moolenaar75c50c42005-06-04 22:06:24 +00003242 buf->b_spell_cluster_id = 0;
3243 buf->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003244
3245 buf->b_syn_sync_flags = 0;
3246 buf->b_syn_sync_minlines = 0;
3247 buf->b_syn_sync_maxlines = 0;
3248 buf->b_syn_sync_linebreaks = 0;
3249
3250 vim_free(buf->b_syn_linecont_prog);
3251 buf->b_syn_linecont_prog = NULL;
3252 vim_free(buf->b_syn_linecont_pat);
3253 buf->b_syn_linecont_pat = NULL;
3254#ifdef FEAT_FOLDING
3255 buf->b_syn_folditems = 0;
3256#endif
3257
3258 /* free the stored states */
3259 syn_stack_free_all(buf);
3260 invalidate_current_state();
3261}
3262
3263/*
3264 * Clear syncing info for one buffer.
3265 */
3266 static void
3267syntax_sync_clear()
3268{
3269 int i;
3270
3271 /* free the syntax patterns */
3272 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
3273 if (SYN_ITEMS(curbuf)[i].sp_syncing)
3274 syn_remove_pattern(curbuf, i);
3275
3276 curbuf->b_syn_sync_flags = 0;
3277 curbuf->b_syn_sync_minlines = 0;
3278 curbuf->b_syn_sync_maxlines = 0;
3279 curbuf->b_syn_sync_linebreaks = 0;
3280
3281 vim_free(curbuf->b_syn_linecont_prog);
3282 curbuf->b_syn_linecont_prog = NULL;
3283 vim_free(curbuf->b_syn_linecont_pat);
3284 curbuf->b_syn_linecont_pat = NULL;
3285
3286 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3287}
3288
3289/*
3290 * Remove one pattern from the buffer's pattern list.
3291 */
3292 static void
3293syn_remove_pattern(buf, idx)
3294 buf_T *buf;
3295 int idx;
3296{
3297 synpat_T *spp;
3298
3299 spp = &(SYN_ITEMS(buf)[idx]);
3300#ifdef FEAT_FOLDING
3301 if (spp->sp_flags & HL_FOLD)
3302 --buf->b_syn_folditems;
3303#endif
3304 syn_clear_pattern(buf, idx);
3305 mch_memmove(spp, spp + 1,
3306 sizeof(synpat_T) * (buf->b_syn_patterns.ga_len - idx - 1));
3307 --buf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003308}
3309
3310/*
3311 * Clear and free one syntax pattern. When clearing all, must be called from
3312 * last to first!
3313 */
3314 static void
3315syn_clear_pattern(buf, i)
3316 buf_T *buf;
3317 int i;
3318{
3319 vim_free(SYN_ITEMS(buf)[i].sp_pattern);
3320 vim_free(SYN_ITEMS(buf)[i].sp_prog);
3321 /* Only free sp_cont_list and sp_next_list of first start pattern */
3322 if (i == 0 || SYN_ITEMS(buf)[i - 1].sp_type != SPTYPE_START)
3323 {
3324 vim_free(SYN_ITEMS(buf)[i].sp_cont_list);
3325 vim_free(SYN_ITEMS(buf)[i].sp_next_list);
3326 }
3327}
3328
3329/*
3330 * Clear and free one syntax cluster.
3331 */
3332 static void
3333syn_clear_cluster(buf, i)
3334 buf_T *buf;
3335 int i;
3336{
3337 vim_free(SYN_CLSTR(buf)[i].scl_name);
3338 vim_free(SYN_CLSTR(buf)[i].scl_name_u);
3339 vim_free(SYN_CLSTR(buf)[i].scl_list);
3340}
3341
3342/*
3343 * Handle ":syntax clear" command.
3344 */
3345 static void
3346syn_cmd_clear(eap, syncing)
3347 exarg_T *eap;
3348 int syncing;
3349{
3350 char_u *arg = eap->arg;
3351 char_u *arg_end;
3352 int id;
3353
3354 eap->nextcmd = find_nextcmd(arg);
3355 if (eap->skip)
3356 return;
3357
3358 /*
3359 * We have to disable this within ":syn include @group filename",
3360 * because otherwise @group would get deleted.
3361 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3362 * clear".
3363 */
3364 if (curbuf->b_syn_topgrp != 0)
3365 return;
3366
3367 if (ends_excmd(*arg))
3368 {
3369 /*
3370 * No argument: Clear all syntax items.
3371 */
3372 if (syncing)
3373 syntax_sync_clear();
3374 else
3375 {
3376 syntax_clear(curbuf);
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003377 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003378 }
3379 }
3380 else
3381 {
3382 /*
3383 * Clear the group IDs that are in the argument.
3384 */
3385 while (!ends_excmd(*arg))
3386 {
3387 arg_end = skiptowhite(arg);
3388 if (*arg == '@')
3389 {
3390 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3391 if (id == 0)
3392 {
3393 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3394 break;
3395 }
3396 else
3397 {
3398 /*
3399 * We can't physically delete a cluster without changing
3400 * the IDs of other clusters, so we do the next best thing
3401 * and make it empty.
3402 */
3403 short scl_id = id - SYNID_CLUSTER;
3404
3405 vim_free(SYN_CLSTR(curbuf)[scl_id].scl_list);
3406 SYN_CLSTR(curbuf)[scl_id].scl_list = NULL;
3407 }
3408 }
3409 else
3410 {
3411 id = syn_namen2id(arg, (int)(arg_end - arg));
3412 if (id == 0)
3413 {
3414 EMSG2(_(e_nogroup), arg);
3415 break;
3416 }
3417 else
3418 syn_clear_one(id, syncing);
3419 }
3420 arg = skipwhite(arg_end);
3421 }
3422 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003423 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003424 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
3425}
3426
3427/*
3428 * Clear one syntax group for the current buffer.
3429 */
3430 static void
3431syn_clear_one(id, syncing)
3432 int id;
3433 int syncing;
3434{
3435 synpat_T *spp;
3436 int idx;
3437
3438 /* Clear keywords only when not ":syn sync clear group-name" */
3439 if (!syncing)
3440 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003441 (void)syn_clear_keyword(id, &curbuf->b_keywtab);
3442 (void)syn_clear_keyword(id, &curbuf->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443 }
3444
3445 /* clear the patterns for "id" */
3446 for (idx = curbuf->b_syn_patterns.ga_len; --idx >= 0; )
3447 {
3448 spp = &(SYN_ITEMS(curbuf)[idx]);
3449 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3450 continue;
3451 syn_remove_pattern(curbuf, idx);
3452 }
3453}
3454
3455/*
3456 * Handle ":syntax on" command.
3457 */
3458/* ARGSUSED */
3459 static void
3460syn_cmd_on(eap, syncing)
3461 exarg_T *eap;
3462 int syncing; /* not used */
3463{
3464 syn_cmd_onoff(eap, "syntax");
3465}
3466
3467/*
3468 * Handle ":syntax enable" command.
3469 */
3470/* ARGSUSED */
3471 static void
3472syn_cmd_enable(eap, syncing)
3473 exarg_T *eap;
3474 int syncing; /* not used */
3475{
3476 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3477 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003478 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003479}
3480
3481/*
3482 * Handle ":syntax reset" command.
3483 */
3484/* ARGSUSED */
3485 static void
3486syn_cmd_reset(eap, syncing)
3487 exarg_T *eap;
3488 int syncing; /* not used */
3489{
3490 eap->nextcmd = check_nextcmd(eap->arg);
3491 if (!eap->skip)
3492 {
3493 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3494 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003495 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003496 }
3497}
3498
3499/*
3500 * Handle ":syntax manual" command.
3501 */
3502/* ARGSUSED */
3503 static void
3504syn_cmd_manual(eap, syncing)
3505 exarg_T *eap;
3506 int syncing; /* not used */
3507{
3508 syn_cmd_onoff(eap, "manual");
3509}
3510
3511/*
3512 * Handle ":syntax off" command.
3513 */
3514/* ARGSUSED */
3515 static void
3516syn_cmd_off(eap, syncing)
3517 exarg_T *eap;
3518 int syncing; /* not used */
3519{
3520 syn_cmd_onoff(eap, "nosyntax");
3521}
3522
3523 static void
3524syn_cmd_onoff(eap, name)
3525 exarg_T *eap;
3526 char *name;
3527{
3528 char_u buf[100];
3529
3530 eap->nextcmd = check_nextcmd(eap->arg);
3531 if (!eap->skip)
3532 {
3533 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003534 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003535 do_cmdline_cmd(buf);
3536 }
3537}
3538
3539/*
3540 * Handle ":syntax [list]" command: list current syntax words.
3541 */
3542 static void
3543syn_cmd_list(eap, syncing)
3544 exarg_T *eap;
3545 int syncing; /* when TRUE: list syncing items */
3546{
3547 char_u *arg = eap->arg;
3548 int id;
3549 char_u *arg_end;
3550
3551 eap->nextcmd = find_nextcmd(arg);
3552 if (eap->skip)
3553 return;
3554
3555 if (!syntax_present(curbuf))
3556 {
3557 MSG(_("No Syntax items defined for this buffer"));
3558 return;
3559 }
3560
3561 if (syncing)
3562 {
3563 if (curbuf->b_syn_sync_flags & SF_CCOMMENT)
3564 {
3565 MSG_PUTS(_("syncing on C-style comments"));
3566 syn_lines_msg();
3567 syn_match_msg();
3568 return;
3569 }
3570 else if (!(curbuf->b_syn_sync_flags & SF_MATCH))
3571 {
3572 if (curbuf->b_syn_sync_minlines == 0)
3573 MSG_PUTS(_("no syncing"));
3574 else
3575 {
3576 MSG_PUTS(_("syncing starts "));
3577 msg_outnum(curbuf->b_syn_sync_minlines);
3578 MSG_PUTS(_(" lines before top line"));
3579 syn_match_msg();
3580 }
3581 return;
3582 }
3583 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
3584 if (curbuf->b_syn_sync_minlines > 0
3585 || curbuf->b_syn_sync_maxlines > 0
3586 || curbuf->b_syn_sync_linebreaks > 0)
3587 {
3588 MSG_PUTS(_("\nsyncing on items"));
3589 syn_lines_msg();
3590 syn_match_msg();
3591 }
3592 }
3593 else
3594 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3595 if (ends_excmd(*arg))
3596 {
3597 /*
3598 * No argument: List all group IDs and all syntax clusters.
3599 */
3600 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3601 syn_list_one(id, syncing, FALSE);
3602 for (id = 0; id < curbuf->b_syn_clusters.ga_len && !got_int; ++id)
3603 syn_list_cluster(id);
3604 }
3605 else
3606 {
3607 /*
3608 * List the group IDs and syntax clusters that are in the argument.
3609 */
3610 while (!ends_excmd(*arg) && !got_int)
3611 {
3612 arg_end = skiptowhite(arg);
3613 if (*arg == '@')
3614 {
3615 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3616 if (id == 0)
3617 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3618 else
3619 syn_list_cluster(id - SYNID_CLUSTER);
3620 }
3621 else
3622 {
3623 id = syn_namen2id(arg, (int)(arg_end - arg));
3624 if (id == 0)
3625 EMSG2(_(e_nogroup), arg);
3626 else
3627 syn_list_one(id, syncing, TRUE);
3628 }
3629 arg = skipwhite(arg_end);
3630 }
3631 }
3632 eap->nextcmd = check_nextcmd(arg);
3633}
3634
3635 static void
3636syn_lines_msg()
3637{
3638 if (curbuf->b_syn_sync_maxlines > 0 || curbuf->b_syn_sync_minlines > 0)
3639 {
3640 MSG_PUTS("; ");
3641 if (curbuf->b_syn_sync_minlines > 0)
3642 {
3643 MSG_PUTS(_("minimal "));
3644 msg_outnum(curbuf->b_syn_sync_minlines);
3645 if (curbuf->b_syn_sync_maxlines)
3646 MSG_PUTS(", ");
3647 }
3648 if (curbuf->b_syn_sync_maxlines > 0)
3649 {
3650 MSG_PUTS(_("maximal "));
3651 msg_outnum(curbuf->b_syn_sync_maxlines);
3652 }
3653 MSG_PUTS(_(" lines before top line"));
3654 }
3655}
3656
3657 static void
3658syn_match_msg()
3659{
3660 if (curbuf->b_syn_sync_linebreaks > 0)
3661 {
3662 MSG_PUTS(_("; match "));
3663 msg_outnum(curbuf->b_syn_sync_linebreaks);
3664 MSG_PUTS(_(" line breaks"));
3665 }
3666}
3667
3668static int last_matchgroup;
3669
3670struct name_list
3671{
3672 int flag;
3673 char *name;
3674};
3675
3676static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3677
3678/*
3679 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3680 */
3681 static void
3682syn_list_one(id, syncing, link_only)
3683 int id;
3684 int syncing; /* when TRUE: list syncing items */
3685 int link_only; /* when TRUE; list link-only too */
3686{
3687 int attr;
3688 int idx;
3689 int did_header = FALSE;
3690 synpat_T *spp;
3691 static struct name_list namelist1[] =
3692 {
3693 {HL_DISPLAY, "display"},
3694 {HL_CONTAINED, "contained"},
3695 {HL_ONELINE, "oneline"},
3696 {HL_KEEPEND, "keepend"},
3697 {HL_EXTEND, "extend"},
3698 {HL_EXCLUDENL, "excludenl"},
3699 {HL_TRANSP, "transparent"},
3700 {HL_FOLD, "fold"},
3701 {0, NULL}
3702 };
3703 static struct name_list namelist2[] =
3704 {
3705 {HL_SKIPWHITE, "skipwhite"},
3706 {HL_SKIPNL, "skipnl"},
3707 {HL_SKIPEMPTY, "skipempty"},
3708 {0, NULL}
3709 };
3710
3711 attr = hl_attr(HLF_D); /* highlight like directories */
3712
3713 /* list the keywords for "id" */
3714 if (!syncing)
3715 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003716 did_header = syn_list_keywords(id, &curbuf->b_keywtab, FALSE, attr);
3717 did_header = syn_list_keywords(id, &curbuf->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718 did_header, attr);
3719 }
3720
3721 /* list the patterns for "id" */
3722 for (idx = 0; idx < curbuf->b_syn_patterns.ga_len && !got_int; ++idx)
3723 {
3724 spp = &(SYN_ITEMS(curbuf)[idx]);
3725 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3726 continue;
3727
3728 (void)syn_list_header(did_header, 999, id);
3729 did_header = TRUE;
3730 last_matchgroup = 0;
3731 if (spp->sp_type == SPTYPE_MATCH)
3732 {
3733 put_pattern("match", ' ', spp, attr);
3734 msg_putchar(' ');
3735 }
3736 else if (spp->sp_type == SPTYPE_START)
3737 {
3738 while (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_START)
3739 put_pattern("start", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3740 if (SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_SKIP)
3741 put_pattern("skip", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3742 while (idx < curbuf->b_syn_patterns.ga_len
3743 && SYN_ITEMS(curbuf)[idx].sp_type == SPTYPE_END)
3744 put_pattern("end", '=', &SYN_ITEMS(curbuf)[idx++], attr);
3745 --idx;
3746 msg_putchar(' ');
3747 }
3748 syn_list_flags(namelist1, spp->sp_flags, attr);
3749
3750 if (spp->sp_cont_list != NULL)
3751 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
3752
3753 if (spp->sp_syn.cont_in_list != NULL)
3754 put_id_list((char_u *)"containedin",
3755 spp->sp_syn.cont_in_list, attr);
3756
3757 if (spp->sp_next_list != NULL)
3758 {
3759 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
3760 syn_list_flags(namelist2, spp->sp_flags, attr);
3761 }
3762 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
3763 {
3764 if (spp->sp_flags & HL_SYNC_HERE)
3765 msg_puts_attr((char_u *)"grouphere", attr);
3766 else
3767 msg_puts_attr((char_u *)"groupthere", attr);
3768 msg_putchar(' ');
3769 if (spp->sp_sync_idx >= 0)
3770 msg_outtrans(HL_TABLE()[SYN_ITEMS(curbuf)
3771 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
3772 else
3773 MSG_PUTS("NONE");
3774 msg_putchar(' ');
3775 }
3776 }
3777
3778 /* list the link, if there is one */
3779 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
3780 {
3781 (void)syn_list_header(did_header, 999, id);
3782 msg_puts_attr((char_u *)"links to", attr);
3783 msg_putchar(' ');
3784 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3785 }
3786}
3787
3788 static void
3789syn_list_flags(nl, flags, attr)
3790 struct name_list *nl;
3791 int flags;
3792 int attr;
3793{
3794 int i;
3795
3796 for (i = 0; nl[i].flag != 0; ++i)
3797 if (flags & nl[i].flag)
3798 {
3799 msg_puts_attr((char_u *)nl[i].name, attr);
3800 msg_putchar(' ');
3801 }
3802}
3803
3804/*
3805 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
3806 */
3807 static void
3808syn_list_cluster(id)
3809 int id;
3810{
3811 int endcol = 15;
3812
3813 /* slight hack: roughly duplicate the guts of syn_list_header() */
3814 msg_putchar('\n');
3815 msg_outtrans(SYN_CLSTR(curbuf)[id].scl_name);
3816
3817 if (msg_col >= endcol) /* output at least one space */
3818 endcol = msg_col + 1;
3819 if (Columns <= endcol) /* avoid hang for tiny window */
3820 endcol = Columns - 1;
3821
3822 msg_advance(endcol);
3823 if (SYN_CLSTR(curbuf)[id].scl_list != NULL)
3824 {
3825 put_id_list((char_u *)"cluster", SYN_CLSTR(curbuf)[id].scl_list,
3826 hl_attr(HLF_D));
3827 }
3828 else
3829 {
3830 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
3831 msg_puts((char_u *)"=NONE");
3832 }
3833}
3834
3835 static void
3836put_id_list(name, list, attr)
3837 char_u *name;
3838 short *list;
3839 int attr;
3840{
3841 short *p;
3842
3843 msg_puts_attr(name, attr);
3844 msg_putchar('=');
3845 for (p = list; *p; ++p)
3846 {
3847 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
3848 {
3849 if (p[1])
3850 MSG_PUTS("ALLBUT");
3851 else
3852 MSG_PUTS("ALL");
3853 }
3854 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
3855 {
3856 MSG_PUTS("TOP");
3857 }
3858 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
3859 {
3860 MSG_PUTS("CONTAINED");
3861 }
3862 else if (*p >= SYNID_CLUSTER)
3863 {
3864 short scl_id = *p - SYNID_CLUSTER;
3865
3866 msg_putchar('@');
3867 msg_outtrans(SYN_CLSTR(curbuf)[scl_id].scl_name);
3868 }
3869 else
3870 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
3871 if (p[1])
3872 msg_putchar(',');
3873 }
3874 msg_putchar(' ');
3875}
3876
3877 static void
3878put_pattern(s, c, spp, attr)
3879 char *s;
3880 int c;
3881 synpat_T *spp;
3882 int attr;
3883{
3884 long n;
3885 int mask;
3886 int first;
3887 static char *sepchars = "/+=-#@\"|'^&";
3888 int i;
3889
3890 /* May have to write "matchgroup=group" */
3891 if (last_matchgroup != spp->sp_syn_match_id)
3892 {
3893 last_matchgroup = spp->sp_syn_match_id;
3894 msg_puts_attr((char_u *)"matchgroup", attr);
3895 msg_putchar('=');
3896 if (last_matchgroup == 0)
3897 msg_outtrans((char_u *)"NONE");
3898 else
3899 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
3900 msg_putchar(' ');
3901 }
3902
3903 /* output the name of the pattern and an '=' or ' ' */
3904 msg_puts_attr((char_u *)s, attr);
3905 msg_putchar(c);
3906
3907 /* output the pattern, in between a char that is not in the pattern */
3908 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
3909 if (sepchars[++i] == NUL)
3910 {
3911 i = 0; /* no good char found, just use the first one */
3912 break;
3913 }
3914 msg_putchar(sepchars[i]);
3915 msg_outtrans(spp->sp_pattern);
3916 msg_putchar(sepchars[i]);
3917
3918 /* output any pattern options */
3919 first = TRUE;
3920 for (i = 0; i < SPO_COUNT; ++i)
3921 {
3922 mask = (1 << i);
3923 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
3924 {
3925 if (!first)
3926 msg_putchar(','); /* separate with commas */
3927 msg_puts((char_u *)spo_name_tab[i]);
3928 n = spp->sp_offsets[i];
3929 if (i != SPO_LC_OFF)
3930 {
3931 if (spp->sp_off_flags & mask)
3932 msg_putchar('s');
3933 else
3934 msg_putchar('e');
3935 if (n > 0)
3936 msg_putchar('+');
3937 }
3938 if (n || i == SPO_LC_OFF)
3939 msg_outnum(n);
3940 first = FALSE;
3941 }
3942 }
3943 msg_putchar(' ');
3944}
3945
3946/*
3947 * List or clear the keywords for one syntax group.
3948 * Return TRUE if the header has been printed.
3949 */
3950 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00003951syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003952 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003953 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003954 int did_header; /* header has already been printed */
3955 int attr;
3956{
Bram Moolenaar071d4272004-06-13 20:20:40 +00003957 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003958 hashitem_T *hi;
3959 keyentry_T *kp;
3960 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003961 int prev_contained = 0;
3962 short *prev_next_list = NULL;
3963 short *prev_cont_in_list = NULL;
3964 int prev_skipnl = 0;
3965 int prev_skipwhite = 0;
3966 int prev_skipempty = 0;
3967
Bram Moolenaar071d4272004-06-13 20:20:40 +00003968 /*
3969 * Unfortunately, this list of keywords is not sorted on alphabet but on
3970 * hash value...
3971 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00003972 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003973 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003974 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003975 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003976 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003977 --todo;
3978 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003979 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003980 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003981 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003982 if (prev_contained != (kp->flags & HL_CONTAINED)
3983 || prev_skipnl != (kp->flags & HL_SKIPNL)
3984 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
3985 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
3986 || prev_cont_in_list != kp->k_syn.cont_in_list
3987 || prev_next_list != kp->next_list)
3988 outlen = 9999;
3989 else
3990 outlen = (int)STRLEN(kp->keyword);
3991 /* output "contained" and "nextgroup" on each line */
3992 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003993 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003994 prev_contained = 0;
3995 prev_next_list = NULL;
3996 prev_cont_in_list = NULL;
3997 prev_skipnl = 0;
3998 prev_skipwhite = 0;
3999 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004000 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004001 did_header = TRUE;
4002 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004003 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004004 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004006 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004007 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004008 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004009 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004010 put_id_list((char_u *)"containedin",
4011 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004012 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004013 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004014 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004015 if (kp->next_list != prev_next_list)
4016 {
4017 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4018 msg_putchar(' ');
4019 prev_next_list = kp->next_list;
4020 if (kp->flags & HL_SKIPNL)
4021 {
4022 msg_puts_attr((char_u *)"skipnl", attr);
4023 msg_putchar(' ');
4024 prev_skipnl = (kp->flags & HL_SKIPNL);
4025 }
4026 if (kp->flags & HL_SKIPWHITE)
4027 {
4028 msg_puts_attr((char_u *)"skipwhite", attr);
4029 msg_putchar(' ');
4030 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4031 }
4032 if (kp->flags & HL_SKIPEMPTY)
4033 {
4034 msg_puts_attr((char_u *)"skipempty", attr);
4035 msg_putchar(' ');
4036 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4037 }
4038 }
4039 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004041 }
4042 }
4043 }
4044
4045 return did_header;
4046}
4047
4048 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004049syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004050 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004051 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004052{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004053 hashitem_T *hi;
4054 keyentry_T *kp;
4055 keyentry_T *kp_prev;
4056 keyentry_T *kp_next;
4057 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004058
Bram Moolenaardad6b692005-01-25 22:14:34 +00004059 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004060 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004061 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004062 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004063 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004064 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004065 --todo;
4066 kp_prev = NULL;
4067 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004068 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004069 if (kp->k_syn.id == id)
4070 {
4071 kp_next = kp->ke_next;
4072 if (kp_prev == NULL)
4073 {
4074 if (kp_next == NULL)
4075 hash_remove(ht, hi);
4076 else
4077 hi->hi_key = KE2HIKEY(kp_next);
4078 }
4079 else
4080 kp_prev->ke_next = kp_next;
4081 vim_free(kp->next_list);
4082 vim_free(kp->k_syn.cont_in_list);
4083 vim_free(kp);
4084 kp = kp_next;
4085 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004086 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004087 {
4088 kp_prev = kp;
4089 kp = kp->ke_next;
4090 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004091 }
4092 }
4093 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004094 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004095}
4096
4097/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004098 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004099 */
4100 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004101clear_keywtab(ht)
4102 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004103{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004104 hashitem_T *hi;
4105 int todo;
4106 keyentry_T *kp;
4107 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004108
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004109 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004110 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004111 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004112 if (!HASHITEM_EMPTY(hi))
4113 {
4114 --todo;
4115 kp = HI2KE(hi);
4116 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004117 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004118 kp_next = kp->ke_next;
4119 vim_free(kp->next_list);
4120 vim_free(kp->k_syn.cont_in_list);
4121 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004122 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004123 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004124 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004125 hash_clear(ht);
4126 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004127}
4128
4129/*
4130 * Add a keyword to the list of keywords.
4131 */
4132 static void
4133add_keyword(name, id, flags, cont_in_list, next_list)
4134 char_u *name; /* name of keyword */
4135 int id; /* group ID for this keyword */
4136 int flags; /* flags for this keyword */
4137 short *cont_in_list; /* containedin for this keyword */
4138 short *next_list; /* nextgroup for this keyword */
4139{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004140 keyentry_T *kp;
4141 hashtab_T *ht;
4142 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004143 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004144 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004145 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004146
4147 if (curbuf->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004148 name_ic = str_foldcase(name, (int)STRLEN(name),
4149 name_folded, MAXKEYWLEN + 1);
4150 else
4151 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004152 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4153 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004154 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004155 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004156 kp->k_syn.id = id;
4157 kp->k_syn.inc_tag = current_syn_inc_tag;
4158 kp->flags = flags;
4159 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004160 if (cont_in_list != NULL)
4161 curbuf->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004162 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004163
4164 if (curbuf->b_syn_ic)
Bram Moolenaardad6b692005-01-25 22:14:34 +00004165 ht = &curbuf->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004166 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004167 ht = &curbuf->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004168
Bram Moolenaardad6b692005-01-25 22:14:34 +00004169 hash = hash_hash(kp->keyword);
4170 hi = hash_lookup(ht, kp->keyword, hash);
4171 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004172 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004173 /* new keyword, add to hashtable */
4174 kp->ke_next = NULL;
4175 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004176 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004177 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004178 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004179 /* keyword already exists, prepend to list */
4180 kp->ke_next = HI2KE(hi);
4181 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004182 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004183}
4184
4185/*
4186 * Get the start and end of the group name argument.
4187 * Return a pointer to the first argument.
4188 * Return NULL if the end of the command was found instead of further args.
4189 */
4190 static char_u *
4191get_group_name(arg, name_end)
4192 char_u *arg; /* start of the argument */
4193 char_u **name_end; /* pointer to end of the name */
4194{
4195 char_u *rest;
4196
4197 *name_end = skiptowhite(arg);
4198 rest = skipwhite(*name_end);
4199
4200 /*
4201 * Check if there are enough arguments. The first argument may be a
4202 * pattern, where '|' is allowed, so only check for NUL.
4203 */
4204 if (ends_excmd(*arg) || *rest == NUL)
4205 return NULL;
4206 return rest;
4207}
4208
4209/*
4210 * Check for syntax command option arguments.
4211 * This can be called at any place in the list of arguments, and just picks
4212 * out the arguments that are known. Can be called several times in a row to
4213 * collect all options in between other arguments.
4214 * Return a pointer to the next argument (which isn't an option).
4215 * Return NULL for any error;
4216 */
4217 static char_u *
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004218get_syn_options(arg, opt)
4219 char_u *arg; /* next argument to be checked */
4220 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004222 char_u *gname_start, *gname;
4223 int syn_id;
4224 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004225 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226 int i;
4227 int fidx;
4228 static struct flag
4229 {
4230 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004231 int argtype;
4232 int flags;
4233 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4234 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4235 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4236 {"eExXtTeEnNdD", 0, HL_EXTEND},
4237 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4238 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4239 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4240 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4241 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4242 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4243 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4244 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4245 {"fFoOlLdD", 0, HL_FOLD},
4246 {"cCoOnNtTaAiInNsS", 1, 0},
4247 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4248 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004249 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004250 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251
4252 if (arg == NULL) /* already detected error */
4253 return NULL;
4254
Bram Moolenaar071d4272004-06-13 20:20:40 +00004255 for (;;)
4256 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004257 /*
4258 * This is used very often when a large number of keywords is defined.
4259 * Need to skip quickly when no option name is found.
4260 * Also avoid tolower(), it's slow.
4261 */
4262 if (strchr(first_letters, *arg) == NULL)
4263 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004264
4265 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4266 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004267 p = flagtab[fidx].name;
4268 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4269 if (arg[len] != p[i] && arg[len] != p[i + 1])
4270 break;
4271 if (p[i] == NUL && (vim_iswhite(arg[len])
4272 || (flagtab[fidx].argtype > 0
4273 ? arg[len] == '='
4274 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004275 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004276 if (opt->keyword
4277 && (flagtab[fidx].flags == HL_DISPLAY
4278 || flagtab[fidx].flags == HL_FOLD
4279 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004280 /* treat "display", "fold" and "extend" as a keyword */
4281 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004282 break;
4283 }
4284 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004285 if (fidx < 0) /* no match found */
4286 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004287
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004288 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004289 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004290 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004291 {
4292 EMSG(_("E395: contains argument not accepted here"));
4293 return NULL;
4294 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004295 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004296 return NULL;
4297 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004298 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004299 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004300#if 0 /* cannot happen */
4301 if (opt->cont_in_list == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302 {
4303 EMSG(_("E396: containedin argument not accepted here"));
4304 return NULL;
4305 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004306#endif
4307 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004308 return NULL;
4309 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004310 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004311 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004312 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004313 return NULL;
4314 }
4315 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004316 {
4317 opt->flags |= flagtab[fidx].flags;
4318 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004319
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004320 if (flagtab[fidx].flags == HL_SYNC_HERE
4321 || flagtab[fidx].flags == HL_SYNC_THERE)
4322 {
4323 if (opt->sync_idx == NULL)
4324 {
4325 EMSG(_("E393: group[t]here not accepted here"));
4326 return NULL;
4327 }
4328 gname_start = arg;
4329 arg = skiptowhite(arg);
4330 if (gname_start == arg)
4331 return NULL;
4332 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4333 if (gname == NULL)
4334 return NULL;
4335 if (STRCMP(gname, "NONE") == 0)
4336 *opt->sync_idx = NONE_IDX;
4337 else
4338 {
4339 syn_id = syn_name2id(gname);
4340 for (i = curbuf->b_syn_patterns.ga_len; --i >= 0; )
4341 if (SYN_ITEMS(curbuf)[i].sp_syn.id == syn_id
4342 && SYN_ITEMS(curbuf)[i].sp_type == SPTYPE_START)
4343 {
4344 *opt->sync_idx = i;
4345 break;
4346 }
4347 if (i < 0)
4348 {
4349 EMSG2(_("E394: Didn't find region item for %s"), gname);
4350 vim_free(gname);
4351 return NULL;
4352 }
4353 }
4354
4355 vim_free(gname);
4356 arg = skipwhite(arg);
4357 }
4358#ifdef FEAT_FOLDING
4359 else if (flagtab[fidx].flags == HL_FOLD
4360 && foldmethodIsSyntax(curwin))
4361 /* Need to update folds later. */
4362 foldUpdateAll(curwin);
4363#endif
4364 }
4365 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004366
4367 return arg;
4368}
4369
4370/*
4371 * Adjustments to syntax item when declared in a ":syn include"'d file.
4372 * Set the contained flag, and if the item is not already contained, add it
4373 * to the specified top-level group, if any.
4374 */
4375 static void
4376syn_incl_toplevel(id, flagsp)
4377 int id;
4378 int *flagsp;
4379{
4380 if ((*flagsp & HL_CONTAINED) || curbuf->b_syn_topgrp == 0)
4381 return;
4382 *flagsp |= HL_CONTAINED;
4383 if (curbuf->b_syn_topgrp >= SYNID_CLUSTER)
4384 {
4385 /* We have to alloc this, because syn_combine_list() will free it. */
4386 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
4387 int tlg_id = curbuf->b_syn_topgrp - SYNID_CLUSTER;
4388
4389 if (grp_list != NULL)
4390 {
4391 grp_list[0] = id;
4392 grp_list[1] = 0;
4393 syn_combine_list(&SYN_CLSTR(curbuf)[tlg_id].scl_list, &grp_list,
4394 CLUSTER_ADD);
4395 }
4396 }
4397}
4398
4399/*
4400 * Handle ":syntax include [@{group-name}] filename" command.
4401 */
4402/* ARGSUSED */
4403 static void
4404syn_cmd_include(eap, syncing)
4405 exarg_T *eap;
4406 int syncing; /* not used */
4407{
4408 char_u *arg = eap->arg;
4409 int sgl_id = 1;
4410 char_u *group_name_end;
4411 char_u *rest;
4412 char_u *errormsg = NULL;
4413 int prev_toplvl_grp;
4414 int prev_syn_inc_tag;
4415 int source = FALSE;
4416
4417 eap->nextcmd = find_nextcmd(arg);
4418 if (eap->skip)
4419 return;
4420
4421 if (arg[0] == '@')
4422 {
4423 ++arg;
4424 rest = get_group_name(arg, &group_name_end);
4425 if (rest == NULL)
4426 {
4427 EMSG((char_u *)_("E397: Filename required"));
4428 return;
4429 }
4430 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
4431 /* separate_nextcmd() and expand_filename() depend on this */
4432 eap->arg = rest;
4433 }
4434
4435 /*
4436 * Everything that's left, up to the next command, should be the
4437 * filename to include.
4438 */
4439 eap->argt |= (XFILE | NOSPC);
4440 separate_nextcmd(eap);
4441 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4442 {
4443 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4444 * file. Need to expand the file name first. In other cases
4445 * ":runtime!" is used. */
4446 source = TRUE;
4447 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4448 {
4449 if (errormsg != NULL)
4450 EMSG(errormsg);
4451 return;
4452 }
4453 }
4454
4455 /*
4456 * Save and restore the existing top-level grouplist id and ":syn
4457 * include" tag around the actual inclusion.
4458 */
4459 prev_syn_inc_tag = current_syn_inc_tag;
4460 current_syn_inc_tag = ++running_syn_inc_tag;
4461 prev_toplvl_grp = curbuf->b_syn_topgrp;
4462 curbuf->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004463 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4464 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004465 EMSG2(_(e_notopen), eap->arg);
4466 curbuf->b_syn_topgrp = prev_toplvl_grp;
4467 current_syn_inc_tag = prev_syn_inc_tag;
4468}
4469
4470/*
4471 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4472 */
4473/* ARGSUSED */
4474 static void
4475syn_cmd_keyword(eap, syncing)
4476 exarg_T *eap;
4477 int syncing; /* not used */
4478{
4479 char_u *arg = eap->arg;
4480 char_u *group_name_end;
4481 int syn_id;
4482 char_u *rest;
4483 char_u *keyword_copy;
4484 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004485 char_u *kw;
4486 syn_opt_arg_T syn_opt_arg;
4487 int cnt;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004488
4489 rest = get_group_name(arg, &group_name_end);
4490
4491 if (rest != NULL)
4492 {
4493 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
4494
4495 /* allocate a buffer, for removing the backslashes in the keyword */
4496 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
4497 if (keyword_copy != NULL)
4498 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004499 syn_opt_arg.flags = 0;
4500 syn_opt_arg.keyword = TRUE;
4501 syn_opt_arg.sync_idx = NULL;
4502 syn_opt_arg.has_cont_list = FALSE;
4503 syn_opt_arg.cont_in_list = NULL;
4504 syn_opt_arg.next_list = NULL;
4505
Bram Moolenaar071d4272004-06-13 20:20:40 +00004506 /*
4507 * The options given apply to ALL keywords, so all options must be
4508 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004509 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004511 cnt = 0;
4512 p = keyword_copy;
4513 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004515 rest = get_syn_options(rest, &syn_opt_arg);
4516 if (rest == NULL || ends_excmd(*rest))
4517 break;
4518 /* Copy the keyword, removing backslashes, and add a NUL. */
4519 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004520 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004521 if (*rest == '\\' && rest[1] != NUL)
4522 ++rest;
4523 *p++ = *rest++;
4524 }
4525 *p++ = NUL;
4526 ++cnt;
4527 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004528
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004529 if (!eap->skip)
4530 {
4531 /* Adjust flags for use of ":syn include". */
4532 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4533
4534 /*
4535 * 2: Add an entry for each keyword.
4536 */
4537 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4538 {
4539 for (p = vim_strchr(kw, '['); ; )
4540 {
4541 if (p != NULL)
4542 *p = NUL;
4543 add_keyword(kw, syn_id, syn_opt_arg.flags,
4544 syn_opt_arg.cont_in_list,
4545 syn_opt_arg.next_list);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004546 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004547 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004548 if (p[1] == NUL)
4549 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004550 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004551 kw = p + 2; /* skip over the NUL */
4552 break;
4553 }
4554 if (p[1] == ']')
4555 {
4556 kw = p + 1; /* skip over the "]" */
4557 break;
4558 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004559#ifdef FEAT_MBYTE
4560 if (has_mbyte)
4561 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004562 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004563
4564 mch_memmove(p, p + 1, l);
4565 p += l;
4566 }
4567 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004569 {
4570 p[0] = p[1];
4571 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572 }
4573 }
4574 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004576
Bram Moolenaar071d4272004-06-13 20:20:40 +00004577 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004578 vim_free(syn_opt_arg.cont_in_list);
4579 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580 }
4581 }
4582
4583 if (rest != NULL)
4584 eap->nextcmd = check_nextcmd(rest);
4585 else
4586 EMSG2(_(e_invarg2), arg);
4587
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004588 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004589 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4590}
4591
4592/*
4593 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4594 *
4595 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4596 */
4597 static void
4598syn_cmd_match(eap, syncing)
4599 exarg_T *eap;
4600 int syncing; /* TRUE for ":syntax sync match .. " */
4601{
4602 char_u *arg = eap->arg;
4603 char_u *group_name_end;
4604 char_u *rest;
4605 synpat_T item; /* the item found in the line */
4606 int syn_id;
4607 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004608 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004609 int sync_idx = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004610
4611 /* Isolate the group name, check for validity */
4612 rest = get_group_name(arg, &group_name_end);
4613
4614 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004615 syn_opt_arg.flags = 0;
4616 syn_opt_arg.keyword = FALSE;
4617 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4618 syn_opt_arg.has_cont_list = TRUE;
4619 syn_opt_arg.cont_list = NULL;
4620 syn_opt_arg.cont_in_list = NULL;
4621 syn_opt_arg.next_list = NULL;
4622 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004623
4624 /* get the pattern. */
4625 init_syn_patterns();
4626 vim_memset(&item, 0, sizeof(item));
4627 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004628 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4629 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004630
4631 /* Get options after the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004632 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004633
4634 if (rest != NULL) /* all arguments are valid */
4635 {
4636 /*
4637 * Check for trailing command and illegal trailing arguments.
4638 */
4639 eap->nextcmd = check_nextcmd(rest);
4640 if (!ends_excmd(*rest) || eap->skip)
4641 rest = NULL;
4642 else if (ga_grow(&curbuf->b_syn_patterns, 1) != FAIL
4643 && (syn_id = syn_check_group(arg,
4644 (int)(group_name_end - arg))) != 0)
4645 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004646 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004647 /*
4648 * Store the pattern in the syn_items list
4649 */
4650 idx = curbuf->b_syn_patterns.ga_len;
4651 SYN_ITEMS(curbuf)[idx] = item;
4652 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4653 SYN_ITEMS(curbuf)[idx].sp_type = SPTYPE_MATCH;
4654 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4655 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004656 SYN_ITEMS(curbuf)[idx].sp_flags = syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004657 SYN_ITEMS(curbuf)[idx].sp_sync_idx = sync_idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004658 SYN_ITEMS(curbuf)[idx].sp_cont_list = syn_opt_arg.cont_list;
4659 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
4660 syn_opt_arg.cont_in_list;
4661 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004662 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004663 SYN_ITEMS(curbuf)[idx].sp_next_list = syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004664 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004665
4666 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004667 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004668 curbuf->b_syn_sync_flags |= SF_MATCH;
4669#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004670 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004671 ++curbuf->b_syn_folditems;
4672#endif
4673
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004674 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004675 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4676 return; /* don't free the progs and patterns now */
4677 }
4678 }
4679
4680 /*
4681 * Something failed, free the allocated memory.
4682 */
4683 vim_free(item.sp_prog);
4684 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004685 vim_free(syn_opt_arg.cont_list);
4686 vim_free(syn_opt_arg.cont_in_list);
4687 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688
4689 if (rest == NULL)
4690 EMSG2(_(e_invarg2), arg);
4691}
4692
4693/*
4694 * Handle ":syntax region {group-name} [matchgroup={group-name}]
4695 * start {start} .. [skip {skip}] end {end} .. [{options}]".
4696 */
4697 static void
4698syn_cmd_region(eap, syncing)
4699 exarg_T *eap;
4700 int syncing; /* TRUE for ":syntax sync region .." */
4701{
4702 char_u *arg = eap->arg;
4703 char_u *group_name_end;
4704 char_u *rest; /* next arg, NULL on error */
4705 char_u *key_end;
4706 char_u *key = NULL;
4707 char_u *p;
4708 int item;
4709#define ITEM_START 0
4710#define ITEM_SKIP 1
4711#define ITEM_END 2
4712#define ITEM_MATCHGROUP 3
4713 struct pat_ptr
4714 {
4715 synpat_T *pp_synp; /* pointer to syn_pattern */
4716 int pp_matchgroup_id; /* matchgroup ID */
4717 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
4718 } *(pat_ptrs[3]);
4719 /* patterns found in the line */
4720 struct pat_ptr *ppp;
4721 struct pat_ptr *ppp_next;
4722 int pat_count = 0; /* nr of syn_patterns found */
4723 int syn_id;
4724 int matchgroup_id = 0;
4725 int not_enough = FALSE; /* not enough arguments */
4726 int illegal = FALSE; /* illegal arguments */
4727 int success = FALSE;
4728 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004729 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004730
4731 /* Isolate the group name, check for validity */
4732 rest = get_group_name(arg, &group_name_end);
4733
4734 pat_ptrs[0] = NULL;
4735 pat_ptrs[1] = NULL;
4736 pat_ptrs[2] = NULL;
4737
4738 init_syn_patterns();
4739
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004740 syn_opt_arg.flags = 0;
4741 syn_opt_arg.keyword = FALSE;
4742 syn_opt_arg.sync_idx = NULL;
4743 syn_opt_arg.has_cont_list = TRUE;
4744 syn_opt_arg.cont_list = NULL;
4745 syn_opt_arg.cont_in_list = NULL;
4746 syn_opt_arg.next_list = NULL;
4747
Bram Moolenaar071d4272004-06-13 20:20:40 +00004748 /*
4749 * get the options, patterns and matchgroup.
4750 */
4751 while (rest != NULL && !ends_excmd(*rest))
4752 {
4753 /* Check for option arguments */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004754 rest = get_syn_options(rest, &syn_opt_arg);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004755 if (rest == NULL || ends_excmd(*rest))
4756 break;
4757
4758 /* must be a pattern or matchgroup then */
4759 key_end = rest;
4760 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
4761 ++key_end;
4762 vim_free(key);
4763 key = vim_strnsave_up(rest, (int)(key_end - rest));
4764 if (key == NULL) /* out of memory */
4765 {
4766 rest = NULL;
4767 break;
4768 }
4769 if (STRCMP(key, "MATCHGROUP") == 0)
4770 item = ITEM_MATCHGROUP;
4771 else if (STRCMP(key, "START") == 0)
4772 item = ITEM_START;
4773 else if (STRCMP(key, "END") == 0)
4774 item = ITEM_END;
4775 else if (STRCMP(key, "SKIP") == 0)
4776 {
4777 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
4778 {
4779 illegal = TRUE;
4780 break;
4781 }
4782 item = ITEM_SKIP;
4783 }
4784 else
4785 break;
4786 rest = skipwhite(key_end);
4787 if (*rest != '=')
4788 {
4789 rest = NULL;
4790 EMSG2(_("E398: Missing '=': %s"), arg);
4791 break;
4792 }
4793 rest = skipwhite(rest + 1);
4794 if (*rest == NUL)
4795 {
4796 not_enough = TRUE;
4797 break;
4798 }
4799
4800 if (item == ITEM_MATCHGROUP)
4801 {
4802 p = skiptowhite(rest);
4803 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
4804 matchgroup_id = 0;
4805 else
4806 {
4807 matchgroup_id = syn_check_group(rest, (int)(p - rest));
4808 if (matchgroup_id == 0)
4809 {
4810 illegal = TRUE;
4811 break;
4812 }
4813 }
4814 rest = skipwhite(p);
4815 }
4816 else
4817 {
4818 /*
4819 * Allocate room for a syn_pattern, and link it in the list of
4820 * syn_patterns for this item, at the start (because the list is
4821 * used from end to start).
4822 */
4823 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
4824 if (ppp == NULL)
4825 {
4826 rest = NULL;
4827 break;
4828 }
4829 ppp->pp_next = pat_ptrs[item];
4830 pat_ptrs[item] = ppp;
4831 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
4832 if (ppp->pp_synp == NULL)
4833 {
4834 rest = NULL;
4835 break;
4836 }
4837
4838 /*
4839 * Get the syntax pattern and the following offset(s).
4840 */
4841 /* Enable the appropriate \z specials. */
4842 if (item == ITEM_START)
4843 reg_do_extmatch = REX_SET;
4844 else if (item == ITEM_SKIP || item == ITEM_END)
4845 reg_do_extmatch = REX_USE;
4846 rest = get_syn_pattern(rest, ppp->pp_synp);
4847 reg_do_extmatch = 0;
4848 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004849 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004850 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
4851 ppp->pp_matchgroup_id = matchgroup_id;
4852 ++pat_count;
4853 }
4854 }
4855 vim_free(key);
4856 if (illegal || not_enough)
4857 rest = NULL;
4858
4859 /*
4860 * Must have a "start" and "end" pattern.
4861 */
4862 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
4863 pat_ptrs[ITEM_END] == NULL))
4864 {
4865 not_enough = TRUE;
4866 rest = NULL;
4867 }
4868
4869 if (rest != NULL)
4870 {
4871 /*
4872 * Check for trailing garbage or command.
4873 * If OK, add the item.
4874 */
4875 eap->nextcmd = check_nextcmd(rest);
4876 if (!ends_excmd(*rest) || eap->skip)
4877 rest = NULL;
4878 else if (ga_grow(&(curbuf->b_syn_patterns), pat_count) != FAIL
4879 && (syn_id = syn_check_group(arg,
4880 (int)(group_name_end - arg))) != 0)
4881 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004882 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004883 /*
4884 * Store the start/skip/end in the syn_items list
4885 */
4886 idx = curbuf->b_syn_patterns.ga_len;
4887 for (item = ITEM_START; item <= ITEM_END; ++item)
4888 {
4889 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
4890 {
4891 SYN_ITEMS(curbuf)[idx] = *(ppp->pp_synp);
4892 SYN_ITEMS(curbuf)[idx].sp_syncing = syncing;
4893 SYN_ITEMS(curbuf)[idx].sp_type =
4894 (item == ITEM_START) ? SPTYPE_START :
4895 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004896 SYN_ITEMS(curbuf)[idx].sp_flags |= syn_opt_arg.flags;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897 SYN_ITEMS(curbuf)[idx].sp_syn.id = syn_id;
4898 SYN_ITEMS(curbuf)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4899 SYN_ITEMS(curbuf)[idx].sp_syn_match_id =
4900 ppp->pp_matchgroup_id;
4901 if (item == ITEM_START)
4902 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004903 SYN_ITEMS(curbuf)[idx].sp_cont_list =
4904 syn_opt_arg.cont_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 SYN_ITEMS(curbuf)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004906 syn_opt_arg.cont_in_list;
4907 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908 curbuf->b_syn_containedin = TRUE;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004909 SYN_ITEMS(curbuf)[idx].sp_next_list =
4910 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004911 }
4912 ++curbuf->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913 ++idx;
4914#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004915 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916 ++curbuf->b_syn_folditems;
4917#endif
4918 }
4919 }
4920
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004921 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004922 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
4923 success = TRUE; /* don't free the progs and patterns now */
4924 }
4925 }
4926
4927 /*
4928 * Free the allocated memory.
4929 */
4930 for (item = ITEM_START; item <= ITEM_END; ++item)
4931 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
4932 {
4933 if (!success)
4934 {
4935 vim_free(ppp->pp_synp->sp_prog);
4936 vim_free(ppp->pp_synp->sp_pattern);
4937 }
4938 vim_free(ppp->pp_synp);
4939 ppp_next = ppp->pp_next;
4940 vim_free(ppp);
4941 }
4942
4943 if (!success)
4944 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004945 vim_free(syn_opt_arg.cont_list);
4946 vim_free(syn_opt_arg.cont_in_list);
4947 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004948 if (not_enough)
4949 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
4950 else if (illegal || rest == NULL)
4951 EMSG2(_(e_invarg2), arg);
4952 }
4953}
4954
4955/*
4956 * A simple syntax group ID comparison function suitable for use in qsort()
4957 */
4958 static int
4959#ifdef __BORLANDC__
4960_RTLENTRYF
4961#endif
4962syn_compare_stub(v1, v2)
4963 const void *v1;
4964 const void *v2;
4965{
4966 const short *s1 = v1;
4967 const short *s2 = v2;
4968
4969 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
4970}
4971
4972/*
4973 * Combines lists of syntax clusters.
4974 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
4975 */
4976 static void
4977syn_combine_list(clstr1, clstr2, list_op)
4978 short **clstr1;
4979 short **clstr2;
4980 int list_op;
4981{
4982 int count1 = 0;
4983 int count2 = 0;
4984 short *g1;
4985 short *g2;
4986 short *clstr = NULL;
4987 int count;
4988 int round;
4989
4990 /*
4991 * Handle degenerate cases.
4992 */
4993 if (*clstr2 == NULL)
4994 return;
4995 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
4996 {
4997 if (list_op == CLUSTER_REPLACE)
4998 vim_free(*clstr1);
4999 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5000 *clstr1 = *clstr2;
5001 else
5002 vim_free(*clstr2);
5003 return;
5004 }
5005
5006 for (g1 = *clstr1; *g1; g1++)
5007 ++count1;
5008 for (g2 = *clstr2; *g2; g2++)
5009 ++count2;
5010
5011 /*
5012 * For speed purposes, sort both lists.
5013 */
5014 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5015 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5016
5017 /*
5018 * We proceed in two passes; in round 1, we count the elements to place
5019 * in the new list, and in round 2, we allocate and populate the new
5020 * list. For speed, we use a mergesort-like method, adding the smaller
5021 * of the current elements in each list to the new list.
5022 */
5023 for (round = 1; round <= 2; round++)
5024 {
5025 g1 = *clstr1;
5026 g2 = *clstr2;
5027 count = 0;
5028
5029 /*
5030 * First, loop through the lists until one of them is empty.
5031 */
5032 while (*g1 && *g2)
5033 {
5034 /*
5035 * We always want to add from the first list.
5036 */
5037 if (*g1 < *g2)
5038 {
5039 if (round == 2)
5040 clstr[count] = *g1;
5041 count++;
5042 g1++;
5043 continue;
5044 }
5045 /*
5046 * We only want to add from the second list if we're adding the
5047 * lists.
5048 */
5049 if (list_op == CLUSTER_ADD)
5050 {
5051 if (round == 2)
5052 clstr[count] = *g2;
5053 count++;
5054 }
5055 if (*g1 == *g2)
5056 g1++;
5057 g2++;
5058 }
5059
5060 /*
5061 * Now add the leftovers from whichever list didn't get finished
5062 * first. As before, we only want to add from the second list if
5063 * we're adding the lists.
5064 */
5065 for (; *g1; g1++, count++)
5066 if (round == 2)
5067 clstr[count] = *g1;
5068 if (list_op == CLUSTER_ADD)
5069 for (; *g2; g2++, count++)
5070 if (round == 2)
5071 clstr[count] = *g2;
5072
5073 if (round == 1)
5074 {
5075 /*
5076 * If the group ended up empty, we don't need to allocate any
5077 * space for it.
5078 */
5079 if (count == 0)
5080 {
5081 clstr = NULL;
5082 break;
5083 }
5084 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5085 if (clstr == NULL)
5086 break;
5087 clstr[count] = 0;
5088 }
5089 }
5090
5091 /*
5092 * Finally, put the new list in place.
5093 */
5094 vim_free(*clstr1);
5095 vim_free(*clstr2);
5096 *clstr1 = clstr;
5097}
5098
5099/*
5100 * Lookup a syntax cluster name and return it's ID.
5101 * If it is not found, 0 is returned.
5102 */
5103 static int
5104syn_scl_name2id(name)
5105 char_u *name;
5106{
5107 int i;
5108 char_u *name_u;
5109
5110 /* Avoid using stricmp() too much, it's slow on some systems */
5111 name_u = vim_strsave_up(name);
5112 if (name_u == NULL)
5113 return 0;
5114 for (i = curbuf->b_syn_clusters.ga_len; --i >= 0; )
5115 if (SYN_CLSTR(curbuf)[i].scl_name_u != NULL
5116 && STRCMP(name_u, SYN_CLSTR(curbuf)[i].scl_name_u) == 0)
5117 break;
5118 vim_free(name_u);
5119 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5120}
5121
5122/*
5123 * Like syn_scl_name2id(), but take a pointer + length argument.
5124 */
5125 static int
5126syn_scl_namen2id(linep, len)
5127 char_u *linep;
5128 int len;
5129{
5130 char_u *name;
5131 int id = 0;
5132
5133 name = vim_strnsave(linep, len);
5134 if (name != NULL)
5135 {
5136 id = syn_scl_name2id(name);
5137 vim_free(name);
5138 }
5139 return id;
5140}
5141
5142/*
5143 * Find syntax cluster name in the table and return it's ID.
5144 * The argument is a pointer to the name and the length of the name.
5145 * If it doesn't exist yet, a new entry is created.
5146 * Return 0 for failure.
5147 */
5148 static int
5149syn_check_cluster(pp, len)
5150 char_u *pp;
5151 int len;
5152{
5153 int id;
5154 char_u *name;
5155
5156 name = vim_strnsave(pp, len);
5157 if (name == NULL)
5158 return 0;
5159
5160 id = syn_scl_name2id(name);
5161 if (id == 0) /* doesn't exist yet */
5162 id = syn_add_cluster(name);
5163 else
5164 vim_free(name);
5165 return id;
5166}
5167
5168/*
5169 * Add new syntax cluster and return it's ID.
5170 * "name" must be an allocated string, it will be consumed.
5171 * Return 0 for failure.
5172 */
5173 static int
5174syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005175 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005176{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005177 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005178
5179 /*
5180 * First call for this growarray: init growing array.
5181 */
5182 if (curbuf->b_syn_clusters.ga_data == NULL)
5183 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00005184 curbuf->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005185 curbuf->b_syn_clusters.ga_growsize = 10;
5186 }
5187
5188 /*
5189 * Make room for at least one other cluster entry.
5190 */
5191 if (ga_grow(&curbuf->b_syn_clusters, 1) == FAIL)
5192 {
5193 vim_free(name);
5194 return 0;
5195 }
5196 len = curbuf->b_syn_clusters.ga_len;
5197
Bram Moolenaar217ad922005-03-20 22:37:15 +00005198 vim_memset(&(SYN_CLSTR(curbuf)[len]), 0, sizeof(syn_cluster_T));
Bram Moolenaar071d4272004-06-13 20:20:40 +00005199 SYN_CLSTR(curbuf)[len].scl_name = name;
5200 SYN_CLSTR(curbuf)[len].scl_name_u = vim_strsave_up(name);
5201 SYN_CLSTR(curbuf)[len].scl_list = NULL;
5202 ++curbuf->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005203
Bram Moolenaar217ad922005-03-20 22:37:15 +00005204 if (STRICMP(name, "Spell") == 0)
5205 curbuf->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005206 if (STRICMP(name, "NoSpell") == 0)
5207 curbuf->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005208
Bram Moolenaar071d4272004-06-13 20:20:40 +00005209 return len + SYNID_CLUSTER;
5210}
5211
5212/*
5213 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5214 * [add={groupname},..] [remove={groupname},..]".
5215 */
5216/* ARGSUSED */
5217 static void
5218syn_cmd_cluster(eap, syncing)
5219 exarg_T *eap;
5220 int syncing; /* not used */
5221{
5222 char_u *arg = eap->arg;
5223 char_u *group_name_end;
5224 char_u *rest;
5225 int scl_id;
5226 short *clstr_list;
5227 int got_clstr = FALSE;
5228 int opt_len;
5229 int list_op;
5230
5231 eap->nextcmd = find_nextcmd(arg);
5232 if (eap->skip)
5233 return;
5234
5235 rest = get_group_name(arg, &group_name_end);
5236
5237 if (rest != NULL)
5238 {
5239 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg))
Bram Moolenaar217ad922005-03-20 22:37:15 +00005240 - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005241
5242 for (;;)
5243 {
5244 if (STRNICMP(rest, "add", 3) == 0
5245 && (vim_iswhite(rest[3]) || rest[3] == '='))
5246 {
5247 opt_len = 3;
5248 list_op = CLUSTER_ADD;
5249 }
5250 else if (STRNICMP(rest, "remove", 6) == 0
5251 && (vim_iswhite(rest[6]) || rest[6] == '='))
5252 {
5253 opt_len = 6;
5254 list_op = CLUSTER_SUBTRACT;
5255 }
5256 else if (STRNICMP(rest, "contains", 8) == 0
5257 && (vim_iswhite(rest[8]) || rest[8] == '='))
5258 {
5259 opt_len = 8;
5260 list_op = CLUSTER_REPLACE;
5261 }
5262 else
5263 break;
5264
5265 clstr_list = NULL;
5266 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5267 {
5268 EMSG2(_(e_invarg2), rest);
5269 break;
5270 }
5271 syn_combine_list(&SYN_CLSTR(curbuf)[scl_id].scl_list,
5272 &clstr_list, list_op);
5273 got_clstr = TRUE;
5274 }
5275
5276 if (got_clstr)
5277 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005278 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005279 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5280 }
5281 }
5282
5283 if (!got_clstr)
5284 EMSG(_("E400: No cluster specified"));
5285 if (rest == NULL || !ends_excmd(*rest))
5286 EMSG2(_(e_invarg2), arg);
5287}
5288
5289/*
5290 * On first call for current buffer: Init growing array.
5291 */
5292 static void
5293init_syn_patterns()
5294{
5295 curbuf->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5296 curbuf->b_syn_patterns.ga_growsize = 10;
5297}
5298
5299/*
5300 * Get one pattern for a ":syntax match" or ":syntax region" command.
5301 * Stores the pattern and program in a synpat_T.
5302 * Returns a pointer to the next argument, or NULL in case of an error.
5303 */
5304 static char_u *
5305get_syn_pattern(arg, ci)
5306 char_u *arg;
5307 synpat_T *ci;
5308{
5309 char_u *end;
5310 int *p;
5311 int idx;
5312 char_u *cpo_save;
5313
5314 /* need at least three chars */
5315 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5316 return NULL;
5317
5318 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5319 if (*end != *arg) /* end delimiter not found */
5320 {
5321 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5322 return NULL;
5323 }
5324 /* store the pattern and compiled regexp program */
5325 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5326 return NULL;
5327
5328 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5329 cpo_save = p_cpo;
5330 p_cpo = (char_u *)"";
5331 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5332 p_cpo = cpo_save;
5333
5334 if (ci->sp_prog == NULL)
5335 return NULL;
5336 ci->sp_ic = curbuf->b_syn_ic;
5337
5338 /*
5339 * Check for a match, highlight or region offset.
5340 */
5341 ++end;
5342 do
5343 {
5344 for (idx = SPO_COUNT; --idx >= 0; )
5345 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5346 break;
5347 if (idx >= 0)
5348 {
5349 p = &(ci->sp_offsets[idx]);
5350 if (idx != SPO_LC_OFF)
5351 switch (end[3])
5352 {
5353 case 's': break;
5354 case 'b': break;
5355 case 'e': idx += SPO_COUNT; break;
5356 default: idx = -1; break;
5357 }
5358 if (idx >= 0)
5359 {
5360 ci->sp_off_flags |= (1 << idx);
5361 if (idx == SPO_LC_OFF) /* lc=99 */
5362 {
5363 end += 3;
5364 *p = getdigits(&end);
5365
5366 /* "lc=" offset automatically sets "ms=" offset */
5367 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5368 {
5369 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5370 ci->sp_offsets[SPO_MS_OFF] = *p;
5371 }
5372 }
5373 else /* yy=x+99 */
5374 {
5375 end += 4;
5376 if (*end == '+')
5377 {
5378 ++end;
5379 *p = getdigits(&end); /* positive offset */
5380 }
5381 else if (*end == '-')
5382 {
5383 ++end;
5384 *p = -getdigits(&end); /* negative offset */
5385 }
5386 }
5387 if (*end != ',')
5388 break;
5389 ++end;
5390 }
5391 }
5392 } while (idx >= 0);
5393
5394 if (!ends_excmd(*end) && !vim_iswhite(*end))
5395 {
5396 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5397 return NULL;
5398 }
5399 return skipwhite(end);
5400}
5401
5402/*
5403 * Handle ":syntax sync .." command.
5404 */
5405/* ARGSUSED */
5406 static void
5407syn_cmd_sync(eap, syncing)
5408 exarg_T *eap;
5409 int syncing; /* not used */
5410{
5411 char_u *arg_start = eap->arg;
5412 char_u *arg_end;
5413 char_u *key = NULL;
5414 char_u *next_arg;
5415 int illegal = FALSE;
5416 int finished = FALSE;
5417 long n;
5418 char_u *cpo_save;
5419
5420 if (ends_excmd(*arg_start))
5421 {
5422 syn_cmd_list(eap, TRUE);
5423 return;
5424 }
5425
5426 while (!ends_excmd(*arg_start))
5427 {
5428 arg_end = skiptowhite(arg_start);
5429 next_arg = skipwhite(arg_end);
5430 vim_free(key);
5431 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5432 if (STRCMP(key, "CCOMMENT") == 0)
5433 {
5434 if (!eap->skip)
5435 curbuf->b_syn_sync_flags |= SF_CCOMMENT;
5436 if (!ends_excmd(*next_arg))
5437 {
5438 arg_end = skiptowhite(next_arg);
5439 if (!eap->skip)
5440 curbuf->b_syn_sync_id = syn_check_group(next_arg,
5441 (int)(arg_end - next_arg));
5442 next_arg = skipwhite(arg_end);
5443 }
5444 else if (!eap->skip)
5445 curbuf->b_syn_sync_id = syn_name2id((char_u *)"Comment");
5446 }
5447 else if ( STRNCMP(key, "LINES", 5) == 0
5448 || STRNCMP(key, "MINLINES", 8) == 0
5449 || STRNCMP(key, "MAXLINES", 8) == 0
5450 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5451 {
5452 if (key[4] == 'S')
5453 arg_end = key + 6;
5454 else if (key[0] == 'L')
5455 arg_end = key + 11;
5456 else
5457 arg_end = key + 9;
5458 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5459 {
5460 illegal = TRUE;
5461 break;
5462 }
5463 n = getdigits(&arg_end);
5464 if (!eap->skip)
5465 {
5466 if (key[4] == 'B')
5467 curbuf->b_syn_sync_linebreaks = n;
5468 else if (key[1] == 'A')
5469 curbuf->b_syn_sync_maxlines = n;
5470 else
5471 curbuf->b_syn_sync_minlines = n;
5472 }
5473 }
5474 else if (STRCMP(key, "FROMSTART") == 0)
5475 {
5476 if (!eap->skip)
5477 {
5478 curbuf->b_syn_sync_minlines = MAXLNUM;
5479 curbuf->b_syn_sync_maxlines = 0;
5480 }
5481 }
5482 else if (STRCMP(key, "LINECONT") == 0)
5483 {
5484 if (curbuf->b_syn_linecont_pat != NULL)
5485 {
5486 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5487 finished = TRUE;
5488 break;
5489 }
5490 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5491 if (*arg_end != *next_arg) /* end delimiter not found */
5492 {
5493 illegal = TRUE;
5494 break;
5495 }
5496
5497 if (!eap->skip)
5498 {
5499 /* store the pattern and compiled regexp program */
5500 if ((curbuf->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
5501 (int)(arg_end - next_arg - 1))) == NULL)
5502 {
5503 finished = TRUE;
5504 break;
5505 }
5506 curbuf->b_syn_linecont_ic = curbuf->b_syn_ic;
5507
5508 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5509 cpo_save = p_cpo;
5510 p_cpo = (char_u *)"";
5511 curbuf->b_syn_linecont_prog =
5512 vim_regcomp(curbuf->b_syn_linecont_pat, RE_MAGIC);
5513 p_cpo = cpo_save;
5514
5515 if (curbuf->b_syn_linecont_prog == NULL)
5516 {
5517 vim_free(curbuf->b_syn_linecont_pat);
5518 curbuf->b_syn_linecont_pat = NULL;
5519 finished = TRUE;
5520 break;
5521 }
5522 }
5523 next_arg = skipwhite(arg_end + 1);
5524 }
5525 else
5526 {
5527 eap->arg = next_arg;
5528 if (STRCMP(key, "MATCH") == 0)
5529 syn_cmd_match(eap, TRUE);
5530 else if (STRCMP(key, "REGION") == 0)
5531 syn_cmd_region(eap, TRUE);
5532 else if (STRCMP(key, "CLEAR") == 0)
5533 syn_cmd_clear(eap, TRUE);
5534 else
5535 illegal = TRUE;
5536 finished = TRUE;
5537 break;
5538 }
5539 arg_start = next_arg;
5540 }
5541 vim_free(key);
5542 if (illegal)
5543 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5544 else if (!finished)
5545 {
5546 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005547 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005548 syn_stack_free_all(curbuf); /* Need to recompute all syntax. */
5549 }
5550}
5551
5552/*
5553 * Convert a line of highlight group names into a list of group ID numbers.
5554 * "arg" should point to the "contains" or "nextgroup" keyword.
5555 * "arg" is advanced to after the last group name.
5556 * Careful: the argument is modified (NULs added).
5557 * returns FAIL for some error, OK for success.
5558 */
5559 static int
5560get_id_list(arg, keylen, list)
5561 char_u **arg;
5562 int keylen; /* length of keyword */
5563 short **list; /* where to store the resulting list, if not
5564 NULL, the list is silently skipped! */
5565{
5566 char_u *p = NULL;
5567 char_u *end;
5568 int round;
5569 int count;
5570 int total_count = 0;
5571 short *retval = NULL;
5572 char_u *name;
5573 regmatch_T regmatch;
5574 int id;
5575 int i;
5576 int failed = FALSE;
5577
5578 /*
5579 * We parse the list twice:
5580 * round == 1: count the number of items, allocate the array.
5581 * round == 2: fill the array with the items.
5582 * In round 1 new groups may be added, causing the number of items to
5583 * grow when a regexp is used. In that case round 1 is done once again.
5584 */
5585 for (round = 1; round <= 2; ++round)
5586 {
5587 /*
5588 * skip "contains"
5589 */
5590 p = skipwhite(*arg + keylen);
5591 if (*p != '=')
5592 {
5593 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5594 break;
5595 }
5596 p = skipwhite(p + 1);
5597 if (ends_excmd(*p))
5598 {
5599 EMSG2(_("E406: Empty argument: %s"), *arg);
5600 break;
5601 }
5602
5603 /*
5604 * parse the arguments after "contains"
5605 */
5606 count = 0;
5607 while (!ends_excmd(*p))
5608 {
5609 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5610 ;
5611 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5612 if (name == NULL)
5613 {
5614 failed = TRUE;
5615 break;
5616 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005617 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005618 if ( STRCMP(name + 1, "ALLBUT") == 0
5619 || STRCMP(name + 1, "ALL") == 0
5620 || STRCMP(name + 1, "TOP") == 0
5621 || STRCMP(name + 1, "CONTAINED") == 0)
5622 {
5623 if (TOUPPER_ASC(**arg) != 'C')
5624 {
5625 EMSG2(_("E407: %s not allowed here"), name + 1);
5626 failed = TRUE;
5627 vim_free(name);
5628 break;
5629 }
5630 if (count != 0)
5631 {
5632 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5633 failed = TRUE;
5634 vim_free(name);
5635 break;
5636 }
5637 if (name[1] == 'A')
5638 id = SYNID_ALLBUT;
5639 else if (name[1] == 'T')
5640 id = SYNID_TOP;
5641 else
5642 id = SYNID_CONTAINED;
5643 id += current_syn_inc_tag;
5644 }
5645 else if (name[1] == '@')
5646 {
5647 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5648 }
5649 else
5650 {
5651 /*
5652 * Handle full group name.
5653 */
5654 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5655 id = syn_check_group(name + 1, (int)(end - p));
5656 else
5657 {
5658 /*
5659 * Handle match of regexp with group names.
5660 */
5661 *name = '^';
5662 STRCAT(name, "$");
5663 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5664 if (regmatch.regprog == NULL)
5665 {
5666 failed = TRUE;
5667 vim_free(name);
5668 break;
5669 }
5670
5671 regmatch.rm_ic = TRUE;
5672 id = 0;
5673 for (i = highlight_ga.ga_len; --i >= 0; )
5674 {
5675 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
5676 (colnr_T)0))
5677 {
5678 if (round == 2)
5679 {
5680 /* Got more items than expected; can happen
5681 * when adding items that match:
5682 * "contains=a.*b,axb".
5683 * Go back to first round */
5684 if (count >= total_count)
5685 {
5686 vim_free(retval);
5687 round = 1;
5688 }
5689 else
5690 retval[count] = i + 1;
5691 }
5692 ++count;
5693 id = -1; /* remember that we found one */
5694 }
5695 }
5696 vim_free(regmatch.regprog);
5697 }
5698 }
5699 vim_free(name);
5700 if (id == 0)
5701 {
5702 EMSG2(_("E409: Unknown group name: %s"), p);
5703 failed = TRUE;
5704 break;
5705 }
5706 if (id > 0)
5707 {
5708 if (round == 2)
5709 {
5710 /* Got more items than expected, go back to first round */
5711 if (count >= total_count)
5712 {
5713 vim_free(retval);
5714 round = 1;
5715 }
5716 else
5717 retval[count] = id;
5718 }
5719 ++count;
5720 }
5721 p = skipwhite(end);
5722 if (*p != ',')
5723 break;
5724 p = skipwhite(p + 1); /* skip comma in between arguments */
5725 }
5726 if (failed)
5727 break;
5728 if (round == 1)
5729 {
5730 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5731 if (retval == NULL)
5732 break;
5733 retval[count] = 0; /* zero means end of the list */
5734 total_count = count;
5735 }
5736 }
5737
5738 *arg = p;
5739 if (failed || retval == NULL)
5740 {
5741 vim_free(retval);
5742 return FAIL;
5743 }
5744
5745 if (*list == NULL)
5746 *list = retval;
5747 else
5748 vim_free(retval); /* list already found, don't overwrite it */
5749
5750 return OK;
5751}
5752
5753/*
5754 * Make a copy of an ID list.
5755 */
5756 static short *
5757copy_id_list(list)
5758 short *list;
5759{
5760 int len;
5761 int count;
5762 short *retval;
5763
5764 if (list == NULL)
5765 return NULL;
5766
5767 for (count = 0; list[count]; ++count)
5768 ;
5769 len = (count + 1) * sizeof(short);
5770 retval = (short *)alloc((unsigned)len);
5771 if (retval != NULL)
5772 mch_memmove(retval, list, (size_t)len);
5773
5774 return retval;
5775}
5776
5777/*
5778 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
5779 * "cur_si" can be NULL if not checking the "containedin" list.
5780 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
5781 * the current item.
5782 * This function is called very often, keep it fast!!
5783 */
5784 static int
5785in_id_list(cur_si, list, ssp, contained)
5786 stateitem_T *cur_si; /* current item or NULL */
5787 short *list; /* id list */
5788 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
5789 int contained; /* group id is contained */
5790{
5791 int retval;
5792 short *scl_list;
5793 short item;
5794 short id = ssp->id;
5795 static int depth = 0;
5796 int r;
5797
5798 /* If spp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00005799 if (cur_si != NULL && ssp->cont_in_list != NULL
5800 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005801 {
5802 /* Ignore transparent items without a contains argument. Double check
5803 * that we don't go back past the first one. */
5804 while ((cur_si->si_flags & HL_TRANS_CONT)
5805 && cur_si > (stateitem_T *)(current_state.ga_data))
5806 --cur_si;
5807 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
5808 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
5809 &(SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_syn),
5810 SYN_ITEMS(syn_buf)[cur_si->si_idx].sp_flags & HL_CONTAINED))
5811 return TRUE;
5812 }
5813
5814 if (list == NULL)
5815 return FALSE;
5816
5817 /*
5818 * If list is ID_LIST_ALL, we are in a transparent item that isn't
5819 * inside anything. Only allow not-contained groups.
5820 */
5821 if (list == ID_LIST_ALL)
5822 return !contained;
5823
5824 /*
5825 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
5826 * contains list. We also require that "id" is at the same ":syn include"
5827 * level as the list.
5828 */
5829 item = *list;
5830 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
5831 {
5832 if (item < SYNID_TOP)
5833 {
5834 /* ALL or ALLBUT: accept all groups in the same file */
5835 if (item - SYNID_ALLBUT != ssp->inc_tag)
5836 return FALSE;
5837 }
5838 else if (item < SYNID_CONTAINED)
5839 {
5840 /* TOP: accept all not-contained groups in the same file */
5841 if (item - SYNID_TOP != ssp->inc_tag || contained)
5842 return FALSE;
5843 }
5844 else
5845 {
5846 /* CONTAINED: accept all contained groups in the same file */
5847 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
5848 return FALSE;
5849 }
5850 item = *++list;
5851 retval = FALSE;
5852 }
5853 else
5854 retval = TRUE;
5855
5856 /*
5857 * Return "retval" if id is in the contains list.
5858 */
5859 while (item != 0)
5860 {
5861 if (item == id)
5862 return retval;
5863 if (item >= SYNID_CLUSTER)
5864 {
5865 scl_list = SYN_CLSTR(syn_buf)[item - SYNID_CLUSTER].scl_list;
5866 /* restrict recursiveness to 30 to avoid an endless loop for a
5867 * cluster that includes itself (indirectly) */
5868 if (scl_list != NULL && depth < 30)
5869 {
5870 ++depth;
5871 r = in_id_list(NULL, scl_list, ssp, contained);
5872 --depth;
5873 if (r)
5874 return retval;
5875 }
5876 }
5877 item = *++list;
5878 }
5879 return !retval;
5880}
5881
5882struct subcommand
5883{
5884 char *name; /* subcommand name */
5885 void (*func)__ARGS((exarg_T *, int)); /* function to call */
5886};
5887
5888static struct subcommand subcommands[] =
5889{
5890 {"case", syn_cmd_case},
5891 {"clear", syn_cmd_clear},
5892 {"cluster", syn_cmd_cluster},
5893 {"enable", syn_cmd_enable},
5894 {"include", syn_cmd_include},
5895 {"keyword", syn_cmd_keyword},
5896 {"list", syn_cmd_list},
5897 {"manual", syn_cmd_manual},
5898 {"match", syn_cmd_match},
5899 {"on", syn_cmd_on},
5900 {"off", syn_cmd_off},
5901 {"region", syn_cmd_region},
5902 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005903 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00005904 {"sync", syn_cmd_sync},
5905 {"", syn_cmd_list},
5906 {NULL, NULL}
5907};
5908
5909/*
5910 * ":syntax".
5911 * This searches the subcommands[] table for the subcommand name, and calls a
5912 * syntax_subcommand() function to do the rest.
5913 */
5914 void
5915ex_syntax(eap)
5916 exarg_T *eap;
5917{
5918 char_u *arg = eap->arg;
5919 char_u *subcmd_end;
5920 char_u *subcmd_name;
5921 int i;
5922
5923 syn_cmdlinep = eap->cmdlinep;
5924
5925 /* isolate subcommand name */
5926 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
5927 ;
5928 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
5929 if (subcmd_name != NULL)
5930 {
5931 if (eap->skip) /* skip error messages for all subcommands */
5932 ++emsg_skip;
5933 for (i = 0; ; ++i)
5934 {
5935 if (subcommands[i].name == NULL)
5936 {
5937 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
5938 break;
5939 }
5940 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
5941 {
5942 eap->arg = skipwhite(subcmd_end);
5943 (subcommands[i].func)(eap, FALSE);
5944 break;
5945 }
5946 }
5947 vim_free(subcmd_name);
5948 if (eap->skip)
5949 --emsg_skip;
5950 }
5951}
5952
5953 int
5954syntax_present(buf)
5955 buf_T *buf;
5956{
5957 return (buf->b_syn_patterns.ga_len != 0
5958 || buf->b_syn_clusters.ga_len != 0
Bram Moolenaardad6b692005-01-25 22:14:34 +00005959 || curbuf->b_keywtab.ht_used > 0
5960 || curbuf->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005961}
5962
5963#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
5964
5965static enum
5966{
5967 EXP_SUBCMD, /* expand ":syn" sub-commands */
5968 EXP_CASE /* expand ":syn case" arguments */
5969} expand_what;
5970
5971
5972/*
5973 * Handle command line completion for :syntax command.
5974 */
5975 void
5976set_context_in_syntax_cmd(xp, arg)
5977 expand_T *xp;
5978 char_u *arg;
5979{
5980 char_u *p;
5981
5982 /* Default: expand subcommands */
5983 xp->xp_context = EXPAND_SYNTAX;
5984 expand_what = EXP_SUBCMD;
5985 xp->xp_pattern = arg;
5986 include_link = FALSE;
5987 include_default = FALSE;
5988
5989 /* (part of) subcommand already typed */
5990 if (*arg != NUL)
5991 {
5992 p = skiptowhite(arg);
5993 if (*p != NUL) /* past first word */
5994 {
5995 xp->xp_pattern = skipwhite(p);
5996 if (*skiptowhite(xp->xp_pattern) != NUL)
5997 xp->xp_context = EXPAND_NOTHING;
5998 else if (STRNICMP(arg, "case", p - arg) == 0)
5999 expand_what = EXP_CASE;
6000 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6001 || STRNICMP(arg, "region", p - arg) == 0
6002 || STRNICMP(arg, "match", p - arg) == 0
6003 || STRNICMP(arg, "list", p - arg) == 0)
6004 xp->xp_context = EXPAND_HIGHLIGHT;
6005 else
6006 xp->xp_context = EXPAND_NOTHING;
6007 }
6008 }
6009}
6010
6011static char *(case_args[]) = {"match", "ignore", NULL};
6012
6013/*
6014 * Function given to ExpandGeneric() to obtain the list syntax names for
6015 * expansion.
6016 */
6017/*ARGSUSED*/
6018 char_u *
6019get_syntax_name(xp, idx)
6020 expand_T *xp;
6021 int idx;
6022{
6023 if (expand_what == EXP_SUBCMD)
6024 return (char_u *)subcommands[idx].name;
6025 return (char_u *)case_args[idx];
6026}
6027
6028#endif /* FEAT_CMDL_COMPL */
6029
Bram Moolenaar071d4272004-06-13 20:20:40 +00006030/*
6031 * Function called for expression evaluation: get syntax ID at file position.
6032 */
6033 int
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006034syn_get_id(wp, lnum, col, trans, spellp)
6035 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006036 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006037 colnr_T col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006038 int trans; /* remove transparancy */
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006039 int *spellp; /* return: can do spell checking */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006040{
6041 /* When the position is not after the current position and in the same
6042 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006043 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006044 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006045 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006046 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006047
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006048 (void)get_syntax_attr(col, spellp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006049
6050 return (trans ? current_trans_id : current_id);
6051}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006052
6053#if defined(FEAT_FOLDING) || defined(PROTO)
6054/*
6055 * Function called to get folding level for line "lnum" in window "wp".
6056 */
6057 int
6058syn_get_foldlevel(wp, lnum)
6059 win_T *wp;
6060 long lnum;
6061{
6062 int level = 0;
6063 int i;
6064
6065 /* Return quickly when there are no fold items at all. */
6066 if (wp->w_buffer->b_syn_folditems != 0)
6067 {
6068 syntax_start(wp, lnum);
6069
6070 for (i = 0; i < current_state.ga_len; ++i)
6071 if (CUR_STATE(i).si_flags & HL_FOLD)
6072 ++level;
6073 }
6074 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006075 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006076 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006077 if (level < 0)
6078 level = 0;
6079 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006080 return level;
6081}
6082#endif
6083
6084#endif /* FEAT_SYN_HL */
6085
6086
6087/**************************************
6088 * Highlighting stuff *
6089 **************************************/
6090
6091/*
6092 * The default highlight groups. These are compiled-in for fast startup and
6093 * they still work when the runtime files can't be found.
6094 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006095 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6096 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006097 */
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006098#ifdef FEAT_GUI
6099# define CENT(a, b) b
6100#else
6101# define CENT(a, b) a
6102#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006103static char *(highlight_init_both[]) =
6104 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006105 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6106 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
Bram Moolenaarda65f152007-05-06 13:54:35 +00006107#ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006108 CENT("IncSearch term=reverse cterm=reverse",
6109 "IncSearch term=reverse cterm=reverse gui=reverse"),
Bram Moolenaarda65f152007-05-06 13:54:35 +00006110#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006111 CENT("ModeMsg term=bold cterm=bold",
6112 "ModeMsg term=bold cterm=bold gui=bold"),
6113 CENT("NonText term=bold ctermfg=Blue",
6114 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6115 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6116 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6117 CENT("StatusLineNC term=reverse cterm=reverse",
6118 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006119#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006120 CENT("VertSplit term=reverse cterm=reverse",
6121 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006122#endif
6123#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006124 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6125 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006126#endif
6127#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006128 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6129 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006130#endif
6131#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006132 CENT("PmenuThumb cterm=reverse",
6133 "PmenuThumb cterm=reverse gui=reverse"),
6134 CENT("PmenuSbar ctermbg=Grey",
6135 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006136#endif
6137#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006138 CENT("TabLineSel term=bold cterm=bold",
6139 "TabLineSel term=bold cterm=bold gui=bold"),
6140 CENT("TabLineFill term=reverse cterm=reverse",
6141 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006142#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006143#ifdef FEAT_GUI
6144 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006145 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006146#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006147 NULL
6148 };
6149
6150static char *(highlight_init_light[]) =
6151 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006152 CENT("Directory term=bold ctermfg=DarkBlue",
6153 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6154 CENT("LineNr term=underline ctermfg=Brown",
6155 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6156 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6157 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6158 CENT("Question term=standout ctermfg=DarkGreen",
6159 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6160 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6161 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006162#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006163 CENT("SpellBad term=reverse ctermbg=LightRed",
6164 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6165 CENT("SpellCap term=reverse ctermbg=LightBlue",
6166 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6167 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6168 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6169 CENT("SpellLocal term=underline ctermbg=Cyan",
6170 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006171#endif
6172#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006173 CENT("Pmenu ctermbg=LightMagenta",
6174 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6175 CENT("PmenuSel ctermbg=LightGrey",
6176 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006177#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006178 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6179 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6180 CENT("Title term=bold ctermfg=DarkMagenta",
6181 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6182 CENT("WarningMsg term=standout ctermfg=DarkRed",
6183 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006184#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006185 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6186 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006187#endif
6188#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006189 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6190 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6191 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6192 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006193#endif
6194#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006195 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6196 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006197#endif
6198#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006199 CENT("Visual term=reverse",
6200 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006201#endif
6202#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006203 CENT("DiffAdd term=bold ctermbg=LightBlue",
6204 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6205 CENT("DiffChange term=bold ctermbg=LightMagenta",
6206 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6207 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6208 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006209#endif
6210#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006211 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6212 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006213#endif
6214#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006215 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006216 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006217 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006218 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006219#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006220#ifdef FEAT_AUTOCMD
6221 CENT("MatchParen term=reverse ctermbg=Cyan",
6222 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6223#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006224#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006225 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006226#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006227 NULL
6228 };
6229
6230static char *(highlight_init_dark[]) =
6231 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006232 CENT("Directory term=bold ctermfg=LightCyan",
6233 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6234 CENT("LineNr term=underline ctermfg=Yellow",
6235 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6236 CENT("MoreMsg term=bold ctermfg=LightGreen",
6237 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6238 CENT("Question term=standout ctermfg=LightGreen",
6239 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6240 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6241 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6242 CENT("SpecialKey term=bold ctermfg=LightBlue",
6243 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006244#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006245 CENT("SpellBad term=reverse ctermbg=Red",
6246 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6247 CENT("SpellCap term=reverse ctermbg=Blue",
6248 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6249 CENT("SpellRare term=reverse ctermbg=Magenta",
6250 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6251 CENT("SpellLocal term=underline ctermbg=Cyan",
6252 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006253#endif
6254#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006255 CENT("Pmenu ctermbg=Magenta",
6256 "Pmenu ctermbg=Magenta guibg=Magenta"),
6257 CENT("PmenuSel ctermbg=DarkGrey",
6258 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006259#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006260 CENT("Title term=bold ctermfg=LightMagenta",
6261 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6262 CENT("WarningMsg term=standout ctermfg=LightRed",
6263 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006264#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006265 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6266 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006267#endif
6268#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006269 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6270 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6271 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6272 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006273#endif
6274#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006275 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6276 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006277#endif
6278#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006279 CENT("Visual term=reverse",
6280 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006281#endif
6282#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006283 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6284 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6285 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6286 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6287 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6288 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006289#endif
6290#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006291 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6292 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006293#endif
6294#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006295 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006296 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006297 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006298 "CursorLine term=underline cterm=underline guibg=Grey40"),
6299#endif
6300#ifdef FEAT_AUTOCMD
6301 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6302 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006303#endif
6304#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006306#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006307 NULL
6308 };
6309
6310 void
6311init_highlight(both, reset)
6312 int both; /* include groups where 'bg' doesn't matter */
6313 int reset; /* clear group first */
6314{
6315 int i;
6316 char **pp;
6317 static int had_both = FALSE;
6318#ifdef FEAT_EVAL
6319 char_u *p;
6320
6321 /*
6322 * Try finding the color scheme file. Used when a color file was loaded
6323 * and 'background' or 't_Co' is changed.
6324 */
6325 p = get_var_value((char_u *)"g:colors_name");
6326 if (p != NULL && load_colors(p) == OK)
6327 return;
6328#endif
6329
6330 /*
6331 * Didn't use a color file, use the compiled-in colors.
6332 */
6333 if (both)
6334 {
6335 had_both = TRUE;
6336 pp = highlight_init_both;
6337 for (i = 0; pp[i] != NULL; ++i)
6338 do_highlight((char_u *)pp[i], reset, TRUE);
6339 }
6340 else if (!had_both)
6341 /* Don't do anything before the call with both == TRUE from main().
6342 * Not everything has been setup then, and that call will overrule
6343 * everything anyway. */
6344 return;
6345
6346 if (*p_bg == 'l')
6347 pp = highlight_init_light;
6348 else
6349 pp = highlight_init_dark;
6350 for (i = 0; pp[i] != NULL; ++i)
6351 do_highlight((char_u *)pp[i], reset, TRUE);
6352
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006353 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006354 * depend on the number of colors available.
6355 * With 8 colors brown is equal to yellow, need to use black for Search fg
6356 * to avoid Statement highlighted text disappears. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006357 if (t_colors > 8)
6358 do_highlight((char_u *)(*p_bg == 'l' ? "Visual ctermbg=LightGrey"
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00006359 : "Visual ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006360 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006361 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00006362 do_highlight((char_u *)"Visual cterm=reverse", FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006363 if (*p_bg == 'l')
6364 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6365 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006366
Bram Moolenaar071d4272004-06-13 20:20:40 +00006367#ifdef FEAT_SYN_HL
6368 /*
6369 * If syntax highlighting is enabled load the highlighting for it.
6370 */
6371 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006372 {
6373 static int recursive = 0;
6374
6375 if (recursive >= 5)
6376 EMSG(_("E679: recursive loop loading syncolor.vim"));
6377 else
6378 {
6379 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006380 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006381 --recursive;
6382 }
6383 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006384#endif
6385}
6386
6387/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006388 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006389 * Return OK for success, FAIL for failure.
6390 */
6391 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006392load_colors(name)
6393 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006394{
6395 char_u *buf;
6396 int retval = FAIL;
6397 static int recursive = FALSE;
6398
6399 /* When being called recursively, this is probably because setting
6400 * 'background' caused the highlighting to be reloaded. This means it is
6401 * working, thus we should return OK. */
6402 if (recursive)
6403 return OK;
6404
6405 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006406 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006407 if (buf != NULL)
6408 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006409 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006410 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006411 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006412#ifdef FEAT_AUTOCMD
6413 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6414#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006415 }
6416 recursive = FALSE;
6417
6418 return retval;
6419}
6420
6421/*
6422 * Handle the ":highlight .." command.
6423 * When using ":hi clear" this is called recursively for each group with
6424 * "forceit" and "init" both TRUE.
6425 */
6426 void
6427do_highlight(line, forceit, init)
6428 char_u *line;
6429 int forceit;
6430 int init; /* TRUE when called for initializing */
6431{
6432 char_u *name_end;
6433 char_u *p;
6434 char_u *linep;
6435 char_u *key_start;
6436 char_u *arg_start;
6437 char_u *key = NULL, *arg = NULL;
6438 long i;
6439 int off;
6440 int len;
6441 int attr;
6442 int id;
6443 int idx;
6444 int dodefault = FALSE;
6445 int doclear = FALSE;
6446 int dolink = FALSE;
6447 int error = FALSE;
6448 int color;
6449 int is_normal_group = FALSE; /* "Normal" group */
6450#ifdef FEAT_GUI_X11
6451 int is_menu_group = FALSE; /* "Menu" group */
6452 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6453 int is_tooltip_group = FALSE; /* "Tooltip" group */
6454 int do_colors = FALSE; /* need to update colors? */
6455#else
6456# define is_menu_group 0
6457# define is_tooltip_group 0
6458#endif
6459
6460 /*
6461 * If no argument, list current highlighting.
6462 */
6463 if (ends_excmd(*line))
6464 {
6465 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6466 /* TODO: only call when the group has attributes set */
6467 highlight_list_one((int)i);
6468 return;
6469 }
6470
6471 /*
6472 * Isolate the name.
6473 */
6474 name_end = skiptowhite(line);
6475 linep = skipwhite(name_end);
6476
6477 /*
6478 * Check for "default" argument.
6479 */
6480 if (STRNCMP(line, "default", name_end - line) == 0)
6481 {
6482 dodefault = TRUE;
6483 line = linep;
6484 name_end = skiptowhite(line);
6485 linep = skipwhite(name_end);
6486 }
6487
6488 /*
6489 * Check for "clear" or "link" argument.
6490 */
6491 if (STRNCMP(line, "clear", name_end - line) == 0)
6492 doclear = TRUE;
6493 if (STRNCMP(line, "link", name_end - line) == 0)
6494 dolink = TRUE;
6495
6496 /*
6497 * ":highlight {group-name}": list highlighting for one group.
6498 */
6499 if (!doclear && !dolink && ends_excmd(*linep))
6500 {
6501 id = syn_namen2id(line, (int)(name_end - line));
6502 if (id == 0)
6503 EMSG2(_("E411: highlight group not found: %s"), line);
6504 else
6505 highlight_list_one(id);
6506 return;
6507 }
6508
6509 /*
6510 * Handle ":highlight link {from} {to}" command.
6511 */
6512 if (dolink)
6513 {
6514 char_u *from_start = linep;
6515 char_u *from_end;
6516 char_u *to_start;
6517 char_u *to_end;
6518 int from_id;
6519 int to_id;
6520
6521 from_end = skiptowhite(from_start);
6522 to_start = skipwhite(from_end);
6523 to_end = skiptowhite(to_start);
6524
6525 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6526 {
6527 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6528 from_start);
6529 return;
6530 }
6531
6532 if (!ends_excmd(*skipwhite(to_end)))
6533 {
6534 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6535 return;
6536 }
6537
6538 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6539 if (STRNCMP(to_start, "NONE", 4) == 0)
6540 to_id = 0;
6541 else
6542 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6543
6544 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6545 {
6546 /*
6547 * Don't allow a link when there already is some highlighting
6548 * for the group, unless '!' is used
6549 */
6550 if (to_id > 0 && !forceit && !init
6551 && hl_has_settings(from_id - 1, dodefault))
6552 {
6553 if (sourcing_name == NULL && !dodefault)
6554 EMSG(_("E414: group has settings, highlight link ignored"));
6555 }
6556 else
6557 {
6558 if (!init)
6559 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6560 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006561#ifdef FEAT_EVAL
6562 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6563#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006564 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006565 }
6566 }
6567
6568 /* Only call highlight_changed() once, after sourcing a syntax file */
6569 need_highlight_changed = TRUE;
6570
6571 return;
6572 }
6573
6574 if (doclear)
6575 {
6576 /*
6577 * ":highlight clear [group]" command.
6578 */
6579 line = linep;
6580 if (ends_excmd(*line))
6581 {
6582#ifdef FEAT_GUI
6583 /* First, we do not destroy the old values, but allocate the new
6584 * ones and update the display. THEN we destroy the old values.
6585 * If we destroy the old values first, then the old values
6586 * (such as GuiFont's or GuiFontset's) will still be displayed but
6587 * invalid because they were free'd.
6588 */
6589 if (gui.in_use)
6590 {
6591# ifdef FEAT_BEVAL_TIP
6592 gui_init_tooltip_font();
6593# endif
6594# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6595 gui_init_menu_font();
6596# endif
6597 }
6598# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6599 gui_mch_def_colors();
6600# endif
6601# ifdef FEAT_GUI_X11
6602# ifdef FEAT_MENU
6603
6604 /* This only needs to be done when there is no Menu highlight
6605 * group defined by default, which IS currently the case.
6606 */
6607 gui_mch_new_menu_colors();
6608# endif
6609 if (gui.in_use)
6610 {
6611 gui_new_scrollbar_colors();
6612# ifdef FEAT_BEVAL
6613 gui_mch_new_tooltip_colors();
6614# endif
6615# ifdef FEAT_MENU
6616 gui_mch_new_menu_font();
6617# endif
6618 }
6619# endif
6620
6621 /* Ok, we're done allocating the new default graphics items.
6622 * The screen should already be refreshed at this point.
6623 * It is now Ok to clear out the old data.
6624 */
6625#endif
6626#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006627 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006628#endif
6629 restore_cterm_colors();
6630
6631 /*
6632 * Clear all default highlight groups and load the defaults.
6633 */
6634 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6635 highlight_clear(idx);
6636 init_highlight(TRUE, TRUE);
6637#ifdef FEAT_GUI
6638 if (gui.in_use)
6639 highlight_gui_started();
6640#endif
6641 highlight_changed();
6642 redraw_later_clear();
6643 return;
6644 }
6645 name_end = skiptowhite(line);
6646 linep = skipwhite(name_end);
6647 }
6648
6649 /*
6650 * Find the group name in the table. If it does not exist yet, add it.
6651 */
6652 id = syn_check_group(line, (int)(name_end - line));
6653 if (id == 0) /* failed (out of memory) */
6654 return;
6655 idx = id - 1; /* index is ID minus one */
6656
6657 /* Return if "default" was used and the group already has settings. */
6658 if (dodefault && hl_has_settings(idx, TRUE))
6659 return;
6660
6661 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6662 is_normal_group = TRUE;
6663#ifdef FEAT_GUI_X11
6664 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6665 is_menu_group = TRUE;
6666 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6667 is_scrollbar_group = TRUE;
6668 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6669 is_tooltip_group = TRUE;
6670#endif
6671
6672 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6673 if (doclear || (forceit && init))
6674 {
6675 highlight_clear(idx);
6676 if (!doclear)
6677 HL_TABLE()[idx].sg_set = 0;
6678 }
6679
6680 if (!doclear)
6681 while (!ends_excmd(*linep))
6682 {
6683 key_start = linep;
6684 if (*linep == '=')
6685 {
6686 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6687 error = TRUE;
6688 break;
6689 }
6690
6691 /*
6692 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6693 * "guibg").
6694 */
6695 while (*linep && !vim_iswhite(*linep) && *linep != '=')
6696 ++linep;
6697 vim_free(key);
6698 key = vim_strnsave_up(key_start, (int)(linep - key_start));
6699 if (key == NULL)
6700 {
6701 error = TRUE;
6702 break;
6703 }
6704 linep = skipwhite(linep);
6705
6706 if (STRCMP(key, "NONE") == 0)
6707 {
6708 if (!init || HL_TABLE()[idx].sg_set == 0)
6709 {
6710 if (!init)
6711 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6712 highlight_clear(idx);
6713 }
6714 continue;
6715 }
6716
6717 /*
6718 * Check for the equal sign.
6719 */
6720 if (*linep != '=')
6721 {
6722 EMSG2(_("E416: missing equal sign: %s"), key_start);
6723 error = TRUE;
6724 break;
6725 }
6726 ++linep;
6727
6728 /*
6729 * Isolate the argument.
6730 */
6731 linep = skipwhite(linep);
6732 if (*linep == '\'') /* guifg='color name' */
6733 {
6734 arg_start = ++linep;
6735 linep = vim_strchr(linep, '\'');
6736 if (linep == NULL)
6737 {
6738 EMSG2(_(e_invarg2), key_start);
6739 error = TRUE;
6740 break;
6741 }
6742 }
6743 else
6744 {
6745 arg_start = linep;
6746 linep = skiptowhite(linep);
6747 }
6748 if (linep == arg_start)
6749 {
6750 EMSG2(_("E417: missing argument: %s"), key_start);
6751 error = TRUE;
6752 break;
6753 }
6754 vim_free(arg);
6755 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6756 if (arg == NULL)
6757 {
6758 error = TRUE;
6759 break;
6760 }
6761 if (*linep == '\'')
6762 ++linep;
6763
6764 /*
6765 * Store the argument.
6766 */
6767 if ( STRCMP(key, "TERM") == 0
6768 || STRCMP(key, "CTERM") == 0
6769 || STRCMP(key, "GUI") == 0)
6770 {
6771 attr = 0;
6772 off = 0;
6773 while (arg[off] != NUL)
6774 {
6775 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6776 {
6777 len = (int)STRLEN(hl_name_table[i]);
6778 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6779 {
6780 attr |= hl_attr_table[i];
6781 off += len;
6782 break;
6783 }
6784 }
6785 if (i < 0)
6786 {
6787 EMSG2(_("E418: Illegal value: %s"), arg);
6788 error = TRUE;
6789 break;
6790 }
6791 if (arg[off] == ',') /* another one follows */
6792 ++off;
6793 }
6794 if (error)
6795 break;
6796 if (*key == 'T')
6797 {
6798 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6799 {
6800 if (!init)
6801 HL_TABLE()[idx].sg_set |= SG_TERM;
6802 HL_TABLE()[idx].sg_term = attr;
6803 }
6804 }
6805 else if (*key == 'C')
6806 {
6807 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6808 {
6809 if (!init)
6810 HL_TABLE()[idx].sg_set |= SG_CTERM;
6811 HL_TABLE()[idx].sg_cterm = attr;
6812 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6813 }
6814 }
6815#ifdef FEAT_GUI
6816 else
6817 {
6818 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6819 {
6820 if (!init)
6821 HL_TABLE()[idx].sg_set |= SG_GUI;
6822 HL_TABLE()[idx].sg_gui = attr;
6823 }
6824 }
6825#endif
6826 }
6827 else if (STRCMP(key, "FONT") == 0)
6828 {
6829 /* in non-GUI fonts are simply ignored */
6830#ifdef FEAT_GUI
6831 if (!gui.shell_created)
6832 {
6833 /* GUI not started yet, always accept the name. */
6834 vim_free(HL_TABLE()[idx].sg_font_name);
6835 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6836 }
6837 else
6838 {
6839 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6840# ifdef FEAT_XFONTSET
6841 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6842# endif
6843 /* First, save the current font/fontset.
6844 * Then try to allocate the font/fontset.
6845 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6846 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6847 */
6848
6849 HL_TABLE()[idx].sg_font = NOFONT;
6850# ifdef FEAT_XFONTSET
6851 HL_TABLE()[idx].sg_fontset = NOFONTSET;
6852# endif
6853 hl_do_font(idx, arg, is_normal_group, is_menu_group,
6854 is_tooltip_group);
6855
6856# ifdef FEAT_XFONTSET
6857 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6858 {
6859 /* New fontset was accepted. Free the old one, if there was
6860 * one.
6861 */
6862 gui_mch_free_fontset(temp_sg_fontset);
6863 vim_free(HL_TABLE()[idx].sg_font_name);
6864 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6865 }
6866 else
6867 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6868# endif
6869 if (HL_TABLE()[idx].sg_font != NOFONT)
6870 {
6871 /* New font was accepted. Free the old one, if there was
6872 * one.
6873 */
6874 gui_mch_free_font(temp_sg_font);
6875 vim_free(HL_TABLE()[idx].sg_font_name);
6876 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6877 }
6878 else
6879 HL_TABLE()[idx].sg_font = temp_sg_font;
6880 }
6881#endif
6882 }
6883 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6884 {
6885 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6886 {
6887 if (!init)
6888 HL_TABLE()[idx].sg_set |= SG_CTERM;
6889
6890 /* When setting the foreground color, and previously the "bold"
6891 * flag was set for a light color, reset it now */
6892 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6893 {
6894 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6895 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6896 }
6897
6898 if (VIM_ISDIGIT(*arg))
6899 color = atoi((char *)arg);
6900 else if (STRICMP(arg, "fg") == 0)
6901 {
6902 if (cterm_normal_fg_color)
6903 color = cterm_normal_fg_color - 1;
6904 else
6905 {
6906 EMSG(_("E419: FG color unknown"));
6907 error = TRUE;
6908 break;
6909 }
6910 }
6911 else if (STRICMP(arg, "bg") == 0)
6912 {
6913 if (cterm_normal_bg_color > 0)
6914 color = cterm_normal_bg_color - 1;
6915 else
6916 {
6917 EMSG(_("E420: BG color unknown"));
6918 error = TRUE;
6919 break;
6920 }
6921 }
6922 else
6923 {
6924 static char *(color_names[28]) = {
6925 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6926 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6927 "Gray", "Grey",
6928 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6929 "Blue", "LightBlue", "Green", "LightGreen",
6930 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6931 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6932 static int color_numbers_16[28] = {0, 1, 2, 3,
6933 4, 5, 6, 6,
6934 7, 7,
6935 7, 7, 8, 8,
6936 9, 9, 10, 10,
6937 11, 11, 12, 12, 13,
6938 13, 14, 14, 15, -1};
6939 /* for xterm with 88 colors... */
6940 static int color_numbers_88[28] = {0, 4, 2, 6,
6941 1, 5, 32, 72,
6942 84, 84,
6943 7, 7, 82, 82,
6944 12, 43, 10, 61,
6945 14, 63, 9, 74, 13,
6946 75, 11, 78, 15, -1};
6947 /* for xterm with 256 colors... */
6948 static int color_numbers_256[28] = {0, 4, 2, 6,
6949 1, 5, 130, 130,
6950 248, 248,
6951 7, 7, 242, 242,
6952 12, 81, 10, 121,
6953 14, 159, 9, 224, 13,
6954 225, 11, 229, 15, -1};
6955 /* for terminals with less than 16 colors... */
6956 static int color_numbers_8[28] = {0, 4, 2, 6,
6957 1, 5, 3, 3,
6958 7, 7,
6959 7, 7, 0+8, 0+8,
6960 4+8, 4+8, 2+8, 2+8,
6961 6+8, 6+8, 1+8, 1+8, 5+8,
6962 5+8, 3+8, 3+8, 7+8, -1};
6963#if defined(__QNXNTO__)
6964 static int *color_numbers_8_qansi = color_numbers_8;
6965 /* On qnx, the 8 & 16 color arrays are the same */
6966 if (STRNCMP(T_NAME, "qansi", 5) == 0)
6967 color_numbers_8_qansi = color_numbers_16;
6968#endif
6969
6970 /* reduce calls to STRICMP a bit, it can be slow */
6971 off = TOUPPER_ASC(*arg);
6972 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6973 if (off == color_names[i][0]
6974 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6975 break;
6976 if (i < 0)
6977 {
6978 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6979 error = TRUE;
6980 break;
6981 }
6982
6983 /* Use the _16 table to check if its a valid color name. */
6984 color = color_numbers_16[i];
6985 if (color >= 0)
6986 {
6987 if (t_colors == 8)
6988 {
6989 /* t_Co is 8: use the 8 colors table */
6990#if defined(__QNXNTO__)
6991 color = color_numbers_8_qansi[i];
6992#else
6993 color = color_numbers_8[i];
6994#endif
6995 if (key[5] == 'F')
6996 {
6997 /* set/reset bold attribute to get light foreground
6998 * colors (on some terminals, e.g. "linux") */
6999 if (color & 8)
7000 {
7001 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7002 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7003 }
7004 else
7005 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7006 }
7007 color &= 7; /* truncate to 8 colors */
7008 }
7009 else if (t_colors == 16 || t_colors == 88
7010 || t_colors == 256)
7011 {
7012 /*
7013 * Guess: if the termcap entry ends in 'm', it is
7014 * probably an xterm-like terminal. Use the changed
7015 * order for colors.
7016 */
7017 if (*T_CAF != NUL)
7018 p = T_CAF;
7019 else
7020 p = T_CSF;
7021 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7022 switch (t_colors)
7023 {
7024 case 16:
7025 color = color_numbers_8[i];
7026 break;
7027 case 88:
7028 color = color_numbers_88[i];
7029 break;
7030 case 256:
7031 color = color_numbers_256[i];
7032 break;
7033 }
7034 }
7035 }
7036 }
7037 /* Add one to the argument, to avoid zero */
7038 if (key[5] == 'F')
7039 {
7040 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7041 if (is_normal_group)
7042 {
7043 cterm_normal_fg_color = color + 1;
7044 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7045#ifdef FEAT_GUI
7046 /* Don't do this if the GUI is used. */
7047 if (!gui.in_use && !gui.starting)
7048#endif
7049 {
7050 must_redraw = CLEAR;
7051 if (termcap_active)
7052 term_fg_color(color);
7053 }
7054 }
7055 }
7056 else
7057 {
7058 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7059 if (is_normal_group)
7060 {
7061 cterm_normal_bg_color = color + 1;
7062#ifdef FEAT_GUI
7063 /* Don't mess with 'background' if the GUI is used. */
7064 if (!gui.in_use && !gui.starting)
7065#endif
7066 {
7067 must_redraw = CLEAR;
7068 if (termcap_active)
7069 term_bg_color(color);
7070 if (t_colors < 16)
7071 i = (color == 0 || color == 4);
7072 else
7073 i = (color < 7 || color == 8);
7074 /* Set the 'background' option if the value is wrong. */
7075 if (i != (*p_bg == 'd'))
7076 set_option_value((char_u *)"bg", 0L,
7077 i ? (char_u *)"dark" : (char_u *)"light", 0);
7078 }
7079 }
7080 }
7081 }
7082 }
7083 else if (STRCMP(key, "GUIFG") == 0)
7084 {
7085#ifdef FEAT_GUI /* in non-GUI guifg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007086 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007087 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007088 if (!init)
7089 HL_TABLE()[idx].sg_set |= SG_GUI;
7090
7091 i = color_name2handle(arg);
7092 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7093 {
7094 HL_TABLE()[idx].sg_gui_fg = i;
7095 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7096 if (STRCMP(arg, "NONE"))
7097 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7098 else
7099 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007100# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007101 if (is_menu_group)
7102 gui.menu_fg_pixel = i;
7103 if (is_scrollbar_group)
7104 gui.scroll_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007105# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007106 if (is_tooltip_group)
7107 gui.tooltip_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007108# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007109 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007111 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007112 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007113#endif
7114 }
7115 else if (STRCMP(key, "GUIBG") == 0)
7116 {
7117#ifdef FEAT_GUI /* in non-GUI guibg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007118 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007119 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007120 if (!init)
7121 HL_TABLE()[idx].sg_set |= SG_GUI;
7122
7123 i = color_name2handle(arg);
7124 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7125 {
7126 HL_TABLE()[idx].sg_gui_bg = i;
7127 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7128 if (STRCMP(arg, "NONE") != 0)
7129 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7130 else
7131 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007132# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007133 if (is_menu_group)
7134 gui.menu_bg_pixel = i;
7135 if (is_scrollbar_group)
7136 gui.scroll_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007137# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007138 if (is_tooltip_group)
7139 gui.tooltip_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007140# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007141 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007142# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007143 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007144 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007145#endif
7146 }
7147 else if (STRCMP(key, "GUISP") == 0)
7148 {
7149#ifdef FEAT_GUI /* in non-GUI guisp colors are simply ignored */
7150 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7151 {
7152 if (!init)
7153 HL_TABLE()[idx].sg_set |= SG_GUI;
7154
7155 i = color_name2handle(arg);
7156 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7157 {
7158 HL_TABLE()[idx].sg_gui_sp = i;
7159 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7160 if (STRCMP(arg, "NONE") != 0)
7161 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7162 else
7163 HL_TABLE()[idx].sg_gui_sp_name = NULL;
7164 }
7165 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007166#endif
7167 }
7168 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7169 {
7170 char_u buf[100];
7171 char_u *tname;
7172
7173 if (!init)
7174 HL_TABLE()[idx].sg_set |= SG_TERM;
7175
7176 /*
7177 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007178 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007179 */
7180 if (STRNCMP(arg, "t_", 2) == 0)
7181 {
7182 off = 0;
7183 buf[0] = 0;
7184 while (arg[off] != NUL)
7185 {
7186 /* Isolate one termcap name */
7187 for (len = 0; arg[off + len] &&
7188 arg[off + len] != ','; ++len)
7189 ;
7190 tname = vim_strnsave(arg + off, len);
7191 if (tname == NULL) /* out of memory */
7192 {
7193 error = TRUE;
7194 break;
7195 }
7196 /* lookup the escape sequence for the item */
7197 p = get_term_code(tname);
7198 vim_free(tname);
7199 if (p == NULL) /* ignore non-existing things */
7200 p = (char_u *)"";
7201
7202 /* Append it to the already found stuff */
7203 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7204 {
7205 EMSG2(_("E422: terminal code too long: %s"), arg);
7206 error = TRUE;
7207 break;
7208 }
7209 STRCAT(buf, p);
7210
7211 /* Advance to the next item */
7212 off += len;
7213 if (arg[off] == ',') /* another one follows */
7214 ++off;
7215 }
7216 }
7217 else
7218 {
7219 /*
7220 * Copy characters from arg[] to buf[], translating <> codes.
7221 */
7222 for (p = arg, off = 0; off < 100 && *p; )
7223 {
7224 len = trans_special(&p, buf + off, FALSE);
7225 if (len) /* recognized special char */
7226 off += len;
7227 else /* copy as normal char */
7228 buf[off++] = *p++;
7229 }
7230 buf[off] = NUL;
7231 }
7232 if (error)
7233 break;
7234
7235 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7236 p = NULL;
7237 else
7238 p = vim_strsave(buf);
7239 if (key[2] == 'A')
7240 {
7241 vim_free(HL_TABLE()[idx].sg_start);
7242 HL_TABLE()[idx].sg_start = p;
7243 }
7244 else
7245 {
7246 vim_free(HL_TABLE()[idx].sg_stop);
7247 HL_TABLE()[idx].sg_stop = p;
7248 }
7249 }
7250 else
7251 {
7252 EMSG2(_("E423: Illegal argument: %s"), key_start);
7253 error = TRUE;
7254 break;
7255 }
7256
7257 /*
7258 * When highlighting has been given for a group, don't link it.
7259 */
7260 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7261 HL_TABLE()[idx].sg_link = 0;
7262
7263 /*
7264 * Continue with next argument.
7265 */
7266 linep = skipwhite(linep);
7267 }
7268
7269 /*
7270 * If there is an error, and it's a new entry, remove it from the table.
7271 */
7272 if (error && idx == highlight_ga.ga_len)
7273 syn_unadd_group();
7274 else
7275 {
7276 if (is_normal_group)
7277 {
7278 HL_TABLE()[idx].sg_term_attr = 0;
7279 HL_TABLE()[idx].sg_cterm_attr = 0;
7280#ifdef FEAT_GUI
7281 HL_TABLE()[idx].sg_gui_attr = 0;
7282 /*
7283 * Need to update all groups, because they might be using "bg"
7284 * and/or "fg", which have been changed now.
7285 */
7286 if (gui.in_use)
7287 highlight_gui_started();
7288#endif
7289 }
7290#ifdef FEAT_GUI_X11
7291# ifdef FEAT_MENU
7292 else if (is_menu_group)
7293 {
7294 if (gui.in_use && do_colors)
7295 gui_mch_new_menu_colors();
7296 }
7297# endif
7298 else if (is_scrollbar_group)
7299 {
7300 if (gui.in_use && do_colors)
7301 gui_new_scrollbar_colors();
7302 }
7303# ifdef FEAT_BEVAL
7304 else if (is_tooltip_group)
7305 {
7306 if (gui.in_use && do_colors)
7307 gui_mch_new_tooltip_colors();
7308 }
7309# endif
7310#endif
7311 else
7312 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007313#ifdef FEAT_EVAL
7314 HL_TABLE()[idx].sg_scriptID = current_SID;
7315#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007316 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007317 }
7318 vim_free(key);
7319 vim_free(arg);
7320
7321 /* Only call highlight_changed() once, after sourcing a syntax file */
7322 need_highlight_changed = TRUE;
7323}
7324
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007325#if defined(EXITFREE) || defined(PROTO)
7326 void
7327free_highlight()
7328{
7329 int i;
7330
7331 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007332 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007333 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007334 vim_free(HL_TABLE()[i].sg_name);
7335 vim_free(HL_TABLE()[i].sg_name_u);
7336 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007337 ga_clear(&highlight_ga);
7338}
7339#endif
7340
Bram Moolenaar071d4272004-06-13 20:20:40 +00007341/*
7342 * Reset the cterm colors to what they were before Vim was started, if
7343 * possible. Otherwise reset them to zero.
7344 */
7345 void
7346restore_cterm_colors()
7347{
7348#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7349 /* Since t_me has been set, this probably means that the user
7350 * wants to use this as default colors. Need to reset default
7351 * background/foreground colors. */
7352 mch_set_normal_colors();
7353#else
7354 cterm_normal_fg_color = 0;
7355 cterm_normal_fg_bold = 0;
7356 cterm_normal_bg_color = 0;
7357#endif
7358}
7359
7360/*
7361 * Return TRUE if highlight group "idx" has any settings.
7362 * When "check_link" is TRUE also check for an existing link.
7363 */
7364 static int
7365hl_has_settings(idx, check_link)
7366 int idx;
7367 int check_link;
7368{
7369 return ( HL_TABLE()[idx].sg_term_attr != 0
7370 || HL_TABLE()[idx].sg_cterm_attr != 0
7371#ifdef FEAT_GUI
7372 || HL_TABLE()[idx].sg_gui_attr != 0
7373#endif
7374 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7375}
7376
7377/*
7378 * Clear highlighting for one group.
7379 */
7380 static void
7381highlight_clear(idx)
7382 int idx;
7383{
7384 HL_TABLE()[idx].sg_term = 0;
7385 vim_free(HL_TABLE()[idx].sg_start);
7386 HL_TABLE()[idx].sg_start = NULL;
7387 vim_free(HL_TABLE()[idx].sg_stop);
7388 HL_TABLE()[idx].sg_stop = NULL;
7389 HL_TABLE()[idx].sg_term_attr = 0;
7390 HL_TABLE()[idx].sg_cterm = 0;
7391 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7392 HL_TABLE()[idx].sg_cterm_fg = 0;
7393 HL_TABLE()[idx].sg_cterm_bg = 0;
7394 HL_TABLE()[idx].sg_cterm_attr = 0;
7395#ifdef FEAT_GUI /* in non-GUI fonts are simply ignored */
7396 HL_TABLE()[idx].sg_gui = 0;
7397 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7398 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7399 HL_TABLE()[idx].sg_gui_fg_name = NULL;
7400 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7401 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7402 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007403 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7404 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7405 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007406 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7407 HL_TABLE()[idx].sg_font = NOFONT;
7408# ifdef FEAT_XFONTSET
7409 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7410 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7411# endif
7412 vim_free(HL_TABLE()[idx].sg_font_name);
7413 HL_TABLE()[idx].sg_font_name = NULL;
7414 HL_TABLE()[idx].sg_gui_attr = 0;
7415#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007416#ifdef FEAT_EVAL
7417 /* Clear the script ID only when there is no link, since that is not
7418 * cleared. */
7419 if (HL_TABLE()[idx].sg_link == 0)
7420 HL_TABLE()[idx].sg_scriptID = 0;
7421#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007422}
7423
7424#if defined(FEAT_GUI) || defined(PROTO)
7425/*
7426 * Set the normal foreground and background colors according to the "Normal"
7427 * highlighighting group. For X11 also set "Menu", "Scrollbar", and
7428 * "Tooltip" colors.
7429 */
7430 void
7431set_normal_colors()
7432{
7433 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007434 &gui.norm_pixel, &gui.back_pixel,
7435 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007436 {
7437 gui_mch_new_colors();
7438 must_redraw = CLEAR;
7439 }
7440#ifdef FEAT_GUI_X11
7441 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007442 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7443 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007444 {
7445# ifdef FEAT_MENU
7446 gui_mch_new_menu_colors();
7447# endif
7448 must_redraw = CLEAR;
7449 }
7450# ifdef FEAT_BEVAL
7451 if (set_group_colors((char_u *)"Tooltip",
7452 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7453 FALSE, FALSE, TRUE))
7454 {
7455# ifdef FEAT_TOOLBAR
7456 gui_mch_new_tooltip_colors();
7457# endif
7458 must_redraw = CLEAR;
7459 }
7460#endif
7461 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007462 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7463 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007464 {
7465 gui_new_scrollbar_colors();
7466 must_redraw = CLEAR;
7467 }
7468#endif
7469}
7470
7471/*
7472 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7473 */
7474 static int
7475set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7476 char_u *name;
7477 guicolor_T *fgp;
7478 guicolor_T *bgp;
7479 int do_menu;
7480 int use_norm;
7481 int do_tooltip;
7482{
7483 int idx;
7484
7485 idx = syn_name2id(name) - 1;
7486 if (idx >= 0)
7487 {
7488 gui_do_one_color(idx, do_menu, do_tooltip);
7489
7490 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7491 *fgp = HL_TABLE()[idx].sg_gui_fg;
7492 else if (use_norm)
7493 *fgp = gui.def_norm_pixel;
7494 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7495 *bgp = HL_TABLE()[idx].sg_gui_bg;
7496 else if (use_norm)
7497 *bgp = gui.def_back_pixel;
7498 return TRUE;
7499 }
7500 return FALSE;
7501}
7502
7503/*
7504 * Get the font of the "Normal" group.
7505 * Returns "" when it's not found or not set.
7506 */
7507 char_u *
7508hl_get_font_name()
7509{
7510 int id;
7511 char_u *s;
7512
7513 id = syn_name2id((char_u *)"Normal");
7514 if (id > 0)
7515 {
7516 s = HL_TABLE()[id - 1].sg_font_name;
7517 if (s != NULL)
7518 return s;
7519 }
7520 return (char_u *)"";
7521}
7522
7523/*
7524 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7525 * actually chosen to be used.
7526 */
7527 void
7528hl_set_font_name(font_name)
7529 char_u *font_name;
7530{
7531 int id;
7532
7533 id = syn_name2id((char_u *)"Normal");
7534 if (id > 0)
7535 {
7536 vim_free(HL_TABLE()[id - 1].sg_font_name);
7537 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7538 }
7539}
7540
7541/*
7542 * Set background color for "Normal" group. Called by gui_set_bg_color()
7543 * when the color is known.
7544 */
7545 void
7546hl_set_bg_color_name(name)
7547 char_u *name; /* must have been allocated */
7548{
7549 int id;
7550
7551 if (name != NULL)
7552 {
7553 id = syn_name2id((char_u *)"Normal");
7554 if (id > 0)
7555 {
7556 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7557 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7558 }
7559 }
7560}
7561
7562/*
7563 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7564 * when the color is known.
7565 */
7566 void
7567hl_set_fg_color_name(name)
7568 char_u *name; /* must have been allocated */
7569{
7570 int id;
7571
7572 if (name != NULL)
7573 {
7574 id = syn_name2id((char_u *)"Normal");
7575 if (id > 0)
7576 {
7577 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7578 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7579 }
7580 }
7581}
7582
7583/*
7584 * Return the handle for a color name.
7585 * Returns INVALCOLOR when failed.
7586 */
7587 static guicolor_T
7588color_name2handle(name)
7589 char_u *name;
7590{
7591 if (STRCMP(name, "NONE") == 0)
7592 return INVALCOLOR;
7593
7594 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7595 return gui.norm_pixel;
7596 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7597 return gui.back_pixel;
7598
7599 return gui_get_color(name);
7600}
7601
7602/*
7603 * Return the handle for a font name.
7604 * Returns NOFONT when failed.
7605 */
7606 static GuiFont
7607font_name2handle(name)
7608 char_u *name;
7609{
7610 if (STRCMP(name, "NONE") == 0)
7611 return NOFONT;
7612
7613 return gui_mch_get_font(name, TRUE);
7614}
7615
7616# ifdef FEAT_XFONTSET
7617/*
7618 * Return the handle for a fontset name.
7619 * Returns NOFONTSET when failed.
7620 */
7621 static GuiFontset
7622fontset_name2handle(name, fixed_width)
7623 char_u *name;
7624 int fixed_width;
7625{
7626 if (STRCMP(name, "NONE") == 0)
7627 return NOFONTSET;
7628
7629 return gui_mch_get_fontset(name, TRUE, fixed_width);
7630}
7631# endif
7632
7633/*
7634 * Get the font or fontset for one highlight group.
7635 */
7636/*ARGSUSED*/
7637 static void
7638hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7639 int idx;
7640 char_u *arg;
7641 int do_normal; /* set normal font */
7642 int do_menu; /* set menu font */
7643 int do_tooltip; /* set tooltip font */
7644{
7645# ifdef FEAT_XFONTSET
7646 /* If 'guifontset' is not empty, first try using the name as a
7647 * fontset. If that doesn't work, use it as a font name. */
7648 if (*p_guifontset != NUL
7649# ifdef FONTSET_ALWAYS
7650 || do_menu
7651# endif
7652# ifdef FEAT_BEVAL_TIP
7653 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
7654 || do_tooltip
7655# endif
7656 )
7657 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7658# ifdef FONTSET_ALWAYS
7659 || do_menu
7660# endif
7661# ifdef FEAT_BEVAL_TIP
7662 || do_tooltip
7663# endif
7664 );
7665 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7666 {
7667 /* If it worked and it's the Normal group, use it as the
7668 * normal fontset. Same for the Menu group. */
7669 if (do_normal)
7670 gui_init_font(arg, TRUE);
7671# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7672 if (do_menu)
7673 {
7674# ifdef FONTSET_ALWAYS
7675 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7676# else
7677 /* YIKES! This is a bug waiting to crash the program */
7678 gui.menu_font = HL_TABLE()[idx].sg_fontset;
7679# endif
7680 gui_mch_new_menu_font();
7681 }
7682# ifdef FEAT_BEVAL
7683 if (do_tooltip)
7684 {
7685 /* The Athena widget set cannot currently handle switching between
7686 * displaying a single font and a fontset.
7687 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007688 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00007689 * XFontStruct is used.
7690 */
7691 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7692 gui_mch_new_tooltip_font();
7693 }
7694# endif
7695# endif
7696 }
7697 else
7698# endif
7699 {
7700 HL_TABLE()[idx].sg_font = font_name2handle(arg);
7701 /* If it worked and it's the Normal group, use it as the
7702 * normal font. Same for the Menu group. */
7703 if (HL_TABLE()[idx].sg_font != NOFONT)
7704 {
7705 if (do_normal)
7706 gui_init_font(arg, FALSE);
7707#ifndef FONTSET_ALWAYS
7708# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7709 if (do_menu)
7710 {
7711 gui.menu_font = HL_TABLE()[idx].sg_font;
7712 gui_mch_new_menu_font();
7713 }
7714# endif
7715#endif
7716 }
7717 }
7718}
7719
7720#endif /* FEAT_GUI */
7721
7722/*
7723 * Table with the specifications for an attribute number.
7724 * Note that this table is used by ALL buffers. This is required because the
7725 * GUI can redraw at any time for any buffer.
7726 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007727static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007728
7729#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7730
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007731static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007732
7733#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7734
7735#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007736static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007737
7738#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7739#endif
7740
7741/*
7742 * Return the attr number for a set of colors and font.
7743 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7744 * if the combination is new.
7745 * Return 0 for error (no more room).
7746 */
7747 static int
7748get_attr_entry(table, aep)
7749 garray_T *table;
7750 attrentry_T *aep;
7751{
7752 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007753 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007754 static int recursive = FALSE;
7755
7756 /*
7757 * Init the table, in case it wasn't done yet.
7758 */
7759 table->ga_itemsize = sizeof(attrentry_T);
7760 table->ga_growsize = 7;
7761
7762 /*
7763 * Try to find an entry with the same specifications.
7764 */
7765 for (i = 0; i < table->ga_len; ++i)
7766 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007767 taep = &(((attrentry_T *)table->ga_data)[i]);
7768 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00007769 && (
7770#ifdef FEAT_GUI
7771 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007772 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
7773 && aep->ae_u.gui.bg_color
7774 == taep->ae_u.gui.bg_color
7775 && aep->ae_u.gui.sp_color
7776 == taep->ae_u.gui.sp_color
7777 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00007778# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007779 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00007780# endif
7781 ))
7782 ||
7783#endif
7784 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007785 && (aep->ae_u.term.start == NULL)
7786 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007787 && (aep->ae_u.term.start == NULL
7788 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007789 taep->ae_u.term.start) == 0)
7790 && (aep->ae_u.term.stop == NULL)
7791 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007792 && (aep->ae_u.term.stop == NULL
7793 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007794 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007795 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007796 && aep->ae_u.cterm.fg_color
7797 == taep->ae_u.cterm.fg_color
7798 && aep->ae_u.cterm.bg_color
7799 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007800 ))
7801
7802 return i + ATTR_OFF;
7803 }
7804
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00007805 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007806 {
7807 /*
7808 * Running out of attribute entries! remove all attributes, and
7809 * compute new ones for all groups.
7810 * When called recursively, we are really out of numbers.
7811 */
7812 if (recursive)
7813 {
7814 EMSG(_("E424: Too many different highlighting attributes in use"));
7815 return 0;
7816 }
7817 recursive = TRUE;
7818
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007819 clear_hl_tables();
7820
Bram Moolenaar071d4272004-06-13 20:20:40 +00007821 must_redraw = CLEAR;
7822
7823 for (i = 0; i < highlight_ga.ga_len; ++i)
7824 set_hl_attr(i);
7825
7826 recursive = FALSE;
7827 }
7828
7829 /*
7830 * This is a new combination of colors and font, add an entry.
7831 */
7832 if (ga_grow(table, 1) == FAIL)
7833 return 0;
7834
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007835 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7836 vim_memset(taep, 0, sizeof(attrentry_T));
7837 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007838#ifdef FEAT_GUI
7839 if (table == &gui_attr_table)
7840 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007841 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7842 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
7843 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
7844 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007845# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007846 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007847# endif
7848 }
7849#endif
7850 if (table == &term_attr_table)
7851 {
7852 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007853 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007855 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007856 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007857 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007858 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007859 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007860 }
7861 else if (table == &cterm_attr_table)
7862 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007863 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7864 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007865 }
7866 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007867 return (table->ga_len - 1 + ATTR_OFF);
7868}
7869
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007870/*
7871 * Clear all highlight tables.
7872 */
7873 void
7874clear_hl_tables()
7875{
7876 int i;
7877 attrentry_T *taep;
7878
7879#ifdef FEAT_GUI
7880 ga_clear(&gui_attr_table);
7881#endif
7882 for (i = 0; i < term_attr_table.ga_len; ++i)
7883 {
7884 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
7885 vim_free(taep->ae_u.term.start);
7886 vim_free(taep->ae_u.term.stop);
7887 }
7888 ga_clear(&term_attr_table);
7889 ga_clear(&cterm_attr_table);
7890}
7891
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007892#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00007893/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00007894 * Combine special attributes (e.g., for spelling) with other attributes
7895 * (e.g., for syntax highlighting).
7896 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00007897 * This creates a new group when required.
7898 * Since we expect there to be few spelling mistakes we don't cache the
7899 * result.
7900 * Return the resulting attributes.
7901 */
7902 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00007903hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00007904 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00007905 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007906{
7907 attrentry_T *char_aep = NULL;
7908 attrentry_T *spell_aep;
7909 attrentry_T new_en;
7910
7911 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00007912 return prim_attr;
7913 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
7914 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007915#ifdef FEAT_GUI
7916 if (gui.in_use)
7917 {
7918 if (char_attr > HL_ALL)
7919 char_aep = syn_gui_attr2entry(char_attr);
7920 if (char_aep != NULL)
7921 new_en = *char_aep;
7922 else
7923 {
7924 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00007925 new_en.ae_u.gui.fg_color = INVALCOLOR;
7926 new_en.ae_u.gui.bg_color = INVALCOLOR;
7927 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007928 if (char_attr <= HL_ALL)
7929 new_en.ae_attr = char_attr;
7930 }
7931
Bram Moolenaar30abd282005-06-22 22:35:10 +00007932 if (prim_attr <= HL_ALL)
7933 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007934 else
7935 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00007936 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00007937 if (spell_aep != NULL)
7938 {
7939 new_en.ae_attr |= spell_aep->ae_attr;
7940 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7941 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7942 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7943 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7944 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7945 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7946 if (spell_aep->ae_u.gui.font != NOFONT)
7947 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7948# ifdef FEAT_XFONTSET
7949 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7950 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7951# endif
7952 }
7953 }
7954 return get_attr_entry(&gui_attr_table, &new_en);
7955 }
7956#endif
7957
7958 if (t_colors > 1)
7959 {
7960 if (char_attr > HL_ALL)
7961 char_aep = syn_cterm_attr2entry(char_attr);
7962 if (char_aep != NULL)
7963 new_en = *char_aep;
7964 else
7965 {
7966 vim_memset(&new_en, 0, sizeof(new_en));
7967 if (char_attr <= HL_ALL)
7968 new_en.ae_attr = char_attr;
7969 }
7970
Bram Moolenaar30abd282005-06-22 22:35:10 +00007971 if (prim_attr <= HL_ALL)
7972 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007973 else
7974 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00007975 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00007976 if (spell_aep != NULL)
7977 {
7978 new_en.ae_attr |= spell_aep->ae_attr;
7979 if (spell_aep->ae_u.cterm.fg_color > 0)
7980 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
7981 if (spell_aep->ae_u.cterm.bg_color > 0)
7982 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
7983 }
7984 }
7985 return get_attr_entry(&cterm_attr_table, &new_en);
7986 }
7987
7988 if (char_attr > HL_ALL)
7989 char_aep = syn_term_attr2entry(char_attr);
7990 if (char_aep != NULL)
7991 new_en = *char_aep;
7992 else
7993 {
7994 vim_memset(&new_en, 0, sizeof(new_en));
7995 if (char_attr <= HL_ALL)
7996 new_en.ae_attr = char_attr;
7997 }
7998
Bram Moolenaar30abd282005-06-22 22:35:10 +00007999 if (prim_attr <= HL_ALL)
8000 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008001 else
8002 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008003 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008004 if (spell_aep != NULL)
8005 {
8006 new_en.ae_attr |= spell_aep->ae_attr;
8007 if (spell_aep->ae_u.term.start != NULL)
8008 {
8009 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8010 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8011 }
8012 }
8013 }
8014 return get_attr_entry(&term_attr_table, &new_en);
8015}
8016#endif
8017
Bram Moolenaar071d4272004-06-13 20:20:40 +00008018#ifdef FEAT_GUI
8019
8020 attrentry_T *
8021syn_gui_attr2entry(attr)
8022 int attr;
8023{
8024 attr -= ATTR_OFF;
8025 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8026 return NULL;
8027 return &(GUI_ATTR_ENTRY(attr));
8028}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008029#endif /* FEAT_GUI */
8030
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008031/*
8032 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8033 * Only to be used when "attr" > HL_ALL.
8034 */
8035 int
8036syn_attr2attr(attr)
8037 int attr;
8038{
8039 attrentry_T *aep;
8040
8041#ifdef FEAT_GUI
8042 if (gui.in_use)
8043 aep = syn_gui_attr2entry(attr);
8044 else
8045#endif
8046 if (t_colors > 1)
8047 aep = syn_cterm_attr2entry(attr);
8048 else
8049 aep = syn_term_attr2entry(attr);
8050
8051 if (aep == NULL) /* highlighting not set */
8052 return 0;
8053 return aep->ae_attr;
8054}
8055
8056
Bram Moolenaar071d4272004-06-13 20:20:40 +00008057 attrentry_T *
8058syn_term_attr2entry(attr)
8059 int attr;
8060{
8061 attr -= ATTR_OFF;
8062 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8063 return NULL;
8064 return &(TERM_ATTR_ENTRY(attr));
8065}
8066
8067 attrentry_T *
8068syn_cterm_attr2entry(attr)
8069 int attr;
8070{
8071 attr -= ATTR_OFF;
8072 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8073 return NULL;
8074 return &(CTERM_ATTR_ENTRY(attr));
8075}
8076
8077#define LIST_ATTR 1
8078#define LIST_STRING 2
8079#define LIST_INT 3
8080
8081 static void
8082highlight_list_one(id)
8083 int id;
8084{
8085 struct hl_group *sgp;
8086 int didh = FALSE;
8087
8088 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8089
8090 didh = highlight_list_arg(id, didh, LIST_ATTR,
8091 sgp->sg_term, NULL, "term");
8092 didh = highlight_list_arg(id, didh, LIST_STRING,
8093 0, sgp->sg_start, "start");
8094 didh = highlight_list_arg(id, didh, LIST_STRING,
8095 0, sgp->sg_stop, "stop");
8096
8097 didh = highlight_list_arg(id, didh, LIST_ATTR,
8098 sgp->sg_cterm, NULL, "cterm");
8099 didh = highlight_list_arg(id, didh, LIST_INT,
8100 sgp->sg_cterm_fg, NULL, "ctermfg");
8101 didh = highlight_list_arg(id, didh, LIST_INT,
8102 sgp->sg_cterm_bg, NULL, "ctermbg");
8103
8104#ifdef FEAT_GUI
8105 didh = highlight_list_arg(id, didh, LIST_ATTR,
8106 sgp->sg_gui, NULL, "gui");
8107 didh = highlight_list_arg(id, didh, LIST_STRING,
8108 0, sgp->sg_gui_fg_name, "guifg");
8109 didh = highlight_list_arg(id, didh, LIST_STRING,
8110 0, sgp->sg_gui_bg_name, "guibg");
8111 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008112 0, sgp->sg_gui_sp_name, "guisp");
8113 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008114 0, sgp->sg_font_name, "font");
8115#endif
8116
Bram Moolenaar661b1822005-07-28 22:36:45 +00008117 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008118 {
8119 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008120 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008121 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8122 msg_putchar(' ');
8123 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8124 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008125
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008126 if (!didh)
8127 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008128#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008129 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008130 last_set_msg(sgp->sg_scriptID);
8131#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008132}
8133
8134 static int
8135highlight_list_arg(id, didh, type, iarg, sarg, name)
8136 int id;
8137 int didh;
8138 int type;
8139 int iarg;
8140 char_u *sarg;
8141 char *name;
8142{
8143 char_u buf[100];
8144 char_u *ts;
8145 int i;
8146
Bram Moolenaar661b1822005-07-28 22:36:45 +00008147 if (got_int)
8148 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008149 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8150 {
8151 ts = buf;
8152 if (type == LIST_INT)
8153 sprintf((char *)buf, "%d", iarg - 1);
8154 else if (type == LIST_STRING)
8155 ts = sarg;
8156 else /* type == LIST_ATTR */
8157 {
8158 buf[0] = NUL;
8159 for (i = 0; hl_attr_table[i] != 0; ++i)
8160 {
8161 if (iarg & hl_attr_table[i])
8162 {
8163 if (buf[0] != NUL)
8164 STRCAT(buf, ",");
8165 STRCAT(buf, hl_name_table[i]);
8166 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8167 }
8168 }
8169 }
8170
8171 (void)syn_list_header(didh,
8172 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8173 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008174 if (!got_int)
8175 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008176 if (*name != NUL)
8177 {
8178 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8179 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8180 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008181 msg_outtrans(ts);
8182 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008183 }
8184 return didh;
8185}
8186
8187#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8188/*
8189 * Return "1" if highlight group "id" has attribute "flag".
8190 * Return NULL otherwise.
8191 */
8192 char_u *
8193highlight_has_attr(id, flag, modec)
8194 int id;
8195 int flag;
8196 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8197{
8198 int attr;
8199
8200 if (id <= 0 || id > highlight_ga.ga_len)
8201 return NULL;
8202
8203#ifdef FEAT_GUI
8204 if (modec == 'g')
8205 attr = HL_TABLE()[id - 1].sg_gui;
8206 else
8207#endif
8208 if (modec == 'c')
8209 attr = HL_TABLE()[id - 1].sg_cterm;
8210 else
8211 attr = HL_TABLE()[id - 1].sg_term;
8212
8213 if (attr & flag)
8214 return (char_u *)"1";
8215 return NULL;
8216}
8217#endif
8218
8219#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8220/*
8221 * Return color name of highlight group "id".
8222 */
8223 char_u *
8224highlight_color(id, what, modec)
8225 int id;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008226 char_u *what; /* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008227 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8228{
8229 static char_u name[20];
8230 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008231 int fg = FALSE;
8232# ifdef FEAT_GUI
8233 int sp = FALSE;
8234# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008235
8236 if (id <= 0 || id > highlight_ga.ga_len)
8237 return NULL;
8238
8239 if (TOLOWER_ASC(what[0]) == 'f')
8240 fg = TRUE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008241# ifdef FEAT_GUI
8242 else if (TOLOWER_ASC(what[0]) == 's')
8243 sp = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008244 if (modec == 'g')
8245 {
8246 /* return #RRGGBB form (only possible when GUI is running) */
8247 if (gui.in_use && what[1] && what[2] == '#')
8248 {
8249 guicolor_T color;
8250 long_u rgb;
8251 static char_u buf[10];
8252
8253 if (fg)
8254 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008255 else if (sp)
8256 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008257 else
8258 color = HL_TABLE()[id - 1].sg_gui_bg;
8259 if (color == INVALCOLOR)
8260 return NULL;
8261 rgb = gui_mch_get_rgb(color);
8262 sprintf((char *)buf, "#%02x%02x%02x",
8263 (unsigned)(rgb >> 16),
8264 (unsigned)(rgb >> 8) & 255,
8265 (unsigned)rgb & 255);
8266 return buf;
8267 }
8268 if (fg)
8269 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008270 if (sp)
8271 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008272 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8273 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008274# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008275 if (modec == 'c')
8276 {
8277 if (fg)
8278 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8279 else
8280 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8281 sprintf((char *)name, "%d", n);
8282 return name;
8283 }
8284 /* term doesn't have color */
8285 return NULL;
8286}
8287#endif
8288
8289#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8290 || defined(PROTO)
8291/*
8292 * Return color name of highlight group "id" as RGB value.
8293 */
8294 long_u
8295highlight_gui_color_rgb(id, fg)
8296 int id;
8297 int fg; /* TRUE = fg, FALSE = bg */
8298{
8299 guicolor_T color;
8300
8301 if (id <= 0 || id > highlight_ga.ga_len)
8302 return 0L;
8303
8304 if (fg)
8305 color = HL_TABLE()[id - 1].sg_gui_fg;
8306 else
8307 color = HL_TABLE()[id - 1].sg_gui_bg;
8308
8309 if (color == INVALCOLOR)
8310 return 0L;
8311
8312 return gui_mch_get_rgb(color);
8313}
8314#endif
8315
8316/*
8317 * Output the syntax list header.
8318 * Return TRUE when started a new line.
8319 */
8320 static int
8321syn_list_header(did_header, outlen, id)
8322 int did_header; /* did header already */
8323 int outlen; /* length of string that comes */
8324 int id; /* highlight group id */
8325{
8326 int endcol = 19;
8327 int newline = TRUE;
8328
8329 if (!did_header)
8330 {
8331 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008332 if (got_int)
8333 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008334 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8335 endcol = 15;
8336 }
8337 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008338 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008339 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008340 if (got_int)
8341 return TRUE;
8342 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008343 else
8344 {
8345 if (msg_col >= endcol) /* wrap around is like starting a new line */
8346 newline = FALSE;
8347 }
8348
8349 if (msg_col >= endcol) /* output at least one space */
8350 endcol = msg_col + 1;
8351 if (Columns <= endcol) /* avoid hang for tiny window */
8352 endcol = Columns - 1;
8353
8354 msg_advance(endcol);
8355
8356 /* Show "xxx" with the attributes. */
8357 if (!did_header)
8358 {
8359 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8360 msg_putchar(' ');
8361 }
8362
8363 return newline;
8364}
8365
8366/*
8367 * Set the attribute numbers for a highlight group.
8368 * Called after one of the attributes has changed.
8369 */
8370 static void
8371set_hl_attr(idx)
8372 int idx; /* index in array */
8373{
8374 attrentry_T at_en;
8375 struct hl_group *sgp = HL_TABLE() + idx;
8376
8377 /* The "Normal" group doesn't need an attribute number */
8378 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8379 return;
8380
8381#ifdef FEAT_GUI
8382 /*
8383 * For the GUI mode: If there are other than "normal" highlighting
8384 * attributes, need to allocate an attr number.
8385 */
8386 if (sgp->sg_gui_fg == INVALCOLOR
8387 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008388 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008389 && sgp->sg_font == NOFONT
8390# ifdef FEAT_XFONTSET
8391 && sgp->sg_fontset == NOFONTSET
8392# endif
8393 )
8394 {
8395 sgp->sg_gui_attr = sgp->sg_gui;
8396 }
8397 else
8398 {
8399 at_en.ae_attr = sgp->sg_gui;
8400 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8401 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008402 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008403 at_en.ae_u.gui.font = sgp->sg_font;
8404# ifdef FEAT_XFONTSET
8405 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8406# endif
8407 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8408 }
8409#endif
8410 /*
8411 * For the term mode: If there are other than "normal" highlighting
8412 * attributes, need to allocate an attr number.
8413 */
8414 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8415 sgp->sg_term_attr = sgp->sg_term;
8416 else
8417 {
8418 at_en.ae_attr = sgp->sg_term;
8419 at_en.ae_u.term.start = sgp->sg_start;
8420 at_en.ae_u.term.stop = sgp->sg_stop;
8421 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8422 }
8423
8424 /*
8425 * For the color term mode: If there are other than "normal"
8426 * highlighting attributes, need to allocate an attr number.
8427 */
8428 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8429 sgp->sg_cterm_attr = sgp->sg_cterm;
8430 else
8431 {
8432 at_en.ae_attr = sgp->sg_cterm;
8433 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8434 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8435 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8436 }
8437}
8438
8439/*
8440 * Lookup a highlight group name and return it's ID.
8441 * If it is not found, 0 is returned.
8442 */
8443 int
8444syn_name2id(name)
8445 char_u *name;
8446{
8447 int i;
8448 char_u name_u[200];
8449
8450 /* Avoid using stricmp() too much, it's slow on some systems */
8451 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8452 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008453 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008454 vim_strup(name_u);
8455 for (i = highlight_ga.ga_len; --i >= 0; )
8456 if (HL_TABLE()[i].sg_name_u != NULL
8457 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8458 break;
8459 return i + 1;
8460}
8461
8462#if defined(FEAT_EVAL) || defined(PROTO)
8463/*
8464 * Return TRUE if highlight group "name" exists.
8465 */
8466 int
8467highlight_exists(name)
8468 char_u *name;
8469{
8470 return (syn_name2id(name) > 0);
8471}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008472
8473# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8474/*
8475 * Return the name of highlight group "id".
8476 * When not a valid ID return an empty string.
8477 */
8478 char_u *
8479syn_id2name(id)
8480 int id;
8481{
8482 if (id <= 0 || id >= highlight_ga.ga_len)
8483 return (char_u *)"";
8484 return HL_TABLE()[id - 1].sg_name;
8485}
8486# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008487#endif
8488
8489/*
8490 * Like syn_name2id(), but take a pointer + length argument.
8491 */
8492 int
8493syn_namen2id(linep, len)
8494 char_u *linep;
8495 int len;
8496{
8497 char_u *name;
8498 int id = 0;
8499
8500 name = vim_strnsave(linep, len);
8501 if (name != NULL)
8502 {
8503 id = syn_name2id(name);
8504 vim_free(name);
8505 }
8506 return id;
8507}
8508
8509/*
8510 * Find highlight group name in the table and return it's ID.
8511 * The argument is a pointer to the name and the length of the name.
8512 * If it doesn't exist yet, a new entry is created.
8513 * Return 0 for failure.
8514 */
8515 int
8516syn_check_group(pp, len)
8517 char_u *pp;
8518 int len;
8519{
8520 int id;
8521 char_u *name;
8522
8523 name = vim_strnsave(pp, len);
8524 if (name == NULL)
8525 return 0;
8526
8527 id = syn_name2id(name);
8528 if (id == 0) /* doesn't exist yet */
8529 id = syn_add_group(name);
8530 else
8531 vim_free(name);
8532 return id;
8533}
8534
8535/*
8536 * Add new highlight group and return it's ID.
8537 * "name" must be an allocated string, it will be consumed.
8538 * Return 0 for failure.
8539 */
8540 static int
8541syn_add_group(name)
8542 char_u *name;
8543{
8544 char_u *p;
8545
8546 /* Check that the name is ASCII letters, digits and underscore. */
8547 for (p = name; *p != NUL; ++p)
8548 {
8549 if (!vim_isprintc(*p))
8550 {
8551 EMSG(_("E669: Unprintable character in group name"));
8552 return 0;
8553 }
8554 else if (!ASCII_ISALNUM(*p) && *p != '_')
8555 {
8556 /* This is an error, but since there previously was no check only
8557 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008558 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008559 MSG(_("W18: Invalid character in group name"));
8560 break;
8561 }
8562 }
8563
8564 /*
8565 * First call for this growarray: init growing array.
8566 */
8567 if (highlight_ga.ga_data == NULL)
8568 {
8569 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8570 highlight_ga.ga_growsize = 10;
8571 }
8572
8573 /*
8574 * Make room for at least one other syntax_highlight entry.
8575 */
8576 if (ga_grow(&highlight_ga, 1) == FAIL)
8577 {
8578 vim_free(name);
8579 return 0;
8580 }
8581
8582 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8583 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8584 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8585#ifdef FEAT_GUI
8586 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8587 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008588 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008589#endif
8590 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008591
8592 return highlight_ga.ga_len; /* ID is index plus one */
8593}
8594
8595/*
8596 * When, just after calling syn_add_group(), an error is discovered, this
8597 * function deletes the new name.
8598 */
8599 static void
8600syn_unadd_group()
8601{
8602 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008603 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8604 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8605}
8606
8607/*
8608 * Translate a group ID to highlight attributes.
8609 */
8610 int
8611syn_id2attr(hl_id)
8612 int hl_id;
8613{
8614 int attr;
8615 struct hl_group *sgp;
8616
8617 hl_id = syn_get_final_id(hl_id);
8618 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8619
8620#ifdef FEAT_GUI
8621 /*
8622 * Only use GUI attr when the GUI is being used.
8623 */
8624 if (gui.in_use)
8625 attr = sgp->sg_gui_attr;
8626 else
8627#endif
8628 if (t_colors > 1)
8629 attr = sgp->sg_cterm_attr;
8630 else
8631 attr = sgp->sg_term_attr;
8632
8633 return attr;
8634}
8635
8636#ifdef FEAT_GUI
8637/*
8638 * Get the GUI colors and attributes for a group ID.
8639 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8640 */
8641 int
8642syn_id2colors(hl_id, fgp, bgp)
8643 int hl_id;
8644 guicolor_T *fgp;
8645 guicolor_T *bgp;
8646{
8647 struct hl_group *sgp;
8648
8649 hl_id = syn_get_final_id(hl_id);
8650 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8651
8652 *fgp = sgp->sg_gui_fg;
8653 *bgp = sgp->sg_gui_bg;
8654 return sgp->sg_gui;
8655}
8656#endif
8657
8658/*
8659 * Translate a group ID to the final group ID (following links).
8660 */
8661 int
8662syn_get_final_id(hl_id)
8663 int hl_id;
8664{
8665 int count;
8666 struct hl_group *sgp;
8667
8668 if (hl_id > highlight_ga.ga_len || hl_id < 1)
8669 return 0; /* Can be called from eval!! */
8670
8671 /*
8672 * Follow links until there is no more.
8673 * Look out for loops! Break after 100 links.
8674 */
8675 for (count = 100; --count >= 0; )
8676 {
8677 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8678 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8679 break;
8680 hl_id = sgp->sg_link;
8681 }
8682
8683 return hl_id;
8684}
8685
8686#ifdef FEAT_GUI
8687/*
8688 * Call this function just after the GUI has started.
8689 * It finds the font and color handles for the highlighting groups.
8690 */
8691 void
8692highlight_gui_started()
8693{
8694 int idx;
8695
8696 /* First get the colors from the "Normal" and "Menu" group, if set */
8697 set_normal_colors();
8698
8699 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8700 gui_do_one_color(idx, FALSE, FALSE);
8701
8702 highlight_changed();
8703}
8704
8705 static void
8706gui_do_one_color(idx, do_menu, do_tooltip)
8707 int idx;
8708 int do_menu; /* TRUE: might set the menu font */
8709 int do_tooltip; /* TRUE: might set the tooltip font */
8710{
8711 int didit = FALSE;
8712
8713 if (HL_TABLE()[idx].sg_font_name != NULL)
8714 {
8715 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8716 do_tooltip);
8717 didit = TRUE;
8718 }
8719 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8720 {
8721 HL_TABLE()[idx].sg_gui_fg =
8722 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8723 didit = TRUE;
8724 }
8725 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8726 {
8727 HL_TABLE()[idx].sg_gui_bg =
8728 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8729 didit = TRUE;
8730 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008731 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8732 {
8733 HL_TABLE()[idx].sg_gui_sp =
8734 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8735 didit = TRUE;
8736 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008737 if (didit) /* need to get a new attr number */
8738 set_hl_attr(idx);
8739}
8740
8741#endif
8742
8743/*
8744 * Translate the 'highlight' option into attributes in highlight_attr[] and
8745 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
8746 * corresponding highlights to use on top of HLF_SNC is computed.
8747 * Called only when the 'highlight' option has been changed and upon first
8748 * screen redraw after any :highlight command.
8749 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
8750 */
8751 int
8752highlight_changed()
8753{
8754 int hlf;
8755 int i;
8756 char_u *p;
8757 int attr;
8758 char_u *end;
8759 int id;
8760#ifdef USER_HIGHLIGHT
8761 char_u userhl[10];
8762# ifdef FEAT_STL_OPT
8763 int id_SNC = -1;
8764 int id_S = -1;
8765 int hlcnt;
8766# endif
8767#endif
8768 static int hl_flags[HLF_COUNT] = HL_FLAGS;
8769
8770 need_highlight_changed = FALSE;
8771
8772 /*
8773 * Clear all attributes.
8774 */
8775 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8776 highlight_attr[hlf] = 0;
8777
8778 /*
8779 * First set all attributes to their default value.
8780 * Then use the attributes from the 'highlight' option.
8781 */
8782 for (i = 0; i < 2; ++i)
8783 {
8784 if (i)
8785 p = p_hl;
8786 else
8787 p = get_highlight_default();
8788 if (p == NULL) /* just in case */
8789 continue;
8790
8791 while (*p)
8792 {
8793 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8794 if (hl_flags[hlf] == *p)
8795 break;
8796 ++p;
8797 if (hlf == (int)HLF_COUNT || *p == NUL)
8798 return FAIL;
8799
8800 /*
8801 * Allow several hl_flags to be combined, like "bu" for
8802 * bold-underlined.
8803 */
8804 attr = 0;
8805 for ( ; *p && *p != ','; ++p) /* parse upto comma */
8806 {
8807 if (vim_iswhite(*p)) /* ignore white space */
8808 continue;
8809
8810 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
8811 return FAIL;
8812
8813 switch (*p)
8814 {
8815 case 'b': attr |= HL_BOLD;
8816 break;
8817 case 'i': attr |= HL_ITALIC;
8818 break;
8819 case '-':
8820 case 'n': /* no highlighting */
8821 break;
8822 case 'r': attr |= HL_INVERSE;
8823 break;
8824 case 's': attr |= HL_STANDOUT;
8825 break;
8826 case 'u': attr |= HL_UNDERLINE;
8827 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008828 case 'c': attr |= HL_UNDERCURL;
8829 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008830 case ':': ++p; /* highlight group name */
8831 if (attr || *p == NUL) /* no combinations */
8832 return FAIL;
8833 end = vim_strchr(p, ',');
8834 if (end == NULL)
8835 end = p + STRLEN(p);
8836 id = syn_check_group(p, (int)(end - p));
8837 if (id == 0)
8838 return FAIL;
8839 attr = syn_id2attr(id);
8840 p = end - 1;
8841#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8842 if (hlf == (int)HLF_SNC)
8843 id_SNC = syn_get_final_id(id);
8844 else if (hlf == (int)HLF_S)
8845 id_S = syn_get_final_id(id);
8846#endif
8847 break;
8848 default: return FAIL;
8849 }
8850 }
8851 highlight_attr[hlf] = attr;
8852
8853 p = skip_to_option_part(p); /* skip comma and spaces */
8854 }
8855 }
8856
8857#ifdef USER_HIGHLIGHT
8858 /* Setup the user highlights
8859 *
8860 * Temporarily utilize 10 more hl entries. Have to be in there
8861 * simultaneously in case of table overflows in get_attr_entry()
8862 */
8863# ifdef FEAT_STL_OPT
8864 if (ga_grow(&highlight_ga, 10) == FAIL)
8865 return FAIL;
8866 hlcnt = highlight_ga.ga_len;
8867 if (id_S == 0)
8868 { /* Make sure id_S is always valid to simplify code below */
8869 memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8870 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8871 id_S = hlcnt + 10;
8872 }
8873# endif
8874 for (i = 0; i < 9; i++)
8875 {
8876 sprintf((char *)userhl, "User%d", i + 1);
8877 id = syn_name2id(userhl);
8878 if (id == 0)
8879 {
8880 highlight_user[i] = 0;
8881# ifdef FEAT_STL_OPT
8882 highlight_stlnc[i] = 0;
8883# endif
8884 }
8885 else
8886 {
8887# ifdef FEAT_STL_OPT
8888 struct hl_group *hlt = HL_TABLE();
8889# endif
8890
8891 highlight_user[i] = syn_id2attr(id);
8892# ifdef FEAT_STL_OPT
8893 if (id_SNC == 0)
8894 {
8895 memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8896 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8897 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8898# ifdef FEAT_GUI
8899 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8900# endif
8901 }
8902 else
8903 mch_memmove(&hlt[hlcnt + i],
8904 &hlt[id_SNC - 1],
8905 sizeof(struct hl_group));
8906 hlt[hlcnt + i].sg_link = 0;
8907
8908 /* Apply difference between UserX and HLF_S to HLF_SNC */
8909 hlt[hlcnt + i].sg_term ^=
8910 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8911 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8912 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8913 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8914 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8915 hlt[hlcnt + i].sg_cterm ^=
8916 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8917 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8918 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8919 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8920 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8921# ifdef FEAT_GUI
8922 hlt[hlcnt + i].sg_gui ^=
8923 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8924 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8925 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8926 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8927 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008928 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8929 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008930 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8931 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8932# ifdef FEAT_XFONTSET
8933 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8934 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8935# endif
8936# endif
8937 highlight_ga.ga_len = hlcnt + i + 1;
8938 set_hl_attr(hlcnt + i); /* At long last we can apply */
8939 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8940# endif
8941 }
8942 }
8943# ifdef FEAT_STL_OPT
8944 highlight_ga.ga_len = hlcnt;
8945# endif
8946
8947#endif /* USER_HIGHLIGHT */
8948
8949 return OK;
8950}
8951
8952#ifdef FEAT_CMDL_COMPL
8953
8954static void highlight_list __ARGS((void));
8955static void highlight_list_two __ARGS((int cnt, int attr));
8956
8957/*
8958 * Handle command line completion for :highlight command.
8959 */
8960 void
8961set_context_in_highlight_cmd(xp, arg)
8962 expand_T *xp;
8963 char_u *arg;
8964{
8965 char_u *p;
8966
8967 /* Default: expand group names */
8968 xp->xp_context = EXPAND_HIGHLIGHT;
8969 xp->xp_pattern = arg;
8970 include_link = TRUE;
8971 include_default = TRUE;
8972
8973 /* (part of) subcommand already typed */
8974 if (*arg != NUL)
8975 {
8976 p = skiptowhite(arg);
8977 if (*p != NUL) /* past "default" or group name */
8978 {
8979 include_default = FALSE;
8980 if (STRNCMP("default", arg, p - arg) == 0)
8981 {
8982 arg = skipwhite(p);
8983 xp->xp_pattern = arg;
8984 p = skiptowhite(arg);
8985 }
8986 if (*p != NUL) /* past group name */
8987 {
8988 include_link = FALSE;
8989 if (arg[1] == 'i' && arg[0] == 'N')
8990 highlight_list();
8991 if (STRNCMP("link", arg, p - arg) == 0
8992 || STRNCMP("clear", arg, p - arg) == 0)
8993 {
8994 xp->xp_pattern = skipwhite(p);
8995 p = skiptowhite(xp->xp_pattern);
8996 if (*p != NUL) /* past first group name */
8997 {
8998 xp->xp_pattern = skipwhite(p);
8999 p = skiptowhite(xp->xp_pattern);
9000 }
9001 }
9002 if (*p != NUL) /* past group name(s) */
9003 xp->xp_context = EXPAND_NOTHING;
9004 }
9005 }
9006 }
9007}
9008
9009/*
9010 * List highlighting matches in a nice way.
9011 */
9012 static void
9013highlight_list()
9014{
9015 int i;
9016
9017 for (i = 10; --i >= 0; )
9018 highlight_list_two(i, hl_attr(HLF_D));
9019 for (i = 40; --i >= 0; )
9020 highlight_list_two(99, 0);
9021}
9022
9023 static void
9024highlight_list_two(cnt, attr)
9025 int cnt;
9026 int attr;
9027{
9028 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9029 msg_clr_eos();
9030 out_flush();
9031 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9032}
9033
9034#endif /* FEAT_CMDL_COMPL */
9035
9036#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9037 || defined(FEAT_SIGNS) || defined(PROTO)
9038/*
9039 * Function given to ExpandGeneric() to obtain the list of group names.
9040 * Also used for synIDattr() function.
9041 */
9042/*ARGSUSED*/
9043 char_u *
9044get_highlight_name(xp, idx)
9045 expand_T *xp;
9046 int idx;
9047{
9048 if (idx == highlight_ga.ga_len
9049#ifdef FEAT_CMDL_COMPL
9050 && include_link
9051#endif
9052 )
9053 return (char_u *)"link";
9054 if (idx == highlight_ga.ga_len + 1
9055#ifdef FEAT_CMDL_COMPL
9056 && include_link
9057#endif
9058 )
9059 return (char_u *)"clear";
9060 if (idx == highlight_ga.ga_len + 2
9061#ifdef FEAT_CMDL_COMPL
9062 && include_default
9063#endif
9064 )
9065 return (char_u *)"default";
9066 if (idx < 0 || idx >= highlight_ga.ga_len)
9067 return NULL;
9068 return HL_TABLE()[idx].sg_name;
9069}
9070#endif
9071
9072#ifdef FEAT_GUI
9073/*
9074 * Free all the highlight group fonts.
9075 * Used when quitting for systems which need it.
9076 */
9077 void
9078free_highlight_fonts()
9079{
9080 int idx;
9081
9082 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9083 {
9084 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9085 HL_TABLE()[idx].sg_font = NOFONT;
9086# ifdef FEAT_XFONTSET
9087 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9088 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9089# endif
9090 }
9091
9092 gui_mch_free_font(gui.norm_font);
9093# ifdef FEAT_XFONTSET
9094 gui_mch_free_fontset(gui.fontset);
9095# endif
9096# ifndef HAVE_GTK2
9097 gui_mch_free_font(gui.bold_font);
9098 gui_mch_free_font(gui.ital_font);
9099 gui_mch_free_font(gui.boldital_font);
9100# endif
9101}
9102#endif
9103
9104/**************************************
9105 * End of Highlighting stuff *
9106 **************************************/