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