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