blob: d5047c7c6c66bde04889fd9cd5f5f640a2c28a26 [file] [log] [blame]
Bram Moolenaar071d4272004-06-13 20:20:40 +00001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * syntax.c: code for syntax highlighting
12 */
13
14#include "vim.h"
15
16/*
17 * Structure that stores information about a highlight group.
18 * The ID of a highlight group is also called group ID. It is the index in
19 * the highlight_ga array PLUS ONE.
20 */
21struct hl_group
22{
23 char_u *sg_name; /* highlight group name */
24 char_u *sg_name_u; /* uppercase of sg_name */
25/* for normal terminals */
26 int sg_term; /* "term=" highlighting attributes */
27 char_u *sg_start; /* terminal string for start highl */
28 char_u *sg_stop; /* terminal string for stop highl */
29 int sg_term_attr; /* Screen attr for term mode */
30/* for color terminals */
31 int sg_cterm; /* "cterm=" highlighting attr */
32 int sg_cterm_bold; /* bold attr was set for light color */
33 int sg_cterm_fg; /* terminal fg color number + 1 */
34 int sg_cterm_bg; /* terminal bg color number + 1 */
35 int sg_cterm_attr; /* Screen attr for color term mode */
36#ifdef FEAT_GUI
37/* for when using the GUI */
38 int sg_gui; /* "gui=" highlighting attributes */
39 guicolor_T sg_gui_fg; /* GUI foreground color handle */
40 char_u *sg_gui_fg_name;/* GUI foreground color name */
41 guicolor_T sg_gui_bg; /* GUI background color handle */
42 char_u *sg_gui_bg_name;/* GUI background color name */
Bram Moolenaare2cc9702005-03-15 22:43:58 +000043 guicolor_T sg_gui_sp; /* GUI special color handle */
44 char_u *sg_gui_sp_name;/* GUI special color name */
Bram Moolenaar071d4272004-06-13 20:20:40 +000045 GuiFont sg_font; /* GUI font handle */
46#ifdef FEAT_XFONTSET
47 GuiFontset sg_fontset; /* GUI fontset handle */
48#endif
49 char_u *sg_font_name; /* GUI font or fontset name */
50 int sg_gui_attr; /* Screen attr for GUI mode */
51#endif
52 int sg_link; /* link to this highlight group ID */
53 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000054#ifdef FEAT_EVAL
55 scid_T sg_scriptID; /* script in which the group was last set */
56#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000057};
58
59#define SG_TERM 1 /* term has been set */
60#define SG_CTERM 2 /* cterm has been set */
61#define SG_GUI 4 /* gui has been set */
62#define SG_LINK 8 /* link has been set */
63
64static garray_T highlight_ga; /* highlight groups for 'highlight' option */
65
66#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
67
68#ifdef FEAT_CMDL_COMPL
69static int include_default = FALSE; /* include "default" for expansion */
70static int include_link = FALSE; /* include "link" for expansion */
71#endif
72
73/*
74 * The "term", "cterm" and "gui" arguments can be any combination of the
75 * following names, separated by commas (but no spaces!).
76 */
77static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000078 {"bold", "standout", "underline", "undercurl",
79 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000080static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000081 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000082
83static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
84static void syn_unadd_group __ARGS((void));
85static void set_hl_attr __ARGS((int idx));
86static void highlight_list_one __ARGS((int id));
87static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
88static int syn_add_group __ARGS((char_u *name));
89static int syn_list_header __ARGS((int did_header, int outlen, int id));
90static int hl_has_settings __ARGS((int idx, int check_link));
91static void highlight_clear __ARGS((int idx));
92
93#ifdef FEAT_GUI
94static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
95static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
96static guicolor_T color_name2handle __ARGS((char_u *name));
97static GuiFont font_name2handle __ARGS((char_u *name));
98# ifdef FEAT_XFONTSET
99static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
100# endif
101static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip));
102#endif
103
104/*
105 * An attribute number is the index in attr_table plus ATTR_OFF.
106 */
107#define ATTR_OFF (HL_ALL + 1)
108
109#if defined(FEAT_SYN_HL) || defined(PROTO)
110
111#define SYN_NAMELEN 50 /* maximum length of a syntax name */
112
113/* different types of offsets that are possible */
114#define SPO_MS_OFF 0 /* match start offset */
115#define SPO_ME_OFF 1 /* match end offset */
116#define SPO_HS_OFF 2 /* highl. start offset */
117#define SPO_HE_OFF 3 /* highl. end offset */
118#define SPO_RS_OFF 4 /* region start offset */
119#define SPO_RE_OFF 5 /* region end offset */
120#define SPO_LC_OFF 6 /* leading context offset */
121#define SPO_COUNT 7
122
123static char *(spo_name_tab[SPO_COUNT]) =
124 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
125
126/*
127 * The patterns that are being searched for are stored in a syn_pattern.
128 * A match item consists of one pattern.
129 * A start/end item consists of n start patterns and m end patterns.
130 * A start/skip/end item consists of n start patterns, one skip pattern and m
131 * end patterns.
132 * For the latter two, the patterns are always consecutive: start-skip-end.
133 *
134 * A character offset can be given for the matched text (_m_start and _m_end)
135 * and for the actually highlighted text (_h_start and _h_end).
136 */
137typedef struct syn_pattern
138{
139 char sp_type; /* see SPTYPE_ defines below */
140 char sp_syncing; /* this item used for syncing */
141 short sp_flags; /* see HL_ defines below */
142 struct sp_syn sp_syn; /* struct passed to in_id_list() */
143 short sp_syn_match_id; /* highlight group ID of pattern */
144 char_u *sp_pattern; /* regexp to match, pattern */
145 regprog_T *sp_prog; /* regexp to match, program */
146 int sp_ic; /* ignore-case flag for sp_prog */
147 short sp_off_flags; /* see below */
148 int sp_offsets[SPO_COUNT]; /* offsets */
149 short *sp_cont_list; /* cont. group IDs, if non-zero */
150 short *sp_next_list; /* next group IDs, if non-zero */
151 int sp_sync_idx; /* sync item index (syncing only) */
152 int sp_line_id; /* ID of last line where tried */
153 int sp_startcol; /* next match in sp_line_id line */
154} synpat_T;
155
156/* The sp_off_flags are computed like this:
157 * offset from the start of the matched text: (1 << SPO_XX_OFF)
158 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
159 * When both are present, only one is used.
160 */
161
162#define SPTYPE_MATCH 1 /* match keyword with this group ID */
163#define SPTYPE_START 2 /* match a regexp, start of item */
164#define SPTYPE_END 3 /* match a regexp, end of item */
165#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
166
167#define HL_CONTAINED 0x01 /* not used on toplevel */
168#define HL_TRANSP 0x02 /* has no highlighting */
169#define HL_ONELINE 0x04 /* match within one line only */
170#define HL_HAS_EOL 0x08 /* end pattern that matches with $ */
171#define HL_SYNC_HERE 0x10 /* sync point after this item (syncing only) */
172#define HL_SYNC_THERE 0x20 /* sync point at current line (syncing only) */
173#define HL_MATCH 0x40 /* use match ID instead of item ID */
174#define HL_SKIPNL 0x80 /* nextgroup can skip newlines */
175#define HL_SKIPWHITE 0x100 /* nextgroup can skip white space */
176#define HL_SKIPEMPTY 0x200 /* nextgroup can skip empty lines */
177#define HL_KEEPEND 0x400 /* end match always kept */
178#define HL_EXCLUDENL 0x800 /* exclude NL from match */
179#define HL_DISPLAY 0x1000 /* only used for displaying, not syncing */
180#define HL_FOLD 0x2000 /* define fold */
181#define HL_EXTEND 0x4000 /* ignore a keepend */
182/* These don't fit in a short, thus can't be used for syntax items, only for
183 * si_flags and bs_flags. */
184#define HL_MATCHCONT 0x8000 /* match continued from previous line */
185#define HL_TRANS_CONT 0x10000L /* transparent item without contains arg */
186
187#define SYN_ITEMS(buf) ((synpat_T *)((buf)->b_syn_patterns.ga_data))
188
189#define NONE_IDX -2 /* value of sp_sync_idx for "NONE" */
190
191/*
192 * Flags for b_syn_sync_flags:
193 */
194#define SF_CCOMMENT 0x01 /* sync on a C-style comment */
195#define SF_MATCH 0x02 /* sync by matching a pattern */
196
197#define SYN_STATE_P(ssp) ((bufstate_T *)((ssp)->ga_data))
198
Bram Moolenaar071d4272004-06-13 20:20:40 +0000199#define MAXKEYWLEN 80 /* maximum length of a keyword */
200
201/*
202 * The attributes of the syntax item that has been recognized.
203 */
204static int current_attr = 0; /* attr of current syntax word */
205#ifdef FEAT_EVAL
206static int current_id = 0; /* ID of current char for syn_get_id() */
207static int current_trans_id = 0; /* idem, transparancy removed */
208#endif
209
Bram Moolenaar217ad922005-03-20 22:37:15 +0000210typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000211{
212 char_u *scl_name; /* syntax cluster name */
213 char_u *scl_name_u; /* uppercase of scl_name */
214 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000215} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000216
217/*
218 * Methods of combining two clusters
219 */
220#define CLUSTER_REPLACE 1 /* replace first list with second */
221#define CLUSTER_ADD 2 /* add second list to first */
222#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
223
Bram Moolenaar217ad922005-03-20 22:37:15 +0000224#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000225
226/*
227 * Syntax group IDs have different types:
228 * 0 - 9999 normal syntax groups
229 * 10000 - 14999 ALLBUT indicator (current_syn_inc_tag added)
230 * 15000 - 19999 TOP indicator (current_syn_inc_tag added)
231 * 20000 - 24999 CONTAINED indicator (current_syn_inc_tag added)
232 * >= 25000 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
233 */
234#define SYNID_ALLBUT 10000 /* syntax group ID for contains=ALLBUT */
235#define SYNID_TOP 15000 /* syntax group ID for contains=TOP */
236#define SYNID_CONTAINED 20000 /* syntax group ID for contains=CONTAINED */
237#define SYNID_CLUSTER 25000 /* first syntax group ID for clusters */
238
239/*
240 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
241 * expand_filename(). Most of the other syntax commands don't need it, so
242 * instead of passing it to them, we stow it here.
243 */
244static char_u **syn_cmdlinep;
245
246/*
247 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
248 * files from from leaking into ALLBUT lists, we assign a unique ID to the
249 * rules in each ":syn include"'d file.
250 */
251static int current_syn_inc_tag = 0;
252static int running_syn_inc_tag = 0;
253
254/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000255 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
256 * This avoids adding a pointer to the hashtable item.
257 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
258 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
259 * HI2KE() converts a hashitem pointer to a var pointer.
260 */
261static keyentry_T dumkey;
262#define KE2HIKEY(kp) ((kp)->keyword)
263#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
264#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
265
266/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000267 * To reduce the time spent in keepend(), remember at which level in the state
268 * stack the first item with "keepend" is present. When "-1", there is no
269 * "keepend" on the stack.
270 */
271static int keepend_level = -1;
272
273/*
274 * For the current state we need to remember more than just the idx.
275 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
276 * (The end positions have the column number of the next char)
277 */
278typedef struct state_item
279{
280 int si_idx; /* index of syntax pattern */
281 int si_id; /* highlight group ID for keywords */
282 int si_trans_id; /* idem, transparancy removed */
283 int si_m_lnum; /* lnum of the match */
284 int si_m_startcol; /* starting column of the match */
285 lpos_T si_m_endpos; /* just after end posn of the match */
286 lpos_T si_h_startpos; /* start position of the highlighting */
287 lpos_T si_h_endpos; /* end position of the highlighting */
288 lpos_T si_eoe_pos; /* end position of end pattern */
289 int si_end_idx; /* group ID for end pattern or zero */
290 int si_ends; /* if match ends before si_m_endpos */
291 int si_attr; /* attributes in this state */
292 long si_flags; /* HL_HAS_EOL flag in this state, and
293 * HL_SKIP* for si_next_list */
294 short *si_cont_list; /* list of contained groups */
295 short *si_next_list; /* nextgroup IDs after this item ends */
296 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
297 * pattern */
298} stateitem_T;
299
300#define KEYWORD_IDX -1 /* value of si_idx for keywords */
301#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
302 but contained groups */
303
304/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000305 * Struct to reduce the number of arguments to get_syn_options(), it's used
306 * very often.
307 */
308typedef struct
309{
310 int flags; /* flags for contained and transpartent */
311 int keyword; /* TRUE for ":syn keyword" */
312 int *sync_idx; /* syntax item for "grouphere" argument, NULL
313 if not allowed */
314 char has_cont_list; /* TRUE if "cont_list" can be used */
315 short *cont_list; /* group IDs for "contains" argument */
316 short *cont_in_list; /* group IDs for "containedin" argument */
317 short *next_list; /* group IDs for "nextgroup" argument */
318} syn_opt_arg_T;
319
320/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000321 * The next possible match in the current line for any pattern is remembered,
322 * to avoid having to try for a match in each column.
323 * If next_match_idx == -1, not tried (in this line) yet.
324 * If next_match_col == MAXCOL, no match found in this line.
325 * (All end positions have the column of the char after the end)
326 */
327static int next_match_col; /* column for start of next match */
328static lpos_T next_match_m_endpos; /* position for end of next match */
329static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
330static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
331static int next_match_idx; /* index of matched item */
332static long next_match_flags; /* flags for next match */
333static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
334static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
335static int next_match_end_idx; /* ID of group for end pattn or zero */
336static reg_extmatch_T *next_match_extmatch = NULL;
337
338/*
339 * A state stack is an array of integers or stateitem_T, stored in a
340 * garray_T. A state stack is invalid if it's itemsize entry is zero.
341 */
342#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
343#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
344
345/*
346 * The current state (within the line) of the recognition engine.
347 * When current_state.ga_itemsize is 0 the current state is invalid.
348 */
349static win_T *syn_win; /* current window for highlighting */
350static buf_T *syn_buf; /* current buffer for highlighting */
351static linenr_T current_lnum = 0; /* lnum of current state */
352static colnr_T current_col = 0; /* column of current state */
353static int current_state_stored = 0; /* TRUE if stored current state
354 * after setting current_finished */
355static int current_finished = 0; /* current line has been finished */
356static garray_T current_state /* current stack of state_items */
357 = {0, 0, 0, 0, NULL};
358static short *current_next_list = NULL; /* when non-zero, nextgroup list */
359static int current_next_flags = 0; /* flags for current_next_list */
360static int current_line_id = 0; /* unique number for current line */
361
362#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
363
364static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
365static int syn_match_linecont __ARGS((linenr_T lnum));
366static void syn_start_line __ARGS((void));
367static void syn_update_ends __ARGS((int startofline));
368static void syn_stack_alloc __ARGS((void));
369static int syn_stack_cleanup __ARGS((void));
370static void syn_stack_free_entry __ARGS((buf_T *buf, synstate_T *p));
371static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
372static synstate_T *store_current_state __ARGS((synstate_T *sp));
373static void load_current_state __ARGS((synstate_T *from));
374static void invalidate_current_state __ARGS((void));
375static int syn_stack_equal __ARGS((synstate_T *sp));
376static void validate_current_state __ARGS((void));
377static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar217ad922005-03-20 22:37:15 +0000378static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000379static int did_match_already __ARGS((int idx, garray_T *gap));
380static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
381static void check_state_ends __ARGS((void));
382static void update_si_attr __ARGS((int idx));
383static void check_keepend __ARGS((void));
384static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
385static short *copy_id_list __ARGS((short *list));
386static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
387static int push_current_state __ARGS((int idx));
388static void pop_current_state __ARGS((void));
389
390static void find_endpos __ARGS((int idx, lpos_T *startpos, lpos_T *m_endpos, lpos_T *hl_endpos, long *flagsp, lpos_T *end_endpos, int *end_idx, reg_extmatch_T *start_ext));
391static void clear_syn_state __ARGS((synstate_T *p));
392static void clear_current_state __ARGS((void));
393
394static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
395static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
396static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
397static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
398static char_u *syn_getcurline __ARGS((void));
399static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col));
400static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si));
401static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000402static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000403static void syntax_sync_clear __ARGS((void));
404static void syn_remove_pattern __ARGS((buf_T *buf, int idx));
405static void syn_clear_pattern __ARGS((buf_T *buf, int i));
406static void syn_clear_cluster __ARGS((buf_T *buf, int i));
407static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
408static void syn_clear_one __ARGS((int id, int syncing));
409static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
410static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
411static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
412static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
413static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
414static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
415static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
416static void syn_lines_msg __ARGS((void));
417static void syn_match_msg __ARGS((void));
418static void syn_list_one __ARGS((int id, int syncing, int link_only));
419static void syn_list_cluster __ARGS((int id));
420static void put_id_list __ARGS((char_u *name, short *list, int attr));
421static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000422static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
423static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
424static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000425static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000426static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000427static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000428static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
429static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
430static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
431static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
432#ifdef __BORLANDC__
433static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
434#else
435static int syn_compare_stub __ARGS((const void *v1, const void *v2));
436#endif
437static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
438static int syn_scl_name2id __ARGS((char_u *name));
439static int syn_scl_namen2id __ARGS((char_u *linep, int len));
440static int syn_check_cluster __ARGS((char_u *pp, int len));
441static int syn_add_cluster __ARGS((char_u *name));
442static void init_syn_patterns __ARGS((void));
443static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
444static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
445static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
446static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
447static void syn_incl_toplevel __ARGS((int id, int *flagsp));
448
449/*
450 * Start the syntax recognition for a line. This function is normally called
451 * from the screen updating, once for each displayed line.
452 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
453 * it. Careful: curbuf and curwin are likely to point to another buffer and
454 * window.
455 */
456 void
457syntax_start(wp, lnum)
458 win_T *wp;
459 linenr_T lnum;
460{
461 synstate_T *p;
462 synstate_T *last_valid = NULL;
463 synstate_T *last_min_valid = NULL;
464 synstate_T *sp, *prev;
465 linenr_T parsed_lnum;
466 linenr_T first_stored;
467 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000468 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000469
Bram Moolenaar071d4272004-06-13 20:20:40 +0000470 /*
471 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000472 * Also do this when a change was made, the current state may be invalid
473 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000474 */
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000475 if (syn_buf != wp->w_buffer || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000476 {
477 invalidate_current_state();
478 syn_buf = wp->w_buffer;
479 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000480 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000481 syn_win = wp;
482
483 /*
484 * Allocate syntax stack when needed.
485 */
486 syn_stack_alloc();
487 if (syn_buf->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000488 return; /* out of memory */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000489 syn_buf->b_sst_lasttick = display_tick;
490
491 /*
492 * If the state of the end of the previous line is useful, store it.
493 */
494 if (VALID_STATE(&current_state)
495 && current_lnum < lnum
496 && current_lnum < syn_buf->b_ml.ml_line_count)
497 {
498 (void)syn_finish_line(FALSE);
499 if (!current_state_stored)
500 {
501 ++current_lnum;
502 (void)store_current_state(NULL);
503 }
504
505 /*
506 * If the current_lnum is now the same as "lnum", keep the current
507 * state (this happens very often!). Otherwise invalidate
508 * current_state and figure it out below.
509 */
510 if (current_lnum != lnum)
511 invalidate_current_state();
512 }
513 else
514 invalidate_current_state();
515
516 /*
517 * Try to synchronize from a saved state in b_sst_array[].
518 * Only do this if lnum is not before and not to far beyond a saved state.
519 */
520 if (INVALID_STATE(&current_state) && syn_buf->b_sst_array != NULL)
521 {
522 /* Find last valid saved state before start_lnum. */
523 for (p = syn_buf->b_sst_first; p != NULL; p = p->sst_next)
524 {
525 if (p->sst_lnum > lnum)
526 break;
527 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
528 {
529 last_valid = p;
530 if (p->sst_lnum >= lnum - syn_buf->b_syn_sync_minlines)
531 last_min_valid = p;
532 }
533 }
534 if (last_min_valid != NULL)
535 load_current_state(last_min_valid);
536 }
537
538 /*
539 * If "lnum" is before or far beyond a line with a saved state, need to
540 * re-synchronize.
541 */
542 if (INVALID_STATE(&current_state))
543 {
544 syn_sync(wp, lnum, last_valid);
545 first_stored = current_lnum + syn_buf->b_syn_sync_minlines;
546 }
547 else
548 first_stored = current_lnum;
549
550 /*
551 * Advance from the sync point or saved state until the current line.
552 * Save some entries for syncing with later on.
553 */
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;
3209 else if (STRNICMP(arg, "default", 4) == 0 && next - arg == 4)
3210 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;
4463 if (source ? do_source(eap->arg, FALSE, FALSE) == FAIL
Bram Moolenaar910f66f2006-04-05 20:41:53 +00004464 : source_runtime(eap->arg, DOSO_NONE) == 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"),
6107 CENT("IncSearch term=reverse cterm=reverse",
6108 "IncSearch term=reverse cterm=reverse gui=reverse"),
6109 CENT("ModeMsg term=bold cterm=bold",
6110 "ModeMsg term=bold cterm=bold gui=bold"),
6111 CENT("NonText term=bold ctermfg=Blue",
6112 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6113 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6114 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6115 CENT("StatusLineNC term=reverse cterm=reverse",
6116 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006117#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006118 CENT("VertSplit term=reverse cterm=reverse",
6119 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006120#endif
6121#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006122 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6123 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006124#endif
6125#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006126 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6127 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006128#endif
6129#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006130 CENT("PmenuThumb cterm=reverse",
6131 "PmenuThumb cterm=reverse gui=reverse"),
6132 CENT("PmenuSbar ctermbg=Grey",
6133 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006134#endif
6135#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006136 CENT("TabLineSel term=bold cterm=bold",
6137 "TabLineSel term=bold cterm=bold gui=bold"),
6138 CENT("TabLineFill term=reverse cterm=reverse",
6139 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006140#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006141#ifdef FEAT_GUI
6142 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006143 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006144#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006145 NULL
6146 };
6147
6148static char *(highlight_init_light[]) =
6149 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006150 CENT("Directory term=bold ctermfg=DarkBlue",
6151 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6152 CENT("LineNr term=underline ctermfg=Brown",
6153 "LineNr term=underline ctermfg=Brown guifg=Brown"),
6154 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6155 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6156 CENT("Question term=standout ctermfg=DarkGreen",
6157 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6158 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6159 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006160#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006161 CENT("SpellBad term=reverse ctermbg=LightRed",
6162 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6163 CENT("SpellCap term=reverse ctermbg=LightBlue",
6164 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6165 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6166 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6167 CENT("SpellLocal term=underline ctermbg=Cyan",
6168 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006169#endif
6170#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006171 CENT("Pmenu ctermbg=LightMagenta",
6172 "Pmenu ctermbg=LightMagenta guibg=LightMagenta"),
6173 CENT("PmenuSel ctermbg=LightGrey",
6174 "PmenuSel ctermbg=LightGrey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006175#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006176 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6177 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6178 CENT("Title term=bold ctermfg=DarkMagenta",
6179 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6180 CENT("WarningMsg term=standout ctermfg=DarkRed",
6181 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006182#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006183 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6184 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006185#endif
6186#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006187 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6188 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6189 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6190 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006191#endif
6192#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006193 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6194 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006195#endif
6196#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006197 CENT("Visual term=reverse",
6198 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006199#endif
6200#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006201 CENT("DiffAdd term=bold ctermbg=LightBlue",
6202 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6203 CENT("DiffChange term=bold ctermbg=LightMagenta",
6204 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6205 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6206 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006207#endif
6208#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006209 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6210 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006211#endif
6212#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006213 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006214 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006215 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006216 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006217#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006218#ifdef FEAT_AUTOCMD
6219 CENT("MatchParen term=reverse ctermbg=Cyan",
6220 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6221#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006222#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006223 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006224#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006225 NULL
6226 };
6227
6228static char *(highlight_init_dark[]) =
6229 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006230 CENT("Directory term=bold ctermfg=LightCyan",
6231 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6232 CENT("LineNr term=underline ctermfg=Yellow",
6233 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
6234 CENT("MoreMsg term=bold ctermfg=LightGreen",
6235 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6236 CENT("Question term=standout ctermfg=LightGreen",
6237 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6238 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6239 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6240 CENT("SpecialKey term=bold ctermfg=LightBlue",
6241 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006242#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006243 CENT("SpellBad term=reverse ctermbg=Red",
6244 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6245 CENT("SpellCap term=reverse ctermbg=Blue",
6246 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6247 CENT("SpellRare term=reverse ctermbg=Magenta",
6248 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6249 CENT("SpellLocal term=underline ctermbg=Cyan",
6250 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006251#endif
6252#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006253 CENT("Pmenu ctermbg=Magenta",
6254 "Pmenu ctermbg=Magenta guibg=Magenta"),
6255 CENT("PmenuSel ctermbg=DarkGrey",
6256 "PmenuSel ctermbg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006257#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006258 CENT("Title term=bold ctermfg=LightMagenta",
6259 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6260 CENT("WarningMsg term=standout ctermfg=LightRed",
6261 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006262#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006263 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6264 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006265#endif
6266#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006267 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6268 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6269 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6270 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006271#endif
6272#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006273 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6274 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006275#endif
6276#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006277 CENT("Visual term=reverse",
6278 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006279#endif
6280#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006281 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6282 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6283 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6284 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6285 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6286 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006287#endif
6288#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006289 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6290 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006291#endif
6292#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006293 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006294 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006295 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006296 "CursorLine term=underline cterm=underline guibg=Grey40"),
6297#endif
6298#ifdef FEAT_AUTOCMD
6299 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6300 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006301#endif
6302#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006303 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006304#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006305 NULL
6306 };
6307
6308 void
6309init_highlight(both, reset)
6310 int both; /* include groups where 'bg' doesn't matter */
6311 int reset; /* clear group first */
6312{
6313 int i;
6314 char **pp;
6315 static int had_both = FALSE;
6316#ifdef FEAT_EVAL
6317 char_u *p;
6318
6319 /*
6320 * Try finding the color scheme file. Used when a color file was loaded
6321 * and 'background' or 't_Co' is changed.
6322 */
6323 p = get_var_value((char_u *)"g:colors_name");
6324 if (p != NULL && load_colors(p) == OK)
6325 return;
6326#endif
6327
6328 /*
6329 * Didn't use a color file, use the compiled-in colors.
6330 */
6331 if (both)
6332 {
6333 had_both = TRUE;
6334 pp = highlight_init_both;
6335 for (i = 0; pp[i] != NULL; ++i)
6336 do_highlight((char_u *)pp[i], reset, TRUE);
6337 }
6338 else if (!had_both)
6339 /* Don't do anything before the call with both == TRUE from main().
6340 * Not everything has been setup then, and that call will overrule
6341 * everything anyway. */
6342 return;
6343
6344 if (*p_bg == 'l')
6345 pp = highlight_init_light;
6346 else
6347 pp = highlight_init_dark;
6348 for (i = 0; pp[i] != NULL; ++i)
6349 do_highlight((char_u *)pp[i], reset, TRUE);
6350
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006351 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006352 * depend on the number of colors available.
6353 * With 8 colors brown is equal to yellow, need to use black for Search fg
6354 * to avoid Statement highlighted text disappears. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006355 if (t_colors > 8)
6356 do_highlight((char_u *)(*p_bg == 'l' ? "Visual ctermbg=LightGrey"
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00006357 : "Visual ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006358 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006359 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00006360 do_highlight((char_u *)"Visual cterm=reverse", FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006361 if (*p_bg == 'l')
6362 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
6363 }
Bram Moolenaarab194812005-09-14 21:40:12 +00006364
Bram Moolenaar071d4272004-06-13 20:20:40 +00006365#ifdef FEAT_SYN_HL
6366 /*
6367 * If syntax highlighting is enabled load the highlighting for it.
6368 */
6369 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006370 {
6371 static int recursive = 0;
6372
6373 if (recursive >= 5)
6374 EMSG(_("E679: recursive loop loading syncolor.vim"));
6375 else
6376 {
6377 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006378 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00006379 --recursive;
6380 }
6381 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006382#endif
6383}
6384
6385/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006386 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00006387 * Return OK for success, FAIL for failure.
6388 */
6389 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006390load_colors(name)
6391 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006392{
6393 char_u *buf;
6394 int retval = FAIL;
6395 static int recursive = FALSE;
6396
6397 /* When being called recursively, this is probably because setting
6398 * 'background' caused the highlighting to be reloaded. This means it is
6399 * working, thus we should return OK. */
6400 if (recursive)
6401 return OK;
6402
6403 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006404 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00006405 if (buf != NULL)
6406 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006407 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00006408 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006409 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00006410#ifdef FEAT_AUTOCMD
6411 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
6412#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006413 }
6414 recursive = FALSE;
6415
6416 return retval;
6417}
6418
6419/*
6420 * Handle the ":highlight .." command.
6421 * When using ":hi clear" this is called recursively for each group with
6422 * "forceit" and "init" both TRUE.
6423 */
6424 void
6425do_highlight(line, forceit, init)
6426 char_u *line;
6427 int forceit;
6428 int init; /* TRUE when called for initializing */
6429{
6430 char_u *name_end;
6431 char_u *p;
6432 char_u *linep;
6433 char_u *key_start;
6434 char_u *arg_start;
6435 char_u *key = NULL, *arg = NULL;
6436 long i;
6437 int off;
6438 int len;
6439 int attr;
6440 int id;
6441 int idx;
6442 int dodefault = FALSE;
6443 int doclear = FALSE;
6444 int dolink = FALSE;
6445 int error = FALSE;
6446 int color;
6447 int is_normal_group = FALSE; /* "Normal" group */
6448#ifdef FEAT_GUI_X11
6449 int is_menu_group = FALSE; /* "Menu" group */
6450 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
6451 int is_tooltip_group = FALSE; /* "Tooltip" group */
6452 int do_colors = FALSE; /* need to update colors? */
6453#else
6454# define is_menu_group 0
6455# define is_tooltip_group 0
6456#endif
6457
6458 /*
6459 * If no argument, list current highlighting.
6460 */
6461 if (ends_excmd(*line))
6462 {
6463 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
6464 /* TODO: only call when the group has attributes set */
6465 highlight_list_one((int)i);
6466 return;
6467 }
6468
6469 /*
6470 * Isolate the name.
6471 */
6472 name_end = skiptowhite(line);
6473 linep = skipwhite(name_end);
6474
6475 /*
6476 * Check for "default" argument.
6477 */
6478 if (STRNCMP(line, "default", name_end - line) == 0)
6479 {
6480 dodefault = TRUE;
6481 line = linep;
6482 name_end = skiptowhite(line);
6483 linep = skipwhite(name_end);
6484 }
6485
6486 /*
6487 * Check for "clear" or "link" argument.
6488 */
6489 if (STRNCMP(line, "clear", name_end - line) == 0)
6490 doclear = TRUE;
6491 if (STRNCMP(line, "link", name_end - line) == 0)
6492 dolink = TRUE;
6493
6494 /*
6495 * ":highlight {group-name}": list highlighting for one group.
6496 */
6497 if (!doclear && !dolink && ends_excmd(*linep))
6498 {
6499 id = syn_namen2id(line, (int)(name_end - line));
6500 if (id == 0)
6501 EMSG2(_("E411: highlight group not found: %s"), line);
6502 else
6503 highlight_list_one(id);
6504 return;
6505 }
6506
6507 /*
6508 * Handle ":highlight link {from} {to}" command.
6509 */
6510 if (dolink)
6511 {
6512 char_u *from_start = linep;
6513 char_u *from_end;
6514 char_u *to_start;
6515 char_u *to_end;
6516 int from_id;
6517 int to_id;
6518
6519 from_end = skiptowhite(from_start);
6520 to_start = skipwhite(from_end);
6521 to_end = skiptowhite(to_start);
6522
6523 if (ends_excmd(*from_start) || ends_excmd(*to_start))
6524 {
6525 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
6526 from_start);
6527 return;
6528 }
6529
6530 if (!ends_excmd(*skipwhite(to_end)))
6531 {
6532 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
6533 return;
6534 }
6535
6536 from_id = syn_check_group(from_start, (int)(from_end - from_start));
6537 if (STRNCMP(to_start, "NONE", 4) == 0)
6538 to_id = 0;
6539 else
6540 to_id = syn_check_group(to_start, (int)(to_end - to_start));
6541
6542 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
6543 {
6544 /*
6545 * Don't allow a link when there already is some highlighting
6546 * for the group, unless '!' is used
6547 */
6548 if (to_id > 0 && !forceit && !init
6549 && hl_has_settings(from_id - 1, dodefault))
6550 {
6551 if (sourcing_name == NULL && !dodefault)
6552 EMSG(_("E414: group has settings, highlight link ignored"));
6553 }
6554 else
6555 {
6556 if (!init)
6557 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
6558 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00006559#ifdef FEAT_EVAL
6560 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
6561#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00006562 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006563 }
6564 }
6565
6566 /* Only call highlight_changed() once, after sourcing a syntax file */
6567 need_highlight_changed = TRUE;
6568
6569 return;
6570 }
6571
6572 if (doclear)
6573 {
6574 /*
6575 * ":highlight clear [group]" command.
6576 */
6577 line = linep;
6578 if (ends_excmd(*line))
6579 {
6580#ifdef FEAT_GUI
6581 /* First, we do not destroy the old values, but allocate the new
6582 * ones and update the display. THEN we destroy the old values.
6583 * If we destroy the old values first, then the old values
6584 * (such as GuiFont's or GuiFontset's) will still be displayed but
6585 * invalid because they were free'd.
6586 */
6587 if (gui.in_use)
6588 {
6589# ifdef FEAT_BEVAL_TIP
6590 gui_init_tooltip_font();
6591# endif
6592# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
6593 gui_init_menu_font();
6594# endif
6595 }
6596# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
6597 gui_mch_def_colors();
6598# endif
6599# ifdef FEAT_GUI_X11
6600# ifdef FEAT_MENU
6601
6602 /* This only needs to be done when there is no Menu highlight
6603 * group defined by default, which IS currently the case.
6604 */
6605 gui_mch_new_menu_colors();
6606# endif
6607 if (gui.in_use)
6608 {
6609 gui_new_scrollbar_colors();
6610# ifdef FEAT_BEVAL
6611 gui_mch_new_tooltip_colors();
6612# endif
6613# ifdef FEAT_MENU
6614 gui_mch_new_menu_font();
6615# endif
6616 }
6617# endif
6618
6619 /* Ok, we're done allocating the new default graphics items.
6620 * The screen should already be refreshed at this point.
6621 * It is now Ok to clear out the old data.
6622 */
6623#endif
6624#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00006625 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006626#endif
6627 restore_cterm_colors();
6628
6629 /*
6630 * Clear all default highlight groups and load the defaults.
6631 */
6632 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
6633 highlight_clear(idx);
6634 init_highlight(TRUE, TRUE);
6635#ifdef FEAT_GUI
6636 if (gui.in_use)
6637 highlight_gui_started();
6638#endif
6639 highlight_changed();
6640 redraw_later_clear();
6641 return;
6642 }
6643 name_end = skiptowhite(line);
6644 linep = skipwhite(name_end);
6645 }
6646
6647 /*
6648 * Find the group name in the table. If it does not exist yet, add it.
6649 */
6650 id = syn_check_group(line, (int)(name_end - line));
6651 if (id == 0) /* failed (out of memory) */
6652 return;
6653 idx = id - 1; /* index is ID minus one */
6654
6655 /* Return if "default" was used and the group already has settings. */
6656 if (dodefault && hl_has_settings(idx, TRUE))
6657 return;
6658
6659 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
6660 is_normal_group = TRUE;
6661#ifdef FEAT_GUI_X11
6662 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
6663 is_menu_group = TRUE;
6664 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
6665 is_scrollbar_group = TRUE;
6666 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
6667 is_tooltip_group = TRUE;
6668#endif
6669
6670 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
6671 if (doclear || (forceit && init))
6672 {
6673 highlight_clear(idx);
6674 if (!doclear)
6675 HL_TABLE()[idx].sg_set = 0;
6676 }
6677
6678 if (!doclear)
6679 while (!ends_excmd(*linep))
6680 {
6681 key_start = linep;
6682 if (*linep == '=')
6683 {
6684 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
6685 error = TRUE;
6686 break;
6687 }
6688
6689 /*
6690 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
6691 * "guibg").
6692 */
6693 while (*linep && !vim_iswhite(*linep) && *linep != '=')
6694 ++linep;
6695 vim_free(key);
6696 key = vim_strnsave_up(key_start, (int)(linep - key_start));
6697 if (key == NULL)
6698 {
6699 error = TRUE;
6700 break;
6701 }
6702 linep = skipwhite(linep);
6703
6704 if (STRCMP(key, "NONE") == 0)
6705 {
6706 if (!init || HL_TABLE()[idx].sg_set == 0)
6707 {
6708 if (!init)
6709 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
6710 highlight_clear(idx);
6711 }
6712 continue;
6713 }
6714
6715 /*
6716 * Check for the equal sign.
6717 */
6718 if (*linep != '=')
6719 {
6720 EMSG2(_("E416: missing equal sign: %s"), key_start);
6721 error = TRUE;
6722 break;
6723 }
6724 ++linep;
6725
6726 /*
6727 * Isolate the argument.
6728 */
6729 linep = skipwhite(linep);
6730 if (*linep == '\'') /* guifg='color name' */
6731 {
6732 arg_start = ++linep;
6733 linep = vim_strchr(linep, '\'');
6734 if (linep == NULL)
6735 {
6736 EMSG2(_(e_invarg2), key_start);
6737 error = TRUE;
6738 break;
6739 }
6740 }
6741 else
6742 {
6743 arg_start = linep;
6744 linep = skiptowhite(linep);
6745 }
6746 if (linep == arg_start)
6747 {
6748 EMSG2(_("E417: missing argument: %s"), key_start);
6749 error = TRUE;
6750 break;
6751 }
6752 vim_free(arg);
6753 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
6754 if (arg == NULL)
6755 {
6756 error = TRUE;
6757 break;
6758 }
6759 if (*linep == '\'')
6760 ++linep;
6761
6762 /*
6763 * Store the argument.
6764 */
6765 if ( STRCMP(key, "TERM") == 0
6766 || STRCMP(key, "CTERM") == 0
6767 || STRCMP(key, "GUI") == 0)
6768 {
6769 attr = 0;
6770 off = 0;
6771 while (arg[off] != NUL)
6772 {
6773 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
6774 {
6775 len = (int)STRLEN(hl_name_table[i]);
6776 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
6777 {
6778 attr |= hl_attr_table[i];
6779 off += len;
6780 break;
6781 }
6782 }
6783 if (i < 0)
6784 {
6785 EMSG2(_("E418: Illegal value: %s"), arg);
6786 error = TRUE;
6787 break;
6788 }
6789 if (arg[off] == ',') /* another one follows */
6790 ++off;
6791 }
6792 if (error)
6793 break;
6794 if (*key == 'T')
6795 {
6796 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
6797 {
6798 if (!init)
6799 HL_TABLE()[idx].sg_set |= SG_TERM;
6800 HL_TABLE()[idx].sg_term = attr;
6801 }
6802 }
6803 else if (*key == 'C')
6804 {
6805 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6806 {
6807 if (!init)
6808 HL_TABLE()[idx].sg_set |= SG_CTERM;
6809 HL_TABLE()[idx].sg_cterm = attr;
6810 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6811 }
6812 }
6813#ifdef FEAT_GUI
6814 else
6815 {
6816 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
6817 {
6818 if (!init)
6819 HL_TABLE()[idx].sg_set |= SG_GUI;
6820 HL_TABLE()[idx].sg_gui = attr;
6821 }
6822 }
6823#endif
6824 }
6825 else if (STRCMP(key, "FONT") == 0)
6826 {
6827 /* in non-GUI fonts are simply ignored */
6828#ifdef FEAT_GUI
6829 if (!gui.shell_created)
6830 {
6831 /* GUI not started yet, always accept the name. */
6832 vim_free(HL_TABLE()[idx].sg_font_name);
6833 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6834 }
6835 else
6836 {
6837 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
6838# ifdef FEAT_XFONTSET
6839 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
6840# endif
6841 /* First, save the current font/fontset.
6842 * Then try to allocate the font/fontset.
6843 * If the allocation fails, HL_TABLE()[idx].sg_font OR
6844 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
6845 */
6846
6847 HL_TABLE()[idx].sg_font = NOFONT;
6848# ifdef FEAT_XFONTSET
6849 HL_TABLE()[idx].sg_fontset = NOFONTSET;
6850# endif
6851 hl_do_font(idx, arg, is_normal_group, is_menu_group,
6852 is_tooltip_group);
6853
6854# ifdef FEAT_XFONTSET
6855 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
6856 {
6857 /* New fontset was accepted. Free the old one, if there was
6858 * one.
6859 */
6860 gui_mch_free_fontset(temp_sg_fontset);
6861 vim_free(HL_TABLE()[idx].sg_font_name);
6862 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6863 }
6864 else
6865 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
6866# endif
6867 if (HL_TABLE()[idx].sg_font != NOFONT)
6868 {
6869 /* New font was accepted. Free the old one, if there was
6870 * one.
6871 */
6872 gui_mch_free_font(temp_sg_font);
6873 vim_free(HL_TABLE()[idx].sg_font_name);
6874 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
6875 }
6876 else
6877 HL_TABLE()[idx].sg_font = temp_sg_font;
6878 }
6879#endif
6880 }
6881 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
6882 {
6883 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
6884 {
6885 if (!init)
6886 HL_TABLE()[idx].sg_set |= SG_CTERM;
6887
6888 /* When setting the foreground color, and previously the "bold"
6889 * flag was set for a light color, reset it now */
6890 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
6891 {
6892 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
6893 HL_TABLE()[idx].sg_cterm_bold = FALSE;
6894 }
6895
6896 if (VIM_ISDIGIT(*arg))
6897 color = atoi((char *)arg);
6898 else if (STRICMP(arg, "fg") == 0)
6899 {
6900 if (cterm_normal_fg_color)
6901 color = cterm_normal_fg_color - 1;
6902 else
6903 {
6904 EMSG(_("E419: FG color unknown"));
6905 error = TRUE;
6906 break;
6907 }
6908 }
6909 else if (STRICMP(arg, "bg") == 0)
6910 {
6911 if (cterm_normal_bg_color > 0)
6912 color = cterm_normal_bg_color - 1;
6913 else
6914 {
6915 EMSG(_("E420: BG color unknown"));
6916 error = TRUE;
6917 break;
6918 }
6919 }
6920 else
6921 {
6922 static char *(color_names[28]) = {
6923 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
6924 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
6925 "Gray", "Grey",
6926 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
6927 "Blue", "LightBlue", "Green", "LightGreen",
6928 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
6929 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
6930 static int color_numbers_16[28] = {0, 1, 2, 3,
6931 4, 5, 6, 6,
6932 7, 7,
6933 7, 7, 8, 8,
6934 9, 9, 10, 10,
6935 11, 11, 12, 12, 13,
6936 13, 14, 14, 15, -1};
6937 /* for xterm with 88 colors... */
6938 static int color_numbers_88[28] = {0, 4, 2, 6,
6939 1, 5, 32, 72,
6940 84, 84,
6941 7, 7, 82, 82,
6942 12, 43, 10, 61,
6943 14, 63, 9, 74, 13,
6944 75, 11, 78, 15, -1};
6945 /* for xterm with 256 colors... */
6946 static int color_numbers_256[28] = {0, 4, 2, 6,
6947 1, 5, 130, 130,
6948 248, 248,
6949 7, 7, 242, 242,
6950 12, 81, 10, 121,
6951 14, 159, 9, 224, 13,
6952 225, 11, 229, 15, -1};
6953 /* for terminals with less than 16 colors... */
6954 static int color_numbers_8[28] = {0, 4, 2, 6,
6955 1, 5, 3, 3,
6956 7, 7,
6957 7, 7, 0+8, 0+8,
6958 4+8, 4+8, 2+8, 2+8,
6959 6+8, 6+8, 1+8, 1+8, 5+8,
6960 5+8, 3+8, 3+8, 7+8, -1};
6961#if defined(__QNXNTO__)
6962 static int *color_numbers_8_qansi = color_numbers_8;
6963 /* On qnx, the 8 & 16 color arrays are the same */
6964 if (STRNCMP(T_NAME, "qansi", 5) == 0)
6965 color_numbers_8_qansi = color_numbers_16;
6966#endif
6967
6968 /* reduce calls to STRICMP a bit, it can be slow */
6969 off = TOUPPER_ASC(*arg);
6970 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
6971 if (off == color_names[i][0]
6972 && STRICMP(arg + 1, color_names[i] + 1) == 0)
6973 break;
6974 if (i < 0)
6975 {
6976 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
6977 error = TRUE;
6978 break;
6979 }
6980
6981 /* Use the _16 table to check if its a valid color name. */
6982 color = color_numbers_16[i];
6983 if (color >= 0)
6984 {
6985 if (t_colors == 8)
6986 {
6987 /* t_Co is 8: use the 8 colors table */
6988#if defined(__QNXNTO__)
6989 color = color_numbers_8_qansi[i];
6990#else
6991 color = color_numbers_8[i];
6992#endif
6993 if (key[5] == 'F')
6994 {
6995 /* set/reset bold attribute to get light foreground
6996 * colors (on some terminals, e.g. "linux") */
6997 if (color & 8)
6998 {
6999 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7000 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7001 }
7002 else
7003 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7004 }
7005 color &= 7; /* truncate to 8 colors */
7006 }
7007 else if (t_colors == 16 || t_colors == 88
7008 || t_colors == 256)
7009 {
7010 /*
7011 * Guess: if the termcap entry ends in 'm', it is
7012 * probably an xterm-like terminal. Use the changed
7013 * order for colors.
7014 */
7015 if (*T_CAF != NUL)
7016 p = T_CAF;
7017 else
7018 p = T_CSF;
7019 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7020 switch (t_colors)
7021 {
7022 case 16:
7023 color = color_numbers_8[i];
7024 break;
7025 case 88:
7026 color = color_numbers_88[i];
7027 break;
7028 case 256:
7029 color = color_numbers_256[i];
7030 break;
7031 }
7032 }
7033 }
7034 }
7035 /* Add one to the argument, to avoid zero */
7036 if (key[5] == 'F')
7037 {
7038 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7039 if (is_normal_group)
7040 {
7041 cterm_normal_fg_color = color + 1;
7042 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7043#ifdef FEAT_GUI
7044 /* Don't do this if the GUI is used. */
7045 if (!gui.in_use && !gui.starting)
7046#endif
7047 {
7048 must_redraw = CLEAR;
7049 if (termcap_active)
7050 term_fg_color(color);
7051 }
7052 }
7053 }
7054 else
7055 {
7056 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7057 if (is_normal_group)
7058 {
7059 cterm_normal_bg_color = color + 1;
7060#ifdef FEAT_GUI
7061 /* Don't mess with 'background' if the GUI is used. */
7062 if (!gui.in_use && !gui.starting)
7063#endif
7064 {
7065 must_redraw = CLEAR;
7066 if (termcap_active)
7067 term_bg_color(color);
7068 if (t_colors < 16)
7069 i = (color == 0 || color == 4);
7070 else
7071 i = (color < 7 || color == 8);
7072 /* Set the 'background' option if the value is wrong. */
7073 if (i != (*p_bg == 'd'))
7074 set_option_value((char_u *)"bg", 0L,
7075 i ? (char_u *)"dark" : (char_u *)"light", 0);
7076 }
7077 }
7078 }
7079 }
7080 }
7081 else if (STRCMP(key, "GUIFG") == 0)
7082 {
7083#ifdef FEAT_GUI /* in non-GUI guifg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007084 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007085 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007086 if (!init)
7087 HL_TABLE()[idx].sg_set |= SG_GUI;
7088
7089 i = color_name2handle(arg);
7090 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7091 {
7092 HL_TABLE()[idx].sg_gui_fg = i;
7093 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7094 if (STRCMP(arg, "NONE"))
7095 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7096 else
7097 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007098# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007099 if (is_menu_group)
7100 gui.menu_fg_pixel = i;
7101 if (is_scrollbar_group)
7102 gui.scroll_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007103# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007104 if (is_tooltip_group)
7105 gui.tooltip_fg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007106# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007107 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007108# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007109 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007111#endif
7112 }
7113 else if (STRCMP(key, "GUIBG") == 0)
7114 {
7115#ifdef FEAT_GUI /* in non-GUI guibg colors are simply ignored */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007116 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007117 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007118 if (!init)
7119 HL_TABLE()[idx].sg_set |= SG_GUI;
7120
7121 i = color_name2handle(arg);
7122 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7123 {
7124 HL_TABLE()[idx].sg_gui_bg = i;
7125 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7126 if (STRCMP(arg, "NONE") != 0)
7127 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7128 else
7129 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007130# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007131 if (is_menu_group)
7132 gui.menu_bg_pixel = i;
7133 if (is_scrollbar_group)
7134 gui.scroll_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007135# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007136 if (is_tooltip_group)
7137 gui.tooltip_bg_pixel = i;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007138# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007139 do_colors = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007140# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007141 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007142 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007143#endif
7144 }
7145 else if (STRCMP(key, "GUISP") == 0)
7146 {
7147#ifdef FEAT_GUI /* in non-GUI guisp colors are simply ignored */
7148 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7149 {
7150 if (!init)
7151 HL_TABLE()[idx].sg_set |= SG_GUI;
7152
7153 i = color_name2handle(arg);
7154 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7155 {
7156 HL_TABLE()[idx].sg_gui_sp = i;
7157 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7158 if (STRCMP(arg, "NONE") != 0)
7159 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7160 else
7161 HL_TABLE()[idx].sg_gui_sp_name = NULL;
7162 }
7163 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007164#endif
7165 }
7166 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7167 {
7168 char_u buf[100];
7169 char_u *tname;
7170
7171 if (!init)
7172 HL_TABLE()[idx].sg_set |= SG_TERM;
7173
7174 /*
7175 * The "start" and "stop" arguments can be a literal escape
7176 * sequence, or a comma seperated list of terminal codes.
7177 */
7178 if (STRNCMP(arg, "t_", 2) == 0)
7179 {
7180 off = 0;
7181 buf[0] = 0;
7182 while (arg[off] != NUL)
7183 {
7184 /* Isolate one termcap name */
7185 for (len = 0; arg[off + len] &&
7186 arg[off + len] != ','; ++len)
7187 ;
7188 tname = vim_strnsave(arg + off, len);
7189 if (tname == NULL) /* out of memory */
7190 {
7191 error = TRUE;
7192 break;
7193 }
7194 /* lookup the escape sequence for the item */
7195 p = get_term_code(tname);
7196 vim_free(tname);
7197 if (p == NULL) /* ignore non-existing things */
7198 p = (char_u *)"";
7199
7200 /* Append it to the already found stuff */
7201 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7202 {
7203 EMSG2(_("E422: terminal code too long: %s"), arg);
7204 error = TRUE;
7205 break;
7206 }
7207 STRCAT(buf, p);
7208
7209 /* Advance to the next item */
7210 off += len;
7211 if (arg[off] == ',') /* another one follows */
7212 ++off;
7213 }
7214 }
7215 else
7216 {
7217 /*
7218 * Copy characters from arg[] to buf[], translating <> codes.
7219 */
7220 for (p = arg, off = 0; off < 100 && *p; )
7221 {
7222 len = trans_special(&p, buf + off, FALSE);
7223 if (len) /* recognized special char */
7224 off += len;
7225 else /* copy as normal char */
7226 buf[off++] = *p++;
7227 }
7228 buf[off] = NUL;
7229 }
7230 if (error)
7231 break;
7232
7233 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7234 p = NULL;
7235 else
7236 p = vim_strsave(buf);
7237 if (key[2] == 'A')
7238 {
7239 vim_free(HL_TABLE()[idx].sg_start);
7240 HL_TABLE()[idx].sg_start = p;
7241 }
7242 else
7243 {
7244 vim_free(HL_TABLE()[idx].sg_stop);
7245 HL_TABLE()[idx].sg_stop = p;
7246 }
7247 }
7248 else
7249 {
7250 EMSG2(_("E423: Illegal argument: %s"), key_start);
7251 error = TRUE;
7252 break;
7253 }
7254
7255 /*
7256 * When highlighting has been given for a group, don't link it.
7257 */
7258 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7259 HL_TABLE()[idx].sg_link = 0;
7260
7261 /*
7262 * Continue with next argument.
7263 */
7264 linep = skipwhite(linep);
7265 }
7266
7267 /*
7268 * If there is an error, and it's a new entry, remove it from the table.
7269 */
7270 if (error && idx == highlight_ga.ga_len)
7271 syn_unadd_group();
7272 else
7273 {
7274 if (is_normal_group)
7275 {
7276 HL_TABLE()[idx].sg_term_attr = 0;
7277 HL_TABLE()[idx].sg_cterm_attr = 0;
7278#ifdef FEAT_GUI
7279 HL_TABLE()[idx].sg_gui_attr = 0;
7280 /*
7281 * Need to update all groups, because they might be using "bg"
7282 * and/or "fg", which have been changed now.
7283 */
7284 if (gui.in_use)
7285 highlight_gui_started();
7286#endif
7287 }
7288#ifdef FEAT_GUI_X11
7289# ifdef FEAT_MENU
7290 else if (is_menu_group)
7291 {
7292 if (gui.in_use && do_colors)
7293 gui_mch_new_menu_colors();
7294 }
7295# endif
7296 else if (is_scrollbar_group)
7297 {
7298 if (gui.in_use && do_colors)
7299 gui_new_scrollbar_colors();
7300 }
7301# ifdef FEAT_BEVAL
7302 else if (is_tooltip_group)
7303 {
7304 if (gui.in_use && do_colors)
7305 gui_mch_new_tooltip_colors();
7306 }
7307# endif
7308#endif
7309 else
7310 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007311#ifdef FEAT_EVAL
7312 HL_TABLE()[idx].sg_scriptID = current_SID;
7313#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007314 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007315 }
7316 vim_free(key);
7317 vim_free(arg);
7318
7319 /* Only call highlight_changed() once, after sourcing a syntax file */
7320 need_highlight_changed = TRUE;
7321}
7322
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007323#if defined(EXITFREE) || defined(PROTO)
7324 void
7325free_highlight()
7326{
7327 int i;
7328
7329 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007330 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007331 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007332 vim_free(HL_TABLE()[i].sg_name);
7333 vim_free(HL_TABLE()[i].sg_name_u);
7334 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007335 ga_clear(&highlight_ga);
7336}
7337#endif
7338
Bram Moolenaar071d4272004-06-13 20:20:40 +00007339/*
7340 * Reset the cterm colors to what they were before Vim was started, if
7341 * possible. Otherwise reset them to zero.
7342 */
7343 void
7344restore_cterm_colors()
7345{
7346#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
7347 /* Since t_me has been set, this probably means that the user
7348 * wants to use this as default colors. Need to reset default
7349 * background/foreground colors. */
7350 mch_set_normal_colors();
7351#else
7352 cterm_normal_fg_color = 0;
7353 cterm_normal_fg_bold = 0;
7354 cterm_normal_bg_color = 0;
7355#endif
7356}
7357
7358/*
7359 * Return TRUE if highlight group "idx" has any settings.
7360 * When "check_link" is TRUE also check for an existing link.
7361 */
7362 static int
7363hl_has_settings(idx, check_link)
7364 int idx;
7365 int check_link;
7366{
7367 return ( HL_TABLE()[idx].sg_term_attr != 0
7368 || HL_TABLE()[idx].sg_cterm_attr != 0
7369#ifdef FEAT_GUI
7370 || HL_TABLE()[idx].sg_gui_attr != 0
7371#endif
7372 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
7373}
7374
7375/*
7376 * Clear highlighting for one group.
7377 */
7378 static void
7379highlight_clear(idx)
7380 int idx;
7381{
7382 HL_TABLE()[idx].sg_term = 0;
7383 vim_free(HL_TABLE()[idx].sg_start);
7384 HL_TABLE()[idx].sg_start = NULL;
7385 vim_free(HL_TABLE()[idx].sg_stop);
7386 HL_TABLE()[idx].sg_stop = NULL;
7387 HL_TABLE()[idx].sg_term_attr = 0;
7388 HL_TABLE()[idx].sg_cterm = 0;
7389 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7390 HL_TABLE()[idx].sg_cterm_fg = 0;
7391 HL_TABLE()[idx].sg_cterm_bg = 0;
7392 HL_TABLE()[idx].sg_cterm_attr = 0;
7393#ifdef FEAT_GUI /* in non-GUI fonts are simply ignored */
7394 HL_TABLE()[idx].sg_gui = 0;
7395 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
7396 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7397 HL_TABLE()[idx].sg_gui_fg_name = NULL;
7398 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
7399 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7400 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007401 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
7402 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7403 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007404 gui_mch_free_font(HL_TABLE()[idx].sg_font);
7405 HL_TABLE()[idx].sg_font = NOFONT;
7406# ifdef FEAT_XFONTSET
7407 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
7408 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7409# endif
7410 vim_free(HL_TABLE()[idx].sg_font_name);
7411 HL_TABLE()[idx].sg_font_name = NULL;
7412 HL_TABLE()[idx].sg_gui_attr = 0;
7413#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00007414#ifdef FEAT_EVAL
7415 /* Clear the script ID only when there is no link, since that is not
7416 * cleared. */
7417 if (HL_TABLE()[idx].sg_link == 0)
7418 HL_TABLE()[idx].sg_scriptID = 0;
7419#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007420}
7421
7422#if defined(FEAT_GUI) || defined(PROTO)
7423/*
7424 * Set the normal foreground and background colors according to the "Normal"
7425 * highlighighting group. For X11 also set "Menu", "Scrollbar", and
7426 * "Tooltip" colors.
7427 */
7428 void
7429set_normal_colors()
7430{
7431 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007432 &gui.norm_pixel, &gui.back_pixel,
7433 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007434 {
7435 gui_mch_new_colors();
7436 must_redraw = CLEAR;
7437 }
7438#ifdef FEAT_GUI_X11
7439 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007440 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
7441 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007442 {
7443# ifdef FEAT_MENU
7444 gui_mch_new_menu_colors();
7445# endif
7446 must_redraw = CLEAR;
7447 }
7448# ifdef FEAT_BEVAL
7449 if (set_group_colors((char_u *)"Tooltip",
7450 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
7451 FALSE, FALSE, TRUE))
7452 {
7453# ifdef FEAT_TOOLBAR
7454 gui_mch_new_tooltip_colors();
7455# endif
7456 must_redraw = CLEAR;
7457 }
7458#endif
7459 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007460 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
7461 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007462 {
7463 gui_new_scrollbar_colors();
7464 must_redraw = CLEAR;
7465 }
7466#endif
7467}
7468
7469/*
7470 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
7471 */
7472 static int
7473set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
7474 char_u *name;
7475 guicolor_T *fgp;
7476 guicolor_T *bgp;
7477 int do_menu;
7478 int use_norm;
7479 int do_tooltip;
7480{
7481 int idx;
7482
7483 idx = syn_name2id(name) - 1;
7484 if (idx >= 0)
7485 {
7486 gui_do_one_color(idx, do_menu, do_tooltip);
7487
7488 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
7489 *fgp = HL_TABLE()[idx].sg_gui_fg;
7490 else if (use_norm)
7491 *fgp = gui.def_norm_pixel;
7492 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
7493 *bgp = HL_TABLE()[idx].sg_gui_bg;
7494 else if (use_norm)
7495 *bgp = gui.def_back_pixel;
7496 return TRUE;
7497 }
7498 return FALSE;
7499}
7500
7501/*
7502 * Get the font of the "Normal" group.
7503 * Returns "" when it's not found or not set.
7504 */
7505 char_u *
7506hl_get_font_name()
7507{
7508 int id;
7509 char_u *s;
7510
7511 id = syn_name2id((char_u *)"Normal");
7512 if (id > 0)
7513 {
7514 s = HL_TABLE()[id - 1].sg_font_name;
7515 if (s != NULL)
7516 return s;
7517 }
7518 return (char_u *)"";
7519}
7520
7521/*
7522 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
7523 * actually chosen to be used.
7524 */
7525 void
7526hl_set_font_name(font_name)
7527 char_u *font_name;
7528{
7529 int id;
7530
7531 id = syn_name2id((char_u *)"Normal");
7532 if (id > 0)
7533 {
7534 vim_free(HL_TABLE()[id - 1].sg_font_name);
7535 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
7536 }
7537}
7538
7539/*
7540 * Set background color for "Normal" group. Called by gui_set_bg_color()
7541 * when the color is known.
7542 */
7543 void
7544hl_set_bg_color_name(name)
7545 char_u *name; /* must have been allocated */
7546{
7547 int id;
7548
7549 if (name != NULL)
7550 {
7551 id = syn_name2id((char_u *)"Normal");
7552 if (id > 0)
7553 {
7554 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
7555 HL_TABLE()[id - 1].sg_gui_bg_name = name;
7556 }
7557 }
7558}
7559
7560/*
7561 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
7562 * when the color is known.
7563 */
7564 void
7565hl_set_fg_color_name(name)
7566 char_u *name; /* must have been allocated */
7567{
7568 int id;
7569
7570 if (name != NULL)
7571 {
7572 id = syn_name2id((char_u *)"Normal");
7573 if (id > 0)
7574 {
7575 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
7576 HL_TABLE()[id - 1].sg_gui_fg_name = name;
7577 }
7578 }
7579}
7580
7581/*
7582 * Return the handle for a color name.
7583 * Returns INVALCOLOR when failed.
7584 */
7585 static guicolor_T
7586color_name2handle(name)
7587 char_u *name;
7588{
7589 if (STRCMP(name, "NONE") == 0)
7590 return INVALCOLOR;
7591
7592 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
7593 return gui.norm_pixel;
7594 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
7595 return gui.back_pixel;
7596
7597 return gui_get_color(name);
7598}
7599
7600/*
7601 * Return the handle for a font name.
7602 * Returns NOFONT when failed.
7603 */
7604 static GuiFont
7605font_name2handle(name)
7606 char_u *name;
7607{
7608 if (STRCMP(name, "NONE") == 0)
7609 return NOFONT;
7610
7611 return gui_mch_get_font(name, TRUE);
7612}
7613
7614# ifdef FEAT_XFONTSET
7615/*
7616 * Return the handle for a fontset name.
7617 * Returns NOFONTSET when failed.
7618 */
7619 static GuiFontset
7620fontset_name2handle(name, fixed_width)
7621 char_u *name;
7622 int fixed_width;
7623{
7624 if (STRCMP(name, "NONE") == 0)
7625 return NOFONTSET;
7626
7627 return gui_mch_get_fontset(name, TRUE, fixed_width);
7628}
7629# endif
7630
7631/*
7632 * Get the font or fontset for one highlight group.
7633 */
7634/*ARGSUSED*/
7635 static void
7636hl_do_font(idx, arg, do_normal, do_menu, do_tooltip)
7637 int idx;
7638 char_u *arg;
7639 int do_normal; /* set normal font */
7640 int do_menu; /* set menu font */
7641 int do_tooltip; /* set tooltip font */
7642{
7643# ifdef FEAT_XFONTSET
7644 /* If 'guifontset' is not empty, first try using the name as a
7645 * fontset. If that doesn't work, use it as a font name. */
7646 if (*p_guifontset != NUL
7647# ifdef FONTSET_ALWAYS
7648 || do_menu
7649# endif
7650# ifdef FEAT_BEVAL_TIP
7651 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
7652 || do_tooltip
7653# endif
7654 )
7655 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
7656# ifdef FONTSET_ALWAYS
7657 || do_menu
7658# endif
7659# ifdef FEAT_BEVAL_TIP
7660 || do_tooltip
7661# endif
7662 );
7663 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7664 {
7665 /* If it worked and it's the Normal group, use it as the
7666 * normal fontset. Same for the Menu group. */
7667 if (do_normal)
7668 gui_init_font(arg, TRUE);
7669# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7670 if (do_menu)
7671 {
7672# ifdef FONTSET_ALWAYS
7673 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
7674# else
7675 /* YIKES! This is a bug waiting to crash the program */
7676 gui.menu_font = HL_TABLE()[idx].sg_fontset;
7677# endif
7678 gui_mch_new_menu_font();
7679 }
7680# ifdef FEAT_BEVAL
7681 if (do_tooltip)
7682 {
7683 /* The Athena widget set cannot currently handle switching between
7684 * displaying a single font and a fontset.
7685 * If the XtNinternational resource is set to True at widget
7686 * creation, then a fontset is always used, othwise an
7687 * XFontStruct is used.
7688 */
7689 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
7690 gui_mch_new_tooltip_font();
7691 }
7692# endif
7693# endif
7694 }
7695 else
7696# endif
7697 {
7698 HL_TABLE()[idx].sg_font = font_name2handle(arg);
7699 /* If it worked and it's the Normal group, use it as the
7700 * normal font. Same for the Menu group. */
7701 if (HL_TABLE()[idx].sg_font != NOFONT)
7702 {
7703 if (do_normal)
7704 gui_init_font(arg, FALSE);
7705#ifndef FONTSET_ALWAYS
7706# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
7707 if (do_menu)
7708 {
7709 gui.menu_font = HL_TABLE()[idx].sg_font;
7710 gui_mch_new_menu_font();
7711 }
7712# endif
7713#endif
7714 }
7715 }
7716}
7717
7718#endif /* FEAT_GUI */
7719
7720/*
7721 * Table with the specifications for an attribute number.
7722 * Note that this table is used by ALL buffers. This is required because the
7723 * GUI can redraw at any time for any buffer.
7724 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007725static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007726
7727#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
7728
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007729static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007730
7731#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
7732
7733#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00007734static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00007735
7736#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
7737#endif
7738
7739/*
7740 * Return the attr number for a set of colors and font.
7741 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
7742 * if the combination is new.
7743 * Return 0 for error (no more room).
7744 */
7745 static int
7746get_attr_entry(table, aep)
7747 garray_T *table;
7748 attrentry_T *aep;
7749{
7750 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007751 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007752 static int recursive = FALSE;
7753
7754 /*
7755 * Init the table, in case it wasn't done yet.
7756 */
7757 table->ga_itemsize = sizeof(attrentry_T);
7758 table->ga_growsize = 7;
7759
7760 /*
7761 * Try to find an entry with the same specifications.
7762 */
7763 for (i = 0; i < table->ga_len; ++i)
7764 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007765 taep = &(((attrentry_T *)table->ga_data)[i]);
7766 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00007767 && (
7768#ifdef FEAT_GUI
7769 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007770 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
7771 && aep->ae_u.gui.bg_color
7772 == taep->ae_u.gui.bg_color
7773 && aep->ae_u.gui.sp_color
7774 == taep->ae_u.gui.sp_color
7775 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00007776# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007777 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00007778# endif
7779 ))
7780 ||
7781#endif
7782 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007783 && (aep->ae_u.term.start == NULL)
7784 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007785 && (aep->ae_u.term.start == NULL
7786 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007787 taep->ae_u.term.start) == 0)
7788 && (aep->ae_u.term.stop == NULL)
7789 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007790 && (aep->ae_u.term.stop == NULL
7791 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007792 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007793 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007794 && aep->ae_u.cterm.fg_color
7795 == taep->ae_u.cterm.fg_color
7796 && aep->ae_u.cterm.bg_color
7797 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007798 ))
7799
7800 return i + ATTR_OFF;
7801 }
7802
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00007803 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007804 {
7805 /*
7806 * Running out of attribute entries! remove all attributes, and
7807 * compute new ones for all groups.
7808 * When called recursively, we are really out of numbers.
7809 */
7810 if (recursive)
7811 {
7812 EMSG(_("E424: Too many different highlighting attributes in use"));
7813 return 0;
7814 }
7815 recursive = TRUE;
7816
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007817 clear_hl_tables();
7818
Bram Moolenaar071d4272004-06-13 20:20:40 +00007819 must_redraw = CLEAR;
7820
7821 for (i = 0; i < highlight_ga.ga_len; ++i)
7822 set_hl_attr(i);
7823
7824 recursive = FALSE;
7825 }
7826
7827 /*
7828 * This is a new combination of colors and font, add an entry.
7829 */
7830 if (ga_grow(table, 1) == FAIL)
7831 return 0;
7832
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007833 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
7834 vim_memset(taep, 0, sizeof(attrentry_T));
7835 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007836#ifdef FEAT_GUI
7837 if (table == &gui_attr_table)
7838 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007839 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
7840 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
7841 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
7842 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007843# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007844 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007845# endif
7846 }
7847#endif
7848 if (table == &term_attr_table)
7849 {
7850 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007851 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007852 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007853 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007855 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007856 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007857 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007858 }
7859 else if (table == &cterm_attr_table)
7860 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007861 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
7862 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007863 }
7864 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007865 return (table->ga_len - 1 + ATTR_OFF);
7866}
7867
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007868/*
7869 * Clear all highlight tables.
7870 */
7871 void
7872clear_hl_tables()
7873{
7874 int i;
7875 attrentry_T *taep;
7876
7877#ifdef FEAT_GUI
7878 ga_clear(&gui_attr_table);
7879#endif
7880 for (i = 0; i < term_attr_table.ga_len; ++i)
7881 {
7882 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
7883 vim_free(taep->ae_u.term.start);
7884 vim_free(taep->ae_u.term.stop);
7885 }
7886 ga_clear(&term_attr_table);
7887 ga_clear(&cterm_attr_table);
7888}
7889
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007890#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00007891/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00007892 * Combine special attributes (e.g., for spelling) with other attributes
7893 * (e.g., for syntax highlighting).
7894 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00007895 * This creates a new group when required.
7896 * Since we expect there to be few spelling mistakes we don't cache the
7897 * result.
7898 * Return the resulting attributes.
7899 */
7900 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00007901hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00007902 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00007903 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007904{
7905 attrentry_T *char_aep = NULL;
7906 attrentry_T *spell_aep;
7907 attrentry_T new_en;
7908
7909 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00007910 return prim_attr;
7911 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
7912 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007913#ifdef FEAT_GUI
7914 if (gui.in_use)
7915 {
7916 if (char_attr > HL_ALL)
7917 char_aep = syn_gui_attr2entry(char_attr);
7918 if (char_aep != NULL)
7919 new_en = *char_aep;
7920 else
7921 {
7922 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00007923 new_en.ae_u.gui.fg_color = INVALCOLOR;
7924 new_en.ae_u.gui.bg_color = INVALCOLOR;
7925 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007926 if (char_attr <= HL_ALL)
7927 new_en.ae_attr = char_attr;
7928 }
7929
Bram Moolenaar30abd282005-06-22 22:35:10 +00007930 if (prim_attr <= HL_ALL)
7931 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007932 else
7933 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00007934 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00007935 if (spell_aep != NULL)
7936 {
7937 new_en.ae_attr |= spell_aep->ae_attr;
7938 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
7939 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
7940 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
7941 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
7942 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
7943 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
7944 if (spell_aep->ae_u.gui.font != NOFONT)
7945 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
7946# ifdef FEAT_XFONTSET
7947 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
7948 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
7949# endif
7950 }
7951 }
7952 return get_attr_entry(&gui_attr_table, &new_en);
7953 }
7954#endif
7955
7956 if (t_colors > 1)
7957 {
7958 if (char_attr > HL_ALL)
7959 char_aep = syn_cterm_attr2entry(char_attr);
7960 if (char_aep != NULL)
7961 new_en = *char_aep;
7962 else
7963 {
7964 vim_memset(&new_en, 0, sizeof(new_en));
7965 if (char_attr <= HL_ALL)
7966 new_en.ae_attr = char_attr;
7967 }
7968
Bram Moolenaar30abd282005-06-22 22:35:10 +00007969 if (prim_attr <= HL_ALL)
7970 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007971 else
7972 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00007973 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00007974 if (spell_aep != NULL)
7975 {
7976 new_en.ae_attr |= spell_aep->ae_attr;
7977 if (spell_aep->ae_u.cterm.fg_color > 0)
7978 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
7979 if (spell_aep->ae_u.cterm.bg_color > 0)
7980 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
7981 }
7982 }
7983 return get_attr_entry(&cterm_attr_table, &new_en);
7984 }
7985
7986 if (char_attr > HL_ALL)
7987 char_aep = syn_term_attr2entry(char_attr);
7988 if (char_aep != NULL)
7989 new_en = *char_aep;
7990 else
7991 {
7992 vim_memset(&new_en, 0, sizeof(new_en));
7993 if (char_attr <= HL_ALL)
7994 new_en.ae_attr = char_attr;
7995 }
7996
Bram Moolenaar30abd282005-06-22 22:35:10 +00007997 if (prim_attr <= HL_ALL)
7998 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00007999 else
8000 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008001 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008002 if (spell_aep != NULL)
8003 {
8004 new_en.ae_attr |= spell_aep->ae_attr;
8005 if (spell_aep->ae_u.term.start != NULL)
8006 {
8007 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8008 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8009 }
8010 }
8011 }
8012 return get_attr_entry(&term_attr_table, &new_en);
8013}
8014#endif
8015
Bram Moolenaar071d4272004-06-13 20:20:40 +00008016#ifdef FEAT_GUI
8017
8018 attrentry_T *
8019syn_gui_attr2entry(attr)
8020 int attr;
8021{
8022 attr -= ATTR_OFF;
8023 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8024 return NULL;
8025 return &(GUI_ATTR_ENTRY(attr));
8026}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008027#endif /* FEAT_GUI */
8028
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008029/*
8030 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8031 * Only to be used when "attr" > HL_ALL.
8032 */
8033 int
8034syn_attr2attr(attr)
8035 int attr;
8036{
8037 attrentry_T *aep;
8038
8039#ifdef FEAT_GUI
8040 if (gui.in_use)
8041 aep = syn_gui_attr2entry(attr);
8042 else
8043#endif
8044 if (t_colors > 1)
8045 aep = syn_cterm_attr2entry(attr);
8046 else
8047 aep = syn_term_attr2entry(attr);
8048
8049 if (aep == NULL) /* highlighting not set */
8050 return 0;
8051 return aep->ae_attr;
8052}
8053
8054
Bram Moolenaar071d4272004-06-13 20:20:40 +00008055 attrentry_T *
8056syn_term_attr2entry(attr)
8057 int attr;
8058{
8059 attr -= ATTR_OFF;
8060 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8061 return NULL;
8062 return &(TERM_ATTR_ENTRY(attr));
8063}
8064
8065 attrentry_T *
8066syn_cterm_attr2entry(attr)
8067 int attr;
8068{
8069 attr -= ATTR_OFF;
8070 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8071 return NULL;
8072 return &(CTERM_ATTR_ENTRY(attr));
8073}
8074
8075#define LIST_ATTR 1
8076#define LIST_STRING 2
8077#define LIST_INT 3
8078
8079 static void
8080highlight_list_one(id)
8081 int id;
8082{
8083 struct hl_group *sgp;
8084 int didh = FALSE;
8085
8086 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8087
8088 didh = highlight_list_arg(id, didh, LIST_ATTR,
8089 sgp->sg_term, NULL, "term");
8090 didh = highlight_list_arg(id, didh, LIST_STRING,
8091 0, sgp->sg_start, "start");
8092 didh = highlight_list_arg(id, didh, LIST_STRING,
8093 0, sgp->sg_stop, "stop");
8094
8095 didh = highlight_list_arg(id, didh, LIST_ATTR,
8096 sgp->sg_cterm, NULL, "cterm");
8097 didh = highlight_list_arg(id, didh, LIST_INT,
8098 sgp->sg_cterm_fg, NULL, "ctermfg");
8099 didh = highlight_list_arg(id, didh, LIST_INT,
8100 sgp->sg_cterm_bg, NULL, "ctermbg");
8101
8102#ifdef FEAT_GUI
8103 didh = highlight_list_arg(id, didh, LIST_ATTR,
8104 sgp->sg_gui, NULL, "gui");
8105 didh = highlight_list_arg(id, didh, LIST_STRING,
8106 0, sgp->sg_gui_fg_name, "guifg");
8107 didh = highlight_list_arg(id, didh, LIST_STRING,
8108 0, sgp->sg_gui_bg_name, "guibg");
8109 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008110 0, sgp->sg_gui_sp_name, "guisp");
8111 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008112 0, sgp->sg_font_name, "font");
8113#endif
8114
Bram Moolenaar661b1822005-07-28 22:36:45 +00008115 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008116 {
8117 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008118 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008119 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8120 msg_putchar(' ');
8121 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8122 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008123
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008124 if (!didh)
8125 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008126#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008127 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008128 last_set_msg(sgp->sg_scriptID);
8129#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008130}
8131
8132 static int
8133highlight_list_arg(id, didh, type, iarg, sarg, name)
8134 int id;
8135 int didh;
8136 int type;
8137 int iarg;
8138 char_u *sarg;
8139 char *name;
8140{
8141 char_u buf[100];
8142 char_u *ts;
8143 int i;
8144
Bram Moolenaar661b1822005-07-28 22:36:45 +00008145 if (got_int)
8146 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008147 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8148 {
8149 ts = buf;
8150 if (type == LIST_INT)
8151 sprintf((char *)buf, "%d", iarg - 1);
8152 else if (type == LIST_STRING)
8153 ts = sarg;
8154 else /* type == LIST_ATTR */
8155 {
8156 buf[0] = NUL;
8157 for (i = 0; hl_attr_table[i] != 0; ++i)
8158 {
8159 if (iarg & hl_attr_table[i])
8160 {
8161 if (buf[0] != NUL)
8162 STRCAT(buf, ",");
8163 STRCAT(buf, hl_name_table[i]);
8164 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8165 }
8166 }
8167 }
8168
8169 (void)syn_list_header(didh,
8170 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8171 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008172 if (!got_int)
8173 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008174 if (*name != NUL)
8175 {
8176 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8177 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8178 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008179 msg_outtrans(ts);
8180 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008181 }
8182 return didh;
8183}
8184
8185#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8186/*
8187 * Return "1" if highlight group "id" has attribute "flag".
8188 * Return NULL otherwise.
8189 */
8190 char_u *
8191highlight_has_attr(id, flag, modec)
8192 int id;
8193 int flag;
8194 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8195{
8196 int attr;
8197
8198 if (id <= 0 || id > highlight_ga.ga_len)
8199 return NULL;
8200
8201#ifdef FEAT_GUI
8202 if (modec == 'g')
8203 attr = HL_TABLE()[id - 1].sg_gui;
8204 else
8205#endif
8206 if (modec == 'c')
8207 attr = HL_TABLE()[id - 1].sg_cterm;
8208 else
8209 attr = HL_TABLE()[id - 1].sg_term;
8210
8211 if (attr & flag)
8212 return (char_u *)"1";
8213 return NULL;
8214}
8215#endif
8216
8217#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8218/*
8219 * Return color name of highlight group "id".
8220 */
8221 char_u *
8222highlight_color(id, what, modec)
8223 int id;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008224 char_u *what; /* "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008225 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8226{
8227 static char_u name[20];
8228 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008229 int fg = FALSE;
8230# ifdef FEAT_GUI
8231 int sp = FALSE;
8232# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008233
8234 if (id <= 0 || id > highlight_ga.ga_len)
8235 return NULL;
8236
8237 if (TOLOWER_ASC(what[0]) == 'f')
8238 fg = TRUE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008239# ifdef FEAT_GUI
8240 else if (TOLOWER_ASC(what[0]) == 's')
8241 sp = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008242 if (modec == 'g')
8243 {
8244 /* return #RRGGBB form (only possible when GUI is running) */
8245 if (gui.in_use && what[1] && what[2] == '#')
8246 {
8247 guicolor_T color;
8248 long_u rgb;
8249 static char_u buf[10];
8250
8251 if (fg)
8252 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008253 else if (sp)
8254 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008255 else
8256 color = HL_TABLE()[id - 1].sg_gui_bg;
8257 if (color == INVALCOLOR)
8258 return NULL;
8259 rgb = gui_mch_get_rgb(color);
8260 sprintf((char *)buf, "#%02x%02x%02x",
8261 (unsigned)(rgb >> 16),
8262 (unsigned)(rgb >> 8) & 255,
8263 (unsigned)rgb & 255);
8264 return buf;
8265 }
8266 if (fg)
8267 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008268 if (sp)
8269 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008270 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8271 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008272# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008273 if (modec == 'c')
8274 {
8275 if (fg)
8276 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8277 else
8278 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8279 sprintf((char *)name, "%d", n);
8280 return name;
8281 }
8282 /* term doesn't have color */
8283 return NULL;
8284}
8285#endif
8286
8287#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8288 || defined(PROTO)
8289/*
8290 * Return color name of highlight group "id" as RGB value.
8291 */
8292 long_u
8293highlight_gui_color_rgb(id, fg)
8294 int id;
8295 int fg; /* TRUE = fg, FALSE = bg */
8296{
8297 guicolor_T color;
8298
8299 if (id <= 0 || id > highlight_ga.ga_len)
8300 return 0L;
8301
8302 if (fg)
8303 color = HL_TABLE()[id - 1].sg_gui_fg;
8304 else
8305 color = HL_TABLE()[id - 1].sg_gui_bg;
8306
8307 if (color == INVALCOLOR)
8308 return 0L;
8309
8310 return gui_mch_get_rgb(color);
8311}
8312#endif
8313
8314/*
8315 * Output the syntax list header.
8316 * Return TRUE when started a new line.
8317 */
8318 static int
8319syn_list_header(did_header, outlen, id)
8320 int did_header; /* did header already */
8321 int outlen; /* length of string that comes */
8322 int id; /* highlight group id */
8323{
8324 int endcol = 19;
8325 int newline = TRUE;
8326
8327 if (!did_header)
8328 {
8329 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008330 if (got_int)
8331 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008332 msg_outtrans(HL_TABLE()[id - 1].sg_name);
8333 endcol = 15;
8334 }
8335 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008336 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00008337 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00008338 if (got_int)
8339 return TRUE;
8340 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008341 else
8342 {
8343 if (msg_col >= endcol) /* wrap around is like starting a new line */
8344 newline = FALSE;
8345 }
8346
8347 if (msg_col >= endcol) /* output at least one space */
8348 endcol = msg_col + 1;
8349 if (Columns <= endcol) /* avoid hang for tiny window */
8350 endcol = Columns - 1;
8351
8352 msg_advance(endcol);
8353
8354 /* Show "xxx" with the attributes. */
8355 if (!did_header)
8356 {
8357 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
8358 msg_putchar(' ');
8359 }
8360
8361 return newline;
8362}
8363
8364/*
8365 * Set the attribute numbers for a highlight group.
8366 * Called after one of the attributes has changed.
8367 */
8368 static void
8369set_hl_attr(idx)
8370 int idx; /* index in array */
8371{
8372 attrentry_T at_en;
8373 struct hl_group *sgp = HL_TABLE() + idx;
8374
8375 /* The "Normal" group doesn't need an attribute number */
8376 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
8377 return;
8378
8379#ifdef FEAT_GUI
8380 /*
8381 * For the GUI mode: If there are other than "normal" highlighting
8382 * attributes, need to allocate an attr number.
8383 */
8384 if (sgp->sg_gui_fg == INVALCOLOR
8385 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008386 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00008387 && sgp->sg_font == NOFONT
8388# ifdef FEAT_XFONTSET
8389 && sgp->sg_fontset == NOFONTSET
8390# endif
8391 )
8392 {
8393 sgp->sg_gui_attr = sgp->sg_gui;
8394 }
8395 else
8396 {
8397 at_en.ae_attr = sgp->sg_gui;
8398 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
8399 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008400 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008401 at_en.ae_u.gui.font = sgp->sg_font;
8402# ifdef FEAT_XFONTSET
8403 at_en.ae_u.gui.fontset = sgp->sg_fontset;
8404# endif
8405 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
8406 }
8407#endif
8408 /*
8409 * For the term mode: If there are other than "normal" highlighting
8410 * attributes, need to allocate an attr number.
8411 */
8412 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
8413 sgp->sg_term_attr = sgp->sg_term;
8414 else
8415 {
8416 at_en.ae_attr = sgp->sg_term;
8417 at_en.ae_u.term.start = sgp->sg_start;
8418 at_en.ae_u.term.stop = sgp->sg_stop;
8419 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
8420 }
8421
8422 /*
8423 * For the color term mode: If there are other than "normal"
8424 * highlighting attributes, need to allocate an attr number.
8425 */
8426 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
8427 sgp->sg_cterm_attr = sgp->sg_cterm;
8428 else
8429 {
8430 at_en.ae_attr = sgp->sg_cterm;
8431 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
8432 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
8433 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
8434 }
8435}
8436
8437/*
8438 * Lookup a highlight group name and return it's ID.
8439 * If it is not found, 0 is returned.
8440 */
8441 int
8442syn_name2id(name)
8443 char_u *name;
8444{
8445 int i;
8446 char_u name_u[200];
8447
8448 /* Avoid using stricmp() too much, it's slow on some systems */
8449 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
8450 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00008451 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008452 vim_strup(name_u);
8453 for (i = highlight_ga.ga_len; --i >= 0; )
8454 if (HL_TABLE()[i].sg_name_u != NULL
8455 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
8456 break;
8457 return i + 1;
8458}
8459
8460#if defined(FEAT_EVAL) || defined(PROTO)
8461/*
8462 * Return TRUE if highlight group "name" exists.
8463 */
8464 int
8465highlight_exists(name)
8466 char_u *name;
8467{
8468 return (syn_name2id(name) > 0);
8469}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008470
8471# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
8472/*
8473 * Return the name of highlight group "id".
8474 * When not a valid ID return an empty string.
8475 */
8476 char_u *
8477syn_id2name(id)
8478 int id;
8479{
8480 if (id <= 0 || id >= highlight_ga.ga_len)
8481 return (char_u *)"";
8482 return HL_TABLE()[id - 1].sg_name;
8483}
8484# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008485#endif
8486
8487/*
8488 * Like syn_name2id(), but take a pointer + length argument.
8489 */
8490 int
8491syn_namen2id(linep, len)
8492 char_u *linep;
8493 int len;
8494{
8495 char_u *name;
8496 int id = 0;
8497
8498 name = vim_strnsave(linep, len);
8499 if (name != NULL)
8500 {
8501 id = syn_name2id(name);
8502 vim_free(name);
8503 }
8504 return id;
8505}
8506
8507/*
8508 * Find highlight group name in the table and return it's ID.
8509 * The argument is a pointer to the name and the length of the name.
8510 * If it doesn't exist yet, a new entry is created.
8511 * Return 0 for failure.
8512 */
8513 int
8514syn_check_group(pp, len)
8515 char_u *pp;
8516 int len;
8517{
8518 int id;
8519 char_u *name;
8520
8521 name = vim_strnsave(pp, len);
8522 if (name == NULL)
8523 return 0;
8524
8525 id = syn_name2id(name);
8526 if (id == 0) /* doesn't exist yet */
8527 id = syn_add_group(name);
8528 else
8529 vim_free(name);
8530 return id;
8531}
8532
8533/*
8534 * Add new highlight group and return it's ID.
8535 * "name" must be an allocated string, it will be consumed.
8536 * Return 0 for failure.
8537 */
8538 static int
8539syn_add_group(name)
8540 char_u *name;
8541{
8542 char_u *p;
8543
8544 /* Check that the name is ASCII letters, digits and underscore. */
8545 for (p = name; *p != NUL; ++p)
8546 {
8547 if (!vim_isprintc(*p))
8548 {
8549 EMSG(_("E669: Unprintable character in group name"));
8550 return 0;
8551 }
8552 else if (!ASCII_ISALNUM(*p) && *p != '_')
8553 {
8554 /* This is an error, but since there previously was no check only
8555 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00008556 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00008557 MSG(_("W18: Invalid character in group name"));
8558 break;
8559 }
8560 }
8561
8562 /*
8563 * First call for this growarray: init growing array.
8564 */
8565 if (highlight_ga.ga_data == NULL)
8566 {
8567 highlight_ga.ga_itemsize = sizeof(struct hl_group);
8568 highlight_ga.ga_growsize = 10;
8569 }
8570
8571 /*
8572 * Make room for at least one other syntax_highlight entry.
8573 */
8574 if (ga_grow(&highlight_ga, 1) == FAIL)
8575 {
8576 vim_free(name);
8577 return 0;
8578 }
8579
8580 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
8581 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
8582 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
8583#ifdef FEAT_GUI
8584 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
8585 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008586 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008587#endif
8588 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008589
8590 return highlight_ga.ga_len; /* ID is index plus one */
8591}
8592
8593/*
8594 * When, just after calling syn_add_group(), an error is discovered, this
8595 * function deletes the new name.
8596 */
8597 static void
8598syn_unadd_group()
8599{
8600 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008601 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
8602 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
8603}
8604
8605/*
8606 * Translate a group ID to highlight attributes.
8607 */
8608 int
8609syn_id2attr(hl_id)
8610 int hl_id;
8611{
8612 int attr;
8613 struct hl_group *sgp;
8614
8615 hl_id = syn_get_final_id(hl_id);
8616 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8617
8618#ifdef FEAT_GUI
8619 /*
8620 * Only use GUI attr when the GUI is being used.
8621 */
8622 if (gui.in_use)
8623 attr = sgp->sg_gui_attr;
8624 else
8625#endif
8626 if (t_colors > 1)
8627 attr = sgp->sg_cterm_attr;
8628 else
8629 attr = sgp->sg_term_attr;
8630
8631 return attr;
8632}
8633
8634#ifdef FEAT_GUI
8635/*
8636 * Get the GUI colors and attributes for a group ID.
8637 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
8638 */
8639 int
8640syn_id2colors(hl_id, fgp, bgp)
8641 int hl_id;
8642 guicolor_T *fgp;
8643 guicolor_T *bgp;
8644{
8645 struct hl_group *sgp;
8646
8647 hl_id = syn_get_final_id(hl_id);
8648 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8649
8650 *fgp = sgp->sg_gui_fg;
8651 *bgp = sgp->sg_gui_bg;
8652 return sgp->sg_gui;
8653}
8654#endif
8655
8656/*
8657 * Translate a group ID to the final group ID (following links).
8658 */
8659 int
8660syn_get_final_id(hl_id)
8661 int hl_id;
8662{
8663 int count;
8664 struct hl_group *sgp;
8665
8666 if (hl_id > highlight_ga.ga_len || hl_id < 1)
8667 return 0; /* Can be called from eval!! */
8668
8669 /*
8670 * Follow links until there is no more.
8671 * Look out for loops! Break after 100 links.
8672 */
8673 for (count = 100; --count >= 0; )
8674 {
8675 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
8676 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
8677 break;
8678 hl_id = sgp->sg_link;
8679 }
8680
8681 return hl_id;
8682}
8683
8684#ifdef FEAT_GUI
8685/*
8686 * Call this function just after the GUI has started.
8687 * It finds the font and color handles for the highlighting groups.
8688 */
8689 void
8690highlight_gui_started()
8691{
8692 int idx;
8693
8694 /* First get the colors from the "Normal" and "Menu" group, if set */
8695 set_normal_colors();
8696
8697 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
8698 gui_do_one_color(idx, FALSE, FALSE);
8699
8700 highlight_changed();
8701}
8702
8703 static void
8704gui_do_one_color(idx, do_menu, do_tooltip)
8705 int idx;
8706 int do_menu; /* TRUE: might set the menu font */
8707 int do_tooltip; /* TRUE: might set the tooltip font */
8708{
8709 int didit = FALSE;
8710
8711 if (HL_TABLE()[idx].sg_font_name != NULL)
8712 {
8713 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
8714 do_tooltip);
8715 didit = TRUE;
8716 }
8717 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
8718 {
8719 HL_TABLE()[idx].sg_gui_fg =
8720 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
8721 didit = TRUE;
8722 }
8723 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
8724 {
8725 HL_TABLE()[idx].sg_gui_bg =
8726 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
8727 didit = TRUE;
8728 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008729 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
8730 {
8731 HL_TABLE()[idx].sg_gui_sp =
8732 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
8733 didit = TRUE;
8734 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008735 if (didit) /* need to get a new attr number */
8736 set_hl_attr(idx);
8737}
8738
8739#endif
8740
8741/*
8742 * Translate the 'highlight' option into attributes in highlight_attr[] and
8743 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
8744 * corresponding highlights to use on top of HLF_SNC is computed.
8745 * Called only when the 'highlight' option has been changed and upon first
8746 * screen redraw after any :highlight command.
8747 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
8748 */
8749 int
8750highlight_changed()
8751{
8752 int hlf;
8753 int i;
8754 char_u *p;
8755 int attr;
8756 char_u *end;
8757 int id;
8758#ifdef USER_HIGHLIGHT
8759 char_u userhl[10];
8760# ifdef FEAT_STL_OPT
8761 int id_SNC = -1;
8762 int id_S = -1;
8763 int hlcnt;
8764# endif
8765#endif
8766 static int hl_flags[HLF_COUNT] = HL_FLAGS;
8767
8768 need_highlight_changed = FALSE;
8769
8770 /*
8771 * Clear all attributes.
8772 */
8773 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8774 highlight_attr[hlf] = 0;
8775
8776 /*
8777 * First set all attributes to their default value.
8778 * Then use the attributes from the 'highlight' option.
8779 */
8780 for (i = 0; i < 2; ++i)
8781 {
8782 if (i)
8783 p = p_hl;
8784 else
8785 p = get_highlight_default();
8786 if (p == NULL) /* just in case */
8787 continue;
8788
8789 while (*p)
8790 {
8791 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
8792 if (hl_flags[hlf] == *p)
8793 break;
8794 ++p;
8795 if (hlf == (int)HLF_COUNT || *p == NUL)
8796 return FAIL;
8797
8798 /*
8799 * Allow several hl_flags to be combined, like "bu" for
8800 * bold-underlined.
8801 */
8802 attr = 0;
8803 for ( ; *p && *p != ','; ++p) /* parse upto comma */
8804 {
8805 if (vim_iswhite(*p)) /* ignore white space */
8806 continue;
8807
8808 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
8809 return FAIL;
8810
8811 switch (*p)
8812 {
8813 case 'b': attr |= HL_BOLD;
8814 break;
8815 case 'i': attr |= HL_ITALIC;
8816 break;
8817 case '-':
8818 case 'n': /* no highlighting */
8819 break;
8820 case 'r': attr |= HL_INVERSE;
8821 break;
8822 case 's': attr |= HL_STANDOUT;
8823 break;
8824 case 'u': attr |= HL_UNDERLINE;
8825 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008826 case 'c': attr |= HL_UNDERCURL;
8827 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008828 case ':': ++p; /* highlight group name */
8829 if (attr || *p == NUL) /* no combinations */
8830 return FAIL;
8831 end = vim_strchr(p, ',');
8832 if (end == NULL)
8833 end = p + STRLEN(p);
8834 id = syn_check_group(p, (int)(end - p));
8835 if (id == 0)
8836 return FAIL;
8837 attr = syn_id2attr(id);
8838 p = end - 1;
8839#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
8840 if (hlf == (int)HLF_SNC)
8841 id_SNC = syn_get_final_id(id);
8842 else if (hlf == (int)HLF_S)
8843 id_S = syn_get_final_id(id);
8844#endif
8845 break;
8846 default: return FAIL;
8847 }
8848 }
8849 highlight_attr[hlf] = attr;
8850
8851 p = skip_to_option_part(p); /* skip comma and spaces */
8852 }
8853 }
8854
8855#ifdef USER_HIGHLIGHT
8856 /* Setup the user highlights
8857 *
8858 * Temporarily utilize 10 more hl entries. Have to be in there
8859 * simultaneously in case of table overflows in get_attr_entry()
8860 */
8861# ifdef FEAT_STL_OPT
8862 if (ga_grow(&highlight_ga, 10) == FAIL)
8863 return FAIL;
8864 hlcnt = highlight_ga.ga_len;
8865 if (id_S == 0)
8866 { /* Make sure id_S is always valid to simplify code below */
8867 memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
8868 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
8869 id_S = hlcnt + 10;
8870 }
8871# endif
8872 for (i = 0; i < 9; i++)
8873 {
8874 sprintf((char *)userhl, "User%d", i + 1);
8875 id = syn_name2id(userhl);
8876 if (id == 0)
8877 {
8878 highlight_user[i] = 0;
8879# ifdef FEAT_STL_OPT
8880 highlight_stlnc[i] = 0;
8881# endif
8882 }
8883 else
8884 {
8885# ifdef FEAT_STL_OPT
8886 struct hl_group *hlt = HL_TABLE();
8887# endif
8888
8889 highlight_user[i] = syn_id2attr(id);
8890# ifdef FEAT_STL_OPT
8891 if (id_SNC == 0)
8892 {
8893 memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
8894 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
8895 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
8896# ifdef FEAT_GUI
8897 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
8898# endif
8899 }
8900 else
8901 mch_memmove(&hlt[hlcnt + i],
8902 &hlt[id_SNC - 1],
8903 sizeof(struct hl_group));
8904 hlt[hlcnt + i].sg_link = 0;
8905
8906 /* Apply difference between UserX and HLF_S to HLF_SNC */
8907 hlt[hlcnt + i].sg_term ^=
8908 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
8909 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
8910 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
8911 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
8912 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
8913 hlt[hlcnt + i].sg_cterm ^=
8914 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
8915 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
8916 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
8917 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
8918 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
8919# ifdef FEAT_GUI
8920 hlt[hlcnt + i].sg_gui ^=
8921 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
8922 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
8923 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
8924 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
8925 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008926 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
8927 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008928 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
8929 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
8930# ifdef FEAT_XFONTSET
8931 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
8932 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
8933# endif
8934# endif
8935 highlight_ga.ga_len = hlcnt + i + 1;
8936 set_hl_attr(hlcnt + i); /* At long last we can apply */
8937 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
8938# endif
8939 }
8940 }
8941# ifdef FEAT_STL_OPT
8942 highlight_ga.ga_len = hlcnt;
8943# endif
8944
8945#endif /* USER_HIGHLIGHT */
8946
8947 return OK;
8948}
8949
8950#ifdef FEAT_CMDL_COMPL
8951
8952static void highlight_list __ARGS((void));
8953static void highlight_list_two __ARGS((int cnt, int attr));
8954
8955/*
8956 * Handle command line completion for :highlight command.
8957 */
8958 void
8959set_context_in_highlight_cmd(xp, arg)
8960 expand_T *xp;
8961 char_u *arg;
8962{
8963 char_u *p;
8964
8965 /* Default: expand group names */
8966 xp->xp_context = EXPAND_HIGHLIGHT;
8967 xp->xp_pattern = arg;
8968 include_link = TRUE;
8969 include_default = TRUE;
8970
8971 /* (part of) subcommand already typed */
8972 if (*arg != NUL)
8973 {
8974 p = skiptowhite(arg);
8975 if (*p != NUL) /* past "default" or group name */
8976 {
8977 include_default = FALSE;
8978 if (STRNCMP("default", arg, p - arg) == 0)
8979 {
8980 arg = skipwhite(p);
8981 xp->xp_pattern = arg;
8982 p = skiptowhite(arg);
8983 }
8984 if (*p != NUL) /* past group name */
8985 {
8986 include_link = FALSE;
8987 if (arg[1] == 'i' && arg[0] == 'N')
8988 highlight_list();
8989 if (STRNCMP("link", arg, p - arg) == 0
8990 || STRNCMP("clear", arg, p - arg) == 0)
8991 {
8992 xp->xp_pattern = skipwhite(p);
8993 p = skiptowhite(xp->xp_pattern);
8994 if (*p != NUL) /* past first group name */
8995 {
8996 xp->xp_pattern = skipwhite(p);
8997 p = skiptowhite(xp->xp_pattern);
8998 }
8999 }
9000 if (*p != NUL) /* past group name(s) */
9001 xp->xp_context = EXPAND_NOTHING;
9002 }
9003 }
9004 }
9005}
9006
9007/*
9008 * List highlighting matches in a nice way.
9009 */
9010 static void
9011highlight_list()
9012{
9013 int i;
9014
9015 for (i = 10; --i >= 0; )
9016 highlight_list_two(i, hl_attr(HLF_D));
9017 for (i = 40; --i >= 0; )
9018 highlight_list_two(99, 0);
9019}
9020
9021 static void
9022highlight_list_two(cnt, attr)
9023 int cnt;
9024 int attr;
9025{
9026 msg_puts_attr((char_u *)("N \bI \b! \b" + cnt / 11), attr);
9027 msg_clr_eos();
9028 out_flush();
9029 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9030}
9031
9032#endif /* FEAT_CMDL_COMPL */
9033
9034#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9035 || defined(FEAT_SIGNS) || defined(PROTO)
9036/*
9037 * Function given to ExpandGeneric() to obtain the list of group names.
9038 * Also used for synIDattr() function.
9039 */
9040/*ARGSUSED*/
9041 char_u *
9042get_highlight_name(xp, idx)
9043 expand_T *xp;
9044 int idx;
9045{
9046 if (idx == highlight_ga.ga_len
9047#ifdef FEAT_CMDL_COMPL
9048 && include_link
9049#endif
9050 )
9051 return (char_u *)"link";
9052 if (idx == highlight_ga.ga_len + 1
9053#ifdef FEAT_CMDL_COMPL
9054 && include_link
9055#endif
9056 )
9057 return (char_u *)"clear";
9058 if (idx == highlight_ga.ga_len + 2
9059#ifdef FEAT_CMDL_COMPL
9060 && include_default
9061#endif
9062 )
9063 return (char_u *)"default";
9064 if (idx < 0 || idx >= highlight_ga.ga_len)
9065 return NULL;
9066 return HL_TABLE()[idx].sg_name;
9067}
9068#endif
9069
9070#ifdef FEAT_GUI
9071/*
9072 * Free all the highlight group fonts.
9073 * Used when quitting for systems which need it.
9074 */
9075 void
9076free_highlight_fonts()
9077{
9078 int idx;
9079
9080 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9081 {
9082 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9083 HL_TABLE()[idx].sg_font = NOFONT;
9084# ifdef FEAT_XFONTSET
9085 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9086 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9087# endif
9088 }
9089
9090 gui_mch_free_font(gui.norm_font);
9091# ifdef FEAT_XFONTSET
9092 gui_mch_free_fontset(gui.fontset);
9093# endif
9094# ifndef HAVE_GTK2
9095 gui_mch_free_font(gui.bold_font);
9096 gui_mch_free_font(gui.ital_font);
9097 gui_mch_free_font(gui.boldital_font);
9098# endif
9099}
9100#endif
9101
9102/**************************************
9103 * End of Highlighting stuff *
9104 **************************************/