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