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