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