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