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