blob: 5b86a29da7723818064c7b1ea1f56b3e63913c62 [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 */
Bram Moolenaar071d4272004-06-13 20:20:40 +000038 guicolor_T sg_gui_fg; /* GUI foreground color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000039 guicolor_T sg_gui_bg; /* GUI background color handle */
Bram Moolenaare2cc9702005-03-15 22:43:58 +000040 guicolor_T sg_gui_sp; /* GUI special color handle */
Bram Moolenaar071d4272004-06-13 20:20:40 +000041 GuiFont sg_font; /* GUI font handle */
42#ifdef FEAT_XFONTSET
43 GuiFontset sg_fontset; /* GUI fontset handle */
44#endif
45 char_u *sg_font_name; /* GUI font or fontset name */
46 int sg_gui_attr; /* Screen attr for GUI mode */
47#endif
Bram Moolenaar61623362010-07-14 22:04:22 +020048#if defined(FEAT_GUI) || defined(FEAT_EVAL)
49/* Store the sp color name for the GUI or synIDattr() */
50 int sg_gui; /* "gui=" highlighting attributes */
51 char_u *sg_gui_fg_name;/* GUI foreground color name */
52 char_u *sg_gui_bg_name;/* GUI background color name */
53 char_u *sg_gui_sp_name;/* GUI special color name */
54#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000055 int sg_link; /* link to this highlight group ID */
56 int sg_set; /* combination of SG_* flags */
Bram Moolenaar661b1822005-07-28 22:36:45 +000057#ifdef FEAT_EVAL
58 scid_T sg_scriptID; /* script in which the group was last set */
59#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +000060};
61
62#define SG_TERM 1 /* term has been set */
63#define SG_CTERM 2 /* cterm has been set */
64#define SG_GUI 4 /* gui has been set */
65#define SG_LINK 8 /* link has been set */
66
67static garray_T highlight_ga; /* highlight groups for 'highlight' option */
68
69#define HL_TABLE() ((struct hl_group *)((highlight_ga.ga_data)))
70
Bram Moolenaar2dfb3862011-04-02 15:12:50 +020071#define MAX_HL_ID 20000 /* maximum value for a highlight ID. */
72
Bram Moolenaar071d4272004-06-13 20:20:40 +000073#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +000074/* Flags to indicate an additional string for highlight name completion. */
75static int include_none = 0; /* when 1 include "None" */
76static int include_default = 0; /* when 1 include "default" */
77static int include_link = 0; /* when 2 include "link" and "clear" */
Bram Moolenaar071d4272004-06-13 20:20:40 +000078#endif
79
80/*
81 * The "term", "cterm" and "gui" arguments can be any combination of the
82 * following names, separated by commas (but no spaces!).
83 */
84static char *(hl_name_table[]) =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000085 {"bold", "standout", "underline", "undercurl",
86 "italic", "reverse", "inverse", "NONE"};
Bram Moolenaar071d4272004-06-13 20:20:40 +000087static int hl_attr_table[] =
Bram Moolenaare2cc9702005-03-15 22:43:58 +000088 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, 0};
Bram Moolenaar071d4272004-06-13 20:20:40 +000089
90static int get_attr_entry __ARGS((garray_T *table, attrentry_T *aep));
91static void syn_unadd_group __ARGS((void));
92static void set_hl_attr __ARGS((int idx));
93static void highlight_list_one __ARGS((int id));
94static int highlight_list_arg __ARGS((int id, int didh, int type, int iarg, char_u *sarg, char *name));
95static int syn_add_group __ARGS((char_u *name));
96static int syn_list_header __ARGS((int did_header, int outlen, int id));
97static int hl_has_settings __ARGS((int idx, int check_link));
98static void highlight_clear __ARGS((int idx));
99
100#ifdef FEAT_GUI
101static void gui_do_one_color __ARGS((int idx, int do_menu, int do_tooltip));
102static int set_group_colors __ARGS((char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip));
103static guicolor_T color_name2handle __ARGS((char_u *name));
104static GuiFont font_name2handle __ARGS((char_u *name));
105# ifdef FEAT_XFONTSET
106static GuiFontset fontset_name2handle __ARGS((char_u *name, int fixed_width));
107# endif
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +0200108static void hl_do_font __ARGS((int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000109#endif
110
111/*
112 * An attribute number is the index in attr_table plus ATTR_OFF.
113 */
114#define ATTR_OFF (HL_ALL + 1)
115
116#if defined(FEAT_SYN_HL) || defined(PROTO)
117
118#define SYN_NAMELEN 50 /* maximum length of a syntax name */
119
120/* different types of offsets that are possible */
121#define SPO_MS_OFF 0 /* match start offset */
122#define SPO_ME_OFF 1 /* match end offset */
123#define SPO_HS_OFF 2 /* highl. start offset */
124#define SPO_HE_OFF 3 /* highl. end offset */
125#define SPO_RS_OFF 4 /* region start offset */
126#define SPO_RE_OFF 5 /* region end offset */
127#define SPO_LC_OFF 6 /* leading context offset */
128#define SPO_COUNT 7
129
130static char *(spo_name_tab[SPO_COUNT]) =
131 {"ms=", "me=", "hs=", "he=", "rs=", "re=", "lc="};
132
133/*
134 * The patterns that are being searched for are stored in a syn_pattern.
135 * A match item consists of one pattern.
136 * A start/end item consists of n start patterns and m end patterns.
137 * A start/skip/end item consists of n start patterns, one skip pattern and m
138 * end patterns.
139 * For the latter two, the patterns are always consecutive: start-skip-end.
140 *
141 * A character offset can be given for the matched text (_m_start and _m_end)
142 * and for the actually highlighted text (_h_start and _h_end).
143 */
144typedef struct syn_pattern
145{
146 char sp_type; /* see SPTYPE_ defines below */
147 char sp_syncing; /* this item used for syncing */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200148 int sp_flags; /* see HL_ defines below */
149#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200150 int sp_cchar; /* conceal substitute character */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200151#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000152 struct sp_syn sp_syn; /* struct passed to in_id_list() */
153 short sp_syn_match_id; /* highlight group ID of pattern */
154 char_u *sp_pattern; /* regexp to match, pattern */
155 regprog_T *sp_prog; /* regexp to match, program */
Bram Moolenaarf7512552013-06-06 14:55:19 +0200156#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200157 syn_time_T sp_time;
158#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000159 int sp_ic; /* ignore-case flag for sp_prog */
160 short sp_off_flags; /* see below */
161 int sp_offsets[SPO_COUNT]; /* offsets */
162 short *sp_cont_list; /* cont. group IDs, if non-zero */
163 short *sp_next_list; /* next group IDs, if non-zero */
164 int sp_sync_idx; /* sync item index (syncing only) */
165 int sp_line_id; /* ID of last line where tried */
166 int sp_startcol; /* next match in sp_line_id line */
167} synpat_T;
168
169/* The sp_off_flags are computed like this:
170 * offset from the start of the matched text: (1 << SPO_XX_OFF)
171 * offset from the end of the matched text: (1 << (SPO_XX_OFF + SPO_COUNT))
172 * When both are present, only one is used.
173 */
174
175#define SPTYPE_MATCH 1 /* match keyword with this group ID */
176#define SPTYPE_START 2 /* match a regexp, start of item */
177#define SPTYPE_END 3 /* match a regexp, end of item */
178#define SPTYPE_SKIP 4 /* match a regexp, skip within item */
179
Bram Moolenaar071d4272004-06-13 20:20:40 +0000180
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() */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000201static int current_trans_id = 0; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000202#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +0200203#ifdef FEAT_CONCEAL
204static int current_flags = 0;
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200205static int current_seqnr = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200206static int current_sub_char = 0;
207#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000208
Bram Moolenaar217ad922005-03-20 22:37:15 +0000209typedef struct syn_cluster_S
Bram Moolenaar071d4272004-06-13 20:20:40 +0000210{
211 char_u *scl_name; /* syntax cluster name */
212 char_u *scl_name_u; /* uppercase of scl_name */
213 short *scl_list; /* IDs in this syntax cluster */
Bram Moolenaar217ad922005-03-20 22:37:15 +0000214} syn_cluster_T;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000215
216/*
217 * Methods of combining two clusters
218 */
219#define CLUSTER_REPLACE 1 /* replace first list with second */
220#define CLUSTER_ADD 2 /* add second list to first */
221#define CLUSTER_SUBTRACT 3 /* subtract second list from first */
222
Bram Moolenaar217ad922005-03-20 22:37:15 +0000223#define SYN_CLSTR(buf) ((syn_cluster_T *)((buf)->b_syn_clusters.ga_data))
Bram Moolenaar071d4272004-06-13 20:20:40 +0000224
225/*
226 * Syntax group IDs have different types:
Bram Moolenaar42431a72011-04-01 14:44:59 +0200227 * 0 - 19999 normal syntax groups
228 * 20000 - 20999 ALLBUT indicator (current_syn_inc_tag added)
229 * 21000 - 21999 TOP indicator (current_syn_inc_tag added)
230 * 22000 - 22999 CONTAINED indicator (current_syn_inc_tag added)
231 * 23000 - 32767 cluster IDs (subtract SYNID_CLUSTER for the cluster ID)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000232 */
Bram Moolenaar2dfb3862011-04-02 15:12:50 +0200233#define SYNID_ALLBUT MAX_HL_ID /* syntax group ID for contains=ALLBUT */
Bram Moolenaar42431a72011-04-01 14:44:59 +0200234#define SYNID_TOP 21000 /* syntax group ID for contains=TOP */
235#define SYNID_CONTAINED 22000 /* syntax group ID for contains=CONTAINED */
236#define SYNID_CLUSTER 23000 /* first syntax group ID for clusters */
237
Bram Moolenaar42431a72011-04-01 14:44:59 +0200238#define MAX_SYN_INC_TAG 999 /* maximum before the above overflow */
239#define MAX_CLUSTER_ID (32767 - SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000240
241/*
242 * Annoying Hack(TM): ":syn include" needs this pointer to pass to
243 * expand_filename(). Most of the other syntax commands don't need it, so
244 * instead of passing it to them, we stow it here.
245 */
246static char_u **syn_cmdlinep;
247
248/*
249 * Another Annoying Hack(TM): To prevent rules from other ":syn include"'d
Bram Moolenaar56be9502010-06-06 14:20:26 +0200250 * files from leaking into ALLBUT lists, we assign a unique ID to the
Bram Moolenaar071d4272004-06-13 20:20:40 +0000251 * rules in each ":syn include"'d file.
252 */
253static int current_syn_inc_tag = 0;
254static int running_syn_inc_tag = 0;
255
256/*
Bram Moolenaardad6b692005-01-25 22:14:34 +0000257 * In a hashtable item "hi_key" points to "keyword" in a keyentry.
258 * This avoids adding a pointer to the hashtable item.
259 * KE2HIKEY() converts a var pointer to a hashitem key pointer.
260 * HIKEY2KE() converts a hashitem key pointer to a var pointer.
261 * HI2KE() converts a hashitem pointer to a var pointer.
262 */
263static keyentry_T dumkey;
264#define KE2HIKEY(kp) ((kp)->keyword)
265#define HIKEY2KE(p) ((keyentry_T *)((p) - (dumkey.keyword - (char_u *)&dumkey)))
266#define HI2KE(hi) HIKEY2KE((hi)->hi_key)
267
268/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000269 * To reduce the time spent in keepend(), remember at which level in the state
270 * stack the first item with "keepend" is present. When "-1", there is no
271 * "keepend" on the stack.
272 */
273static int keepend_level = -1;
274
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200275static char msg_no_items[] = N_("No Syntax items defined for this buffer");
276
Bram Moolenaar071d4272004-06-13 20:20:40 +0000277/*
278 * For the current state we need to remember more than just the idx.
279 * When si_m_endpos.lnum is 0, the items other than si_idx are unknown.
280 * (The end positions have the column number of the next char)
281 */
282typedef struct state_item
283{
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000284 int si_idx; /* index of syntax pattern or
285 KEYWORD_IDX */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000286 int si_id; /* highlight group ID for keywords */
Bram Moolenaarf5b63862009-12-16 17:13:44 +0000287 int si_trans_id; /* idem, transparency removed */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000288 int si_m_lnum; /* lnum of the match */
289 int si_m_startcol; /* starting column of the match */
290 lpos_T si_m_endpos; /* just after end posn of the match */
291 lpos_T si_h_startpos; /* start position of the highlighting */
292 lpos_T si_h_endpos; /* end position of the highlighting */
293 lpos_T si_eoe_pos; /* end position of end pattern */
294 int si_end_idx; /* group ID for end pattern or zero */
295 int si_ends; /* if match ends before si_m_endpos */
296 int si_attr; /* attributes in this state */
297 long si_flags; /* HL_HAS_EOL flag in this state, and
298 * HL_SKIP* for si_next_list */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200299#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200300 int si_seqnr; /* sequence number */
Bram Moolenaar6e202e52010-07-28 18:14:45 +0200301 int si_cchar; /* substitution character for conceal */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200302#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000303 short *si_cont_list; /* list of contained groups */
304 short *si_next_list; /* nextgroup IDs after this item ends */
305 reg_extmatch_T *si_extmatch; /* \z(...\) matches from start
306 * pattern */
307} stateitem_T;
308
309#define KEYWORD_IDX -1 /* value of si_idx for keywords */
310#define ID_LIST_ALL (short *)-1 /* valid of si_cont_list for containing all
311 but contained groups */
312
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200313#ifdef FEAT_CONCEAL
314static int next_seqnr = 0; /* value to use for si_seqnr */
315#endif
316
Bram Moolenaar071d4272004-06-13 20:20:40 +0000317/*
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000318 * Struct to reduce the number of arguments to get_syn_options(), it's used
319 * very often.
320 */
321typedef struct
322{
Bram Moolenaarb8017e72007-05-10 18:59:07 +0000323 int flags; /* flags for contained and transparent */
Bram Moolenaar6ac54292005-02-02 23:07:25 +0000324 int keyword; /* TRUE for ":syn keyword" */
325 int *sync_idx; /* syntax item for "grouphere" argument, NULL
326 if not allowed */
327 char has_cont_list; /* TRUE if "cont_list" can be used */
328 short *cont_list; /* group IDs for "contains" argument */
329 short *cont_in_list; /* group IDs for "containedin" argument */
330 short *next_list; /* group IDs for "nextgroup" argument */
331} syn_opt_arg_T;
332
333/*
Bram Moolenaar071d4272004-06-13 20:20:40 +0000334 * The next possible match in the current line for any pattern is remembered,
335 * to avoid having to try for a match in each column.
336 * If next_match_idx == -1, not tried (in this line) yet.
337 * If next_match_col == MAXCOL, no match found in this line.
338 * (All end positions have the column of the char after the end)
339 */
340static int next_match_col; /* column for start of next match */
341static lpos_T next_match_m_endpos; /* position for end of next match */
342static lpos_T next_match_h_startpos; /* pos. for highl. start of next match */
343static lpos_T next_match_h_endpos; /* pos. for highl. end of next match */
344static int next_match_idx; /* index of matched item */
345static long next_match_flags; /* flags for next match */
346static lpos_T next_match_eos_pos; /* end of start pattn (start region) */
347static lpos_T next_match_eoe_pos; /* pos. for end of end pattern */
348static int next_match_end_idx; /* ID of group for end pattn or zero */
349static reg_extmatch_T *next_match_extmatch = NULL;
350
351/*
352 * A state stack is an array of integers or stateitem_T, stored in a
353 * garray_T. A state stack is invalid if it's itemsize entry is zero.
354 */
355#define INVALID_STATE(ssp) ((ssp)->ga_itemsize == 0)
356#define VALID_STATE(ssp) ((ssp)->ga_itemsize != 0)
357
358/*
359 * The current state (within the line) of the recognition engine.
360 * When current_state.ga_itemsize is 0 the current state is invalid.
361 */
362static win_T *syn_win; /* current window for highlighting */
363static buf_T *syn_buf; /* current buffer for highlighting */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200364static synblock_T *syn_block; /* current buffer for highlighting */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000365static linenr_T current_lnum = 0; /* lnum of current state */
366static colnr_T current_col = 0; /* column of current state */
367static int current_state_stored = 0; /* TRUE if stored current state
368 * after setting current_finished */
369static int current_finished = 0; /* current line has been finished */
370static garray_T current_state /* current stack of state_items */
371 = {0, 0, 0, 0, NULL};
372static short *current_next_list = NULL; /* when non-zero, nextgroup list */
373static int current_next_flags = 0; /* flags for current_next_list */
374static int current_line_id = 0; /* unique number for current line */
375
376#define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx]
377
378static void syn_sync __ARGS((win_T *wp, linenr_T lnum, synstate_T *last_valid));
379static int syn_match_linecont __ARGS((linenr_T lnum));
380static void syn_start_line __ARGS((void));
381static void syn_update_ends __ARGS((int startofline));
382static void syn_stack_alloc __ARGS((void));
383static int syn_stack_cleanup __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200384static void syn_stack_free_entry __ARGS((synblock_T *block, synstate_T *p));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000385static synstate_T *syn_stack_find_entry __ARGS((linenr_T lnum));
Bram Moolenaardbe31752008-01-13 16:40:19 +0000386static synstate_T *store_current_state __ARGS((void));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000387static void load_current_state __ARGS((synstate_T *from));
388static void invalidate_current_state __ARGS((void));
389static int syn_stack_equal __ARGS((synstate_T *sp));
390static void validate_current_state __ARGS((void));
391static int syn_finish_line __ARGS((int syncing));
Bram Moolenaar56cefaf2008-01-12 15:47:10 +0000392static int syn_current_attr __ARGS((int syncing, int displaying, int *can_spell, int keep_state));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000393static int did_match_already __ARGS((int idx, garray_T *gap));
394static stateitem_T *push_next_match __ARGS((stateitem_T *cur_si));
395static void check_state_ends __ARGS((void));
396static void update_si_attr __ARGS((int idx));
397static void check_keepend __ARGS((void));
398static void update_si_end __ARGS((stateitem_T *sip, int startcol, int force));
399static short *copy_id_list __ARGS((short *list));
400static int in_id_list __ARGS((stateitem_T *item, short *cont_list, struct sp_syn *ssp, int contained));
401static int push_current_state __ARGS((int idx));
402static void pop_current_state __ARGS((void));
Bram Moolenaarf7512552013-06-06 14:55:19 +0200403#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200404static void syn_clear_time __ARGS((syn_time_T *tt));
405static void syntime_clear __ARGS((void));
406#ifdef __BORLANDC__
407static int _RTLENTRYF syn_compare_syntime __ARGS((const void *v1, const void *v2));
408#else
409static int syn_compare_syntime __ARGS((const void *v1, const void *v2));
410#endif
411static void syntime_report __ARGS((void));
412static int syn_time_on = FALSE;
413# define IF_SYN_TIME(p) (p)
414#else
415# define IF_SYN_TIME(p) NULL
416typedef int syn_time_T;
417#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +0000418
Bram Moolenaar860cae12010-06-05 23:22:07 +0200419static void syn_stack_apply_changes_block __ARGS((synblock_T *block, buf_T *buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000420static 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));
421static void clear_syn_state __ARGS((synstate_T *p));
422static void clear_current_state __ARGS((void));
423
424static void limit_pos __ARGS((lpos_T *pos, lpos_T *limit));
425static void limit_pos_zero __ARGS((lpos_T *pos, lpos_T *limit));
426static void syn_add_end_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
427static void syn_add_start_off __ARGS((lpos_T *result, regmmatch_T *regmatch, synpat_T *spp, int idx, int extra));
428static char_u *syn_getcurline __ARGS((void));
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +0200429static int syn_regexec __ARGS((regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T *st));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200430static int check_keyword_id __ARGS((char_u *line, int startcol, int *endcol, long *flags, short **next_list, stateitem_T *cur_si, int *ccharp));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000431static void syn_cmd_case __ARGS((exarg_T *eap, int syncing));
Bram Moolenaarce0842a2005-07-18 21:58:11 +0000432static void syn_cmd_spell __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000433static void syntax_sync_clear __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200434static void syn_remove_pattern __ARGS((synblock_T *block, int idx));
435static void syn_clear_pattern __ARGS((synblock_T *block, int i));
436static void syn_clear_cluster __ARGS((synblock_T *block, int i));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000437static void syn_cmd_clear __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200438static void syn_cmd_conceal __ARGS((exarg_T *eap, int syncing));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000439static void syn_clear_one __ARGS((int id, int syncing));
440static void syn_cmd_on __ARGS((exarg_T *eap, int syncing));
441static void syn_cmd_enable __ARGS((exarg_T *eap, int syncing));
442static void syn_cmd_reset __ARGS((exarg_T *eap, int syncing));
443static void syn_cmd_manual __ARGS((exarg_T *eap, int syncing));
444static void syn_cmd_off __ARGS((exarg_T *eap, int syncing));
445static void syn_cmd_onoff __ARGS((exarg_T *eap, char *name));
446static void syn_cmd_list __ARGS((exarg_T *eap, int syncing));
447static void syn_lines_msg __ARGS((void));
448static void syn_match_msg __ARGS((void));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200449static void syn_stack_free_block __ARGS((synblock_T *block));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000450static void syn_list_one __ARGS((int id, int syncing, int link_only));
451static void syn_list_cluster __ARGS((int id));
452static void put_id_list __ARGS((char_u *name, short *list, int attr));
453static void put_pattern __ARGS((char *s, int c, synpat_T *spp, int attr));
Bram Moolenaardad6b692005-01-25 22:14:34 +0000454static int syn_list_keywords __ARGS((int id, hashtab_T *ht, int did_header, int attr));
455static void syn_clear_keyword __ARGS((int id, hashtab_T *ht));
456static void clear_keywtab __ARGS((hashtab_T *ht));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200457static void add_keyword __ARGS((char_u *name, int id, int flags, short *cont_in_list, short *next_list, int conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000458static char_u *get_group_name __ARGS((char_u *arg, char_u **name_end));
Bram Moolenaar860cae12010-06-05 23:22:07 +0200459static char_u *get_syn_options __ARGS((char_u *arg, syn_opt_arg_T *opt, int *conceal_char));
Bram Moolenaar071d4272004-06-13 20:20:40 +0000460static void syn_cmd_include __ARGS((exarg_T *eap, int syncing));
461static void syn_cmd_keyword __ARGS((exarg_T *eap, int syncing));
462static void syn_cmd_match __ARGS((exarg_T *eap, int syncing));
463static void syn_cmd_region __ARGS((exarg_T *eap, int syncing));
464#ifdef __BORLANDC__
465static int _RTLENTRYF syn_compare_stub __ARGS((const void *v1, const void *v2));
466#else
467static int syn_compare_stub __ARGS((const void *v1, const void *v2));
468#endif
469static void syn_cmd_cluster __ARGS((exarg_T *eap, int syncing));
470static int syn_scl_name2id __ARGS((char_u *name));
471static int syn_scl_namen2id __ARGS((char_u *linep, int len));
472static int syn_check_cluster __ARGS((char_u *pp, int len));
473static int syn_add_cluster __ARGS((char_u *name));
474static void init_syn_patterns __ARGS((void));
475static char_u *get_syn_pattern __ARGS((char_u *arg, synpat_T *ci));
476static void syn_cmd_sync __ARGS((exarg_T *eap, int syncing));
477static int get_id_list __ARGS((char_u **arg, int keylen, short **list));
478static void syn_combine_list __ARGS((short **clstr1, short **clstr2, int list_op));
479static void syn_incl_toplevel __ARGS((int id, int *flagsp));
480
481/*
482 * Start the syntax recognition for a line. This function is normally called
483 * from the screen updating, once for each displayed line.
484 * The buffer is remembered in syn_buf, because get_syntax_attr() doesn't get
485 * it. Careful: curbuf and curwin are likely to point to another buffer and
486 * window.
487 */
488 void
489syntax_start(wp, lnum)
490 win_T *wp;
491 linenr_T lnum;
492{
493 synstate_T *p;
494 synstate_T *last_valid = NULL;
495 synstate_T *last_min_valid = NULL;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000496 synstate_T *sp, *prev = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000497 linenr_T parsed_lnum;
498 linenr_T first_stored;
499 int dist;
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000500 static int changedtick = 0; /* remember the last change ID */
Bram Moolenaar071d4272004-06-13 20:20:40 +0000501
Bram Moolenaar7510fe72010-07-25 12:46:44 +0200502#ifdef FEAT_CONCEAL
503 current_sub_char = NUL;
504#endif
505
Bram Moolenaar071d4272004-06-13 20:20:40 +0000506 /*
507 * After switching buffers, invalidate current_state.
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000508 * Also do this when a change was made, the current state may be invalid
509 * then.
Bram Moolenaar071d4272004-06-13 20:20:40 +0000510 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200511 if (syn_block != wp->w_s || changedtick != syn_buf->b_changedtick)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000512 {
513 invalidate_current_state();
514 syn_buf = wp->w_buffer;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200515 syn_block = wp->w_s;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000516 }
Bram Moolenaar3fdfa4a2004-10-07 21:02:47 +0000517 changedtick = syn_buf->b_changedtick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000518 syn_win = wp;
519
520 /*
521 * Allocate syntax stack when needed.
522 */
523 syn_stack_alloc();
Bram Moolenaar860cae12010-06-05 23:22:07 +0200524 if (syn_block->b_sst_array == NULL)
Bram Moolenaar3b56eb32005-07-11 22:40:32 +0000525 return; /* out of memory */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200526 syn_block->b_sst_lasttick = display_tick;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000527
528 /*
529 * If the state of the end of the previous line is useful, store it.
530 */
531 if (VALID_STATE(&current_state)
532 && current_lnum < lnum
533 && current_lnum < syn_buf->b_ml.ml_line_count)
534 {
535 (void)syn_finish_line(FALSE);
536 if (!current_state_stored)
537 {
538 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +0000539 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000540 }
541
542 /*
543 * If the current_lnum is now the same as "lnum", keep the current
544 * state (this happens very often!). Otherwise invalidate
545 * current_state and figure it out below.
546 */
547 if (current_lnum != lnum)
548 invalidate_current_state();
549 }
550 else
551 invalidate_current_state();
552
553 /*
554 * Try to synchronize from a saved state in b_sst_array[].
555 * Only do this if lnum is not before and not to far beyond a saved state.
556 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200557 if (INVALID_STATE(&current_state) && syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000558 {
559 /* Find last valid saved state before start_lnum. */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200560 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000561 {
562 if (p->sst_lnum > lnum)
563 break;
564 if (p->sst_lnum <= lnum && p->sst_change_lnum == 0)
565 {
566 last_valid = p;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200567 if (p->sst_lnum >= lnum - syn_block->b_syn_sync_minlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000568 last_min_valid = p;
569 }
570 }
571 if (last_min_valid != NULL)
572 load_current_state(last_min_valid);
573 }
574
575 /*
576 * If "lnum" is before or far beyond a line with a saved state, need to
577 * re-synchronize.
578 */
579 if (INVALID_STATE(&current_state))
580 {
581 syn_sync(wp, lnum, last_valid);
Bram Moolenaard6761c32011-06-19 04:54:21 +0200582 if (current_lnum == 1)
583 /* First line is always valid, no matter "minlines". */
584 first_stored = 1;
585 else
586 /* Need to parse "minlines" lines before state can be considered
587 * valid to store. */
588 first_stored = current_lnum + syn_block->b_syn_sync_minlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000589 }
590 else
591 first_stored = current_lnum;
592
593 /*
594 * Advance from the sync point or saved state until the current line.
595 * Save some entries for syncing with later on.
596 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200597 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +0000598 dist = 999999;
599 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200600 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000601 while (current_lnum < lnum)
602 {
603 syn_start_line();
604 (void)syn_finish_line(FALSE);
605 ++current_lnum;
606
607 /* If we parsed at least "minlines" lines or started at a valid
608 * state, the current state is considered valid. */
609 if (current_lnum >= first_stored)
610 {
611 /* Check if the saved state entry is for the current line and is
612 * equal to the current state. If so, then validate all saved
613 * states that depended on a change before the parsed line. */
614 if (prev == NULL)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000615 prev = syn_stack_find_entry(current_lnum - 1);
616 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +0200617 sp = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000618 else
Bram Moolenaardbe31752008-01-13 16:40:19 +0000619 sp = prev;
620 while (sp != NULL && sp->sst_lnum < current_lnum)
621 sp = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000622 if (sp != NULL
623 && sp->sst_lnum == current_lnum
624 && syn_stack_equal(sp))
625 {
626 parsed_lnum = current_lnum;
627 prev = sp;
628 while (sp != NULL && sp->sst_change_lnum <= parsed_lnum)
629 {
630 if (sp->sst_lnum <= lnum)
631 /* valid state before desired line, use this one */
632 prev = sp;
633 else if (sp->sst_change_lnum == 0)
634 /* past saved states depending on change, break here. */
635 break;
636 sp->sst_change_lnum = 0;
637 sp = sp->sst_next;
638 }
639 load_current_state(prev);
640 }
641 /* Store the state at this line when it's the first one, the line
642 * where we start parsing, or some distance from the previously
643 * saved state. But only when parsed at least 'minlines'. */
644 else if (prev == NULL
645 || current_lnum == lnum
646 || current_lnum >= prev->sst_lnum + dist)
Bram Moolenaardbe31752008-01-13 16:40:19 +0000647 prev = store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000648 }
649
650 /* This can take a long time: break when CTRL-C pressed. The current
651 * state will be wrong then. */
652 line_breakcheck();
653 if (got_int)
654 {
655 current_lnum = lnum;
656 break;
657 }
658 }
659
660 syn_start_line();
Bram Moolenaar071d4272004-06-13 20:20:40 +0000661}
662
663/*
664 * We cannot simply discard growarrays full of state_items or buf_states; we
665 * have to manually release their extmatch pointers first.
666 */
667 static void
668clear_syn_state(p)
669 synstate_T *p;
670{
671 int i;
672 garray_T *gap;
673
674 if (p->sst_stacksize > SST_FIX_STATES)
675 {
676 gap = &(p->sst_union.sst_ga);
677 for (i = 0; i < gap->ga_len; i++)
678 unref_extmatch(SYN_STATE_P(gap)[i].bs_extmatch);
679 ga_clear(gap);
680 }
681 else
682 {
683 for (i = 0; i < p->sst_stacksize; i++)
684 unref_extmatch(p->sst_union.sst_stack[i].bs_extmatch);
685 }
686}
687
688/*
689 * Cleanup the current_state stack.
690 */
691 static void
692clear_current_state()
693{
694 int i;
695 stateitem_T *sip;
696
697 sip = (stateitem_T *)(current_state.ga_data);
698 for (i = 0; i < current_state.ga_len; i++)
699 unref_extmatch(sip[i].si_extmatch);
700 ga_clear(&current_state);
701}
702
703/*
704 * Try to find a synchronisation point for line "lnum".
705 *
706 * This sets current_lnum and the current state. One of three methods is
707 * used:
708 * 1. Search backwards for the end of a C-comment.
709 * 2. Search backwards for given sync patterns.
710 * 3. Simply start on a given number of lines above "lnum".
711 */
712 static void
713syn_sync(wp, start_lnum, last_valid)
714 win_T *wp;
715 linenr_T start_lnum;
716 synstate_T *last_valid;
717{
718 buf_T *curbuf_save;
719 win_T *curwin_save;
720 pos_T cursor_save;
721 int idx;
722 linenr_T lnum;
723 linenr_T end_lnum;
724 linenr_T break_lnum;
725 int had_sync_point;
726 stateitem_T *cur_si;
727 synpat_T *spp;
728 char_u *line;
729 int found_flags = 0;
730 int found_match_idx = 0;
731 linenr_T found_current_lnum = 0;
732 int found_current_col= 0;
733 lpos_T found_m_endpos;
Bram Moolenaar81366db2005-07-24 21:16:51 +0000734 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000735
736 /*
737 * Clear any current state that might be hanging around.
738 */
739 invalidate_current_state();
740
741 /*
742 * Start at least "minlines" back. Default starting point for parsing is
743 * there.
744 * Start further back, to avoid that scrolling backwards will result in
745 * resyncing for every line. Now it resyncs only one out of N lines,
746 * where N is minlines * 1.5, or minlines * 2 if minlines is small.
747 * Watch out for overflow when minlines is MAXLNUM.
748 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200749 if (syn_block->b_syn_sync_minlines > start_lnum)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000750 start_lnum = 1;
751 else
752 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200753 if (syn_block->b_syn_sync_minlines == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000754 lnum = 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +0200755 else if (syn_block->b_syn_sync_minlines < 10)
756 lnum = syn_block->b_syn_sync_minlines * 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000757 else
Bram Moolenaar860cae12010-06-05 23:22:07 +0200758 lnum = syn_block->b_syn_sync_minlines * 3 / 2;
759 if (syn_block->b_syn_sync_maxlines != 0
760 && lnum > syn_block->b_syn_sync_maxlines)
761 lnum = syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000762 if (lnum >= start_lnum)
763 start_lnum = 1;
764 else
765 start_lnum -= lnum;
766 }
767 current_lnum = start_lnum;
768
769 /*
770 * 1. Search backwards for the end of a C-style comment.
771 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200772 if (syn_block->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000773 {
774 /* Need to make syn_buf the current buffer for a moment, to be able to
775 * use find_start_comment(). */
776 curwin_save = curwin;
777 curwin = wp;
778 curbuf_save = curbuf;
779 curbuf = syn_buf;
780
781 /*
782 * Skip lines that end in a backslash.
783 */
784 for ( ; start_lnum > 1; --start_lnum)
785 {
786 line = ml_get(start_lnum - 1);
787 if (*line == NUL || *(line + STRLEN(line) - 1) != '\\')
788 break;
789 }
790 current_lnum = start_lnum;
791
792 /* set cursor to start of search */
793 cursor_save = wp->w_cursor;
794 wp->w_cursor.lnum = start_lnum;
795 wp->w_cursor.col = 0;
796
797 /*
798 * If the line is inside a comment, need to find the syntax item that
799 * defines the comment.
800 * Restrict the search for the end of a comment to b_syn_sync_maxlines.
801 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200802 if (find_start_comment((int)syn_block->b_syn_sync_maxlines) != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000803 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200804 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
805 if (SYN_ITEMS(syn_block)[idx].sp_syn.id
806 == syn_block->b_syn_sync_id
807 && SYN_ITEMS(syn_block)[idx].sp_type == SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000808 {
809 validate_current_state();
810 if (push_current_state(idx) == OK)
811 update_si_attr(current_state.ga_len - 1);
812 break;
813 }
814 }
815
816 /* restore cursor and buffer */
817 wp->w_cursor = cursor_save;
818 curwin = curwin_save;
819 curbuf = curbuf_save;
820 }
821
822 /*
823 * 2. Search backwards for given sync patterns.
824 */
Bram Moolenaar860cae12010-06-05 23:22:07 +0200825 else if (syn_block->b_syn_sync_flags & SF_MATCH)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000826 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200827 if (syn_block->b_syn_sync_maxlines != 0
828 && start_lnum > syn_block->b_syn_sync_maxlines)
829 break_lnum = start_lnum - syn_block->b_syn_sync_maxlines;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000830 else
831 break_lnum = 0;
832
Bram Moolenaarfd2ac762006-03-01 22:09:21 +0000833 found_m_endpos.lnum = 0;
834 found_m_endpos.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000835 end_lnum = start_lnum;
836 lnum = start_lnum;
837 while (--lnum > break_lnum)
838 {
839 /* This can take a long time: break when CTRL-C pressed. */
840 line_breakcheck();
841 if (got_int)
842 {
843 invalidate_current_state();
844 current_lnum = start_lnum;
845 break;
846 }
847
848 /* Check if we have run into a valid saved state stack now. */
849 if (last_valid != NULL && lnum == last_valid->sst_lnum)
850 {
851 load_current_state(last_valid);
852 break;
853 }
854
855 /*
856 * Check if the previous line has the line-continuation pattern.
857 */
858 if (lnum > 1 && syn_match_linecont(lnum - 1))
859 continue;
860
861 /*
862 * Start with nothing on the state stack
863 */
864 validate_current_state();
865
866 for (current_lnum = lnum; current_lnum < end_lnum; ++current_lnum)
867 {
868 syn_start_line();
869 for (;;)
870 {
871 had_sync_point = syn_finish_line(TRUE);
872 /*
873 * When a sync point has been found, remember where, and
874 * continue to look for another one, further on in the line.
875 */
876 if (had_sync_point && current_state.ga_len)
877 {
878 cur_si = &CUR_STATE(current_state.ga_len - 1);
879 if (cur_si->si_m_endpos.lnum > start_lnum)
880 {
881 /* ignore match that goes to after where started */
882 current_lnum = end_lnum;
883 break;
884 }
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000885 if (cur_si->si_idx < 0)
886 {
887 /* Cannot happen? */
888 found_flags = 0;
889 found_match_idx = KEYWORD_IDX;
890 }
891 else
892 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200893 spp = &(SYN_ITEMS(syn_block)[cur_si->si_idx]);
Bram Moolenaar3a36cf72007-08-21 15:29:56 +0000894 found_flags = spp->sp_flags;
895 found_match_idx = spp->sp_sync_idx;
896 }
Bram Moolenaar071d4272004-06-13 20:20:40 +0000897 found_current_lnum = current_lnum;
898 found_current_col = current_col;
899 found_m_endpos = cur_si->si_m_endpos;
900 /*
901 * Continue after the match (be aware of a zero-length
902 * match).
903 */
904 if (found_m_endpos.lnum > current_lnum)
905 {
906 current_lnum = found_m_endpos.lnum;
907 current_col = found_m_endpos.col;
908 if (current_lnum >= end_lnum)
909 break;
910 }
911 else if (found_m_endpos.col > current_col)
912 current_col = found_m_endpos.col;
913 else
914 ++current_col;
915
916 /* syn_current_attr() will have skipped the check for
Bram Moolenaar81366db2005-07-24 21:16:51 +0000917 * an item that ends here, need to do that now. Be
918 * careful not to go past the NUL. */
919 prev_current_col = current_col;
920 if (syn_getcurline()[current_col] != NUL)
921 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000922 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +0000923 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000924 }
925 else
926 break;
927 }
928 }
929
930 /*
931 * If a sync point was encountered, break here.
932 */
933 if (found_flags)
934 {
935 /*
936 * Put the item that was specified by the sync point on the
937 * state stack. If there was no item specified, make the
938 * state stack empty.
939 */
940 clear_current_state();
941 if (found_match_idx >= 0
942 && push_current_state(found_match_idx) == OK)
943 update_si_attr(current_state.ga_len - 1);
944
945 /*
946 * When using "grouphere", continue from the sync point
947 * match, until the end of the line. Parsing starts at
948 * the next line.
949 * For "groupthere" the parsing starts at start_lnum.
950 */
951 if (found_flags & HL_SYNC_HERE)
952 {
953 if (current_state.ga_len)
954 {
955 cur_si = &CUR_STATE(current_state.ga_len - 1);
956 cur_si->si_h_startpos.lnum = found_current_lnum;
957 cur_si->si_h_startpos.col = found_current_col;
958 update_si_end(cur_si, (int)current_col, TRUE);
959 check_keepend();
960 }
961 current_col = found_m_endpos.col;
962 current_lnum = found_m_endpos.lnum;
963 (void)syn_finish_line(FALSE);
964 ++current_lnum;
965 }
966 else
967 current_lnum = start_lnum;
968
969 break;
970 }
971
972 end_lnum = lnum;
973 invalidate_current_state();
974 }
975
976 /* Ran into start of the file or exceeded maximum number of lines */
977 if (lnum <= break_lnum)
978 {
979 invalidate_current_state();
980 current_lnum = break_lnum + 1;
981 }
982 }
983
984 validate_current_state();
985}
986
987/*
988 * Return TRUE if the line-continuation pattern matches in line "lnum".
989 */
990 static int
991syn_match_linecont(lnum)
992 linenr_T lnum;
993{
994 regmmatch_T regmatch;
995
Bram Moolenaar860cae12010-06-05 23:22:07 +0200996 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000997 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200998 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
999 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001000 return syn_regexec(&regmatch, lnum, (colnr_T)0,
1001 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaar071d4272004-06-13 20:20:40 +00001002 }
1003 return FALSE;
1004}
1005
1006/*
1007 * Prepare the current state for the start of a line.
1008 */
1009 static void
1010syn_start_line()
1011{
1012 current_finished = FALSE;
1013 current_col = 0;
1014
1015 /*
1016 * Need to update the end of a start/skip/end that continues from the
1017 * previous line and regions that have "keepend".
1018 */
1019 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001020 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001021 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001022 check_state_ends();
1023 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024
1025 next_match_idx = -1;
1026 ++current_line_id;
1027}
1028
1029/*
1030 * Check for items in the stack that need their end updated.
1031 * When "startofline" is TRUE the last item is always updated.
1032 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1033 */
1034 static void
1035syn_update_ends(startofline)
1036 int startofline;
1037{
1038 stateitem_T *cur_si;
1039 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001040 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001041
1042 if (startofline)
1043 {
1044 /* Check for a match carried over from a previous line with a
1045 * contained region. The match ends as soon as the region ends. */
1046 for (i = 0; i < current_state.ga_len; ++i)
1047 {
1048 cur_si = &CUR_STATE(i);
1049 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001050 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001051 == SPTYPE_MATCH
1052 && cur_si->si_m_endpos.lnum < current_lnum)
1053 {
1054 cur_si->si_flags |= HL_MATCHCONT;
1055 cur_si->si_m_endpos.lnum = 0;
1056 cur_si->si_m_endpos.col = 0;
1057 cur_si->si_h_endpos = cur_si->si_m_endpos;
1058 cur_si->si_ends = TRUE;
1059 }
1060 }
1061 }
1062
1063 /*
1064 * Need to update the end of a start/skip/end that continues from the
1065 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001066 * influence contained items. If we've just removed "extend"
1067 * (startofline == 0) then we should update ends of normal regions
1068 * contained inside "keepend" because "extend" could have extended
1069 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001070 * Then check for items ending in column 0.
1071 */
1072 i = current_state.ga_len - 1;
1073 if (keepend_level >= 0)
1074 for ( ; i > keepend_level; --i)
1075 if (CUR_STATE(i).si_flags & HL_EXTEND)
1076 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001077
1078 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001079 for ( ; i < current_state.ga_len; ++i)
1080 {
1081 cur_si = &CUR_STATE(i);
1082 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001083 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001084 || (i == current_state.ga_len - 1 && startofline))
1085 {
1086 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1087 cur_si->si_h_startpos.lnum = current_lnum;
1088
1089 if (!(cur_si->si_flags & HL_MATCHCONT))
1090 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001091
1092 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1093 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001094 }
1095 }
1096 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001097}
1098
1099/****************************************
1100 * Handling of the state stack cache.
1101 */
1102
1103/*
1104 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1105 *
1106 * To speed up syntax highlighting, the state stack for the start of some
1107 * lines is cached. These entries can be used to start parsing at that point.
1108 *
1109 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1110 * valid entries. b_sst_first points to the first one, then follow sst_next.
1111 * The entries are sorted on line number. The first entry is often for line 2
1112 * (line 1 always starts with an empty stack).
1113 * There is also a list for free entries. This construction is used to avoid
1114 * having to allocate and free memory blocks too often.
1115 *
1116 * When making changes to the buffer, this is logged in b_mod_*. When calling
1117 * update_screen() to update the display, it will call
1118 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1119 * entries. The entries which are inside the changed area are removed,
1120 * because they must be recomputed. Entries below the changed have their line
1121 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1122 * set to indicate that a check must be made if the changed lines would change
1123 * the cached entry.
1124 *
1125 * When later displaying lines, an entry is stored for each line. Displayed
1126 * lines are likely to be displayed again, in which case the state at the
1127 * start of the line is needed.
1128 * For not displayed lines, an entry is stored for every so many lines. These
1129 * entries will be used e.g., when scrolling backwards. The distance between
1130 * entries depends on the number of lines in the buffer. For small buffers
1131 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1132 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1133 */
1134
Bram Moolenaar860cae12010-06-05 23:22:07 +02001135 static void
1136syn_stack_free_block(block)
1137 synblock_T *block;
1138{
1139 synstate_T *p;
1140
1141 if (block->b_sst_array != NULL)
1142 {
1143 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1144 clear_syn_state(p);
1145 vim_free(block->b_sst_array);
1146 block->b_sst_array = NULL;
1147 block->b_sst_len = 0;
1148 }
1149}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001150/*
1151 * Free b_sst_array[] for buffer "buf".
1152 * Used when syntax items changed to force resyncing everywhere.
1153 */
1154 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001155syn_stack_free_all(block)
1156 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001157{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001158 win_T *wp;
1159
Bram Moolenaar860cae12010-06-05 23:22:07 +02001160 syn_stack_free_block(block);
1161
1162
Bram Moolenaar071d4272004-06-13 20:20:40 +00001163#ifdef FEAT_FOLDING
1164 /* When using "syntax" fold method, must update all folds. */
1165 FOR_ALL_WINDOWS(wp)
1166 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001167 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001168 foldUpdateAll(wp);
1169 }
1170#endif
1171}
1172
1173/*
1174 * Allocate the syntax state stack for syn_buf when needed.
1175 * If the number of entries in b_sst_array[] is much too big or a bit too
1176 * small, reallocate it.
1177 * Also used to allocate b_sst_array[] for the first time.
1178 */
1179 static void
1180syn_stack_alloc()
1181{
1182 long len;
1183 synstate_T *to, *from;
1184 synstate_T *sstp;
1185
1186 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1187 if (len < SST_MIN_ENTRIES)
1188 len = SST_MIN_ENTRIES;
1189 else if (len > SST_MAX_ENTRIES)
1190 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001191 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001192 {
1193 /* Allocate 50% too much, to avoid reallocating too often. */
1194 len = syn_buf->b_ml.ml_line_count;
1195 len = (len + len / 2) / SST_DIST + Rows * 2;
1196 if (len < SST_MIN_ENTRIES)
1197 len = SST_MIN_ENTRIES;
1198 else if (len > SST_MAX_ENTRIES)
1199 len = SST_MAX_ENTRIES;
1200
Bram Moolenaar860cae12010-06-05 23:22:07 +02001201 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001202 {
1203 /* When shrinking the array, cleanup the existing stack.
1204 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001205 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001206 && syn_stack_cleanup())
1207 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001208 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1209 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001210 }
1211
1212 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1213 if (sstp == NULL) /* out of memory! */
1214 return;
1215
1216 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001217 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001218 {
1219 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001220 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221 from = from->sst_next)
1222 {
1223 ++to;
1224 *to = *from;
1225 to->sst_next = to + 1;
1226 }
1227 }
1228 if (to != sstp - 1)
1229 {
1230 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001231 syn_block->b_sst_first = sstp;
1232 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001233 }
1234 else
1235 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001236 syn_block->b_sst_first = NULL;
1237 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001238 }
1239
1240 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001241 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001242 while (++to < sstp + len)
1243 to->sst_next = to + 1;
1244 (sstp + len - 1)->sst_next = NULL;
1245
Bram Moolenaar860cae12010-06-05 23:22:07 +02001246 vim_free(syn_block->b_sst_array);
1247 syn_block->b_sst_array = sstp;
1248 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001249 }
1250}
1251
1252/*
1253 * Check for changes in a buffer to affect stored syntax states. Uses the
1254 * b_mod_* fields.
1255 * Called from update_screen(), before screen is being updated, once for each
1256 * displayed buffer.
1257 */
1258 void
1259syn_stack_apply_changes(buf)
1260 buf_T *buf;
1261{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001262 win_T *wp;
1263
1264 syn_stack_apply_changes_block(&buf->b_s, buf);
1265
1266 FOR_ALL_WINDOWS(wp)
1267 {
1268 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1269 syn_stack_apply_changes_block(wp->w_s, buf);
1270 }
1271}
1272
1273 static void
1274syn_stack_apply_changes_block(block, buf)
1275 synblock_T *block;
1276 buf_T *buf;
1277{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001278 synstate_T *p, *prev, *np;
1279 linenr_T n;
1280
Bram Moolenaar860cae12010-06-05 23:22:07 +02001281 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001282 return;
1283
1284 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001285 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001286 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001287 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001288 {
1289 n = p->sst_lnum + buf->b_mod_xlines;
1290 if (n <= buf->b_mod_bot)
1291 {
1292 /* this state is inside the changed area, remove it */
1293 np = p->sst_next;
1294 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001295 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001296 else
1297 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001298 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299 p = np;
1300 continue;
1301 }
1302 /* This state is below the changed area. Remember the line
1303 * that needs to be parsed before this entry can be made valid
1304 * again. */
1305 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1306 {
1307 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1308 p->sst_change_lnum += buf->b_mod_xlines;
1309 else
1310 p->sst_change_lnum = buf->b_mod_top;
1311 }
1312 if (p->sst_change_lnum == 0
1313 || p->sst_change_lnum < buf->b_mod_bot)
1314 p->sst_change_lnum = buf->b_mod_bot;
1315
1316 p->sst_lnum = n;
1317 }
1318 prev = p;
1319 p = p->sst_next;
1320 }
1321}
1322
1323/*
1324 * Reduce the number of entries in the state stack for syn_buf.
1325 * Returns TRUE if at least one entry was freed.
1326 */
1327 static int
1328syn_stack_cleanup()
1329{
1330 synstate_T *p, *prev;
1331 disptick_T tick;
1332 int above;
1333 int dist;
1334 int retval = FALSE;
1335
Bram Moolenaar860cae12010-06-05 23:22:07 +02001336 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001337 return retval;
1338
1339 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001340 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001341 dist = 999999;
1342 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001343 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001344
1345 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001346 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001347 * be removed. Set "above" when the "tick" for the oldest entry is above
1348 * "b_sst_lasttick" (the display tick wraps around).
1349 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001350 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001351 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001352 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001353 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1354 {
1355 if (prev->sst_lnum + dist > p->sst_lnum)
1356 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001357 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001358 {
1359 if (!above || p->sst_tick < tick)
1360 tick = p->sst_tick;
1361 above = TRUE;
1362 }
1363 else if (!above && p->sst_tick < tick)
1364 tick = p->sst_tick;
1365 }
1366 }
1367
1368 /*
1369 * Go through the list to make the entries for the oldest tick at an
1370 * interval of several lines.
1371 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001372 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001373 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1374 {
1375 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1376 {
1377 /* Move this entry from used list to free list */
1378 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001379 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001380 p = prev;
1381 retval = TRUE;
1382 }
1383 }
1384 return retval;
1385}
1386
1387/*
1388 * Free the allocated memory for a syn_state item.
1389 * Move the entry into the free list.
1390 */
1391 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001392syn_stack_free_entry(block, p)
1393 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001394 synstate_T *p;
1395{
1396 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001397 p->sst_next = block->b_sst_firstfree;
1398 block->b_sst_firstfree = p;
1399 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001400}
1401
1402/*
1403 * Find an entry in the list of state stacks at or before "lnum".
1404 * Returns NULL when there is no entry or the first entry is after "lnum".
1405 */
1406 static synstate_T *
1407syn_stack_find_entry(lnum)
1408 linenr_T lnum;
1409{
1410 synstate_T *p, *prev;
1411
1412 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001413 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001414 {
1415 if (p->sst_lnum == lnum)
1416 return p;
1417 if (p->sst_lnum > lnum)
1418 break;
1419 }
1420 return prev;
1421}
1422
1423/*
1424 * Try saving the current state in b_sst_array[].
1425 * The current state must be valid for the start of the current_lnum line!
1426 */
1427 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001428store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001429{
1430 int i;
1431 synstate_T *p;
1432 bufstate_T *bp;
1433 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001434 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001435
1436 /*
1437 * If the current state contains a start or end pattern that continues
1438 * from the previous line, we can't use it. Don't store it then.
1439 */
1440 for (i = current_state.ga_len - 1; i >= 0; --i)
1441 {
1442 cur_si = &CUR_STATE(i);
1443 if (cur_si->si_h_startpos.lnum >= current_lnum
1444 || cur_si->si_m_endpos.lnum >= current_lnum
1445 || cur_si->si_h_endpos.lnum >= current_lnum
1446 || (cur_si->si_end_idx
1447 && cur_si->si_eoe_pos.lnum >= current_lnum))
1448 break;
1449 }
1450 if (i >= 0)
1451 {
1452 if (sp != NULL)
1453 {
1454 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001455 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001456 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001457 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001458 else
1459 {
1460 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001461 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001462 if (p->sst_next == sp)
1463 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001464 if (p != NULL) /* just in case */
1465 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001466 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001467 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001468 sp = NULL;
1469 }
1470 }
1471 else if (sp == NULL || sp->sst_lnum != current_lnum)
1472 {
1473 /*
1474 * Add a new entry
1475 */
1476 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001477 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001478 {
1479 (void)syn_stack_cleanup();
1480 /* "sp" may have been moved to the freelist now */
1481 sp = syn_stack_find_entry(current_lnum);
1482 }
1483 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001484 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001485 sp = NULL;
1486 else
1487 {
1488 /* Take the first item from the free list and put it in the used
1489 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001490 p = syn_block->b_sst_firstfree;
1491 syn_block->b_sst_firstfree = p->sst_next;
1492 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001493 if (sp == NULL)
1494 {
1495 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001496 p->sst_next = syn_block->b_sst_first;
1497 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001498 }
1499 else
1500 {
1501 /* insert in list after *sp */
1502 p->sst_next = sp->sst_next;
1503 sp->sst_next = p;
1504 }
1505 sp = p;
1506 sp->sst_stacksize = 0;
1507 sp->sst_lnum = current_lnum;
1508 }
1509 }
1510 if (sp != NULL)
1511 {
1512 /* When overwriting an existing state stack, clear it first */
1513 clear_syn_state(sp);
1514 sp->sst_stacksize = current_state.ga_len;
1515 if (current_state.ga_len > SST_FIX_STATES)
1516 {
1517 /* Need to clear it, might be something remaining from when the
1518 * length was less than SST_FIX_STATES. */
1519 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1520 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1521 sp->sst_stacksize = 0;
1522 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001523 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001524 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1525 }
1526 else
1527 bp = sp->sst_union.sst_stack;
1528 for (i = 0; i < sp->sst_stacksize; ++i)
1529 {
1530 bp[i].bs_idx = CUR_STATE(i).si_idx;
1531 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001532#ifdef FEAT_CONCEAL
1533 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1534 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1535#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001536 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1537 }
1538 sp->sst_next_flags = current_next_flags;
1539 sp->sst_next_list = current_next_list;
1540 sp->sst_tick = display_tick;
1541 sp->sst_change_lnum = 0;
1542 }
1543 current_state_stored = TRUE;
1544 return sp;
1545}
1546
1547/*
1548 * Copy a state stack from "from" in b_sst_array[] to current_state;
1549 */
1550 static void
1551load_current_state(from)
1552 synstate_T *from;
1553{
1554 int i;
1555 bufstate_T *bp;
1556
1557 clear_current_state();
1558 validate_current_state();
1559 keepend_level = -1;
1560 if (from->sst_stacksize
1561 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1562 {
1563 if (from->sst_stacksize > SST_FIX_STATES)
1564 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1565 else
1566 bp = from->sst_union.sst_stack;
1567 for (i = 0; i < from->sst_stacksize; ++i)
1568 {
1569 CUR_STATE(i).si_idx = bp[i].bs_idx;
1570 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001571#ifdef FEAT_CONCEAL
1572 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1573 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1574#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001575 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1576 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1577 keepend_level = i;
1578 CUR_STATE(i).si_ends = FALSE;
1579 CUR_STATE(i).si_m_lnum = 0;
1580 if (CUR_STATE(i).si_idx >= 0)
1581 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001582 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001583 else
1584 CUR_STATE(i).si_next_list = NULL;
1585 update_si_attr(i);
1586 }
1587 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001588 }
1589 current_next_list = from->sst_next_list;
1590 current_next_flags = from->sst_next_flags;
1591 current_lnum = from->sst_lnum;
1592}
1593
1594/*
1595 * Compare saved state stack "*sp" with the current state.
1596 * Return TRUE when they are equal.
1597 */
1598 static int
1599syn_stack_equal(sp)
1600 synstate_T *sp;
1601{
1602 int i, j;
1603 bufstate_T *bp;
1604 reg_extmatch_T *six, *bsx;
1605
1606 /* First a quick check if the stacks have the same size end nextlist. */
1607 if (sp->sst_stacksize == current_state.ga_len
1608 && sp->sst_next_list == current_next_list)
1609 {
1610 /* Need to compare all states on both stacks. */
1611 if (sp->sst_stacksize > SST_FIX_STATES)
1612 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1613 else
1614 bp = sp->sst_union.sst_stack;
1615
1616 for (i = current_state.ga_len; --i >= 0; )
1617 {
1618 /* If the item has another index the state is different. */
1619 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1620 break;
1621 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1622 {
1623 /* When the extmatch pointers are different, the strings in
1624 * them can still be the same. Check if the extmatch
1625 * references are equal. */
1626 bsx = bp[i].bs_extmatch;
1627 six = CUR_STATE(i).si_extmatch;
1628 /* If one of the extmatch pointers is NULL the states are
1629 * different. */
1630 if (bsx == NULL || six == NULL)
1631 break;
1632 for (j = 0; j < NSUBEXP; ++j)
1633 {
1634 /* Check each referenced match string. They must all be
1635 * equal. */
1636 if (bsx->matches[j] != six->matches[j])
1637 {
1638 /* If the pointer is different it can still be the
1639 * same text. Compare the strings, ignore case when
1640 * the start item has the sp_ic flag set. */
1641 if (bsx->matches[j] == NULL
1642 || six->matches[j] == NULL)
1643 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001644 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001645 ? MB_STRICMP(bsx->matches[j],
1646 six->matches[j]) != 0
1647 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1648 break;
1649 }
1650 }
1651 if (j != NSUBEXP)
1652 break;
1653 }
1654 }
1655 if (i < 0)
1656 return TRUE;
1657 }
1658 return FALSE;
1659}
1660
1661/*
1662 * We stop parsing syntax above line "lnum". If the stored state at or below
1663 * this line depended on a change before it, it now depends on the line below
1664 * the last parsed line.
1665 * The window looks like this:
1666 * line which changed
1667 * displayed line
1668 * displayed line
1669 * lnum -> line below window
1670 */
1671 void
1672syntax_end_parsing(lnum)
1673 linenr_T lnum;
1674{
1675 synstate_T *sp;
1676
1677 sp = syn_stack_find_entry(lnum);
1678 if (sp != NULL && sp->sst_lnum < lnum)
1679 sp = sp->sst_next;
1680
1681 if (sp != NULL && sp->sst_change_lnum != 0)
1682 sp->sst_change_lnum = lnum;
1683}
1684
1685/*
1686 * End of handling of the state stack.
1687 ****************************************/
1688
1689 static void
1690invalidate_current_state()
1691{
1692 clear_current_state();
1693 current_state.ga_itemsize = 0; /* mark current_state invalid */
1694 current_next_list = NULL;
1695 keepend_level = -1;
1696}
1697
1698 static void
1699validate_current_state()
1700{
1701 current_state.ga_itemsize = sizeof(stateitem_T);
1702 current_state.ga_growsize = 3;
1703}
1704
1705/*
1706 * Return TRUE if the syntax at start of lnum changed since last time.
1707 * This will only be called just after get_syntax_attr() for the previous
1708 * line, to check if the next line needs to be redrawn too.
1709 */
1710 int
1711syntax_check_changed(lnum)
1712 linenr_T lnum;
1713{
1714 int retval = TRUE;
1715 synstate_T *sp;
1716
Bram Moolenaar071d4272004-06-13 20:20:40 +00001717 /*
1718 * Check the state stack when:
1719 * - lnum is just below the previously syntaxed line.
1720 * - lnum is not before the lines with saved states.
1721 * - lnum is not past the lines with saved states.
1722 * - lnum is at or before the last changed line.
1723 */
1724 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1725 {
1726 sp = syn_stack_find_entry(lnum);
1727 if (sp != NULL && sp->sst_lnum == lnum)
1728 {
1729 /*
1730 * finish the previous line (needed when not all of the line was
1731 * drawn)
1732 */
1733 (void)syn_finish_line(FALSE);
1734
1735 /*
1736 * Compare the current state with the previously saved state of
1737 * the line.
1738 */
1739 if (syn_stack_equal(sp))
1740 retval = FALSE;
1741
1742 /*
1743 * Store the current state in b_sst_array[] for later use.
1744 */
1745 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001746 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001747 }
1748 }
1749
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750 return retval;
1751}
1752
1753/*
1754 * Finish the current line.
1755 * This doesn't return any attributes, it only gets the state at the end of
1756 * the line. It can start anywhere in the line, as long as the current state
1757 * is valid.
1758 */
1759 static int
1760syn_finish_line(syncing)
1761 int syncing; /* called for syncing */
1762{
1763 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001764 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001765
1766 if (!current_finished)
1767 {
1768 while (!current_finished)
1769 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001770 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001771 /*
1772 * When syncing, and found some item, need to check the item.
1773 */
1774 if (syncing && current_state.ga_len)
1775 {
1776 /*
1777 * Check for match with sync item.
1778 */
1779 cur_si = &CUR_STATE(current_state.ga_len - 1);
1780 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001781 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001782 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1783 return TRUE;
1784
1785 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001786 * that ends here, need to do that now. Be careful not to go
1787 * past the NUL. */
1788 prev_current_col = current_col;
1789 if (syn_getcurline()[current_col] != NUL)
1790 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001791 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001792 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001793 }
1794 ++current_col;
1795 }
1796 }
1797 return FALSE;
1798}
1799
1800/*
1801 * Return highlight attributes for next character.
1802 * Must first call syntax_start() once for the line.
1803 * "col" is normally 0 for the first use in a line, and increments by one each
1804 * time. It's allowed to skip characters and to stop before the end of the
1805 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001806 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1807 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001808 */
1809 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001810get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001812 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001813 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814{
1815 int attr = 0;
1816
Bram Moolenaar349955a2007-08-14 21:07:36 +00001817 if (can_spell != NULL)
1818 /* Default: Only do spelling when there is no @Spell cluster or when
1819 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001820 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1821 ? (syn_block->b_spell_cluster_id == 0)
1822 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001823
Bram Moolenaar071d4272004-06-13 20:20:40 +00001824 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001825 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001826 return 0;
1827
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001828 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001829 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001830 {
1831 clear_current_state();
1832#ifdef FEAT_EVAL
1833 current_id = 0;
1834 current_trans_id = 0;
1835#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001836#ifdef FEAT_CONCEAL
1837 current_flags = 0;
1838#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001839 return 0;
1840 }
1841
Bram Moolenaar071d4272004-06-13 20:20:40 +00001842 /* Make sure current_state is valid */
1843 if (INVALID_STATE(&current_state))
1844 validate_current_state();
1845
1846 /*
1847 * Skip from the current column to "col", get the attributes for "col".
1848 */
1849 while (current_col <= col)
1850 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001851 attr = syn_current_attr(FALSE, TRUE, can_spell,
1852 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001853 ++current_col;
1854 }
1855
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856 return attr;
1857}
1858
1859/*
1860 * Get syntax attributes for current_lnum, current_col.
1861 */
1862 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001863syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001864 int syncing; /* When 1: called for syncing */
1865 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001866 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001867 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001868{
1869 int syn_id;
1870 lpos_T endpos; /* was: char_u *endp; */
1871 lpos_T hl_startpos; /* was: int hl_startcol; */
1872 lpos_T hl_endpos;
1873 lpos_T eos_pos; /* end-of-start match (start region) */
1874 lpos_T eoe_pos; /* end-of-end pattern */
1875 int end_idx; /* group ID for end pattern */
1876 int idx;
1877 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001878 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001879 int startcol;
1880 int endcol;
1881 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001882 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001883 short *next_list;
1884 int found_match; /* found usable match */
1885 static int try_next_column = FALSE; /* must try in next col */
1886 int do_keywords;
1887 regmmatch_T regmatch;
1888 lpos_T pos;
1889 int lc_col;
1890 reg_extmatch_T *cur_extmatch = NULL;
1891 char_u *line; /* current line. NOTE: becomes invalid after
1892 looking for a pattern match! */
1893
1894 /* variables for zero-width matches that have a "nextgroup" argument */
1895 int keep_next_list;
1896 int zero_width_next_list = FALSE;
1897 garray_T zero_width_next_ga;
1898
1899 /*
1900 * No character, no attributes! Past end of line?
1901 * Do try matching with an empty line (could be the start of a region).
1902 */
1903 line = syn_getcurline();
1904 if (line[current_col] == NUL && current_col != 0)
1905 {
1906 /*
1907 * If we found a match after the last column, use it.
1908 */
1909 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1910 && next_match_col != MAXCOL)
1911 (void)push_next_match(NULL);
1912
1913 current_finished = TRUE;
1914 current_state_stored = FALSE;
1915 return 0;
1916 }
1917
1918 /* if the current or next character is NUL, we will finish the line now */
1919 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1920 {
1921 current_finished = TRUE;
1922 current_state_stored = FALSE;
1923 }
1924
1925 /*
1926 * When in the previous column there was a match but it could not be used
1927 * (empty match or already matched in this column) need to try again in
1928 * the next column.
1929 */
1930 if (try_next_column)
1931 {
1932 next_match_idx = -1;
1933 try_next_column = FALSE;
1934 }
1935
1936 /* Only check for keywords when not syncing and there are some. */
1937 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001938 && (syn_block->b_keywtab.ht_used > 0
1939 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001940
1941 /* Init the list of zero-width matches with a nextlist. This is used to
1942 * avoid matching the same item in the same position twice. */
1943 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1944
1945 /*
1946 * Repeat matching keywords and patterns, to find contained items at the
1947 * same column. This stops when there are no extra matches at the current
1948 * column.
1949 */
1950 do
1951 {
1952 found_match = FALSE;
1953 keep_next_list = FALSE;
1954 syn_id = 0;
1955
1956 /*
1957 * 1. Check for a current state.
1958 * Only when there is no current state, or if the current state may
1959 * contain other things, we need to check for keywords and patterns.
1960 * Always need to check for contained items if some item has the
1961 * "containedin" argument (takes extra time!).
1962 */
1963 if (current_state.ga_len)
1964 cur_si = &CUR_STATE(current_state.ga_len - 1);
1965 else
1966 cur_si = NULL;
1967
Bram Moolenaar860cae12010-06-05 23:22:07 +02001968 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001969 || cur_si->si_cont_list != NULL)
1970 {
1971 /*
1972 * 2. Check for keywords, if on a keyword char after a non-keyword
1973 * char. Don't do this when syncing.
1974 */
1975 if (do_keywords)
1976 {
1977 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001978 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001979 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001980 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001981#ifdef FEAT_MBYTE
1982 - (has_mbyte
1983 ? (*mb_head_off)(line, line + current_col - 1)
1984 : 0)
1985#endif
1986 , syn_buf)))
1987 {
1988 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001989 &endcol, &flags, &next_list, cur_si,
1990 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001991 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001992 {
1993 if (push_current_state(KEYWORD_IDX) == OK)
1994 {
1995 cur_si = &CUR_STATE(current_state.ga_len - 1);
1996 cur_si->si_m_startcol = current_col;
1997 cur_si->si_h_startpos.lnum = current_lnum;
1998 cur_si->si_h_startpos.col = 0; /* starts right away */
1999 cur_si->si_m_endpos.lnum = current_lnum;
2000 cur_si->si_m_endpos.col = endcol;
2001 cur_si->si_h_endpos.lnum = current_lnum;
2002 cur_si->si_h_endpos.col = endcol;
2003 cur_si->si_ends = TRUE;
2004 cur_si->si_end_idx = 0;
2005 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002006#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002007 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002008 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002009 if (current_state.ga_len > 1)
2010 cur_si->si_flags |=
2011 CUR_STATE(current_state.ga_len - 2).si_flags
2012 & HL_CONCEAL;
2013#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002014 cur_si->si_id = syn_id;
2015 cur_si->si_trans_id = syn_id;
2016 if (flags & HL_TRANSP)
2017 {
2018 if (current_state.ga_len < 2)
2019 {
2020 cur_si->si_attr = 0;
2021 cur_si->si_trans_id = 0;
2022 }
2023 else
2024 {
2025 cur_si->si_attr = CUR_STATE(
2026 current_state.ga_len - 2).si_attr;
2027 cur_si->si_trans_id = CUR_STATE(
2028 current_state.ga_len - 2).si_trans_id;
2029 }
2030 }
2031 else
2032 cur_si->si_attr = syn_id2attr(syn_id);
2033 cur_si->si_cont_list = NULL;
2034 cur_si->si_next_list = next_list;
2035 check_keepend();
2036 }
2037 else
2038 vim_free(next_list);
2039 }
2040 }
2041 }
2042
2043 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002044 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002045 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002046 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002047 {
2048 /*
2049 * If we didn't check for a match yet, or we are past it, check
2050 * for any match with a pattern.
2051 */
2052 if (next_match_idx < 0 || next_match_col < (int)current_col)
2053 {
2054 /*
2055 * Check all relevant patterns for a match at this
2056 * position. This is complicated, because matching with a
2057 * pattern takes quite a bit of time, thus we want to
2058 * avoid doing it when it's not needed.
2059 */
2060 next_match_idx = 0; /* no match in this line yet */
2061 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002062 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002063 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002064 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002065 if ( spp->sp_syncing == syncing
2066 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2067 && (spp->sp_type == SPTYPE_MATCH
2068 || spp->sp_type == SPTYPE_START)
2069 && (current_next_list != NULL
2070 ? in_id_list(NULL, current_next_list,
2071 &spp->sp_syn, 0)
2072 : (cur_si == NULL
2073 ? !(spp->sp_flags & HL_CONTAINED)
2074 : in_id_list(cur_si,
2075 cur_si->si_cont_list, &spp->sp_syn,
2076 spp->sp_flags & HL_CONTAINED))))
2077 {
2078 /* If we already tried matching in this line, and
2079 * there isn't a match before next_match_col, skip
2080 * this item. */
2081 if (spp->sp_line_id == current_line_id
2082 && spp->sp_startcol >= next_match_col)
2083 continue;
2084 spp->sp_line_id = current_line_id;
2085
2086 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2087 if (lc_col < 0)
2088 lc_col = 0;
2089
2090 regmatch.rmm_ic = spp->sp_ic;
2091 regmatch.regprog = spp->sp_prog;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002092 if (!syn_regexec(&regmatch,
2093 current_lnum,
2094 (colnr_T)lc_col,
2095 IF_SYN_TIME(&spp->sp_time)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002096 {
2097 /* no match in this line, try another one */
2098 spp->sp_startcol = MAXCOL;
2099 continue;
2100 }
2101
2102 /*
2103 * Compute the first column of the match.
2104 */
2105 syn_add_start_off(&pos, &regmatch,
2106 spp, SPO_MS_OFF, -1);
2107 if (pos.lnum > current_lnum)
2108 {
2109 /* must have used end of match in a next line,
2110 * we can't handle that */
2111 spp->sp_startcol = MAXCOL;
2112 continue;
2113 }
2114 startcol = pos.col;
2115
2116 /* remember the next column where this pattern
2117 * matches in the current line */
2118 spp->sp_startcol = startcol;
2119
2120 /*
2121 * If a previously found match starts at a lower
2122 * column number, don't use this one.
2123 */
2124 if (startcol >= next_match_col)
2125 continue;
2126
2127 /*
2128 * If we matched this pattern at this position
2129 * before, skip it. Must retry in the next
2130 * column, because it may match from there.
2131 */
2132 if (did_match_already(idx, &zero_width_next_ga))
2133 {
2134 try_next_column = TRUE;
2135 continue;
2136 }
2137
2138 endpos.lnum = regmatch.endpos[0].lnum;
2139 endpos.col = regmatch.endpos[0].col;
2140
2141 /* Compute the highlight start. */
2142 syn_add_start_off(&hl_startpos, &regmatch,
2143 spp, SPO_HS_OFF, -1);
2144
2145 /* Compute the region start. */
2146 /* Default is to use the end of the match. */
2147 syn_add_end_off(&eos_pos, &regmatch,
2148 spp, SPO_RS_OFF, 0);
2149
2150 /*
2151 * Grab the external submatches before they get
2152 * overwritten. Reference count doesn't change.
2153 */
2154 unref_extmatch(cur_extmatch);
2155 cur_extmatch = re_extmatch_out;
2156 re_extmatch_out = NULL;
2157
2158 flags = 0;
2159 eoe_pos.lnum = 0; /* avoid warning */
2160 eoe_pos.col = 0;
2161 end_idx = 0;
2162 hl_endpos.lnum = 0;
2163
2164 /*
2165 * For a "oneline" the end must be found in the
2166 * same line too. Search for it after the end of
2167 * the match with the start pattern. Set the
2168 * resulting end positions at the same time.
2169 */
2170 if (spp->sp_type == SPTYPE_START
2171 && (spp->sp_flags & HL_ONELINE))
2172 {
2173 lpos_T startpos;
2174
2175 startpos = endpos;
2176 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2177 &flags, &eoe_pos, &end_idx, cur_extmatch);
2178 if (endpos.lnum == 0)
2179 continue; /* not found */
2180 }
2181
2182 /*
2183 * For a "match" the size must be > 0 after the
2184 * end offset needs has been added. Except when
2185 * syncing.
2186 */
2187 else if (spp->sp_type == SPTYPE_MATCH)
2188 {
2189 syn_add_end_off(&hl_endpos, &regmatch, spp,
2190 SPO_HE_OFF, 0);
2191 syn_add_end_off(&endpos, &regmatch, spp,
2192 SPO_ME_OFF, 0);
2193 if (endpos.lnum == current_lnum
2194 && (int)endpos.col + syncing < startcol)
2195 {
2196 /*
2197 * If an empty string is matched, may need
2198 * to try matching again at next column.
2199 */
2200 if (regmatch.startpos[0].col
2201 == regmatch.endpos[0].col)
2202 try_next_column = TRUE;
2203 continue;
2204 }
2205 }
2206
2207 /*
2208 * keep the best match so far in next_match_*
2209 */
2210 /* Highlighting must start after startpos and end
2211 * before endpos. */
2212 if (hl_startpos.lnum == current_lnum
2213 && (int)hl_startpos.col < startcol)
2214 hl_startpos.col = startcol;
2215 limit_pos_zero(&hl_endpos, &endpos);
2216
2217 next_match_idx = idx;
2218 next_match_col = startcol;
2219 next_match_m_endpos = endpos;
2220 next_match_h_endpos = hl_endpos;
2221 next_match_h_startpos = hl_startpos;
2222 next_match_flags = flags;
2223 next_match_eos_pos = eos_pos;
2224 next_match_eoe_pos = eoe_pos;
2225 next_match_end_idx = end_idx;
2226 unref_extmatch(next_match_extmatch);
2227 next_match_extmatch = cur_extmatch;
2228 cur_extmatch = NULL;
2229 }
2230 }
2231 }
2232
2233 /*
2234 * If we found a match at the current column, use it.
2235 */
2236 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2237 {
2238 synpat_T *lspp;
2239
2240 /* When a zero-width item matched which has a nextgroup,
2241 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002242 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002243 if (next_match_m_endpos.lnum == current_lnum
2244 && next_match_m_endpos.col == current_col
2245 && lspp->sp_next_list != NULL)
2246 {
2247 current_next_list = lspp->sp_next_list;
2248 current_next_flags = lspp->sp_flags;
2249 keep_next_list = TRUE;
2250 zero_width_next_list = TRUE;
2251
2252 /* Add the index to a list, so that we can check
2253 * later that we don't match it again (and cause an
2254 * endless loop). */
2255 if (ga_grow(&zero_width_next_ga, 1) == OK)
2256 {
2257 ((int *)(zero_width_next_ga.ga_data))
2258 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002259 }
2260 next_match_idx = -1;
2261 }
2262 else
2263 cur_si = push_next_match(cur_si);
2264 found_match = TRUE;
2265 }
2266 }
2267 }
2268
2269 /*
2270 * Handle searching for nextgroup match.
2271 */
2272 if (current_next_list != NULL && !keep_next_list)
2273 {
2274 /*
2275 * If a nextgroup was not found, continue looking for one if:
2276 * - this is an empty line and the "skipempty" option was given
2277 * - we are on white space and the "skipwhite" option was given
2278 */
2279 if (!found_match)
2280 {
2281 line = syn_getcurline();
2282 if (((current_next_flags & HL_SKIPWHITE)
2283 && vim_iswhite(line[current_col]))
2284 || ((current_next_flags & HL_SKIPEMPTY)
2285 && *line == NUL))
2286 break;
2287 }
2288
2289 /*
2290 * If a nextgroup was found: Use it, and continue looking for
2291 * contained matches.
2292 * If a nextgroup was not found: Continue looking for a normal
2293 * match.
2294 * When did set current_next_list for a zero-width item and no
2295 * match was found don't loop (would get stuck).
2296 */
2297 current_next_list = NULL;
2298 next_match_idx = -1;
2299 if (!zero_width_next_list)
2300 found_match = TRUE;
2301 }
2302
2303 } while (found_match);
2304
2305 /*
2306 * Use attributes from the current state, if within its highlighting.
2307 * If not, use attributes from the current-but-one state, etc.
2308 */
2309 current_attr = 0;
2310#ifdef FEAT_EVAL
2311 current_id = 0;
2312 current_trans_id = 0;
2313#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002314#ifdef FEAT_CONCEAL
2315 current_flags = 0;
2316#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002317 if (cur_si != NULL)
2318 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002319#ifndef FEAT_EVAL
2320 int current_trans_id = 0;
2321#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002322 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2323 {
2324 sip = &CUR_STATE(idx);
2325 if ((current_lnum > sip->si_h_startpos.lnum
2326 || (current_lnum == sip->si_h_startpos.lnum
2327 && current_col >= sip->si_h_startpos.col))
2328 && (sip->si_h_endpos.lnum == 0
2329 || current_lnum < sip->si_h_endpos.lnum
2330 || (current_lnum == sip->si_h_endpos.lnum
2331 && current_col < sip->si_h_endpos.col)))
2332 {
2333 current_attr = sip->si_attr;
2334#ifdef FEAT_EVAL
2335 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002336#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002337 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002338#ifdef FEAT_CONCEAL
2339 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002340 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002341 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002342#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343 break;
2344 }
2345 }
2346
Bram Moolenaar217ad922005-03-20 22:37:15 +00002347 if (can_spell != NULL)
2348 {
2349 struct sp_syn sps;
2350
2351 /*
2352 * set "can_spell" to TRUE if spell checking is supposed to be
2353 * done in the current item.
2354 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002355 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002356 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002357 /* There is no @Spell cluster: Do spelling for items without
2358 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002359 if (syn_block->b_nospell_cluster_id == 0
2360 || current_trans_id == 0)
2361 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002362 else
2363 {
2364 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002365 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002366 sps.cont_in_list = NULL;
2367 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2368 }
2369 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002370 else
2371 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002372 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002373 * the @Spell cluster. But not when @NoSpell is also there.
2374 * At the toplevel only spell check when ":syn spell toplevel"
2375 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002376 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002377 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002378 else
2379 {
2380 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002381 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002382 sps.cont_in_list = NULL;
2383 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2384
Bram Moolenaar860cae12010-06-05 23:22:07 +02002385 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002386 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002387 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002388 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2389 *can_spell = FALSE;
2390 }
2391 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002392 }
2393 }
2394
2395
Bram Moolenaar071d4272004-06-13 20:20:40 +00002396 /*
2397 * Check for end of current state (and the states before it) at the
2398 * next column. Don't do this for syncing, because we would miss a
2399 * single character match.
2400 * First check if the current state ends at the current column. It
2401 * may be for an empty match and a containing item might end in the
2402 * current column.
2403 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002404 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002405 {
2406 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002407 if (current_state.ga_len > 0
2408 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002409 {
2410 ++current_col;
2411 check_state_ends();
2412 --current_col;
2413 }
2414 }
2415 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002416 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002417 /* Default: Only do spelling when there is no @Spell cluster or when
2418 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002419 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2420 ? (syn_block->b_spell_cluster_id == 0)
2421 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002422
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002423 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002424 if (current_next_list != NULL
2425 && syn_getcurline()[current_col + 1] == NUL
2426 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2427 current_next_list = NULL;
2428
2429 if (zero_width_next_ga.ga_len > 0)
2430 ga_clear(&zero_width_next_ga);
2431
2432 /* No longer need external matches. But keep next_match_extmatch. */
2433 unref_extmatch(re_extmatch_out);
2434 re_extmatch_out = NULL;
2435 unref_extmatch(cur_extmatch);
2436
2437 return current_attr;
2438}
2439
2440
2441/*
2442 * Check if we already matched pattern "idx" at the current column.
2443 */
2444 static int
2445did_match_already(idx, gap)
2446 int idx;
2447 garray_T *gap;
2448{
2449 int i;
2450
2451 for (i = current_state.ga_len; --i >= 0; )
2452 if (CUR_STATE(i).si_m_startcol == (int)current_col
2453 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2454 && CUR_STATE(i).si_idx == idx)
2455 return TRUE;
2456
2457 /* Zero-width matches with a nextgroup argument are not put on the syntax
2458 * stack, and can only be matched once anyway. */
2459 for (i = gap->ga_len; --i >= 0; )
2460 if (((int *)(gap->ga_data))[i] == idx)
2461 return TRUE;
2462
2463 return FALSE;
2464}
2465
2466/*
2467 * Push the next match onto the stack.
2468 */
2469 static stateitem_T *
2470push_next_match(cur_si)
2471 stateitem_T *cur_si;
2472{
2473 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002474#ifdef FEAT_CONCEAL
2475 int save_flags;
2476#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002477
Bram Moolenaar860cae12010-06-05 23:22:07 +02002478 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002479
2480 /*
2481 * Push the item in current_state stack;
2482 */
2483 if (push_current_state(next_match_idx) == OK)
2484 {
2485 /*
2486 * If it's a start-skip-end type that crosses lines, figure out how
2487 * much it continues in this line. Otherwise just fill in the length.
2488 */
2489 cur_si = &CUR_STATE(current_state.ga_len - 1);
2490 cur_si->si_h_startpos = next_match_h_startpos;
2491 cur_si->si_m_startcol = current_col;
2492 cur_si->si_m_lnum = current_lnum;
2493 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002494#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002495 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002496 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002497 if (current_state.ga_len > 1)
2498 cur_si->si_flags |=
2499 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2500#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002501 cur_si->si_next_list = spp->sp_next_list;
2502 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2503 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2504 {
2505 /* Try to find the end pattern in the current line */
2506 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2507 check_keepend();
2508 }
2509 else
2510 {
2511 cur_si->si_m_endpos = next_match_m_endpos;
2512 cur_si->si_h_endpos = next_match_h_endpos;
2513 cur_si->si_ends = TRUE;
2514 cur_si->si_flags |= next_match_flags;
2515 cur_si->si_eoe_pos = next_match_eoe_pos;
2516 cur_si->si_end_idx = next_match_end_idx;
2517 }
2518 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2519 keepend_level = current_state.ga_len - 1;
2520 check_keepend();
2521 update_si_attr(current_state.ga_len - 1);
2522
Bram Moolenaar860cae12010-06-05 23:22:07 +02002523#ifdef FEAT_CONCEAL
2524 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2525#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002526 /*
2527 * If the start pattern has another highlight group, push another item
2528 * on the stack for the start pattern.
2529 */
2530 if ( spp->sp_type == SPTYPE_START
2531 && spp->sp_syn_match_id != 0
2532 && push_current_state(next_match_idx) == OK)
2533 {
2534 cur_si = &CUR_STATE(current_state.ga_len - 1);
2535 cur_si->si_h_startpos = next_match_h_startpos;
2536 cur_si->si_m_startcol = current_col;
2537 cur_si->si_m_lnum = current_lnum;
2538 cur_si->si_m_endpos = next_match_eos_pos;
2539 cur_si->si_h_endpos = next_match_eos_pos;
2540 cur_si->si_ends = TRUE;
2541 cur_si->si_end_idx = 0;
2542 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002543#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002544 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002545 cur_si->si_flags |= save_flags;
2546 if (cur_si->si_flags & HL_CONCEALENDS)
2547 cur_si->si_flags |= HL_CONCEAL;
2548#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002549 cur_si->si_next_list = NULL;
2550 check_keepend();
2551 update_si_attr(current_state.ga_len - 1);
2552 }
2553 }
2554
2555 next_match_idx = -1; /* try other match next time */
2556
2557 return cur_si;
2558}
2559
2560/*
2561 * Check for end of current state (and the states before it).
2562 */
2563 static void
2564check_state_ends()
2565{
2566 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002567 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002568
2569 cur_si = &CUR_STATE(current_state.ga_len - 1);
2570 for (;;)
2571 {
2572 if (cur_si->si_ends
2573 && (cur_si->si_m_endpos.lnum < current_lnum
2574 || (cur_si->si_m_endpos.lnum == current_lnum
2575 && cur_si->si_m_endpos.col <= current_col)))
2576 {
2577 /*
2578 * If there is an end pattern group ID, highlight the end pattern
2579 * now. No need to pop the current item from the stack.
2580 * Only do this if the end pattern continues beyond the current
2581 * position.
2582 */
2583 if (cur_si->si_end_idx
2584 && (cur_si->si_eoe_pos.lnum > current_lnum
2585 || (cur_si->si_eoe_pos.lnum == current_lnum
2586 && cur_si->si_eoe_pos.col > current_col)))
2587 {
2588 cur_si->si_idx = cur_si->si_end_idx;
2589 cur_si->si_end_idx = 0;
2590 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2591 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2592 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002593#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002594 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002595 if (cur_si->si_flags & HL_CONCEALENDS)
2596 cur_si->si_flags |= HL_CONCEAL;
2597#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002598 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002599
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002600 /* nextgroup= should not match in the end pattern */
2601 current_next_list = NULL;
2602
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002603 /* what matches next may be different now, clear it */
2604 next_match_idx = 0;
2605 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002606 break;
2607 }
2608 else
2609 {
2610 /* handle next_list, unless at end of line and no "skipnl" or
2611 * "skipempty" */
2612 current_next_list = cur_si->si_next_list;
2613 current_next_flags = cur_si->si_flags;
2614 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2615 && syn_getcurline()[current_col] == NUL)
2616 current_next_list = NULL;
2617
2618 /* When the ended item has "extend", another item with
2619 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002620 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002621
2622 pop_current_state();
2623
2624 if (current_state.ga_len == 0)
2625 break;
2626
Bram Moolenaar81993f42008-01-11 20:27:45 +00002627 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002628 {
2629 syn_update_ends(FALSE);
2630 if (current_state.ga_len == 0)
2631 break;
2632 }
2633
2634 cur_si = &CUR_STATE(current_state.ga_len - 1);
2635
2636 /*
2637 * Only for a region the search for the end continues after
2638 * the end of the contained item. If the contained match
2639 * included the end-of-line, break here, the region continues.
2640 * Don't do this when:
2641 * - "keepend" is used for the contained item
2642 * - not at the end of the line (could be end="x$"me=e-1).
2643 * - "excludenl" is used (HL_HAS_EOL won't be set)
2644 */
2645 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002646 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002647 == SPTYPE_START
2648 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2649 {
2650 update_si_end(cur_si, (int)current_col, TRUE);
2651 check_keepend();
2652 if ((current_next_flags & HL_HAS_EOL)
2653 && keepend_level < 0
2654 && syn_getcurline()[current_col] == NUL)
2655 break;
2656 }
2657 }
2658 }
2659 else
2660 break;
2661 }
2662}
2663
2664/*
2665 * Update an entry in the current_state stack for a match or region. This
2666 * fills in si_attr, si_next_list and si_cont_list.
2667 */
2668 static void
2669update_si_attr(idx)
2670 int idx;
2671{
2672 stateitem_T *sip = &CUR_STATE(idx);
2673 synpat_T *spp;
2674
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002675 /* This should not happen... */
2676 if (sip->si_idx < 0)
2677 return;
2678
Bram Moolenaar860cae12010-06-05 23:22:07 +02002679 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002680 if (sip->si_flags & HL_MATCH)
2681 sip->si_id = spp->sp_syn_match_id;
2682 else
2683 sip->si_id = spp->sp_syn.id;
2684 sip->si_attr = syn_id2attr(sip->si_id);
2685 sip->si_trans_id = sip->si_id;
2686 if (sip->si_flags & HL_MATCH)
2687 sip->si_cont_list = NULL;
2688 else
2689 sip->si_cont_list = spp->sp_cont_list;
2690
2691 /*
2692 * For transparent items, take attr from outer item.
2693 * Also take cont_list, if there is none.
2694 * Don't do this for the matchgroup of a start or end pattern.
2695 */
2696 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2697 {
2698 if (idx == 0)
2699 {
2700 sip->si_attr = 0;
2701 sip->si_trans_id = 0;
2702 if (sip->si_cont_list == NULL)
2703 sip->si_cont_list = ID_LIST_ALL;
2704 }
2705 else
2706 {
2707 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2708 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002709 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2710 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002711 if (sip->si_cont_list == NULL)
2712 {
2713 sip->si_flags |= HL_TRANS_CONT;
2714 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2715 }
2716 }
2717 }
2718}
2719
2720/*
2721 * Check the current stack for patterns with "keepend" flag.
2722 * Propagate the match-end to contained items, until a "skipend" item is found.
2723 */
2724 static void
2725check_keepend()
2726{
2727 int i;
2728 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002729 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002730 stateitem_T *sip;
2731
2732 /*
2733 * This check can consume a lot of time; only do it from the level where
2734 * there really is a keepend.
2735 */
2736 if (keepend_level < 0)
2737 return;
2738
2739 /*
2740 * Find the last index of an "extend" item. "keepend" items before that
2741 * won't do anything. If there is no "extend" item "i" will be
2742 * "keepend_level" and all "keepend" items will work normally.
2743 */
2744 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2745 if (CUR_STATE(i).si_flags & HL_EXTEND)
2746 break;
2747
2748 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002749 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002750 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002751 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002752 for ( ; i < current_state.ga_len; ++i)
2753 {
2754 sip = &CUR_STATE(i);
2755 if (maxpos.lnum != 0)
2756 {
2757 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002758 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002759 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2760 sip->si_ends = TRUE;
2761 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002762 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2763 {
2764 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002765 || maxpos.lnum > sip->si_m_endpos.lnum
2766 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002767 && maxpos.col > sip->si_m_endpos.col))
2768 maxpos = sip->si_m_endpos;
2769 if (maxpos_h.lnum == 0
2770 || maxpos_h.lnum > sip->si_h_endpos.lnum
2771 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2772 && maxpos_h.col > sip->si_h_endpos.col))
2773 maxpos_h = sip->si_h_endpos;
2774 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002775 }
2776}
2777
2778/*
2779 * Update an entry in the current_state stack for a start-skip-end pattern.
2780 * This finds the end of the current item, if it's in the current line.
2781 *
2782 * Return the flags for the matched END.
2783 */
2784 static void
2785update_si_end(sip, startcol, force)
2786 stateitem_T *sip;
2787 int startcol; /* where to start searching for the end */
2788 int force; /* when TRUE overrule a previous end */
2789{
2790 lpos_T startpos;
2791 lpos_T endpos;
2792 lpos_T hl_endpos;
2793 lpos_T end_endpos;
2794 int end_idx;
2795
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002796 /* return quickly for a keyword */
2797 if (sip->si_idx < 0)
2798 return;
2799
Bram Moolenaar071d4272004-06-13 20:20:40 +00002800 /* Don't update when it's already done. Can be a match of an end pattern
2801 * that started in a previous line. Watch out: can also be a "keepend"
2802 * from a containing item. */
2803 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2804 return;
2805
2806 /*
2807 * We need to find the end of the region. It may continue in the next
2808 * line.
2809 */
2810 end_idx = 0;
2811 startpos.lnum = current_lnum;
2812 startpos.col = startcol;
2813 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2814 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2815
2816 if (endpos.lnum == 0)
2817 {
2818 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002819 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002820 {
2821 /* a "oneline" never continues in the next line */
2822 sip->si_ends = TRUE;
2823 sip->si_m_endpos.lnum = current_lnum;
2824 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2825 }
2826 else
2827 {
2828 /* continues in the next line */
2829 sip->si_ends = FALSE;
2830 sip->si_m_endpos.lnum = 0;
2831 }
2832 sip->si_h_endpos = sip->si_m_endpos;
2833 }
2834 else
2835 {
2836 /* match within this line */
2837 sip->si_m_endpos = endpos;
2838 sip->si_h_endpos = hl_endpos;
2839 sip->si_eoe_pos = end_endpos;
2840 sip->si_ends = TRUE;
2841 sip->si_end_idx = end_idx;
2842 }
2843}
2844
2845/*
2846 * Add a new state to the current state stack.
2847 * It is cleared and the index set to "idx".
2848 * Return FAIL if it's not possible (out of memory).
2849 */
2850 static int
2851push_current_state(idx)
2852 int idx;
2853{
2854 if (ga_grow(&current_state, 1) == FAIL)
2855 return FAIL;
2856 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2857 CUR_STATE(current_state.ga_len).si_idx = idx;
2858 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002859 return OK;
2860}
2861
2862/*
2863 * Remove a state from the current_state stack.
2864 */
2865 static void
2866pop_current_state()
2867{
2868 if (current_state.ga_len)
2869 {
2870 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2871 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002872 }
2873 /* after the end of a pattern, try matching a keyword or pattern */
2874 next_match_idx = -1;
2875
2876 /* if first state with "keepend" is popped, reset keepend_level */
2877 if (keepend_level >= current_state.ga_len)
2878 keepend_level = -1;
2879}
2880
2881/*
2882 * Find the end of a start/skip/end syntax region after "startpos".
2883 * Only checks one line.
2884 * Also handles a match item that continued from a previous line.
2885 * If not found, the syntax item continues in the next line. m_endpos->lnum
2886 * will be 0.
2887 * If found, the end of the region and the end of the highlighting is
2888 * computed.
2889 */
2890 static void
2891find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2892 end_idx, start_ext)
2893 int idx; /* index of the pattern */
2894 lpos_T *startpos; /* where to start looking for an END match */
2895 lpos_T *m_endpos; /* return: end of match */
2896 lpos_T *hl_endpos; /* return: end of highlighting */
2897 long *flagsp; /* return: flags of matching END */
2898 lpos_T *end_endpos; /* return: end of end pattern match */
2899 int *end_idx; /* return: group ID for end pat. match, or 0 */
2900 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2901{
2902 colnr_T matchcol;
2903 synpat_T *spp, *spp_skip;
2904 int start_idx;
2905 int best_idx;
2906 regmmatch_T regmatch;
2907 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2908 lpos_T pos;
2909 char_u *line;
2910 int had_match = FALSE;
2911
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002912 /* just in case we are invoked for a keyword */
2913 if (idx < 0)
2914 return;
2915
Bram Moolenaar071d4272004-06-13 20:20:40 +00002916 /*
2917 * Check for being called with a START pattern.
2918 * Can happen with a match that continues to the next line, because it
2919 * contained a region.
2920 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002921 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002922 if (spp->sp_type != SPTYPE_START)
2923 {
2924 *hl_endpos = *startpos;
2925 return;
2926 }
2927
2928 /*
2929 * Find the SKIP or first END pattern after the last START pattern.
2930 */
2931 for (;;)
2932 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002933 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002934 if (spp->sp_type != SPTYPE_START)
2935 break;
2936 ++idx;
2937 }
2938
2939 /*
2940 * Lookup the SKIP pattern (if present)
2941 */
2942 if (spp->sp_type == SPTYPE_SKIP)
2943 {
2944 spp_skip = spp;
2945 ++idx;
2946 }
2947 else
2948 spp_skip = NULL;
2949
2950 /* Setup external matches for syn_regexec(). */
2951 unref_extmatch(re_extmatch_in);
2952 re_extmatch_in = ref_extmatch(start_ext);
2953
2954 matchcol = startpos->col; /* start looking for a match at sstart */
2955 start_idx = idx; /* remember the first END pattern. */
2956 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2957 for (;;)
2958 {
2959 /*
2960 * Find end pattern that matches first after "matchcol".
2961 */
2962 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002963 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002964 {
2965 int lc_col = matchcol;
2966
Bram Moolenaar860cae12010-06-05 23:22:07 +02002967 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002968 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2969 break;
2970 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2971 if (lc_col < 0)
2972 lc_col = 0;
2973
2974 regmatch.rmm_ic = spp->sp_ic;
2975 regmatch.regprog = spp->sp_prog;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002976 if (syn_regexec(&regmatch, startpos->lnum, lc_col,
2977 IF_SYN_TIME(&spp->sp_time)))
Bram Moolenaar071d4272004-06-13 20:20:40 +00002978 {
2979 if (best_idx == -1 || regmatch.startpos[0].col
2980 < best_regmatch.startpos[0].col)
2981 {
2982 best_idx = idx;
2983 best_regmatch.startpos[0] = regmatch.startpos[0];
2984 best_regmatch.endpos[0] = regmatch.endpos[0];
2985 }
2986 }
2987 }
2988
2989 /*
2990 * If all end patterns have been tried, and there is no match, the
2991 * item continues until end-of-line.
2992 */
2993 if (best_idx == -1)
2994 break;
2995
2996 /*
2997 * If the skip pattern matches before the end pattern,
2998 * continue searching after the skip pattern.
2999 */
3000 if (spp_skip != NULL)
3001 {
3002 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
3003
3004 if (lc_col < 0)
3005 lc_col = 0;
3006 regmatch.rmm_ic = spp_skip->sp_ic;
3007 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003008 if (syn_regexec(&regmatch, startpos->lnum, lc_col,
3009 IF_SYN_TIME(&spp_skip->sp_time))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003010 && regmatch.startpos[0].col
3011 <= best_regmatch.startpos[0].col)
3012 {
3013 /* Add offset to skip pattern match */
3014 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3015
3016 /* If the skip pattern goes on to the next line, there is no
3017 * match with an end pattern in this line. */
3018 if (pos.lnum > startpos->lnum)
3019 break;
3020
3021 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
3022
3023 /* take care of an empty match or negative offset */
3024 if (pos.col <= matchcol)
3025 ++matchcol;
3026 else if (pos.col <= regmatch.endpos[0].col)
3027 matchcol = pos.col;
3028 else
3029 /* Be careful not to jump over the NUL at the end-of-line */
3030 for (matchcol = regmatch.endpos[0].col;
3031 line[matchcol] != NUL && matchcol < pos.col;
3032 ++matchcol)
3033 ;
3034
3035 /* if the skip pattern includes end-of-line, break here */
3036 if (line[matchcol] == NUL)
3037 break;
3038
3039 continue; /* start with first end pattern again */
3040 }
3041 }
3042
3043 /*
3044 * Match from start pattern to end pattern.
3045 * Correct for match and highlight offset of end pattern.
3046 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003047 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003048 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3049 /* can't end before the start */
3050 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3051 m_endpos->col = startpos->col;
3052
3053 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3054 /* can't end before the start */
3055 if (end_endpos->lnum == startpos->lnum
3056 && end_endpos->col < startpos->col)
3057 end_endpos->col = startpos->col;
3058 /* can't end after the match */
3059 limit_pos(end_endpos, m_endpos);
3060
3061 /*
3062 * If the end group is highlighted differently, adjust the pointers.
3063 */
3064 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3065 {
3066 *end_idx = best_idx;
3067 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3068 {
3069 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3070 hl_endpos->col = best_regmatch.endpos[0].col;
3071 }
3072 else
3073 {
3074 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3075 hl_endpos->col = best_regmatch.startpos[0].col;
3076 }
3077 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3078
3079 /* can't end before the start */
3080 if (hl_endpos->lnum == startpos->lnum
3081 && hl_endpos->col < startpos->col)
3082 hl_endpos->col = startpos->col;
3083 limit_pos(hl_endpos, m_endpos);
3084
3085 /* now the match ends where the highlighting ends, it is turned
3086 * into the matchgroup for the end */
3087 *m_endpos = *hl_endpos;
3088 }
3089 else
3090 {
3091 *end_idx = 0;
3092 *hl_endpos = *end_endpos;
3093 }
3094
3095 *flagsp = spp->sp_flags;
3096
3097 had_match = TRUE;
3098 break;
3099 }
3100
3101 /* no match for an END pattern in this line */
3102 if (!had_match)
3103 m_endpos->lnum = 0;
3104
3105 /* Remove external matches. */
3106 unref_extmatch(re_extmatch_in);
3107 re_extmatch_in = NULL;
3108}
3109
3110/*
3111 * Limit "pos" not to be after "limit".
3112 */
3113 static void
3114limit_pos(pos, limit)
3115 lpos_T *pos;
3116 lpos_T *limit;
3117{
3118 if (pos->lnum > limit->lnum)
3119 *pos = *limit;
3120 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3121 pos->col = limit->col;
3122}
3123
3124/*
3125 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3126 */
3127 static void
3128limit_pos_zero(pos, limit)
3129 lpos_T *pos;
3130 lpos_T *limit;
3131{
3132 if (pos->lnum == 0)
3133 *pos = *limit;
3134 else
3135 limit_pos(pos, limit);
3136}
3137
3138/*
3139 * Add offset to matched text for end of match or highlight.
3140 */
3141 static void
3142syn_add_end_off(result, regmatch, spp, idx, extra)
3143 lpos_T *result; /* returned position */
3144 regmmatch_T *regmatch; /* start/end of match */
3145 synpat_T *spp; /* matched pattern */
3146 int idx; /* index of offset */
3147 int extra; /* extra chars for offset to start */
3148{
3149 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003150 int off;
3151 char_u *base;
3152 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003153
3154 if (spp->sp_off_flags & (1 << idx))
3155 {
3156 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003157 col = regmatch->startpos[0].col;
3158 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003159 }
3160 else
3161 {
3162 result->lnum = regmatch->endpos[0].lnum;
3163 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003164 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003165 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003166 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3167 * is a matchgroup. Watch out for match with last NL in the buffer. */
3168 if (result->lnum > syn_buf->b_ml.ml_line_count)
3169 col = 0;
3170 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003171 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003172 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3173 p = base + col;
3174 if (off > 0)
3175 {
3176 while (off-- > 0 && *p != NUL)
3177 mb_ptr_adv(p);
3178 }
3179 else if (off < 0)
3180 {
3181 while (off++ < 0 && base < p)
3182 mb_ptr_back(base, p);
3183 }
3184 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003185 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003186 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003187}
3188
3189/*
3190 * Add offset to matched text for start of match or highlight.
3191 * Avoid resulting column to become negative.
3192 */
3193 static void
3194syn_add_start_off(result, regmatch, spp, idx, extra)
3195 lpos_T *result; /* returned position */
3196 regmmatch_T *regmatch; /* start/end of match */
3197 synpat_T *spp;
3198 int idx;
3199 int extra; /* extra chars for offset to end */
3200{
3201 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003202 int off;
3203 char_u *base;
3204 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003205
3206 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3207 {
3208 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003209 col = regmatch->endpos[0].col;
3210 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003211 }
3212 else
3213 {
3214 result->lnum = regmatch->startpos[0].lnum;
3215 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003216 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003217 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003218 if (result->lnum > syn_buf->b_ml.ml_line_count)
3219 {
3220 /* a "\n" at the end of the pattern may take us below the last line */
3221 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003222 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003223 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003224 if (off != 0)
3225 {
3226 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3227 p = base + col;
3228 if (off > 0)
3229 {
3230 while (off-- && *p != NUL)
3231 mb_ptr_adv(p);
3232 }
3233 else if (off < 0)
3234 {
3235 while (off++ && base < p)
3236 mb_ptr_back(base, p);
3237 }
3238 col = (int)(p - base);
3239 }
3240 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003241}
3242
3243/*
3244 * Get current line in syntax buffer.
3245 */
3246 static char_u *
3247syn_getcurline()
3248{
3249 return ml_get_buf(syn_buf, current_lnum, FALSE);
3250}
3251
3252/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003253 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003254 * Returns TRUE when there is a match.
3255 */
3256 static int
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003257syn_regexec(rmp, lnum, col, st)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003258 regmmatch_T *rmp;
3259 linenr_T lnum;
3260 colnr_T col;
Bram Moolenaar4e312962013-06-06 21:19:51 +02003261 syn_time_T *st UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003262{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003263 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003264#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003265 proftime_T pt;
3266
3267 if (syn_time_on)
3268 profile_start(&pt);
3269#endif
3270
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003271 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003272 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3273
Bram Moolenaarf7512552013-06-06 14:55:19 +02003274#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003275 if (syn_time_on)
3276 {
3277 profile_end(&pt);
3278 profile_add(&st->total, &pt);
3279 if (profile_cmp(&pt, &st->slowest) < 0)
3280 st->slowest = pt;
3281 ++st->count;
3282 if (r > 0)
3283 ++st->match;
3284 }
3285#endif
3286
3287 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003288 {
3289 rmp->startpos[0].lnum += lnum;
3290 rmp->endpos[0].lnum += lnum;
3291 return TRUE;
3292 }
3293 return FALSE;
3294}
3295
3296/*
3297 * Check one position in a line for a matching keyword.
3298 * The caller must check if a keyword can start at startcol.
3299 * Return it's ID if found, 0 otherwise.
3300 */
3301 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003302check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003303 char_u *line;
3304 int startcol; /* position in line to check for keyword */
3305 int *endcolp; /* return: character after found keyword */
3306 long *flagsp; /* return: flags of matching keyword */
3307 short **next_listp; /* return: next_list of matching keyword */
3308 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003309 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003310{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003311 keyentry_T *kp;
3312 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003313 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003314 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003315 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003316 hashtab_T *ht;
3317 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003318
3319 /* Find first character after the keyword. First character was already
3320 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003321 kwp = line + startcol;
3322 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003323 do
3324 {
3325#ifdef FEAT_MBYTE
3326 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003327 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328 else
3329#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003330 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003331 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003332 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003333
Bram Moolenaardad6b692005-01-25 22:14:34 +00003334 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335 return 0;
3336
3337 /*
3338 * Must make a copy of the keyword, so we can add a NUL and make it
3339 * lowercase.
3340 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003341 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003342
3343 /*
3344 * Try twice:
3345 * 1. matching case
3346 * 2. ignoring case
3347 */
3348 for (round = 1; round <= 2; ++round)
3349 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003350 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003351 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003352 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003353 if (round == 2) /* ignore case */
3354 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003355
3356 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003357 * Find keywords that match. There can be several with different
3358 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003359 * When current_next_list is non-zero accept only that group, otherwise:
3360 * Accept a not-contained keyword at toplevel.
3361 * Accept a keyword at other levels only if it is in the contains list.
3362 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003363 hi = hash_find(ht, keyword);
3364 if (!HASHITEM_EMPTY(hi))
3365 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003366 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003367 if (current_next_list != 0
3368 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3369 : (cur_si == NULL
3370 ? !(kp->flags & HL_CONTAINED)
3371 : in_id_list(cur_si, cur_si->si_cont_list,
3372 &kp->k_syn, kp->flags & HL_CONTAINED)))
3373 {
3374 *endcolp = startcol + kwlen;
3375 *flagsp = kp->flags;
3376 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003377#ifdef FEAT_CONCEAL
3378 *ccharp = kp->k_char;
3379#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003380 return kp->k_syn.id;
3381 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003382 }
3383 }
3384 return 0;
3385}
3386
3387/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003388 * Handle ":syntax conceal" command.
3389 */
3390 static void
3391syn_cmd_conceal(eap, syncing)
3392 exarg_T *eap UNUSED;
3393 int syncing UNUSED;
3394{
3395#ifdef FEAT_CONCEAL
3396 char_u *arg = eap->arg;
3397 char_u *next;
3398
3399 eap->nextcmd = find_nextcmd(arg);
3400 if (eap->skip)
3401 return;
3402
3403 next = skiptowhite(arg);
3404 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3405 curwin->w_s->b_syn_conceal = TRUE;
3406 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3407 curwin->w_s->b_syn_conceal = FALSE;
3408 else
3409 EMSG2(_("E390: Illegal argument: %s"), arg);
3410#endif
3411}
3412
3413/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003414 * Handle ":syntax case" command.
3415 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003416 static void
3417syn_cmd_case(eap, syncing)
3418 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003419 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003420{
3421 char_u *arg = eap->arg;
3422 char_u *next;
3423
3424 eap->nextcmd = find_nextcmd(arg);
3425 if (eap->skip)
3426 return;
3427
3428 next = skiptowhite(arg);
3429 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003430 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003431 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003432 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003433 else
3434 EMSG2(_("E390: Illegal argument: %s"), arg);
3435}
3436
3437/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003438 * Handle ":syntax spell" command.
3439 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003440 static void
3441syn_cmd_spell(eap, syncing)
3442 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003443 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003444{
3445 char_u *arg = eap->arg;
3446 char_u *next;
3447
3448 eap->nextcmd = find_nextcmd(arg);
3449 if (eap->skip)
3450 return;
3451
3452 next = skiptowhite(arg);
3453 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003454 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003455 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003456 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003457 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003458 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003459 else
3460 EMSG2(_("E390: Illegal argument: %s"), arg);
3461}
3462
3463/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003464 * Clear all syntax info for one buffer.
3465 */
3466 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003467syntax_clear(block)
3468 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003469{
3470 int i;
3471
Bram Moolenaar860cae12010-06-05 23:22:07 +02003472 block->b_syn_error = FALSE; /* clear previous error */
3473 block->b_syn_ic = FALSE; /* Use case, by default */
3474 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3475 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003476
3477 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003478 clear_keywtab(&block->b_keywtab);
3479 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003480
3481 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003482 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3483 syn_clear_pattern(block, i);
3484 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003485
3486 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003487 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3488 syn_clear_cluster(block, i);
3489 ga_clear(&block->b_syn_clusters);
3490 block->b_spell_cluster_id = 0;
3491 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003492
Bram Moolenaar860cae12010-06-05 23:22:07 +02003493 block->b_syn_sync_flags = 0;
3494 block->b_syn_sync_minlines = 0;
3495 block->b_syn_sync_maxlines = 0;
3496 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497
Bram Moolenaar473de612013-06-08 18:19:48 +02003498 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003499 block->b_syn_linecont_prog = NULL;
3500 vim_free(block->b_syn_linecont_pat);
3501 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003502#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003503 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003504#endif
3505
3506 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003507 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003508 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003509
3510 /* Reset the counter for ":syn include" */
3511 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003512}
3513
3514/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003515 * Get rid of ownsyntax for window "wp".
3516 */
3517 void
3518reset_synblock(wp)
3519 win_T *wp;
3520{
3521 if (wp->w_s != &wp->w_buffer->b_s)
3522 {
3523 syntax_clear(wp->w_s);
3524 vim_free(wp->w_s);
3525 wp->w_s = &wp->w_buffer->b_s;
3526 }
3527}
3528
3529/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530 * Clear syncing info for one buffer.
3531 */
3532 static void
3533syntax_sync_clear()
3534{
3535 int i;
3536
3537 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003538 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3539 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3540 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003541
Bram Moolenaar860cae12010-06-05 23:22:07 +02003542 curwin->w_s->b_syn_sync_flags = 0;
3543 curwin->w_s->b_syn_sync_minlines = 0;
3544 curwin->w_s->b_syn_sync_maxlines = 0;
3545 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003546
Bram Moolenaar473de612013-06-08 18:19:48 +02003547 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003548 curwin->w_s->b_syn_linecont_prog = NULL;
3549 vim_free(curwin->w_s->b_syn_linecont_pat);
3550 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551
Bram Moolenaar860cae12010-06-05 23:22:07 +02003552 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003553}
3554
3555/*
3556 * Remove one pattern from the buffer's pattern list.
3557 */
3558 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003559syn_remove_pattern(block, idx)
3560 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003561 int idx;
3562{
3563 synpat_T *spp;
3564
Bram Moolenaar860cae12010-06-05 23:22:07 +02003565 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003566#ifdef FEAT_FOLDING
3567 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003568 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003569#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003570 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003571 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003572 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3573 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574}
3575
3576/*
3577 * Clear and free one syntax pattern. When clearing all, must be called from
3578 * last to first!
3579 */
3580 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003581syn_clear_pattern(block, i)
3582 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003583 int i;
3584{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003585 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003586 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003588 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003589 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003590 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3591 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3592 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003593 }
3594}
3595
3596/*
3597 * Clear and free one syntax cluster.
3598 */
3599 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003600syn_clear_cluster(block, i)
3601 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003602 int i;
3603{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003604 vim_free(SYN_CLSTR(block)[i].scl_name);
3605 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3606 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003607}
3608
3609/*
3610 * Handle ":syntax clear" command.
3611 */
3612 static void
3613syn_cmd_clear(eap, syncing)
3614 exarg_T *eap;
3615 int syncing;
3616{
3617 char_u *arg = eap->arg;
3618 char_u *arg_end;
3619 int id;
3620
3621 eap->nextcmd = find_nextcmd(arg);
3622 if (eap->skip)
3623 return;
3624
3625 /*
3626 * We have to disable this within ":syn include @group filename",
3627 * because otherwise @group would get deleted.
3628 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3629 * clear".
3630 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003631 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003632 return;
3633
3634 if (ends_excmd(*arg))
3635 {
3636 /*
3637 * No argument: Clear all syntax items.
3638 */
3639 if (syncing)
3640 syntax_sync_clear();
3641 else
3642 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003643 syntax_clear(curwin->w_s);
3644 if (curwin->w_s == &curwin->w_buffer->b_s)
3645 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003646 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003647 }
3648 }
3649 else
3650 {
3651 /*
3652 * Clear the group IDs that are in the argument.
3653 */
3654 while (!ends_excmd(*arg))
3655 {
3656 arg_end = skiptowhite(arg);
3657 if (*arg == '@')
3658 {
3659 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3660 if (id == 0)
3661 {
3662 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3663 break;
3664 }
3665 else
3666 {
3667 /*
3668 * We can't physically delete a cluster without changing
3669 * the IDs of other clusters, so we do the next best thing
3670 * and make it empty.
3671 */
3672 short scl_id = id - SYNID_CLUSTER;
3673
Bram Moolenaar860cae12010-06-05 23:22:07 +02003674 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3675 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003676 }
3677 }
3678 else
3679 {
3680 id = syn_namen2id(arg, (int)(arg_end - arg));
3681 if (id == 0)
3682 {
3683 EMSG2(_(e_nogroup), arg);
3684 break;
3685 }
3686 else
3687 syn_clear_one(id, syncing);
3688 }
3689 arg = skipwhite(arg_end);
3690 }
3691 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003692 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003693 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003694}
3695
3696/*
3697 * Clear one syntax group for the current buffer.
3698 */
3699 static void
3700syn_clear_one(id, syncing)
3701 int id;
3702 int syncing;
3703{
3704 synpat_T *spp;
3705 int idx;
3706
3707 /* Clear keywords only when not ":syn sync clear group-name" */
3708 if (!syncing)
3709 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003710 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3711 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712 }
3713
3714 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003715 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003716 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003717 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003718 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3719 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003720 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003721 }
3722}
3723
3724/*
3725 * Handle ":syntax on" command.
3726 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003727 static void
3728syn_cmd_on(eap, syncing)
3729 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003730 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003731{
3732 syn_cmd_onoff(eap, "syntax");
3733}
3734
3735/*
3736 * Handle ":syntax enable" command.
3737 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003738 static void
3739syn_cmd_enable(eap, syncing)
3740 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003741 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003742{
3743 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3744 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003745 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003746}
3747
3748/*
3749 * Handle ":syntax reset" command.
3750 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003751 static void
3752syn_cmd_reset(eap, syncing)
3753 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003754 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003755{
3756 eap->nextcmd = check_nextcmd(eap->arg);
3757 if (!eap->skip)
3758 {
3759 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3760 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003761 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003762 }
3763}
3764
3765/*
3766 * Handle ":syntax manual" command.
3767 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003768 static void
3769syn_cmd_manual(eap, syncing)
3770 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003771 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772{
3773 syn_cmd_onoff(eap, "manual");
3774}
3775
3776/*
3777 * Handle ":syntax off" command.
3778 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003779 static void
3780syn_cmd_off(eap, syncing)
3781 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003782 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003783{
3784 syn_cmd_onoff(eap, "nosyntax");
3785}
3786
3787 static void
3788syn_cmd_onoff(eap, name)
3789 exarg_T *eap;
3790 char *name;
3791{
3792 char_u buf[100];
3793
3794 eap->nextcmd = check_nextcmd(eap->arg);
3795 if (!eap->skip)
3796 {
3797 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003798 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003799 do_cmdline_cmd(buf);
3800 }
3801}
3802
3803/*
3804 * Handle ":syntax [list]" command: list current syntax words.
3805 */
3806 static void
3807syn_cmd_list(eap, syncing)
3808 exarg_T *eap;
3809 int syncing; /* when TRUE: list syncing items */
3810{
3811 char_u *arg = eap->arg;
3812 int id;
3813 char_u *arg_end;
3814
3815 eap->nextcmd = find_nextcmd(arg);
3816 if (eap->skip)
3817 return;
3818
Bram Moolenaar860cae12010-06-05 23:22:07 +02003819 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003821 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003822 return;
3823 }
3824
3825 if (syncing)
3826 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003827 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003828 {
3829 MSG_PUTS(_("syncing on C-style comments"));
3830 syn_lines_msg();
3831 syn_match_msg();
3832 return;
3833 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003834 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003835 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003836 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003837 MSG_PUTS(_("no syncing"));
3838 else
3839 {
3840 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003841 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003842 MSG_PUTS(_(" lines before top line"));
3843 syn_match_msg();
3844 }
3845 return;
3846 }
3847 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003848 if (curwin->w_s->b_syn_sync_minlines > 0
3849 || curwin->w_s->b_syn_sync_maxlines > 0
3850 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003851 {
3852 MSG_PUTS(_("\nsyncing on items"));
3853 syn_lines_msg();
3854 syn_match_msg();
3855 }
3856 }
3857 else
3858 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3859 if (ends_excmd(*arg))
3860 {
3861 /*
3862 * No argument: List all group IDs and all syntax clusters.
3863 */
3864 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3865 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003866 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003867 syn_list_cluster(id);
3868 }
3869 else
3870 {
3871 /*
3872 * List the group IDs and syntax clusters that are in the argument.
3873 */
3874 while (!ends_excmd(*arg) && !got_int)
3875 {
3876 arg_end = skiptowhite(arg);
3877 if (*arg == '@')
3878 {
3879 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3880 if (id == 0)
3881 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3882 else
3883 syn_list_cluster(id - SYNID_CLUSTER);
3884 }
3885 else
3886 {
3887 id = syn_namen2id(arg, (int)(arg_end - arg));
3888 if (id == 0)
3889 EMSG2(_(e_nogroup), arg);
3890 else
3891 syn_list_one(id, syncing, TRUE);
3892 }
3893 arg = skipwhite(arg_end);
3894 }
3895 }
3896 eap->nextcmd = check_nextcmd(arg);
3897}
3898
3899 static void
3900syn_lines_msg()
3901{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003902 if (curwin->w_s->b_syn_sync_maxlines > 0
3903 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003904 {
3905 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003906 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003907 {
3908 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003909 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3910 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003911 MSG_PUTS(", ");
3912 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003913 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003914 {
3915 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003916 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003917 }
3918 MSG_PUTS(_(" lines before top line"));
3919 }
3920}
3921
3922 static void
3923syn_match_msg()
3924{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003925 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003926 {
3927 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003928 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003929 MSG_PUTS(_(" line breaks"));
3930 }
3931}
3932
3933static int last_matchgroup;
3934
3935struct name_list
3936{
3937 int flag;
3938 char *name;
3939};
3940
3941static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3942
3943/*
3944 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3945 */
3946 static void
3947syn_list_one(id, syncing, link_only)
3948 int id;
3949 int syncing; /* when TRUE: list syncing items */
3950 int link_only; /* when TRUE; list link-only too */
3951{
3952 int attr;
3953 int idx;
3954 int did_header = FALSE;
3955 synpat_T *spp;
3956 static struct name_list namelist1[] =
3957 {
3958 {HL_DISPLAY, "display"},
3959 {HL_CONTAINED, "contained"},
3960 {HL_ONELINE, "oneline"},
3961 {HL_KEEPEND, "keepend"},
3962 {HL_EXTEND, "extend"},
3963 {HL_EXCLUDENL, "excludenl"},
3964 {HL_TRANSP, "transparent"},
3965 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003966#ifdef FEAT_CONCEAL
3967 {HL_CONCEAL, "conceal"},
3968 {HL_CONCEALENDS, "concealends"},
3969#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003970 {0, NULL}
3971 };
3972 static struct name_list namelist2[] =
3973 {
3974 {HL_SKIPWHITE, "skipwhite"},
3975 {HL_SKIPNL, "skipnl"},
3976 {HL_SKIPEMPTY, "skipempty"},
3977 {0, NULL}
3978 };
3979
3980 attr = hl_attr(HLF_D); /* highlight like directories */
3981
3982 /* list the keywords for "id" */
3983 if (!syncing)
3984 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003985 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
3986 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00003987 did_header, attr);
3988 }
3989
3990 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003991 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003992 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003993 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003994 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3995 continue;
3996
3997 (void)syn_list_header(did_header, 999, id);
3998 did_header = TRUE;
3999 last_matchgroup = 0;
4000 if (spp->sp_type == SPTYPE_MATCH)
4001 {
4002 put_pattern("match", ' ', spp, attr);
4003 msg_putchar(' ');
4004 }
4005 else if (spp->sp_type == SPTYPE_START)
4006 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004007 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4008 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4009 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4010 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4011 while (idx < curwin->w_s->b_syn_patterns.ga_len
4012 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4013 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004014 --idx;
4015 msg_putchar(' ');
4016 }
4017 syn_list_flags(namelist1, spp->sp_flags, attr);
4018
4019 if (spp->sp_cont_list != NULL)
4020 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4021
4022 if (spp->sp_syn.cont_in_list != NULL)
4023 put_id_list((char_u *)"containedin",
4024 spp->sp_syn.cont_in_list, attr);
4025
4026 if (spp->sp_next_list != NULL)
4027 {
4028 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4029 syn_list_flags(namelist2, spp->sp_flags, attr);
4030 }
4031 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4032 {
4033 if (spp->sp_flags & HL_SYNC_HERE)
4034 msg_puts_attr((char_u *)"grouphere", attr);
4035 else
4036 msg_puts_attr((char_u *)"groupthere", attr);
4037 msg_putchar(' ');
4038 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004039 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004040 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4041 else
4042 MSG_PUTS("NONE");
4043 msg_putchar(' ');
4044 }
4045 }
4046
4047 /* list the link, if there is one */
4048 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4049 {
4050 (void)syn_list_header(did_header, 999, id);
4051 msg_puts_attr((char_u *)"links to", attr);
4052 msg_putchar(' ');
4053 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4054 }
4055}
4056
4057 static void
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004058syn_list_flags(nlist, flags, attr)
4059 struct name_list *nlist;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004060 int flags;
4061 int attr;
4062{
4063 int i;
4064
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004065 for (i = 0; nlist[i].flag != 0; ++i)
4066 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004067 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004068 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004069 msg_putchar(' ');
4070 }
4071}
4072
4073/*
4074 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4075 */
4076 static void
4077syn_list_cluster(id)
4078 int id;
4079{
4080 int endcol = 15;
4081
4082 /* slight hack: roughly duplicate the guts of syn_list_header() */
4083 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004084 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085
4086 if (msg_col >= endcol) /* output at least one space */
4087 endcol = msg_col + 1;
4088 if (Columns <= endcol) /* avoid hang for tiny window */
4089 endcol = Columns - 1;
4090
4091 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004092 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004093 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004094 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004095 hl_attr(HLF_D));
4096 }
4097 else
4098 {
4099 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4100 msg_puts((char_u *)"=NONE");
4101 }
4102}
4103
4104 static void
4105put_id_list(name, list, attr)
4106 char_u *name;
4107 short *list;
4108 int attr;
4109{
4110 short *p;
4111
4112 msg_puts_attr(name, attr);
4113 msg_putchar('=');
4114 for (p = list; *p; ++p)
4115 {
4116 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4117 {
4118 if (p[1])
4119 MSG_PUTS("ALLBUT");
4120 else
4121 MSG_PUTS("ALL");
4122 }
4123 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4124 {
4125 MSG_PUTS("TOP");
4126 }
4127 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4128 {
4129 MSG_PUTS("CONTAINED");
4130 }
4131 else if (*p >= SYNID_CLUSTER)
4132 {
4133 short scl_id = *p - SYNID_CLUSTER;
4134
4135 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004136 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004137 }
4138 else
4139 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4140 if (p[1])
4141 msg_putchar(',');
4142 }
4143 msg_putchar(' ');
4144}
4145
4146 static void
4147put_pattern(s, c, spp, attr)
4148 char *s;
4149 int c;
4150 synpat_T *spp;
4151 int attr;
4152{
4153 long n;
4154 int mask;
4155 int first;
4156 static char *sepchars = "/+=-#@\"|'^&";
4157 int i;
4158
4159 /* May have to write "matchgroup=group" */
4160 if (last_matchgroup != spp->sp_syn_match_id)
4161 {
4162 last_matchgroup = spp->sp_syn_match_id;
4163 msg_puts_attr((char_u *)"matchgroup", attr);
4164 msg_putchar('=');
4165 if (last_matchgroup == 0)
4166 msg_outtrans((char_u *)"NONE");
4167 else
4168 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4169 msg_putchar(' ');
4170 }
4171
4172 /* output the name of the pattern and an '=' or ' ' */
4173 msg_puts_attr((char_u *)s, attr);
4174 msg_putchar(c);
4175
4176 /* output the pattern, in between a char that is not in the pattern */
4177 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4178 if (sepchars[++i] == NUL)
4179 {
4180 i = 0; /* no good char found, just use the first one */
4181 break;
4182 }
4183 msg_putchar(sepchars[i]);
4184 msg_outtrans(spp->sp_pattern);
4185 msg_putchar(sepchars[i]);
4186
4187 /* output any pattern options */
4188 first = TRUE;
4189 for (i = 0; i < SPO_COUNT; ++i)
4190 {
4191 mask = (1 << i);
4192 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4193 {
4194 if (!first)
4195 msg_putchar(','); /* separate with commas */
4196 msg_puts((char_u *)spo_name_tab[i]);
4197 n = spp->sp_offsets[i];
4198 if (i != SPO_LC_OFF)
4199 {
4200 if (spp->sp_off_flags & mask)
4201 msg_putchar('s');
4202 else
4203 msg_putchar('e');
4204 if (n > 0)
4205 msg_putchar('+');
4206 }
4207 if (n || i == SPO_LC_OFF)
4208 msg_outnum(n);
4209 first = FALSE;
4210 }
4211 }
4212 msg_putchar(' ');
4213}
4214
4215/*
4216 * List or clear the keywords for one syntax group.
4217 * Return TRUE if the header has been printed.
4218 */
4219 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004220syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004221 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004222 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004223 int did_header; /* header has already been printed */
4224 int attr;
4225{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004226 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004227 hashitem_T *hi;
4228 keyentry_T *kp;
4229 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004230 int prev_contained = 0;
4231 short *prev_next_list = NULL;
4232 short *prev_cont_in_list = NULL;
4233 int prev_skipnl = 0;
4234 int prev_skipwhite = 0;
4235 int prev_skipempty = 0;
4236
Bram Moolenaar071d4272004-06-13 20:20:40 +00004237 /*
4238 * Unfortunately, this list of keywords is not sorted on alphabet but on
4239 * hash value...
4240 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004241 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004242 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004243 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004244 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004245 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004246 --todo;
4247 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004248 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004249 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004250 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004251 if (prev_contained != (kp->flags & HL_CONTAINED)
4252 || prev_skipnl != (kp->flags & HL_SKIPNL)
4253 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4254 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4255 || prev_cont_in_list != kp->k_syn.cont_in_list
4256 || prev_next_list != kp->next_list)
4257 outlen = 9999;
4258 else
4259 outlen = (int)STRLEN(kp->keyword);
4260 /* output "contained" and "nextgroup" on each line */
4261 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004262 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263 prev_contained = 0;
4264 prev_next_list = NULL;
4265 prev_cont_in_list = NULL;
4266 prev_skipnl = 0;
4267 prev_skipwhite = 0;
4268 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004270 did_header = TRUE;
4271 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004272 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004273 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004274 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004275 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004276 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004277 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004278 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004279 put_id_list((char_u *)"containedin",
4280 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004281 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004282 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004283 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004284 if (kp->next_list != prev_next_list)
4285 {
4286 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4287 msg_putchar(' ');
4288 prev_next_list = kp->next_list;
4289 if (kp->flags & HL_SKIPNL)
4290 {
4291 msg_puts_attr((char_u *)"skipnl", attr);
4292 msg_putchar(' ');
4293 prev_skipnl = (kp->flags & HL_SKIPNL);
4294 }
4295 if (kp->flags & HL_SKIPWHITE)
4296 {
4297 msg_puts_attr((char_u *)"skipwhite", attr);
4298 msg_putchar(' ');
4299 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4300 }
4301 if (kp->flags & HL_SKIPEMPTY)
4302 {
4303 msg_puts_attr((char_u *)"skipempty", attr);
4304 msg_putchar(' ');
4305 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4306 }
4307 }
4308 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004309 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004310 }
4311 }
4312 }
4313
4314 return did_header;
4315}
4316
4317 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004318syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004319 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004320 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004321{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004322 hashitem_T *hi;
4323 keyentry_T *kp;
4324 keyentry_T *kp_prev;
4325 keyentry_T *kp_next;
4326 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327
Bram Moolenaardad6b692005-01-25 22:14:34 +00004328 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004329 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004330 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004332 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004333 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004334 --todo;
4335 kp_prev = NULL;
4336 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004338 if (kp->k_syn.id == id)
4339 {
4340 kp_next = kp->ke_next;
4341 if (kp_prev == NULL)
4342 {
4343 if (kp_next == NULL)
4344 hash_remove(ht, hi);
4345 else
4346 hi->hi_key = KE2HIKEY(kp_next);
4347 }
4348 else
4349 kp_prev->ke_next = kp_next;
4350 vim_free(kp->next_list);
4351 vim_free(kp->k_syn.cont_in_list);
4352 vim_free(kp);
4353 kp = kp_next;
4354 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 {
4357 kp_prev = kp;
4358 kp = kp->ke_next;
4359 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004360 }
4361 }
4362 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004363 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004364}
4365
4366/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004367 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004368 */
4369 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004370clear_keywtab(ht)
4371 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004372{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004373 hashitem_T *hi;
4374 int todo;
4375 keyentry_T *kp;
4376 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004377
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004378 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004379 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004380 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004381 if (!HASHITEM_EMPTY(hi))
4382 {
4383 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004385 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004386 kp_next = kp->ke_next;
4387 vim_free(kp->next_list);
4388 vim_free(kp->k_syn.cont_in_list);
4389 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004390 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004391 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004392 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004393 hash_clear(ht);
4394 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004395}
4396
4397/*
4398 * Add a keyword to the list of keywords.
4399 */
4400 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004401add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004402 char_u *name; /* name of keyword */
4403 int id; /* group ID for this keyword */
4404 int flags; /* flags for this keyword */
4405 short *cont_in_list; /* containedin for this keyword */
4406 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004407 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004409 keyentry_T *kp;
4410 hashtab_T *ht;
4411 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004412 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004413 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004414 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004415
Bram Moolenaar860cae12010-06-05 23:22:07 +02004416 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004417 name_ic = str_foldcase(name, (int)STRLEN(name),
4418 name_folded, MAXKEYWLEN + 1);
4419 else
4420 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004421 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4422 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004424 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004425 kp->k_syn.id = id;
4426 kp->k_syn.inc_tag = current_syn_inc_tag;
4427 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004428 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004429 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004430 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004431 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004432 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433
Bram Moolenaar860cae12010-06-05 23:22:07 +02004434 if (curwin->w_s->b_syn_ic)
4435 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004437 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004438
Bram Moolenaardad6b692005-01-25 22:14:34 +00004439 hash = hash_hash(kp->keyword);
4440 hi = hash_lookup(ht, kp->keyword, hash);
4441 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004442 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004443 /* new keyword, add to hashtable */
4444 kp->ke_next = NULL;
4445 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004446 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004447 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004449 /* keyword already exists, prepend to list */
4450 kp->ke_next = HI2KE(hi);
4451 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004452 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004453}
4454
4455/*
4456 * Get the start and end of the group name argument.
4457 * Return a pointer to the first argument.
4458 * Return NULL if the end of the command was found instead of further args.
4459 */
4460 static char_u *
4461get_group_name(arg, name_end)
4462 char_u *arg; /* start of the argument */
4463 char_u **name_end; /* pointer to end of the name */
4464{
4465 char_u *rest;
4466
4467 *name_end = skiptowhite(arg);
4468 rest = skipwhite(*name_end);
4469
4470 /*
4471 * Check if there are enough arguments. The first argument may be a
4472 * pattern, where '|' is allowed, so only check for NUL.
4473 */
4474 if (ends_excmd(*arg) || *rest == NUL)
4475 return NULL;
4476 return rest;
4477}
4478
4479/*
4480 * Check for syntax command option arguments.
4481 * This can be called at any place in the list of arguments, and just picks
4482 * out the arguments that are known. Can be called several times in a row to
4483 * collect all options in between other arguments.
4484 * Return a pointer to the next argument (which isn't an option).
4485 * Return NULL for any error;
4486 */
4487 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004488get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004489 char_u *arg; /* next argument to be checked */
4490 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004491 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004492{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004493 char_u *gname_start, *gname;
4494 int syn_id;
4495 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004496 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004497 int i;
4498 int fidx;
4499 static struct flag
4500 {
4501 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004502 int argtype;
4503 int flags;
4504 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4505 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4506 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4507 {"eExXtTeEnNdD", 0, HL_EXTEND},
4508 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4509 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4510 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4511 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4512 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4513 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4514 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4515 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4516 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004517 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4518 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4519 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004520 {"cCoOnNtTaAiInNsS", 1, 0},
4521 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4522 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004523 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004524 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004525
4526 if (arg == NULL) /* already detected error */
4527 return NULL;
4528
Bram Moolenaar860cae12010-06-05 23:22:07 +02004529#ifdef FEAT_CONCEAL
4530 if (curwin->w_s->b_syn_conceal)
4531 opt->flags |= HL_CONCEAL;
4532#endif
4533
Bram Moolenaar071d4272004-06-13 20:20:40 +00004534 for (;;)
4535 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004536 /*
4537 * This is used very often when a large number of keywords is defined.
4538 * Need to skip quickly when no option name is found.
4539 * Also avoid tolower(), it's slow.
4540 */
4541 if (strchr(first_letters, *arg) == NULL)
4542 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004543
4544 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4545 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004546 p = flagtab[fidx].name;
4547 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4548 if (arg[len] != p[i] && arg[len] != p[i + 1])
4549 break;
4550 if (p[i] == NUL && (vim_iswhite(arg[len])
4551 || (flagtab[fidx].argtype > 0
4552 ? arg[len] == '='
4553 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004554 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004555 if (opt->keyword
4556 && (flagtab[fidx].flags == HL_DISPLAY
4557 || flagtab[fidx].flags == HL_FOLD
4558 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004559 /* treat "display", "fold" and "extend" as a keyword */
4560 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561 break;
4562 }
4563 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004564 if (fidx < 0) /* no match found */
4565 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004566
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004567 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004568 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004569 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004570 {
4571 EMSG(_("E395: contains argument not accepted here"));
4572 return NULL;
4573 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004574 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 return NULL;
4576 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004577 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004578 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004579 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580 return NULL;
4581 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004582 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004583 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004584 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004585 return NULL;
4586 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004587 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4588 {
4589#ifdef FEAT_MBYTE
4590 /* cchar=? */
4591 if (has_mbyte)
4592 {
4593# ifdef FEAT_CONCEAL
4594 *conceal_char = mb_ptr2char(arg + 6);
4595# endif
4596 arg += mb_ptr2len(arg + 6) - 1;
4597 }
4598 else
4599#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004600 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004601#ifdef FEAT_CONCEAL
4602 *conceal_char = arg[6];
4603#else
4604 ;
4605#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004606 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004607#ifdef FEAT_CONCEAL
4608 if (!vim_isprintc_strict(*conceal_char))
4609 {
4610 EMSG(_("E844: invalid cchar value"));
4611 return NULL;
4612 }
4613#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004614 arg = skipwhite(arg + 7);
4615 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004616 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004617 {
4618 opt->flags |= flagtab[fidx].flags;
4619 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004620
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004621 if (flagtab[fidx].flags == HL_SYNC_HERE
4622 || flagtab[fidx].flags == HL_SYNC_THERE)
4623 {
4624 if (opt->sync_idx == NULL)
4625 {
4626 EMSG(_("E393: group[t]here not accepted here"));
4627 return NULL;
4628 }
4629 gname_start = arg;
4630 arg = skiptowhite(arg);
4631 if (gname_start == arg)
4632 return NULL;
4633 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4634 if (gname == NULL)
4635 return NULL;
4636 if (STRCMP(gname, "NONE") == 0)
4637 *opt->sync_idx = NONE_IDX;
4638 else
4639 {
4640 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004641 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4642 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4643 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004644 {
4645 *opt->sync_idx = i;
4646 break;
4647 }
4648 if (i < 0)
4649 {
4650 EMSG2(_("E394: Didn't find region item for %s"), gname);
4651 vim_free(gname);
4652 return NULL;
4653 }
4654 }
4655
4656 vim_free(gname);
4657 arg = skipwhite(arg);
4658 }
4659#ifdef FEAT_FOLDING
4660 else if (flagtab[fidx].flags == HL_FOLD
4661 && foldmethodIsSyntax(curwin))
4662 /* Need to update folds later. */
4663 foldUpdateAll(curwin);
4664#endif
4665 }
4666 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004667
4668 return arg;
4669}
4670
4671/*
4672 * Adjustments to syntax item when declared in a ":syn include"'d file.
4673 * Set the contained flag, and if the item is not already contained, add it
4674 * to the specified top-level group, if any.
4675 */
4676 static void
4677syn_incl_toplevel(id, flagsp)
4678 int id;
4679 int *flagsp;
4680{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004681 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004682 return;
4683 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004684 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004685 {
4686 /* We have to alloc this, because syn_combine_list() will free it. */
4687 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004688 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004689
4690 if (grp_list != NULL)
4691 {
4692 grp_list[0] = id;
4693 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004694 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004695 CLUSTER_ADD);
4696 }
4697 }
4698}
4699
4700/*
4701 * Handle ":syntax include [@{group-name}] filename" command.
4702 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703 static void
4704syn_cmd_include(eap, syncing)
4705 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004706 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004707{
4708 char_u *arg = eap->arg;
4709 int sgl_id = 1;
4710 char_u *group_name_end;
4711 char_u *rest;
4712 char_u *errormsg = NULL;
4713 int prev_toplvl_grp;
4714 int prev_syn_inc_tag;
4715 int source = FALSE;
4716
4717 eap->nextcmd = find_nextcmd(arg);
4718 if (eap->skip)
4719 return;
4720
4721 if (arg[0] == '@')
4722 {
4723 ++arg;
4724 rest = get_group_name(arg, &group_name_end);
4725 if (rest == NULL)
4726 {
4727 EMSG((char_u *)_("E397: Filename required"));
4728 return;
4729 }
4730 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004731 if (sgl_id == 0)
4732 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004733 /* separate_nextcmd() and expand_filename() depend on this */
4734 eap->arg = rest;
4735 }
4736
4737 /*
4738 * Everything that's left, up to the next command, should be the
4739 * filename to include.
4740 */
4741 eap->argt |= (XFILE | NOSPC);
4742 separate_nextcmd(eap);
4743 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4744 {
4745 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4746 * file. Need to expand the file name first. In other cases
4747 * ":runtime!" is used. */
4748 source = TRUE;
4749 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4750 {
4751 if (errormsg != NULL)
4752 EMSG(errormsg);
4753 return;
4754 }
4755 }
4756
4757 /*
4758 * Save and restore the existing top-level grouplist id and ":syn
4759 * include" tag around the actual inclusion.
4760 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004761 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4762 {
4763 EMSG((char_u *)_("E847: Too many syntax includes"));
4764 return;
4765 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004766 prev_syn_inc_tag = current_syn_inc_tag;
4767 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004768 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4769 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004770 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4771 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004772 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004773 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004774 current_syn_inc_tag = prev_syn_inc_tag;
4775}
4776
4777/*
4778 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4779 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004780 static void
4781syn_cmd_keyword(eap, syncing)
4782 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004783 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784{
4785 char_u *arg = eap->arg;
4786 char_u *group_name_end;
4787 int syn_id;
4788 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004789 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004790 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004791 char_u *kw;
4792 syn_opt_arg_T syn_opt_arg;
4793 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004794 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004795
4796 rest = get_group_name(arg, &group_name_end);
4797
4798 if (rest != NULL)
4799 {
4800 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004801 if (syn_id != 0)
4802 /* allocate a buffer, for removing backslashes in the keyword */
4803 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004804 if (keyword_copy != NULL)
4805 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004806 syn_opt_arg.flags = 0;
4807 syn_opt_arg.keyword = TRUE;
4808 syn_opt_arg.sync_idx = NULL;
4809 syn_opt_arg.has_cont_list = FALSE;
4810 syn_opt_arg.cont_in_list = NULL;
4811 syn_opt_arg.next_list = NULL;
4812
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813 /*
4814 * The options given apply to ALL keywords, so all options must be
4815 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004816 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004817 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004818 cnt = 0;
4819 p = keyword_copy;
4820 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004821 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004822 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004823 if (rest == NULL || ends_excmd(*rest))
4824 break;
4825 /* Copy the keyword, removing backslashes, and add a NUL. */
4826 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004827 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004828 if (*rest == '\\' && rest[1] != NUL)
4829 ++rest;
4830 *p++ = *rest++;
4831 }
4832 *p++ = NUL;
4833 ++cnt;
4834 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004835
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004836 if (!eap->skip)
4837 {
4838 /* Adjust flags for use of ":syn include". */
4839 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4840
4841 /*
4842 * 2: Add an entry for each keyword.
4843 */
4844 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4845 {
4846 for (p = vim_strchr(kw, '['); ; )
4847 {
4848 if (p != NULL)
4849 *p = NUL;
4850 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004851 syn_opt_arg.cont_in_list,
4852 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004853 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004854 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004855 if (p[1] == NUL)
4856 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004857 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004858 kw = p + 2; /* skip over the NUL */
4859 break;
4860 }
4861 if (p[1] == ']')
4862 {
4863 kw = p + 1; /* skip over the "]" */
4864 break;
4865 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004866#ifdef FEAT_MBYTE
4867 if (has_mbyte)
4868 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004869 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004870
4871 mch_memmove(p, p + 1, l);
4872 p += l;
4873 }
4874 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004875#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004876 {
4877 p[0] = p[1];
4878 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004879 }
4880 }
4881 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004882 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004883
Bram Moolenaar071d4272004-06-13 20:20:40 +00004884 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004885 vim_free(syn_opt_arg.cont_in_list);
4886 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004887 }
4888 }
4889
4890 if (rest != NULL)
4891 eap->nextcmd = check_nextcmd(rest);
4892 else
4893 EMSG2(_(e_invarg2), arg);
4894
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004895 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004896 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004897}
4898
4899/*
4900 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4901 *
4902 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4903 */
4904 static void
4905syn_cmd_match(eap, syncing)
4906 exarg_T *eap;
4907 int syncing; /* TRUE for ":syntax sync match .. " */
4908{
4909 char_u *arg = eap->arg;
4910 char_u *group_name_end;
4911 char_u *rest;
4912 synpat_T item; /* the item found in the line */
4913 int syn_id;
4914 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004915 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004916 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004917 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004918
4919 /* Isolate the group name, check for validity */
4920 rest = get_group_name(arg, &group_name_end);
4921
4922 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004923 syn_opt_arg.flags = 0;
4924 syn_opt_arg.keyword = FALSE;
4925 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4926 syn_opt_arg.has_cont_list = TRUE;
4927 syn_opt_arg.cont_list = NULL;
4928 syn_opt_arg.cont_in_list = NULL;
4929 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004930 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004931
4932 /* get the pattern. */
4933 init_syn_patterns();
4934 vim_memset(&item, 0, sizeof(item));
4935 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004936 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4937 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004938
4939 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004940 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941
4942 if (rest != NULL) /* all arguments are valid */
4943 {
4944 /*
4945 * Check for trailing command and illegal trailing arguments.
4946 */
4947 eap->nextcmd = check_nextcmd(rest);
4948 if (!ends_excmd(*rest) || eap->skip)
4949 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004950 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004951 && (syn_id = syn_check_group(arg,
4952 (int)(group_name_end - arg))) != 0)
4953 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004954 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004955 /*
4956 * Store the pattern in the syn_items list
4957 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004958 idx = curwin->w_s->b_syn_patterns.ga_len;
4959 SYN_ITEMS(curwin->w_s)[idx] = item;
4960 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4961 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4962 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4963 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4964 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4965 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4966 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4967 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004968 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004969#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004970 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004971#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004972 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004973 curwin->w_s->b_syn_containedin = TRUE;
4974 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4975 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004976
4977 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004978 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02004979 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004980#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004981 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004982 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004983#endif
4984
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004985 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004986 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004987 return; /* don't free the progs and patterns now */
4988 }
4989 }
4990
4991 /*
4992 * Something failed, free the allocated memory.
4993 */
Bram Moolenaar473de612013-06-08 18:19:48 +02004994 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004995 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004996 vim_free(syn_opt_arg.cont_list);
4997 vim_free(syn_opt_arg.cont_in_list);
4998 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004999
5000 if (rest == NULL)
5001 EMSG2(_(e_invarg2), arg);
5002}
5003
5004/*
5005 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5006 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5007 */
5008 static void
5009syn_cmd_region(eap, syncing)
5010 exarg_T *eap;
5011 int syncing; /* TRUE for ":syntax sync region .." */
5012{
5013 char_u *arg = eap->arg;
5014 char_u *group_name_end;
5015 char_u *rest; /* next arg, NULL on error */
5016 char_u *key_end;
5017 char_u *key = NULL;
5018 char_u *p;
5019 int item;
5020#define ITEM_START 0
5021#define ITEM_SKIP 1
5022#define ITEM_END 2
5023#define ITEM_MATCHGROUP 3
5024 struct pat_ptr
5025 {
5026 synpat_T *pp_synp; /* pointer to syn_pattern */
5027 int pp_matchgroup_id; /* matchgroup ID */
5028 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5029 } *(pat_ptrs[3]);
5030 /* patterns found in the line */
5031 struct pat_ptr *ppp;
5032 struct pat_ptr *ppp_next;
5033 int pat_count = 0; /* nr of syn_patterns found */
5034 int syn_id;
5035 int matchgroup_id = 0;
5036 int not_enough = FALSE; /* not enough arguments */
5037 int illegal = FALSE; /* illegal arguments */
5038 int success = FALSE;
5039 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005040 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005041 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005042
5043 /* Isolate the group name, check for validity */
5044 rest = get_group_name(arg, &group_name_end);
5045
5046 pat_ptrs[0] = NULL;
5047 pat_ptrs[1] = NULL;
5048 pat_ptrs[2] = NULL;
5049
5050 init_syn_patterns();
5051
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005052 syn_opt_arg.flags = 0;
5053 syn_opt_arg.keyword = FALSE;
5054 syn_opt_arg.sync_idx = NULL;
5055 syn_opt_arg.has_cont_list = TRUE;
5056 syn_opt_arg.cont_list = NULL;
5057 syn_opt_arg.cont_in_list = NULL;
5058 syn_opt_arg.next_list = NULL;
5059
Bram Moolenaar071d4272004-06-13 20:20:40 +00005060 /*
5061 * get the options, patterns and matchgroup.
5062 */
5063 while (rest != NULL && !ends_excmd(*rest))
5064 {
5065 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005066 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005067 if (rest == NULL || ends_excmd(*rest))
5068 break;
5069
5070 /* must be a pattern or matchgroup then */
5071 key_end = rest;
5072 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5073 ++key_end;
5074 vim_free(key);
5075 key = vim_strnsave_up(rest, (int)(key_end - rest));
5076 if (key == NULL) /* out of memory */
5077 {
5078 rest = NULL;
5079 break;
5080 }
5081 if (STRCMP(key, "MATCHGROUP") == 0)
5082 item = ITEM_MATCHGROUP;
5083 else if (STRCMP(key, "START") == 0)
5084 item = ITEM_START;
5085 else if (STRCMP(key, "END") == 0)
5086 item = ITEM_END;
5087 else if (STRCMP(key, "SKIP") == 0)
5088 {
5089 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5090 {
5091 illegal = TRUE;
5092 break;
5093 }
5094 item = ITEM_SKIP;
5095 }
5096 else
5097 break;
5098 rest = skipwhite(key_end);
5099 if (*rest != '=')
5100 {
5101 rest = NULL;
5102 EMSG2(_("E398: Missing '=': %s"), arg);
5103 break;
5104 }
5105 rest = skipwhite(rest + 1);
5106 if (*rest == NUL)
5107 {
5108 not_enough = TRUE;
5109 break;
5110 }
5111
5112 if (item == ITEM_MATCHGROUP)
5113 {
5114 p = skiptowhite(rest);
5115 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5116 matchgroup_id = 0;
5117 else
5118 {
5119 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5120 if (matchgroup_id == 0)
5121 {
5122 illegal = TRUE;
5123 break;
5124 }
5125 }
5126 rest = skipwhite(p);
5127 }
5128 else
5129 {
5130 /*
5131 * Allocate room for a syn_pattern, and link it in the list of
5132 * syn_patterns for this item, at the start (because the list is
5133 * used from end to start).
5134 */
5135 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5136 if (ppp == NULL)
5137 {
5138 rest = NULL;
5139 break;
5140 }
5141 ppp->pp_next = pat_ptrs[item];
5142 pat_ptrs[item] = ppp;
5143 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5144 if (ppp->pp_synp == NULL)
5145 {
5146 rest = NULL;
5147 break;
5148 }
5149
5150 /*
5151 * Get the syntax pattern and the following offset(s).
5152 */
5153 /* Enable the appropriate \z specials. */
5154 if (item == ITEM_START)
5155 reg_do_extmatch = REX_SET;
5156 else if (item == ITEM_SKIP || item == ITEM_END)
5157 reg_do_extmatch = REX_USE;
5158 rest = get_syn_pattern(rest, ppp->pp_synp);
5159 reg_do_extmatch = 0;
5160 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005161 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005162 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5163 ppp->pp_matchgroup_id = matchgroup_id;
5164 ++pat_count;
5165 }
5166 }
5167 vim_free(key);
5168 if (illegal || not_enough)
5169 rest = NULL;
5170
5171 /*
5172 * Must have a "start" and "end" pattern.
5173 */
5174 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5175 pat_ptrs[ITEM_END] == NULL))
5176 {
5177 not_enough = TRUE;
5178 rest = NULL;
5179 }
5180
5181 if (rest != NULL)
5182 {
5183 /*
5184 * Check for trailing garbage or command.
5185 * If OK, add the item.
5186 */
5187 eap->nextcmd = check_nextcmd(rest);
5188 if (!ends_excmd(*rest) || eap->skip)
5189 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005190 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005191 && (syn_id = syn_check_group(arg,
5192 (int)(group_name_end - arg))) != 0)
5193 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005194 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005195 /*
5196 * Store the start/skip/end in the syn_items list
5197 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005198 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005199 for (item = ITEM_START; item <= ITEM_END; ++item)
5200 {
5201 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5202 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005203 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5204 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5205 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005206 (item == ITEM_START) ? SPTYPE_START :
5207 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005208 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5209 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005210 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5211 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005212 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005213 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005214#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005215 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005216#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005217 if (item == ITEM_START)
5218 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005219 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005220 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005221 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005222 syn_opt_arg.cont_in_list;
5223 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005224 curwin->w_s->b_syn_containedin = TRUE;
5225 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005226 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005227 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005228 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005229 ++idx;
5230#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005231 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005232 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005233#endif
5234 }
5235 }
5236
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005237 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005238 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005239 success = TRUE; /* don't free the progs and patterns now */
5240 }
5241 }
5242
5243 /*
5244 * Free the allocated memory.
5245 */
5246 for (item = ITEM_START; item <= ITEM_END; ++item)
5247 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5248 {
5249 if (!success)
5250 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005251 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005252 vim_free(ppp->pp_synp->sp_pattern);
5253 }
5254 vim_free(ppp->pp_synp);
5255 ppp_next = ppp->pp_next;
5256 vim_free(ppp);
5257 }
5258
5259 if (!success)
5260 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005261 vim_free(syn_opt_arg.cont_list);
5262 vim_free(syn_opt_arg.cont_in_list);
5263 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005264 if (not_enough)
5265 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5266 else if (illegal || rest == NULL)
5267 EMSG2(_(e_invarg2), arg);
5268 }
5269}
5270
5271/*
5272 * A simple syntax group ID comparison function suitable for use in qsort()
5273 */
5274 static int
5275#ifdef __BORLANDC__
5276_RTLENTRYF
5277#endif
5278syn_compare_stub(v1, v2)
5279 const void *v1;
5280 const void *v2;
5281{
5282 const short *s1 = v1;
5283 const short *s2 = v2;
5284
5285 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5286}
5287
5288/*
5289 * Combines lists of syntax clusters.
5290 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5291 */
5292 static void
5293syn_combine_list(clstr1, clstr2, list_op)
5294 short **clstr1;
5295 short **clstr2;
5296 int list_op;
5297{
5298 int count1 = 0;
5299 int count2 = 0;
5300 short *g1;
5301 short *g2;
5302 short *clstr = NULL;
5303 int count;
5304 int round;
5305
5306 /*
5307 * Handle degenerate cases.
5308 */
5309 if (*clstr2 == NULL)
5310 return;
5311 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5312 {
5313 if (list_op == CLUSTER_REPLACE)
5314 vim_free(*clstr1);
5315 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5316 *clstr1 = *clstr2;
5317 else
5318 vim_free(*clstr2);
5319 return;
5320 }
5321
5322 for (g1 = *clstr1; *g1; g1++)
5323 ++count1;
5324 for (g2 = *clstr2; *g2; g2++)
5325 ++count2;
5326
5327 /*
5328 * For speed purposes, sort both lists.
5329 */
5330 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5331 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5332
5333 /*
5334 * We proceed in two passes; in round 1, we count the elements to place
5335 * in the new list, and in round 2, we allocate and populate the new
5336 * list. For speed, we use a mergesort-like method, adding the smaller
5337 * of the current elements in each list to the new list.
5338 */
5339 for (round = 1; round <= 2; round++)
5340 {
5341 g1 = *clstr1;
5342 g2 = *clstr2;
5343 count = 0;
5344
5345 /*
5346 * First, loop through the lists until one of them is empty.
5347 */
5348 while (*g1 && *g2)
5349 {
5350 /*
5351 * We always want to add from the first list.
5352 */
5353 if (*g1 < *g2)
5354 {
5355 if (round == 2)
5356 clstr[count] = *g1;
5357 count++;
5358 g1++;
5359 continue;
5360 }
5361 /*
5362 * We only want to add from the second list if we're adding the
5363 * lists.
5364 */
5365 if (list_op == CLUSTER_ADD)
5366 {
5367 if (round == 2)
5368 clstr[count] = *g2;
5369 count++;
5370 }
5371 if (*g1 == *g2)
5372 g1++;
5373 g2++;
5374 }
5375
5376 /*
5377 * Now add the leftovers from whichever list didn't get finished
5378 * first. As before, we only want to add from the second list if
5379 * we're adding the lists.
5380 */
5381 for (; *g1; g1++, count++)
5382 if (round == 2)
5383 clstr[count] = *g1;
5384 if (list_op == CLUSTER_ADD)
5385 for (; *g2; g2++, count++)
5386 if (round == 2)
5387 clstr[count] = *g2;
5388
5389 if (round == 1)
5390 {
5391 /*
5392 * If the group ended up empty, we don't need to allocate any
5393 * space for it.
5394 */
5395 if (count == 0)
5396 {
5397 clstr = NULL;
5398 break;
5399 }
5400 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5401 if (clstr == NULL)
5402 break;
5403 clstr[count] = 0;
5404 }
5405 }
5406
5407 /*
5408 * Finally, put the new list in place.
5409 */
5410 vim_free(*clstr1);
5411 vim_free(*clstr2);
5412 *clstr1 = clstr;
5413}
5414
5415/*
5416 * Lookup a syntax cluster name and return it's ID.
5417 * If it is not found, 0 is returned.
5418 */
5419 static int
5420syn_scl_name2id(name)
5421 char_u *name;
5422{
5423 int i;
5424 char_u *name_u;
5425
5426 /* Avoid using stricmp() too much, it's slow on some systems */
5427 name_u = vim_strsave_up(name);
5428 if (name_u == NULL)
5429 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005430 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5431 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5432 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005433 break;
5434 vim_free(name_u);
5435 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5436}
5437
5438/*
5439 * Like syn_scl_name2id(), but take a pointer + length argument.
5440 */
5441 static int
5442syn_scl_namen2id(linep, len)
5443 char_u *linep;
5444 int len;
5445{
5446 char_u *name;
5447 int id = 0;
5448
5449 name = vim_strnsave(linep, len);
5450 if (name != NULL)
5451 {
5452 id = syn_scl_name2id(name);
5453 vim_free(name);
5454 }
5455 return id;
5456}
5457
5458/*
5459 * Find syntax cluster name in the table and return it's ID.
5460 * The argument is a pointer to the name and the length of the name.
5461 * If it doesn't exist yet, a new entry is created.
5462 * Return 0 for failure.
5463 */
5464 static int
5465syn_check_cluster(pp, len)
5466 char_u *pp;
5467 int len;
5468{
5469 int id;
5470 char_u *name;
5471
5472 name = vim_strnsave(pp, len);
5473 if (name == NULL)
5474 return 0;
5475
5476 id = syn_scl_name2id(name);
5477 if (id == 0) /* doesn't exist yet */
5478 id = syn_add_cluster(name);
5479 else
5480 vim_free(name);
5481 return id;
5482}
5483
5484/*
5485 * Add new syntax cluster and return it's ID.
5486 * "name" must be an allocated string, it will be consumed.
5487 * Return 0 for failure.
5488 */
5489 static int
5490syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005491 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005492{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005493 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005494
5495 /*
5496 * First call for this growarray: init growing array.
5497 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005498 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005499 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005500 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5501 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005502 }
5503
Bram Moolenaar42431a72011-04-01 14:44:59 +02005504 len = curwin->w_s->b_syn_clusters.ga_len;
5505 if (len >= MAX_CLUSTER_ID)
5506 {
5507 EMSG((char_u *)_("E848: Too many syntax clusters"));
5508 vim_free(name);
5509 return 0;
5510 }
5511
Bram Moolenaar071d4272004-06-13 20:20:40 +00005512 /*
5513 * Make room for at least one other cluster entry.
5514 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005515 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005516 {
5517 vim_free(name);
5518 return 0;
5519 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005520
Bram Moolenaar860cae12010-06-05 23:22:07 +02005521 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5522 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5523 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5524 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5525 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005526
Bram Moolenaar217ad922005-03-20 22:37:15 +00005527 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005528 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005529 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005530 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005531
Bram Moolenaar071d4272004-06-13 20:20:40 +00005532 return len + SYNID_CLUSTER;
5533}
5534
5535/*
5536 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5537 * [add={groupname},..] [remove={groupname},..]".
5538 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539 static void
5540syn_cmd_cluster(eap, syncing)
5541 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005542 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005543{
5544 char_u *arg = eap->arg;
5545 char_u *group_name_end;
5546 char_u *rest;
5547 int scl_id;
5548 short *clstr_list;
5549 int got_clstr = FALSE;
5550 int opt_len;
5551 int list_op;
5552
5553 eap->nextcmd = find_nextcmd(arg);
5554 if (eap->skip)
5555 return;
5556
5557 rest = get_group_name(arg, &group_name_end);
5558
5559 if (rest != NULL)
5560 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005561 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5562 if (scl_id == 0)
5563 return;
5564 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565
5566 for (;;)
5567 {
5568 if (STRNICMP(rest, "add", 3) == 0
5569 && (vim_iswhite(rest[3]) || rest[3] == '='))
5570 {
5571 opt_len = 3;
5572 list_op = CLUSTER_ADD;
5573 }
5574 else if (STRNICMP(rest, "remove", 6) == 0
5575 && (vim_iswhite(rest[6]) || rest[6] == '='))
5576 {
5577 opt_len = 6;
5578 list_op = CLUSTER_SUBTRACT;
5579 }
5580 else if (STRNICMP(rest, "contains", 8) == 0
5581 && (vim_iswhite(rest[8]) || rest[8] == '='))
5582 {
5583 opt_len = 8;
5584 list_op = CLUSTER_REPLACE;
5585 }
5586 else
5587 break;
5588
5589 clstr_list = NULL;
5590 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5591 {
5592 EMSG2(_(e_invarg2), rest);
5593 break;
5594 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005595 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005596 &clstr_list, list_op);
5597 got_clstr = TRUE;
5598 }
5599
5600 if (got_clstr)
5601 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005602 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005603 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005604 }
5605 }
5606
5607 if (!got_clstr)
5608 EMSG(_("E400: No cluster specified"));
5609 if (rest == NULL || !ends_excmd(*rest))
5610 EMSG2(_(e_invarg2), arg);
5611}
5612
5613/*
5614 * On first call for current buffer: Init growing array.
5615 */
5616 static void
5617init_syn_patterns()
5618{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005619 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5620 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005621}
5622
5623/*
5624 * Get one pattern for a ":syntax match" or ":syntax region" command.
5625 * Stores the pattern and program in a synpat_T.
5626 * Returns a pointer to the next argument, or NULL in case of an error.
5627 */
5628 static char_u *
5629get_syn_pattern(arg, ci)
5630 char_u *arg;
5631 synpat_T *ci;
5632{
5633 char_u *end;
5634 int *p;
5635 int idx;
5636 char_u *cpo_save;
5637
5638 /* need at least three chars */
5639 if (arg == NULL || arg[1] == NUL || arg[2] == NUL)
5640 return NULL;
5641
5642 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5643 if (*end != *arg) /* end delimiter not found */
5644 {
5645 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5646 return NULL;
5647 }
5648 /* store the pattern and compiled regexp program */
5649 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5650 return NULL;
5651
5652 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5653 cpo_save = p_cpo;
5654 p_cpo = (char_u *)"";
5655 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5656 p_cpo = cpo_save;
5657
5658 if (ci->sp_prog == NULL)
5659 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005660 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005661#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005662 syn_clear_time(&ci->sp_time);
5663#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005664
5665 /*
5666 * Check for a match, highlight or region offset.
5667 */
5668 ++end;
5669 do
5670 {
5671 for (idx = SPO_COUNT; --idx >= 0; )
5672 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5673 break;
5674 if (idx >= 0)
5675 {
5676 p = &(ci->sp_offsets[idx]);
5677 if (idx != SPO_LC_OFF)
5678 switch (end[3])
5679 {
5680 case 's': break;
5681 case 'b': break;
5682 case 'e': idx += SPO_COUNT; break;
5683 default: idx = -1; break;
5684 }
5685 if (idx >= 0)
5686 {
5687 ci->sp_off_flags |= (1 << idx);
5688 if (idx == SPO_LC_OFF) /* lc=99 */
5689 {
5690 end += 3;
5691 *p = getdigits(&end);
5692
5693 /* "lc=" offset automatically sets "ms=" offset */
5694 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5695 {
5696 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5697 ci->sp_offsets[SPO_MS_OFF] = *p;
5698 }
5699 }
5700 else /* yy=x+99 */
5701 {
5702 end += 4;
5703 if (*end == '+')
5704 {
5705 ++end;
5706 *p = getdigits(&end); /* positive offset */
5707 }
5708 else if (*end == '-')
5709 {
5710 ++end;
5711 *p = -getdigits(&end); /* negative offset */
5712 }
5713 }
5714 if (*end != ',')
5715 break;
5716 ++end;
5717 }
5718 }
5719 } while (idx >= 0);
5720
5721 if (!ends_excmd(*end) && !vim_iswhite(*end))
5722 {
5723 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5724 return NULL;
5725 }
5726 return skipwhite(end);
5727}
5728
5729/*
5730 * Handle ":syntax sync .." command.
5731 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005732 static void
5733syn_cmd_sync(eap, syncing)
5734 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005735 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005736{
5737 char_u *arg_start = eap->arg;
5738 char_u *arg_end;
5739 char_u *key = NULL;
5740 char_u *next_arg;
5741 int illegal = FALSE;
5742 int finished = FALSE;
5743 long n;
5744 char_u *cpo_save;
5745
5746 if (ends_excmd(*arg_start))
5747 {
5748 syn_cmd_list(eap, TRUE);
5749 return;
5750 }
5751
5752 while (!ends_excmd(*arg_start))
5753 {
5754 arg_end = skiptowhite(arg_start);
5755 next_arg = skipwhite(arg_end);
5756 vim_free(key);
5757 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5758 if (STRCMP(key, "CCOMMENT") == 0)
5759 {
5760 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005761 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005762 if (!ends_excmd(*next_arg))
5763 {
5764 arg_end = skiptowhite(next_arg);
5765 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005766 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005767 (int)(arg_end - next_arg));
5768 next_arg = skipwhite(arg_end);
5769 }
5770 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005771 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005772 }
5773 else if ( STRNCMP(key, "LINES", 5) == 0
5774 || STRNCMP(key, "MINLINES", 8) == 0
5775 || STRNCMP(key, "MAXLINES", 8) == 0
5776 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5777 {
5778 if (key[4] == 'S')
5779 arg_end = key + 6;
5780 else if (key[0] == 'L')
5781 arg_end = key + 11;
5782 else
5783 arg_end = key + 9;
5784 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5785 {
5786 illegal = TRUE;
5787 break;
5788 }
5789 n = getdigits(&arg_end);
5790 if (!eap->skip)
5791 {
5792 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005793 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005794 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005795 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005796 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005797 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005798 }
5799 }
5800 else if (STRCMP(key, "FROMSTART") == 0)
5801 {
5802 if (!eap->skip)
5803 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005804 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5805 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005806 }
5807 }
5808 else if (STRCMP(key, "LINECONT") == 0)
5809 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005810 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005811 {
5812 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5813 finished = TRUE;
5814 break;
5815 }
5816 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5817 if (*arg_end != *next_arg) /* end delimiter not found */
5818 {
5819 illegal = TRUE;
5820 break;
5821 }
5822
5823 if (!eap->skip)
5824 {
5825 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005826 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005827 (int)(arg_end - next_arg - 1))) == NULL)
5828 {
5829 finished = TRUE;
5830 break;
5831 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005832 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005833
5834 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5835 cpo_save = p_cpo;
5836 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005837 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005838 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005839 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005840#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005841 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5842#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005843
Bram Moolenaar860cae12010-06-05 23:22:07 +02005844 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005845 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005846 vim_free(curwin->w_s->b_syn_linecont_pat);
5847 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005848 finished = TRUE;
5849 break;
5850 }
5851 }
5852 next_arg = skipwhite(arg_end + 1);
5853 }
5854 else
5855 {
5856 eap->arg = next_arg;
5857 if (STRCMP(key, "MATCH") == 0)
5858 syn_cmd_match(eap, TRUE);
5859 else if (STRCMP(key, "REGION") == 0)
5860 syn_cmd_region(eap, TRUE);
5861 else if (STRCMP(key, "CLEAR") == 0)
5862 syn_cmd_clear(eap, TRUE);
5863 else
5864 illegal = TRUE;
5865 finished = TRUE;
5866 break;
5867 }
5868 arg_start = next_arg;
5869 }
5870 vim_free(key);
5871 if (illegal)
5872 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5873 else if (!finished)
5874 {
5875 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005876 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005877 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005878 }
5879}
5880
5881/*
5882 * Convert a line of highlight group names into a list of group ID numbers.
5883 * "arg" should point to the "contains" or "nextgroup" keyword.
5884 * "arg" is advanced to after the last group name.
5885 * Careful: the argument is modified (NULs added).
5886 * returns FAIL for some error, OK for success.
5887 */
5888 static int
5889get_id_list(arg, keylen, list)
5890 char_u **arg;
5891 int keylen; /* length of keyword */
5892 short **list; /* where to store the resulting list, if not
5893 NULL, the list is silently skipped! */
5894{
5895 char_u *p = NULL;
5896 char_u *end;
5897 int round;
5898 int count;
5899 int total_count = 0;
5900 short *retval = NULL;
5901 char_u *name;
5902 regmatch_T regmatch;
5903 int id;
5904 int i;
5905 int failed = FALSE;
5906
5907 /*
5908 * We parse the list twice:
5909 * round == 1: count the number of items, allocate the array.
5910 * round == 2: fill the array with the items.
5911 * In round 1 new groups may be added, causing the number of items to
5912 * grow when a regexp is used. In that case round 1 is done once again.
5913 */
5914 for (round = 1; round <= 2; ++round)
5915 {
5916 /*
5917 * skip "contains"
5918 */
5919 p = skipwhite(*arg + keylen);
5920 if (*p != '=')
5921 {
5922 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5923 break;
5924 }
5925 p = skipwhite(p + 1);
5926 if (ends_excmd(*p))
5927 {
5928 EMSG2(_("E406: Empty argument: %s"), *arg);
5929 break;
5930 }
5931
5932 /*
5933 * parse the arguments after "contains"
5934 */
5935 count = 0;
5936 while (!ends_excmd(*p))
5937 {
5938 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5939 ;
5940 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5941 if (name == NULL)
5942 {
5943 failed = TRUE;
5944 break;
5945 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005946 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005947 if ( STRCMP(name + 1, "ALLBUT") == 0
5948 || STRCMP(name + 1, "ALL") == 0
5949 || STRCMP(name + 1, "TOP") == 0
5950 || STRCMP(name + 1, "CONTAINED") == 0)
5951 {
5952 if (TOUPPER_ASC(**arg) != 'C')
5953 {
5954 EMSG2(_("E407: %s not allowed here"), name + 1);
5955 failed = TRUE;
5956 vim_free(name);
5957 break;
5958 }
5959 if (count != 0)
5960 {
5961 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5962 failed = TRUE;
5963 vim_free(name);
5964 break;
5965 }
5966 if (name[1] == 'A')
5967 id = SYNID_ALLBUT;
5968 else if (name[1] == 'T')
5969 id = SYNID_TOP;
5970 else
5971 id = SYNID_CONTAINED;
5972 id += current_syn_inc_tag;
5973 }
5974 else if (name[1] == '@')
5975 {
5976 id = syn_check_cluster(name + 2, (int)(end - p - 1));
5977 }
5978 else
5979 {
5980 /*
5981 * Handle full group name.
5982 */
5983 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
5984 id = syn_check_group(name + 1, (int)(end - p));
5985 else
5986 {
5987 /*
5988 * Handle match of regexp with group names.
5989 */
5990 *name = '^';
5991 STRCAT(name, "$");
5992 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
5993 if (regmatch.regprog == NULL)
5994 {
5995 failed = TRUE;
5996 vim_free(name);
5997 break;
5998 }
5999
6000 regmatch.rm_ic = TRUE;
6001 id = 0;
6002 for (i = highlight_ga.ga_len; --i >= 0; )
6003 {
6004 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6005 (colnr_T)0))
6006 {
6007 if (round == 2)
6008 {
6009 /* Got more items than expected; can happen
6010 * when adding items that match:
6011 * "contains=a.*b,axb".
6012 * Go back to first round */
6013 if (count >= total_count)
6014 {
6015 vim_free(retval);
6016 round = 1;
6017 }
6018 else
6019 retval[count] = i + 1;
6020 }
6021 ++count;
6022 id = -1; /* remember that we found one */
6023 }
6024 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006025 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006026 }
6027 }
6028 vim_free(name);
6029 if (id == 0)
6030 {
6031 EMSG2(_("E409: Unknown group name: %s"), p);
6032 failed = TRUE;
6033 break;
6034 }
6035 if (id > 0)
6036 {
6037 if (round == 2)
6038 {
6039 /* Got more items than expected, go back to first round */
6040 if (count >= total_count)
6041 {
6042 vim_free(retval);
6043 round = 1;
6044 }
6045 else
6046 retval[count] = id;
6047 }
6048 ++count;
6049 }
6050 p = skipwhite(end);
6051 if (*p != ',')
6052 break;
6053 p = skipwhite(p + 1); /* skip comma in between arguments */
6054 }
6055 if (failed)
6056 break;
6057 if (round == 1)
6058 {
6059 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6060 if (retval == NULL)
6061 break;
6062 retval[count] = 0; /* zero means end of the list */
6063 total_count = count;
6064 }
6065 }
6066
6067 *arg = p;
6068 if (failed || retval == NULL)
6069 {
6070 vim_free(retval);
6071 return FAIL;
6072 }
6073
6074 if (*list == NULL)
6075 *list = retval;
6076 else
6077 vim_free(retval); /* list already found, don't overwrite it */
6078
6079 return OK;
6080}
6081
6082/*
6083 * Make a copy of an ID list.
6084 */
6085 static short *
6086copy_id_list(list)
6087 short *list;
6088{
6089 int len;
6090 int count;
6091 short *retval;
6092
6093 if (list == NULL)
6094 return NULL;
6095
6096 for (count = 0; list[count]; ++count)
6097 ;
6098 len = (count + 1) * sizeof(short);
6099 retval = (short *)alloc((unsigned)len);
6100 if (retval != NULL)
6101 mch_memmove(retval, list, (size_t)len);
6102
6103 return retval;
6104}
6105
6106/*
6107 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6108 * "cur_si" can be NULL if not checking the "containedin" list.
6109 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6110 * the current item.
6111 * This function is called very often, keep it fast!!
6112 */
6113 static int
6114in_id_list(cur_si, list, ssp, contained)
6115 stateitem_T *cur_si; /* current item or NULL */
6116 short *list; /* id list */
6117 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6118 int contained; /* group id is contained */
6119{
6120 int retval;
6121 short *scl_list;
6122 short item;
6123 short id = ssp->id;
6124 static int depth = 0;
6125 int r;
6126
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006127 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006128 if (cur_si != NULL && ssp->cont_in_list != NULL
6129 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006130 {
6131 /* Ignore transparent items without a contains argument. Double check
6132 * that we don't go back past the first one. */
6133 while ((cur_si->si_flags & HL_TRANS_CONT)
6134 && cur_si > (stateitem_T *)(current_state.ga_data))
6135 --cur_si;
6136 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6137 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006138 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6139 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006140 return TRUE;
6141 }
6142
6143 if (list == NULL)
6144 return FALSE;
6145
6146 /*
6147 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6148 * inside anything. Only allow not-contained groups.
6149 */
6150 if (list == ID_LIST_ALL)
6151 return !contained;
6152
6153 /*
6154 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6155 * contains list. We also require that "id" is at the same ":syn include"
6156 * level as the list.
6157 */
6158 item = *list;
6159 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6160 {
6161 if (item < SYNID_TOP)
6162 {
6163 /* ALL or ALLBUT: accept all groups in the same file */
6164 if (item - SYNID_ALLBUT != ssp->inc_tag)
6165 return FALSE;
6166 }
6167 else if (item < SYNID_CONTAINED)
6168 {
6169 /* TOP: accept all not-contained groups in the same file */
6170 if (item - SYNID_TOP != ssp->inc_tag || contained)
6171 return FALSE;
6172 }
6173 else
6174 {
6175 /* CONTAINED: accept all contained groups in the same file */
6176 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6177 return FALSE;
6178 }
6179 item = *++list;
6180 retval = FALSE;
6181 }
6182 else
6183 retval = TRUE;
6184
6185 /*
6186 * Return "retval" if id is in the contains list.
6187 */
6188 while (item != 0)
6189 {
6190 if (item == id)
6191 return retval;
6192 if (item >= SYNID_CLUSTER)
6193 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006194 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006195 /* restrict recursiveness to 30 to avoid an endless loop for a
6196 * cluster that includes itself (indirectly) */
6197 if (scl_list != NULL && depth < 30)
6198 {
6199 ++depth;
6200 r = in_id_list(NULL, scl_list, ssp, contained);
6201 --depth;
6202 if (r)
6203 return retval;
6204 }
6205 }
6206 item = *++list;
6207 }
6208 return !retval;
6209}
6210
6211struct subcommand
6212{
6213 char *name; /* subcommand name */
6214 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6215};
6216
6217static struct subcommand subcommands[] =
6218{
6219 {"case", syn_cmd_case},
6220 {"clear", syn_cmd_clear},
6221 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006222 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006223 {"enable", syn_cmd_enable},
6224 {"include", syn_cmd_include},
6225 {"keyword", syn_cmd_keyword},
6226 {"list", syn_cmd_list},
6227 {"manual", syn_cmd_manual},
6228 {"match", syn_cmd_match},
6229 {"on", syn_cmd_on},
6230 {"off", syn_cmd_off},
6231 {"region", syn_cmd_region},
6232 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006233 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006234 {"sync", syn_cmd_sync},
6235 {"", syn_cmd_list},
6236 {NULL, NULL}
6237};
6238
6239/*
6240 * ":syntax".
6241 * This searches the subcommands[] table for the subcommand name, and calls a
6242 * syntax_subcommand() function to do the rest.
6243 */
6244 void
6245ex_syntax(eap)
6246 exarg_T *eap;
6247{
6248 char_u *arg = eap->arg;
6249 char_u *subcmd_end;
6250 char_u *subcmd_name;
6251 int i;
6252
6253 syn_cmdlinep = eap->cmdlinep;
6254
6255 /* isolate subcommand name */
6256 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6257 ;
6258 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6259 if (subcmd_name != NULL)
6260 {
6261 if (eap->skip) /* skip error messages for all subcommands */
6262 ++emsg_skip;
6263 for (i = 0; ; ++i)
6264 {
6265 if (subcommands[i].name == NULL)
6266 {
6267 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6268 break;
6269 }
6270 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6271 {
6272 eap->arg = skipwhite(subcmd_end);
6273 (subcommands[i].func)(eap, FALSE);
6274 break;
6275 }
6276 }
6277 vim_free(subcmd_name);
6278 if (eap->skip)
6279 --emsg_skip;
6280 }
6281}
6282
Bram Moolenaar860cae12010-06-05 23:22:07 +02006283 void
6284ex_ownsyntax(eap)
6285 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006286{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006287 char_u *old_value;
6288 char_u *new_value;
6289
Bram Moolenaar860cae12010-06-05 23:22:07 +02006290 if (curwin->w_s == &curwin->w_buffer->b_s)
6291 {
6292 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6293 memset(curwin->w_s, 0, sizeof(synblock_T));
6294#ifdef FEAT_SPELL
6295 curwin->w_p_spell = FALSE; /* No spell checking */
6296 clear_string_option(&curwin->w_s->b_p_spc);
6297 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar473de612013-06-08 18:19:48 +02006298 vim_regfree(curwin->w_s->b_cap_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006299 curwin->w_s->b_cap_prog = NULL;
6300 clear_string_option(&curwin->w_s->b_p_spl);
6301#endif
6302 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006303
6304 /* save value of b:current_syntax */
6305 old_value = get_var_value((char_u *)"b:current_syntax");
6306 if (old_value != NULL)
6307 old_value = vim_strsave(old_value);
6308
6309 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6310 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006311 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006312
6313 /* move value of b:current_syntax to w:current_syntax */
6314 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006315 if (new_value != NULL)
6316 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006317
6318 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006319 if (old_value == NULL)
6320 do_unlet((char_u *)"b:current_syntax", TRUE);
6321 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006322 {
6323 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6324 vim_free(old_value);
6325 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006326}
6327
6328 int
6329syntax_present(win)
6330 win_T *win;
6331{
6332 return (win->w_s->b_syn_patterns.ga_len != 0
6333 || win->w_s->b_syn_clusters.ga_len != 0
6334 || win->w_s->b_keywtab.ht_used > 0
6335 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006336}
6337
6338#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6339
6340static enum
6341{
6342 EXP_SUBCMD, /* expand ":syn" sub-commands */
6343 EXP_CASE /* expand ":syn case" arguments */
6344} expand_what;
6345
Bram Moolenaar4f688582007-07-24 12:34:30 +00006346/*
6347 * Reset include_link, include_default, include_none to 0.
6348 * Called when we are done expanding.
6349 */
6350 void
6351reset_expand_highlight()
6352{
6353 include_link = include_default = include_none = 0;
6354}
6355
6356/*
6357 * Handle command line completion for :match and :echohl command: Add "None"
6358 * as highlight group.
6359 */
6360 void
6361set_context_in_echohl_cmd(xp, arg)
6362 expand_T *xp;
6363 char_u *arg;
6364{
6365 xp->xp_context = EXPAND_HIGHLIGHT;
6366 xp->xp_pattern = arg;
6367 include_none = 1;
6368}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006369
6370/*
6371 * Handle command line completion for :syntax command.
6372 */
6373 void
6374set_context_in_syntax_cmd(xp, arg)
6375 expand_T *xp;
6376 char_u *arg;
6377{
6378 char_u *p;
6379
6380 /* Default: expand subcommands */
6381 xp->xp_context = EXPAND_SYNTAX;
6382 expand_what = EXP_SUBCMD;
6383 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006384 include_link = 0;
6385 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006386
6387 /* (part of) subcommand already typed */
6388 if (*arg != NUL)
6389 {
6390 p = skiptowhite(arg);
6391 if (*p != NUL) /* past first word */
6392 {
6393 xp->xp_pattern = skipwhite(p);
6394 if (*skiptowhite(xp->xp_pattern) != NUL)
6395 xp->xp_context = EXPAND_NOTHING;
6396 else if (STRNICMP(arg, "case", p - arg) == 0)
6397 expand_what = EXP_CASE;
6398 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6399 || STRNICMP(arg, "region", p - arg) == 0
6400 || STRNICMP(arg, "match", p - arg) == 0
6401 || STRNICMP(arg, "list", p - arg) == 0)
6402 xp->xp_context = EXPAND_HIGHLIGHT;
6403 else
6404 xp->xp_context = EXPAND_NOTHING;
6405 }
6406 }
6407}
6408
6409static char *(case_args[]) = {"match", "ignore", NULL};
6410
6411/*
6412 * Function given to ExpandGeneric() to obtain the list syntax names for
6413 * expansion.
6414 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006415 char_u *
6416get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006417 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006418 int idx;
6419{
6420 if (expand_what == EXP_SUBCMD)
6421 return (char_u *)subcommands[idx].name;
6422 return (char_u *)case_args[idx];
6423}
6424
6425#endif /* FEAT_CMDL_COMPL */
6426
Bram Moolenaar071d4272004-06-13 20:20:40 +00006427/*
6428 * Function called for expression evaluation: get syntax ID at file position.
6429 */
6430 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006431syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006432 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006433 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006434 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006435 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006436 int *spellp; /* return: can do spell checking */
6437 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006438{
6439 /* When the position is not after the current position and in the same
6440 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006441 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006442 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006443 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006444 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006445
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006446 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006447
6448 return (trans ? current_trans_id : current_id);
6449}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450
Bram Moolenaar860cae12010-06-05 23:22:07 +02006451#if defined(FEAT_CONCEAL) || defined(PROTO)
6452/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006453 * Get extra information about the syntax item. Must be called right after
6454 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006455 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006456 * Returns the current flags.
6457 */
6458 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006459get_syntax_info(seqnrp)
6460 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006461{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006462 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006463 return current_flags;
6464}
6465
6466/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006467 * Return conceal substitution character
6468 */
6469 int
6470syn_get_sub_char()
6471{
6472 return current_sub_char;
6473}
6474#endif
6475
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006476#if defined(FEAT_EVAL) || defined(PROTO)
6477/*
6478 * Return the syntax ID at position "i" in the current stack.
6479 * The caller must have called syn_get_id() before to fill the stack.
6480 * Returns -1 when "i" is out of range.
6481 */
6482 int
6483syn_get_stack_item(i)
6484 int i;
6485{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006486 if (i >= current_state.ga_len)
6487 {
6488 /* Need to invalidate the state, because we didn't properly finish it
6489 * for the last character, "keep_state" was TRUE. */
6490 invalidate_current_state();
6491 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006492 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006493 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006494 return CUR_STATE(i).si_id;
6495}
6496#endif
6497
Bram Moolenaar071d4272004-06-13 20:20:40 +00006498#if defined(FEAT_FOLDING) || defined(PROTO)
6499/*
6500 * Function called to get folding level for line "lnum" in window "wp".
6501 */
6502 int
6503syn_get_foldlevel(wp, lnum)
6504 win_T *wp;
6505 long lnum;
6506{
6507 int level = 0;
6508 int i;
6509
6510 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006511 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006512 {
6513 syntax_start(wp, lnum);
6514
6515 for (i = 0; i < current_state.ga_len; ++i)
6516 if (CUR_STATE(i).si_flags & HL_FOLD)
6517 ++level;
6518 }
6519 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006520 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006521 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006522 if (level < 0)
6523 level = 0;
6524 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006525 return level;
6526}
6527#endif
6528
Bram Moolenaarf7512552013-06-06 14:55:19 +02006529#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006530/*
6531 * ":syntime".
6532 */
6533 void
6534ex_syntime(eap)
6535 exarg_T *eap;
6536{
6537 if (STRCMP(eap->arg, "on") == 0)
6538 syn_time_on = TRUE;
6539 else if (STRCMP(eap->arg, "off") == 0)
6540 syn_time_on = FALSE;
6541 else if (STRCMP(eap->arg, "clear") == 0)
6542 syntime_clear();
6543 else if (STRCMP(eap->arg, "report") == 0)
6544 syntime_report();
6545 else
6546 EMSG2(_(e_invarg2), eap->arg);
6547}
6548
6549 static void
6550syn_clear_time(st)
6551 syn_time_T *st;
6552{
6553 profile_zero(&st->total);
6554 profile_zero(&st->slowest);
6555 st->count = 0;
6556 st->match = 0;
6557}
6558
6559/*
6560 * Clear the syntax timing for the current buffer.
6561 */
6562 static void
6563syntime_clear()
6564{
6565 int idx;
6566 synpat_T *spp;
6567
6568 if (!syntax_present(curwin))
6569 {
6570 MSG(_(msg_no_items));
6571 return;
6572 }
6573 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6574 {
6575 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6576 syn_clear_time(&spp->sp_time);
6577 }
6578}
6579
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006580#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6581/*
6582 * Function given to ExpandGeneric() to obtain the possible arguments of the
6583 * ":syntime {on,off,clear,report}" command.
6584 */
6585 char_u *
6586get_syntime_arg(xp, idx)
6587 expand_T *xp UNUSED;
6588 int idx;
6589{
6590 switch (idx)
6591 {
6592 case 0: return (char_u *)"on";
6593 case 1: return (char_u *)"off";
6594 case 2: return (char_u *)"clear";
6595 case 3: return (char_u *)"report";
6596 }
6597 return NULL;
6598}
6599#endif
6600
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006601typedef struct
6602{
6603 proftime_T total;
6604 int count;
6605 int match;
6606 proftime_T slowest;
6607 proftime_T average;
6608 int id;
6609 char_u *pattern;
6610} time_entry_T;
6611
6612 static int
6613#ifdef __BORLANDC__
6614_RTLENTRYF
6615#endif
6616syn_compare_syntime(v1, v2)
6617 const void *v1;
6618 const void *v2;
6619{
6620 const time_entry_T *s1 = v1;
6621 const time_entry_T *s2 = v2;
6622
6623 return profile_cmp(&s1->total, &s2->total);
6624}
6625
6626/*
6627 * Clear the syntax timing for the current buffer.
6628 */
6629 static void
6630syntime_report()
6631{
6632 int idx;
6633 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006634# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006635 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006636# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006637 int len;
6638 proftime_T total_total;
6639 int total_count = 0;
6640 garray_T ga;
6641 time_entry_T *p;
6642
6643 if (!syntax_present(curwin))
6644 {
6645 MSG(_(msg_no_items));
6646 return;
6647 }
6648
6649 ga_init2(&ga, sizeof(time_entry_T), 50);
6650 profile_zero(&total_total);
6651 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6652 {
6653 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6654 if (spp->sp_time.count > 0)
6655 {
6656 ga_grow(&ga, 1);
6657 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6658 p->total = spp->sp_time.total;
6659 profile_add(&total_total, &spp->sp_time.total);
6660 p->count = spp->sp_time.count;
6661 p->match = spp->sp_time.match;
6662 total_count += spp->sp_time.count;
6663 p->slowest = spp->sp_time.slowest;
6664# ifdef FEAT_FLOAT
6665 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6666 p->average = tm;
6667# endif
6668 p->id = spp->sp_syn.id;
6669 p->pattern = spp->sp_pattern;
6670 ++ga.ga_len;
6671 }
6672 }
6673
6674 /* sort on total time */
Bram Moolenaar4e312962013-06-06 21:19:51 +02006675 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
6676 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006677
6678 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6679 MSG_PUTS("\n");
6680 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6681 {
6682 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6683 p = ((time_entry_T *)ga.ga_data) + idx;
6684
6685 MSG_PUTS(profile_msg(&p->total));
6686 MSG_PUTS(" "); /* make sure there is always a separating space */
6687 msg_advance(13);
6688 msg_outnum(p->count);
6689 MSG_PUTS(" ");
6690 msg_advance(20);
6691 msg_outnum(p->match);
6692 MSG_PUTS(" ");
6693 msg_advance(26);
6694 MSG_PUTS(profile_msg(&p->slowest));
6695 MSG_PUTS(" ");
6696 msg_advance(38);
6697# ifdef FEAT_FLOAT
6698 MSG_PUTS(profile_msg(&p->average));
6699 MSG_PUTS(" ");
6700# endif
6701 msg_advance(50);
6702 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6703 MSG_PUTS(" ");
6704
6705 msg_advance(69);
6706 if (Columns < 80)
6707 len = 20; /* will wrap anyway */
6708 else
6709 len = Columns - 70;
6710 if (len > (int)STRLEN(p->pattern))
6711 len = (int)STRLEN(p->pattern);
6712 msg_outtrans_len(p->pattern, len);
6713 MSG_PUTS("\n");
6714 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006715 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006716 if (!got_int)
6717 {
6718 MSG_PUTS("\n");
6719 MSG_PUTS(profile_msg(&total_total));
6720 msg_advance(13);
6721 msg_outnum(total_count);
6722 MSG_PUTS("\n");
6723 }
6724}
6725#endif
6726
Bram Moolenaar071d4272004-06-13 20:20:40 +00006727#endif /* FEAT_SYN_HL */
6728
Bram Moolenaar071d4272004-06-13 20:20:40 +00006729/**************************************
6730 * Highlighting stuff *
6731 **************************************/
6732
6733/*
6734 * The default highlight groups. These are compiled-in for fast startup and
6735 * they still work when the runtime files can't be found.
6736 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006737 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6738 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006739 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006740#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006741# define CENT(a, b) b
6742#else
6743# define CENT(a, b) a
6744#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006745static char *(highlight_init_both[]) =
6746 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006747 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6748 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6749 CENT("IncSearch term=reverse cterm=reverse",
6750 "IncSearch term=reverse cterm=reverse gui=reverse"),
6751 CENT("ModeMsg term=bold cterm=bold",
6752 "ModeMsg term=bold cterm=bold gui=bold"),
6753 CENT("NonText term=bold ctermfg=Blue",
6754 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6755 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6756 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6757 CENT("StatusLineNC term=reverse cterm=reverse",
6758 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006759#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006760 CENT("VertSplit term=reverse cterm=reverse",
6761 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006762#endif
6763#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006764 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6765 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006766#endif
6767#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006768 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6769 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006770#endif
6771#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006772 CENT("PmenuSbar ctermbg=Grey",
6773 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006774#endif
6775#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006776 CENT("TabLineSel term=bold cterm=bold",
6777 "TabLineSel term=bold cterm=bold gui=bold"),
6778 CENT("TabLineFill term=reverse cterm=reverse",
6779 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006780#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006781#ifdef FEAT_GUI
6782 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006783 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006784#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006785 NULL
6786 };
6787
6788static char *(highlight_init_light[]) =
6789 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006790 CENT("Directory term=bold ctermfg=DarkBlue",
6791 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6792 CENT("LineNr term=underline ctermfg=Brown",
6793 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006794 CENT("CursorLineNr term=bold ctermfg=Brown",
6795 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006796 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6797 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6798 CENT("Question term=standout ctermfg=DarkGreen",
6799 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6800 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6801 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006802#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006803 CENT("SpellBad term=reverse ctermbg=LightRed",
6804 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6805 CENT("SpellCap term=reverse ctermbg=LightBlue",
6806 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6807 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6808 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6809 CENT("SpellLocal term=underline ctermbg=Cyan",
6810 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006811#endif
6812#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006813 CENT("PmenuThumb ctermbg=Black",
6814 "PmenuThumb ctermbg=Black guibg=Black"),
6815 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6816 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6817 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6818 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006819#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006820 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6821 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6822 CENT("Title term=bold ctermfg=DarkMagenta",
6823 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6824 CENT("WarningMsg term=standout ctermfg=DarkRed",
6825 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006826#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006827 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6828 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006829#endif
6830#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006831 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6832 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6833 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6834 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006835#endif
6836#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006837 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6838 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006839#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006840 CENT("Visual term=reverse",
6841 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006842#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006843 CENT("DiffAdd term=bold ctermbg=LightBlue",
6844 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6845 CENT("DiffChange term=bold ctermbg=LightMagenta",
6846 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6847 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6848 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006849#endif
6850#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006851 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6852 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006853#endif
6854#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006855 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006856 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006857 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006858 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006859 CENT("ColorColumn term=reverse ctermbg=LightRed",
6860 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006861#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006862#ifdef FEAT_CONCEAL
6863 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6864 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6865#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006866#ifdef FEAT_AUTOCMD
6867 CENT("MatchParen term=reverse ctermbg=Cyan",
6868 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6869#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006870#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006871 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006872#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006873 NULL
6874 };
6875
6876static char *(highlight_init_dark[]) =
6877 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006878 CENT("Directory term=bold ctermfg=LightCyan",
6879 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6880 CENT("LineNr term=underline ctermfg=Yellow",
6881 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006882 CENT("CursorLineNr term=bold ctermfg=Yellow",
6883 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006884 CENT("MoreMsg term=bold ctermfg=LightGreen",
6885 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6886 CENT("Question term=standout ctermfg=LightGreen",
6887 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6888 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6889 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6890 CENT("SpecialKey term=bold ctermfg=LightBlue",
6891 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006892#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006893 CENT("SpellBad term=reverse ctermbg=Red",
6894 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6895 CENT("SpellCap term=reverse ctermbg=Blue",
6896 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6897 CENT("SpellRare term=reverse ctermbg=Magenta",
6898 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6899 CENT("SpellLocal term=underline ctermbg=Cyan",
6900 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006901#endif
6902#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006903 CENT("PmenuThumb ctermbg=White",
6904 "PmenuThumb ctermbg=White guibg=White"),
6905 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6906 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02006907 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6908 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006909#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006910 CENT("Title term=bold ctermfg=LightMagenta",
6911 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6912 CENT("WarningMsg term=standout ctermfg=LightRed",
6913 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006914#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006915 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6916 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006917#endif
6918#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006919 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6920 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6921 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6922 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006923#endif
6924#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006925 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6926 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006927#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006928 CENT("Visual term=reverse",
6929 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006930#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006931 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6932 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6933 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6934 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6935 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6936 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006937#endif
6938#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006939 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6940 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006941#endif
6942#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006943 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006944 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006945 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006946 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006947 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6948 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006949#endif
6950#ifdef FEAT_AUTOCMD
6951 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6952 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006953#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006954#ifdef FEAT_CONCEAL
6955 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6956 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6957#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006958#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006959 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006960#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006961 NULL
6962 };
6963
6964 void
6965init_highlight(both, reset)
6966 int both; /* include groups where 'bg' doesn't matter */
6967 int reset; /* clear group first */
6968{
6969 int i;
6970 char **pp;
6971 static int had_both = FALSE;
6972#ifdef FEAT_EVAL
6973 char_u *p;
6974
6975 /*
6976 * Try finding the color scheme file. Used when a color file was loaded
6977 * and 'background' or 't_Co' is changed.
6978 */
6979 p = get_var_value((char_u *)"g:colors_name");
6980 if (p != NULL && load_colors(p) == OK)
6981 return;
6982#endif
6983
6984 /*
6985 * Didn't use a color file, use the compiled-in colors.
6986 */
6987 if (both)
6988 {
6989 had_both = TRUE;
6990 pp = highlight_init_both;
6991 for (i = 0; pp[i] != NULL; ++i)
6992 do_highlight((char_u *)pp[i], reset, TRUE);
6993 }
6994 else if (!had_both)
6995 /* Don't do anything before the call with both == TRUE from main().
6996 * Not everything has been setup then, and that call will overrule
6997 * everything anyway. */
6998 return;
6999
7000 if (*p_bg == 'l')
7001 pp = highlight_init_light;
7002 else
7003 pp = highlight_init_dark;
7004 for (i = 0; pp[i] != NULL; ++i)
7005 do_highlight((char_u *)pp[i], reset, TRUE);
7006
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007007 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007008 * depend on the number of colors available.
7009 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007010 * to avoid Statement highlighted text disappears.
7011 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007012 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007013 do_highlight((char_u *)(*p_bg == 'l'
7014 ? "Visual cterm=NONE ctermbg=LightGrey"
7015 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007016 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007017 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007018 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7019 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007020 if (*p_bg == 'l')
7021 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7022 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007023
Bram Moolenaar071d4272004-06-13 20:20:40 +00007024#ifdef FEAT_SYN_HL
7025 /*
7026 * If syntax highlighting is enabled load the highlighting for it.
7027 */
7028 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007029 {
7030 static int recursive = 0;
7031
7032 if (recursive >= 5)
7033 EMSG(_("E679: recursive loop loading syncolor.vim"));
7034 else
7035 {
7036 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007037 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007038 --recursive;
7039 }
7040 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007041#endif
7042}
7043
7044/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007045 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007046 * Return OK for success, FAIL for failure.
7047 */
7048 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007049load_colors(name)
7050 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007051{
7052 char_u *buf;
7053 int retval = FAIL;
7054 static int recursive = FALSE;
7055
7056 /* When being called recursively, this is probably because setting
7057 * 'background' caused the highlighting to be reloaded. This means it is
7058 * working, thus we should return OK. */
7059 if (recursive)
7060 return OK;
7061
7062 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007063 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007064 if (buf != NULL)
7065 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007066 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007067 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007068 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007069#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007070 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007071#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007072 }
7073 recursive = FALSE;
7074
7075 return retval;
7076}
7077
7078/*
7079 * Handle the ":highlight .." command.
7080 * When using ":hi clear" this is called recursively for each group with
7081 * "forceit" and "init" both TRUE.
7082 */
7083 void
7084do_highlight(line, forceit, init)
7085 char_u *line;
7086 int forceit;
7087 int init; /* TRUE when called for initializing */
7088{
7089 char_u *name_end;
7090 char_u *p;
7091 char_u *linep;
7092 char_u *key_start;
7093 char_u *arg_start;
7094 char_u *key = NULL, *arg = NULL;
7095 long i;
7096 int off;
7097 int len;
7098 int attr;
7099 int id;
7100 int idx;
7101 int dodefault = FALSE;
7102 int doclear = FALSE;
7103 int dolink = FALSE;
7104 int error = FALSE;
7105 int color;
7106 int is_normal_group = FALSE; /* "Normal" group */
7107#ifdef FEAT_GUI_X11
7108 int is_menu_group = FALSE; /* "Menu" group */
7109 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7110 int is_tooltip_group = FALSE; /* "Tooltip" group */
7111 int do_colors = FALSE; /* need to update colors? */
7112#else
7113# define is_menu_group 0
7114# define is_tooltip_group 0
7115#endif
7116
7117 /*
7118 * If no argument, list current highlighting.
7119 */
7120 if (ends_excmd(*line))
7121 {
7122 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7123 /* TODO: only call when the group has attributes set */
7124 highlight_list_one((int)i);
7125 return;
7126 }
7127
7128 /*
7129 * Isolate the name.
7130 */
7131 name_end = skiptowhite(line);
7132 linep = skipwhite(name_end);
7133
7134 /*
7135 * Check for "default" argument.
7136 */
7137 if (STRNCMP(line, "default", name_end - line) == 0)
7138 {
7139 dodefault = TRUE;
7140 line = linep;
7141 name_end = skiptowhite(line);
7142 linep = skipwhite(name_end);
7143 }
7144
7145 /*
7146 * Check for "clear" or "link" argument.
7147 */
7148 if (STRNCMP(line, "clear", name_end - line) == 0)
7149 doclear = TRUE;
7150 if (STRNCMP(line, "link", name_end - line) == 0)
7151 dolink = TRUE;
7152
7153 /*
7154 * ":highlight {group-name}": list highlighting for one group.
7155 */
7156 if (!doclear && !dolink && ends_excmd(*linep))
7157 {
7158 id = syn_namen2id(line, (int)(name_end - line));
7159 if (id == 0)
7160 EMSG2(_("E411: highlight group not found: %s"), line);
7161 else
7162 highlight_list_one(id);
7163 return;
7164 }
7165
7166 /*
7167 * Handle ":highlight link {from} {to}" command.
7168 */
7169 if (dolink)
7170 {
7171 char_u *from_start = linep;
7172 char_u *from_end;
7173 char_u *to_start;
7174 char_u *to_end;
7175 int from_id;
7176 int to_id;
7177
7178 from_end = skiptowhite(from_start);
7179 to_start = skipwhite(from_end);
7180 to_end = skiptowhite(to_start);
7181
7182 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7183 {
7184 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7185 from_start);
7186 return;
7187 }
7188
7189 if (!ends_excmd(*skipwhite(to_end)))
7190 {
7191 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7192 return;
7193 }
7194
7195 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7196 if (STRNCMP(to_start, "NONE", 4) == 0)
7197 to_id = 0;
7198 else
7199 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7200
7201 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7202 {
7203 /*
7204 * Don't allow a link when there already is some highlighting
7205 * for the group, unless '!' is used
7206 */
7207 if (to_id > 0 && !forceit && !init
7208 && hl_has_settings(from_id - 1, dodefault))
7209 {
7210 if (sourcing_name == NULL && !dodefault)
7211 EMSG(_("E414: group has settings, highlight link ignored"));
7212 }
7213 else
7214 {
7215 if (!init)
7216 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7217 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007218#ifdef FEAT_EVAL
7219 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7220#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007221 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007222 }
7223 }
7224
7225 /* Only call highlight_changed() once, after sourcing a syntax file */
7226 need_highlight_changed = TRUE;
7227
7228 return;
7229 }
7230
7231 if (doclear)
7232 {
7233 /*
7234 * ":highlight clear [group]" command.
7235 */
7236 line = linep;
7237 if (ends_excmd(*line))
7238 {
7239#ifdef FEAT_GUI
7240 /* First, we do not destroy the old values, but allocate the new
7241 * ones and update the display. THEN we destroy the old values.
7242 * If we destroy the old values first, then the old values
7243 * (such as GuiFont's or GuiFontset's) will still be displayed but
7244 * invalid because they were free'd.
7245 */
7246 if (gui.in_use)
7247 {
7248# ifdef FEAT_BEVAL_TIP
7249 gui_init_tooltip_font();
7250# endif
7251# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7252 gui_init_menu_font();
7253# endif
7254 }
7255# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7256 gui_mch_def_colors();
7257# endif
7258# ifdef FEAT_GUI_X11
7259# ifdef FEAT_MENU
7260
7261 /* This only needs to be done when there is no Menu highlight
7262 * group defined by default, which IS currently the case.
7263 */
7264 gui_mch_new_menu_colors();
7265# endif
7266 if (gui.in_use)
7267 {
7268 gui_new_scrollbar_colors();
7269# ifdef FEAT_BEVAL
7270 gui_mch_new_tooltip_colors();
7271# endif
7272# ifdef FEAT_MENU
7273 gui_mch_new_menu_font();
7274# endif
7275 }
7276# endif
7277
7278 /* Ok, we're done allocating the new default graphics items.
7279 * The screen should already be refreshed at this point.
7280 * It is now Ok to clear out the old data.
7281 */
7282#endif
7283#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007284 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007285#endif
7286 restore_cterm_colors();
7287
7288 /*
7289 * Clear all default highlight groups and load the defaults.
7290 */
7291 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7292 highlight_clear(idx);
7293 init_highlight(TRUE, TRUE);
7294#ifdef FEAT_GUI
7295 if (gui.in_use)
7296 highlight_gui_started();
7297#endif
7298 highlight_changed();
7299 redraw_later_clear();
7300 return;
7301 }
7302 name_end = skiptowhite(line);
7303 linep = skipwhite(name_end);
7304 }
7305
7306 /*
7307 * Find the group name in the table. If it does not exist yet, add it.
7308 */
7309 id = syn_check_group(line, (int)(name_end - line));
7310 if (id == 0) /* failed (out of memory) */
7311 return;
7312 idx = id - 1; /* index is ID minus one */
7313
7314 /* Return if "default" was used and the group already has settings. */
7315 if (dodefault && hl_has_settings(idx, TRUE))
7316 return;
7317
7318 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7319 is_normal_group = TRUE;
7320#ifdef FEAT_GUI_X11
7321 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7322 is_menu_group = TRUE;
7323 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7324 is_scrollbar_group = TRUE;
7325 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7326 is_tooltip_group = TRUE;
7327#endif
7328
7329 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7330 if (doclear || (forceit && init))
7331 {
7332 highlight_clear(idx);
7333 if (!doclear)
7334 HL_TABLE()[idx].sg_set = 0;
7335 }
7336
7337 if (!doclear)
7338 while (!ends_excmd(*linep))
7339 {
7340 key_start = linep;
7341 if (*linep == '=')
7342 {
7343 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7344 error = TRUE;
7345 break;
7346 }
7347
7348 /*
7349 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7350 * "guibg").
7351 */
7352 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7353 ++linep;
7354 vim_free(key);
7355 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7356 if (key == NULL)
7357 {
7358 error = TRUE;
7359 break;
7360 }
7361 linep = skipwhite(linep);
7362
7363 if (STRCMP(key, "NONE") == 0)
7364 {
7365 if (!init || HL_TABLE()[idx].sg_set == 0)
7366 {
7367 if (!init)
7368 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7369 highlight_clear(idx);
7370 }
7371 continue;
7372 }
7373
7374 /*
7375 * Check for the equal sign.
7376 */
7377 if (*linep != '=')
7378 {
7379 EMSG2(_("E416: missing equal sign: %s"), key_start);
7380 error = TRUE;
7381 break;
7382 }
7383 ++linep;
7384
7385 /*
7386 * Isolate the argument.
7387 */
7388 linep = skipwhite(linep);
7389 if (*linep == '\'') /* guifg='color name' */
7390 {
7391 arg_start = ++linep;
7392 linep = vim_strchr(linep, '\'');
7393 if (linep == NULL)
7394 {
7395 EMSG2(_(e_invarg2), key_start);
7396 error = TRUE;
7397 break;
7398 }
7399 }
7400 else
7401 {
7402 arg_start = linep;
7403 linep = skiptowhite(linep);
7404 }
7405 if (linep == arg_start)
7406 {
7407 EMSG2(_("E417: missing argument: %s"), key_start);
7408 error = TRUE;
7409 break;
7410 }
7411 vim_free(arg);
7412 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7413 if (arg == NULL)
7414 {
7415 error = TRUE;
7416 break;
7417 }
7418 if (*linep == '\'')
7419 ++linep;
7420
7421 /*
7422 * Store the argument.
7423 */
7424 if ( STRCMP(key, "TERM") == 0
7425 || STRCMP(key, "CTERM") == 0
7426 || STRCMP(key, "GUI") == 0)
7427 {
7428 attr = 0;
7429 off = 0;
7430 while (arg[off] != NUL)
7431 {
7432 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7433 {
7434 len = (int)STRLEN(hl_name_table[i]);
7435 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7436 {
7437 attr |= hl_attr_table[i];
7438 off += len;
7439 break;
7440 }
7441 }
7442 if (i < 0)
7443 {
7444 EMSG2(_("E418: Illegal value: %s"), arg);
7445 error = TRUE;
7446 break;
7447 }
7448 if (arg[off] == ',') /* another one follows */
7449 ++off;
7450 }
7451 if (error)
7452 break;
7453 if (*key == 'T')
7454 {
7455 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7456 {
7457 if (!init)
7458 HL_TABLE()[idx].sg_set |= SG_TERM;
7459 HL_TABLE()[idx].sg_term = attr;
7460 }
7461 }
7462 else if (*key == 'C')
7463 {
7464 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7465 {
7466 if (!init)
7467 HL_TABLE()[idx].sg_set |= SG_CTERM;
7468 HL_TABLE()[idx].sg_cterm = attr;
7469 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7470 }
7471 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007472#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007473 else
7474 {
7475 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7476 {
7477 if (!init)
7478 HL_TABLE()[idx].sg_set |= SG_GUI;
7479 HL_TABLE()[idx].sg_gui = attr;
7480 }
7481 }
7482#endif
7483 }
7484 else if (STRCMP(key, "FONT") == 0)
7485 {
7486 /* in non-GUI fonts are simply ignored */
7487#ifdef FEAT_GUI
7488 if (!gui.shell_created)
7489 {
7490 /* GUI not started yet, always accept the name. */
7491 vim_free(HL_TABLE()[idx].sg_font_name);
7492 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7493 }
7494 else
7495 {
7496 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7497# ifdef FEAT_XFONTSET
7498 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7499# endif
7500 /* First, save the current font/fontset.
7501 * Then try to allocate the font/fontset.
7502 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7503 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7504 */
7505
7506 HL_TABLE()[idx].sg_font = NOFONT;
7507# ifdef FEAT_XFONTSET
7508 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7509# endif
7510 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007511 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007512
7513# ifdef FEAT_XFONTSET
7514 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7515 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007516 /* New fontset was accepted. Free the old one, if there
7517 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007518 gui_mch_free_fontset(temp_sg_fontset);
7519 vim_free(HL_TABLE()[idx].sg_font_name);
7520 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7521 }
7522 else
7523 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7524# endif
7525 if (HL_TABLE()[idx].sg_font != NOFONT)
7526 {
7527 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007528 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007529 gui_mch_free_font(temp_sg_font);
7530 vim_free(HL_TABLE()[idx].sg_font_name);
7531 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7532 }
7533 else
7534 HL_TABLE()[idx].sg_font = temp_sg_font;
7535 }
7536#endif
7537 }
7538 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7539 {
7540 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7541 {
7542 if (!init)
7543 HL_TABLE()[idx].sg_set |= SG_CTERM;
7544
7545 /* When setting the foreground color, and previously the "bold"
7546 * flag was set for a light color, reset it now */
7547 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7548 {
7549 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7550 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7551 }
7552
7553 if (VIM_ISDIGIT(*arg))
7554 color = atoi((char *)arg);
7555 else if (STRICMP(arg, "fg") == 0)
7556 {
7557 if (cterm_normal_fg_color)
7558 color = cterm_normal_fg_color - 1;
7559 else
7560 {
7561 EMSG(_("E419: FG color unknown"));
7562 error = TRUE;
7563 break;
7564 }
7565 }
7566 else if (STRICMP(arg, "bg") == 0)
7567 {
7568 if (cterm_normal_bg_color > 0)
7569 color = cterm_normal_bg_color - 1;
7570 else
7571 {
7572 EMSG(_("E420: BG color unknown"));
7573 error = TRUE;
7574 break;
7575 }
7576 }
7577 else
7578 {
7579 static char *(color_names[28]) = {
7580 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7581 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7582 "Gray", "Grey",
7583 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7584 "Blue", "LightBlue", "Green", "LightGreen",
7585 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7586 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7587 static int color_numbers_16[28] = {0, 1, 2, 3,
7588 4, 5, 6, 6,
7589 7, 7,
7590 7, 7, 8, 8,
7591 9, 9, 10, 10,
7592 11, 11, 12, 12, 13,
7593 13, 14, 14, 15, -1};
7594 /* for xterm with 88 colors... */
7595 static int color_numbers_88[28] = {0, 4, 2, 6,
7596 1, 5, 32, 72,
7597 84, 84,
7598 7, 7, 82, 82,
7599 12, 43, 10, 61,
7600 14, 63, 9, 74, 13,
7601 75, 11, 78, 15, -1};
7602 /* for xterm with 256 colors... */
7603 static int color_numbers_256[28] = {0, 4, 2, 6,
7604 1, 5, 130, 130,
7605 248, 248,
7606 7, 7, 242, 242,
7607 12, 81, 10, 121,
7608 14, 159, 9, 224, 13,
7609 225, 11, 229, 15, -1};
7610 /* for terminals with less than 16 colors... */
7611 static int color_numbers_8[28] = {0, 4, 2, 6,
7612 1, 5, 3, 3,
7613 7, 7,
7614 7, 7, 0+8, 0+8,
7615 4+8, 4+8, 2+8, 2+8,
7616 6+8, 6+8, 1+8, 1+8, 5+8,
7617 5+8, 3+8, 3+8, 7+8, -1};
7618#if defined(__QNXNTO__)
7619 static int *color_numbers_8_qansi = color_numbers_8;
7620 /* On qnx, the 8 & 16 color arrays are the same */
7621 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7622 color_numbers_8_qansi = color_numbers_16;
7623#endif
7624
7625 /* reduce calls to STRICMP a bit, it can be slow */
7626 off = TOUPPER_ASC(*arg);
7627 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7628 if (off == color_names[i][0]
7629 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7630 break;
7631 if (i < 0)
7632 {
7633 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7634 error = TRUE;
7635 break;
7636 }
7637
7638 /* Use the _16 table to check if its a valid color name. */
7639 color = color_numbers_16[i];
7640 if (color >= 0)
7641 {
7642 if (t_colors == 8)
7643 {
7644 /* t_Co is 8: use the 8 colors table */
7645#if defined(__QNXNTO__)
7646 color = color_numbers_8_qansi[i];
7647#else
7648 color = color_numbers_8[i];
7649#endif
7650 if (key[5] == 'F')
7651 {
7652 /* set/reset bold attribute to get light foreground
7653 * colors (on some terminals, e.g. "linux") */
7654 if (color & 8)
7655 {
7656 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7657 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7658 }
7659 else
7660 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7661 }
7662 color &= 7; /* truncate to 8 colors */
7663 }
7664 else if (t_colors == 16 || t_colors == 88
7665 || t_colors == 256)
7666 {
7667 /*
7668 * Guess: if the termcap entry ends in 'm', it is
7669 * probably an xterm-like terminal. Use the changed
7670 * order for colors.
7671 */
7672 if (*T_CAF != NUL)
7673 p = T_CAF;
7674 else
7675 p = T_CSF;
7676 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7677 switch (t_colors)
7678 {
7679 case 16:
7680 color = color_numbers_8[i];
7681 break;
7682 case 88:
7683 color = color_numbers_88[i];
7684 break;
7685 case 256:
7686 color = color_numbers_256[i];
7687 break;
7688 }
7689 }
7690 }
7691 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007692 /* Add one to the argument, to avoid zero. Zero is used for
7693 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007694 if (key[5] == 'F')
7695 {
7696 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7697 if (is_normal_group)
7698 {
7699 cterm_normal_fg_color = color + 1;
7700 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7701#ifdef FEAT_GUI
7702 /* Don't do this if the GUI is used. */
7703 if (!gui.in_use && !gui.starting)
7704#endif
7705 {
7706 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007707 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007708 term_fg_color(color);
7709 }
7710 }
7711 }
7712 else
7713 {
7714 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7715 if (is_normal_group)
7716 {
7717 cterm_normal_bg_color = color + 1;
7718#ifdef FEAT_GUI
7719 /* Don't mess with 'background' if the GUI is used. */
7720 if (!gui.in_use && !gui.starting)
7721#endif
7722 {
7723 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007724 if (color >= 0)
7725 {
7726 if (termcap_active)
7727 term_bg_color(color);
7728 if (t_colors < 16)
7729 i = (color == 0 || color == 4);
7730 else
7731 i = (color < 7 || color == 8);
7732 /* Set the 'background' option if the value is
7733 * wrong. */
7734 if (i != (*p_bg == 'd'))
7735 set_option_value((char_u *)"bg", 0L,
7736 i ? (char_u *)"dark"
7737 : (char_u *)"light", 0);
7738 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007739 }
7740 }
7741 }
7742 }
7743 }
7744 else if (STRCMP(key, "GUIFG") == 0)
7745 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007746#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007747 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007748 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007749 if (!init)
7750 HL_TABLE()[idx].sg_set |= SG_GUI;
7751
Bram Moolenaar61623362010-07-14 22:04:22 +02007752# ifdef FEAT_GUI
7753 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007754 i = color_name2handle(arg);
7755 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7756 {
7757 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007758# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007759 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7760 if (STRCMP(arg, "NONE"))
7761 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7762 else
7763 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007764# ifdef FEAT_GUI
7765# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007766 if (is_menu_group)
7767 gui.menu_fg_pixel = i;
7768 if (is_scrollbar_group)
7769 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007770# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007771 if (is_tooltip_group)
7772 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007773# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007774 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007775# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007776 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007777# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007778 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007779#endif
7780 }
7781 else if (STRCMP(key, "GUIBG") == 0)
7782 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007783#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007784 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007785 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007786 if (!init)
7787 HL_TABLE()[idx].sg_set |= SG_GUI;
7788
Bram Moolenaar61623362010-07-14 22:04:22 +02007789# ifdef FEAT_GUI
7790 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007791 i = color_name2handle(arg);
7792 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7793 {
7794 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007795# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007796 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7797 if (STRCMP(arg, "NONE") != 0)
7798 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7799 else
7800 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007801# ifdef FEAT_GUI
7802# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007803 if (is_menu_group)
7804 gui.menu_bg_pixel = i;
7805 if (is_scrollbar_group)
7806 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007807# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007808 if (is_tooltip_group)
7809 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007810# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007811 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007812# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007813 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007814# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007815 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007816#endif
7817 }
7818 else if (STRCMP(key, "GUISP") == 0)
7819 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007820#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007821 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7822 {
7823 if (!init)
7824 HL_TABLE()[idx].sg_set |= SG_GUI;
7825
Bram Moolenaar61623362010-07-14 22:04:22 +02007826# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007827 i = color_name2handle(arg);
7828 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7829 {
7830 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007831# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007832 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7833 if (STRCMP(arg, "NONE") != 0)
7834 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7835 else
7836 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007837# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007838 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007839# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007840 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007841#endif
7842 }
7843 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7844 {
7845 char_u buf[100];
7846 char_u *tname;
7847
7848 if (!init)
7849 HL_TABLE()[idx].sg_set |= SG_TERM;
7850
7851 /*
7852 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007853 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007854 */
7855 if (STRNCMP(arg, "t_", 2) == 0)
7856 {
7857 off = 0;
7858 buf[0] = 0;
7859 while (arg[off] != NUL)
7860 {
7861 /* Isolate one termcap name */
7862 for (len = 0; arg[off + len] &&
7863 arg[off + len] != ','; ++len)
7864 ;
7865 tname = vim_strnsave(arg + off, len);
7866 if (tname == NULL) /* out of memory */
7867 {
7868 error = TRUE;
7869 break;
7870 }
7871 /* lookup the escape sequence for the item */
7872 p = get_term_code(tname);
7873 vim_free(tname);
7874 if (p == NULL) /* ignore non-existing things */
7875 p = (char_u *)"";
7876
7877 /* Append it to the already found stuff */
7878 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7879 {
7880 EMSG2(_("E422: terminal code too long: %s"), arg);
7881 error = TRUE;
7882 break;
7883 }
7884 STRCAT(buf, p);
7885
7886 /* Advance to the next item */
7887 off += len;
7888 if (arg[off] == ',') /* another one follows */
7889 ++off;
7890 }
7891 }
7892 else
7893 {
7894 /*
7895 * Copy characters from arg[] to buf[], translating <> codes.
7896 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007897 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007898 {
7899 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007900 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007901 off += len;
7902 else /* copy as normal char */
7903 buf[off++] = *p++;
7904 }
7905 buf[off] = NUL;
7906 }
7907 if (error)
7908 break;
7909
7910 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7911 p = NULL;
7912 else
7913 p = vim_strsave(buf);
7914 if (key[2] == 'A')
7915 {
7916 vim_free(HL_TABLE()[idx].sg_start);
7917 HL_TABLE()[idx].sg_start = p;
7918 }
7919 else
7920 {
7921 vim_free(HL_TABLE()[idx].sg_stop);
7922 HL_TABLE()[idx].sg_stop = p;
7923 }
7924 }
7925 else
7926 {
7927 EMSG2(_("E423: Illegal argument: %s"), key_start);
7928 error = TRUE;
7929 break;
7930 }
7931
7932 /*
7933 * When highlighting has been given for a group, don't link it.
7934 */
7935 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7936 HL_TABLE()[idx].sg_link = 0;
7937
7938 /*
7939 * Continue with next argument.
7940 */
7941 linep = skipwhite(linep);
7942 }
7943
7944 /*
7945 * If there is an error, and it's a new entry, remove it from the table.
7946 */
7947 if (error && idx == highlight_ga.ga_len)
7948 syn_unadd_group();
7949 else
7950 {
7951 if (is_normal_group)
7952 {
7953 HL_TABLE()[idx].sg_term_attr = 0;
7954 HL_TABLE()[idx].sg_cterm_attr = 0;
7955#ifdef FEAT_GUI
7956 HL_TABLE()[idx].sg_gui_attr = 0;
7957 /*
7958 * Need to update all groups, because they might be using "bg"
7959 * and/or "fg", which have been changed now.
7960 */
7961 if (gui.in_use)
7962 highlight_gui_started();
7963#endif
7964 }
7965#ifdef FEAT_GUI_X11
7966# ifdef FEAT_MENU
7967 else if (is_menu_group)
7968 {
7969 if (gui.in_use && do_colors)
7970 gui_mch_new_menu_colors();
7971 }
7972# endif
7973 else if (is_scrollbar_group)
7974 {
7975 if (gui.in_use && do_colors)
7976 gui_new_scrollbar_colors();
7977 }
7978# ifdef FEAT_BEVAL
7979 else if (is_tooltip_group)
7980 {
7981 if (gui.in_use && do_colors)
7982 gui_mch_new_tooltip_colors();
7983 }
7984# endif
7985#endif
7986 else
7987 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007988#ifdef FEAT_EVAL
7989 HL_TABLE()[idx].sg_scriptID = current_SID;
7990#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007991 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007992 }
7993 vim_free(key);
7994 vim_free(arg);
7995
7996 /* Only call highlight_changed() once, after sourcing a syntax file */
7997 need_highlight_changed = TRUE;
7998}
7999
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008000#if defined(EXITFREE) || defined(PROTO)
8001 void
8002free_highlight()
8003{
8004 int i;
8005
8006 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008007 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008008 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008009 vim_free(HL_TABLE()[i].sg_name);
8010 vim_free(HL_TABLE()[i].sg_name_u);
8011 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008012 ga_clear(&highlight_ga);
8013}
8014#endif
8015
Bram Moolenaar071d4272004-06-13 20:20:40 +00008016/*
8017 * Reset the cterm colors to what they were before Vim was started, if
8018 * possible. Otherwise reset them to zero.
8019 */
8020 void
8021restore_cterm_colors()
8022{
8023#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
8024 /* Since t_me has been set, this probably means that the user
8025 * wants to use this as default colors. Need to reset default
8026 * background/foreground colors. */
8027 mch_set_normal_colors();
8028#else
8029 cterm_normal_fg_color = 0;
8030 cterm_normal_fg_bold = 0;
8031 cterm_normal_bg_color = 0;
8032#endif
8033}
8034
8035/*
8036 * Return TRUE if highlight group "idx" has any settings.
8037 * When "check_link" is TRUE also check for an existing link.
8038 */
8039 static int
8040hl_has_settings(idx, check_link)
8041 int idx;
8042 int check_link;
8043{
8044 return ( HL_TABLE()[idx].sg_term_attr != 0
8045 || HL_TABLE()[idx].sg_cterm_attr != 0
8046#ifdef FEAT_GUI
8047 || HL_TABLE()[idx].sg_gui_attr != 0
8048#endif
8049 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8050}
8051
8052/*
8053 * Clear highlighting for one group.
8054 */
8055 static void
8056highlight_clear(idx)
8057 int idx;
8058{
8059 HL_TABLE()[idx].sg_term = 0;
8060 vim_free(HL_TABLE()[idx].sg_start);
8061 HL_TABLE()[idx].sg_start = NULL;
8062 vim_free(HL_TABLE()[idx].sg_stop);
8063 HL_TABLE()[idx].sg_stop = NULL;
8064 HL_TABLE()[idx].sg_term_attr = 0;
8065 HL_TABLE()[idx].sg_cterm = 0;
8066 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8067 HL_TABLE()[idx].sg_cterm_fg = 0;
8068 HL_TABLE()[idx].sg_cterm_bg = 0;
8069 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008070#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008071 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008072 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8073 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008074 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8075 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008076 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8077 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008078#endif
8079#ifdef FEAT_GUI
8080 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8081 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
8082 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008083 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8084 HL_TABLE()[idx].sg_font = NOFONT;
8085# ifdef FEAT_XFONTSET
8086 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8087 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8088# endif
8089 vim_free(HL_TABLE()[idx].sg_font_name);
8090 HL_TABLE()[idx].sg_font_name = NULL;
8091 HL_TABLE()[idx].sg_gui_attr = 0;
8092#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008093#ifdef FEAT_EVAL
8094 /* Clear the script ID only when there is no link, since that is not
8095 * cleared. */
8096 if (HL_TABLE()[idx].sg_link == 0)
8097 HL_TABLE()[idx].sg_scriptID = 0;
8098#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008099}
8100
8101#if defined(FEAT_GUI) || defined(PROTO)
8102/*
8103 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008104 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008105 * "Tooltip" colors.
8106 */
8107 void
8108set_normal_colors()
8109{
8110 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008111 &gui.norm_pixel, &gui.back_pixel,
8112 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008113 {
8114 gui_mch_new_colors();
8115 must_redraw = CLEAR;
8116 }
8117#ifdef FEAT_GUI_X11
8118 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008119 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8120 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008121 {
8122# ifdef FEAT_MENU
8123 gui_mch_new_menu_colors();
8124# endif
8125 must_redraw = CLEAR;
8126 }
8127# ifdef FEAT_BEVAL
8128 if (set_group_colors((char_u *)"Tooltip",
8129 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8130 FALSE, FALSE, TRUE))
8131 {
8132# ifdef FEAT_TOOLBAR
8133 gui_mch_new_tooltip_colors();
8134# endif
8135 must_redraw = CLEAR;
8136 }
8137#endif
8138 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008139 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8140 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008141 {
8142 gui_new_scrollbar_colors();
8143 must_redraw = CLEAR;
8144 }
8145#endif
8146}
8147
8148/*
8149 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8150 */
8151 static int
8152set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
8153 char_u *name;
8154 guicolor_T *fgp;
8155 guicolor_T *bgp;
8156 int do_menu;
8157 int use_norm;
8158 int do_tooltip;
8159{
8160 int idx;
8161
8162 idx = syn_name2id(name) - 1;
8163 if (idx >= 0)
8164 {
8165 gui_do_one_color(idx, do_menu, do_tooltip);
8166
8167 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8168 *fgp = HL_TABLE()[idx].sg_gui_fg;
8169 else if (use_norm)
8170 *fgp = gui.def_norm_pixel;
8171 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8172 *bgp = HL_TABLE()[idx].sg_gui_bg;
8173 else if (use_norm)
8174 *bgp = gui.def_back_pixel;
8175 return TRUE;
8176 }
8177 return FALSE;
8178}
8179
8180/*
8181 * Get the font of the "Normal" group.
8182 * Returns "" when it's not found or not set.
8183 */
8184 char_u *
8185hl_get_font_name()
8186{
8187 int id;
8188 char_u *s;
8189
8190 id = syn_name2id((char_u *)"Normal");
8191 if (id > 0)
8192 {
8193 s = HL_TABLE()[id - 1].sg_font_name;
8194 if (s != NULL)
8195 return s;
8196 }
8197 return (char_u *)"";
8198}
8199
8200/*
8201 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8202 * actually chosen to be used.
8203 */
8204 void
8205hl_set_font_name(font_name)
8206 char_u *font_name;
8207{
8208 int id;
8209
8210 id = syn_name2id((char_u *)"Normal");
8211 if (id > 0)
8212 {
8213 vim_free(HL_TABLE()[id - 1].sg_font_name);
8214 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8215 }
8216}
8217
8218/*
8219 * Set background color for "Normal" group. Called by gui_set_bg_color()
8220 * when the color is known.
8221 */
8222 void
8223hl_set_bg_color_name(name)
8224 char_u *name; /* must have been allocated */
8225{
8226 int id;
8227
8228 if (name != NULL)
8229 {
8230 id = syn_name2id((char_u *)"Normal");
8231 if (id > 0)
8232 {
8233 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8234 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8235 }
8236 }
8237}
8238
8239/*
8240 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8241 * when the color is known.
8242 */
8243 void
8244hl_set_fg_color_name(name)
8245 char_u *name; /* must have been allocated */
8246{
8247 int id;
8248
8249 if (name != NULL)
8250 {
8251 id = syn_name2id((char_u *)"Normal");
8252 if (id > 0)
8253 {
8254 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8255 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8256 }
8257 }
8258}
8259
8260/*
8261 * Return the handle for a color name.
8262 * Returns INVALCOLOR when failed.
8263 */
8264 static guicolor_T
8265color_name2handle(name)
8266 char_u *name;
8267{
8268 if (STRCMP(name, "NONE") == 0)
8269 return INVALCOLOR;
8270
8271 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8272 return gui.norm_pixel;
8273 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8274 return gui.back_pixel;
8275
8276 return gui_get_color(name);
8277}
8278
8279/*
8280 * Return the handle for a font name.
8281 * Returns NOFONT when failed.
8282 */
8283 static GuiFont
8284font_name2handle(name)
8285 char_u *name;
8286{
8287 if (STRCMP(name, "NONE") == 0)
8288 return NOFONT;
8289
8290 return gui_mch_get_font(name, TRUE);
8291}
8292
8293# ifdef FEAT_XFONTSET
8294/*
8295 * Return the handle for a fontset name.
8296 * Returns NOFONTSET when failed.
8297 */
8298 static GuiFontset
8299fontset_name2handle(name, fixed_width)
8300 char_u *name;
8301 int fixed_width;
8302{
8303 if (STRCMP(name, "NONE") == 0)
8304 return NOFONTSET;
8305
8306 return gui_mch_get_fontset(name, TRUE, fixed_width);
8307}
8308# endif
8309
8310/*
8311 * Get the font or fontset for one highlight group.
8312 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008313 static void
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008314hl_do_font(idx, arg, do_normal, do_menu, do_tooltip, free_font)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008315 int idx;
8316 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008317 int do_normal; /* set normal font */
8318 int do_menu UNUSED; /* set menu font */
8319 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008320 int free_font; /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008321{
8322# ifdef FEAT_XFONTSET
8323 /* If 'guifontset' is not empty, first try using the name as a
8324 * fontset. If that doesn't work, use it as a font name. */
8325 if (*p_guifontset != NUL
8326# ifdef FONTSET_ALWAYS
8327 || do_menu
8328# endif
8329# ifdef FEAT_BEVAL_TIP
8330 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8331 || do_tooltip
8332# endif
8333 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008334 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008335 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008336 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008337 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8338# ifdef FONTSET_ALWAYS
8339 || do_menu
8340# endif
8341# ifdef FEAT_BEVAL_TIP
8342 || do_tooltip
8343# endif
8344 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008345 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008346 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8347 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008348 /* If it worked and it's the Normal group, use it as the normal
8349 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008350 if (do_normal)
8351 gui_init_font(arg, TRUE);
8352# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8353 if (do_menu)
8354 {
8355# ifdef FONTSET_ALWAYS
8356 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8357# else
8358 /* YIKES! This is a bug waiting to crash the program */
8359 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8360# endif
8361 gui_mch_new_menu_font();
8362 }
8363# ifdef FEAT_BEVAL
8364 if (do_tooltip)
8365 {
8366 /* The Athena widget set cannot currently handle switching between
8367 * displaying a single font and a fontset.
8368 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008369 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008370 * XFontStruct is used.
8371 */
8372 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8373 gui_mch_new_tooltip_font();
8374 }
8375# endif
8376# endif
8377 }
8378 else
8379# endif
8380 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008381 if (free_font)
8382 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008383 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8384 /* If it worked and it's the Normal group, use it as the
8385 * normal font. Same for the Menu group. */
8386 if (HL_TABLE()[idx].sg_font != NOFONT)
8387 {
8388 if (do_normal)
8389 gui_init_font(arg, FALSE);
8390#ifndef FONTSET_ALWAYS
8391# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8392 if (do_menu)
8393 {
8394 gui.menu_font = HL_TABLE()[idx].sg_font;
8395 gui_mch_new_menu_font();
8396 }
8397# endif
8398#endif
8399 }
8400 }
8401}
8402
8403#endif /* FEAT_GUI */
8404
8405/*
8406 * Table with the specifications for an attribute number.
8407 * Note that this table is used by ALL buffers. This is required because the
8408 * GUI can redraw at any time for any buffer.
8409 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008410static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008411
8412#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8413
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008414static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008415
8416#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8417
8418#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008419static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008420
8421#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8422#endif
8423
8424/*
8425 * Return the attr number for a set of colors and font.
8426 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8427 * if the combination is new.
8428 * Return 0 for error (no more room).
8429 */
8430 static int
8431get_attr_entry(table, aep)
8432 garray_T *table;
8433 attrentry_T *aep;
8434{
8435 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008436 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008437 static int recursive = FALSE;
8438
8439 /*
8440 * Init the table, in case it wasn't done yet.
8441 */
8442 table->ga_itemsize = sizeof(attrentry_T);
8443 table->ga_growsize = 7;
8444
8445 /*
8446 * Try to find an entry with the same specifications.
8447 */
8448 for (i = 0; i < table->ga_len; ++i)
8449 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008450 taep = &(((attrentry_T *)table->ga_data)[i]);
8451 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008452 && (
8453#ifdef FEAT_GUI
8454 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008455 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8456 && aep->ae_u.gui.bg_color
8457 == taep->ae_u.gui.bg_color
8458 && aep->ae_u.gui.sp_color
8459 == taep->ae_u.gui.sp_color
8460 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008461# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008462 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008463# endif
8464 ))
8465 ||
8466#endif
8467 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008468 && (aep->ae_u.term.start == NULL)
8469 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008470 && (aep->ae_u.term.start == NULL
8471 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008472 taep->ae_u.term.start) == 0)
8473 && (aep->ae_u.term.stop == NULL)
8474 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008475 && (aep->ae_u.term.stop == NULL
8476 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008477 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008478 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008479 && aep->ae_u.cterm.fg_color
8480 == taep->ae_u.cterm.fg_color
8481 && aep->ae_u.cterm.bg_color
8482 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008483 ))
8484
8485 return i + ATTR_OFF;
8486 }
8487
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008488 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008489 {
8490 /*
8491 * Running out of attribute entries! remove all attributes, and
8492 * compute new ones for all groups.
8493 * When called recursively, we are really out of numbers.
8494 */
8495 if (recursive)
8496 {
8497 EMSG(_("E424: Too many different highlighting attributes in use"));
8498 return 0;
8499 }
8500 recursive = TRUE;
8501
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008502 clear_hl_tables();
8503
Bram Moolenaar071d4272004-06-13 20:20:40 +00008504 must_redraw = CLEAR;
8505
8506 for (i = 0; i < highlight_ga.ga_len; ++i)
8507 set_hl_attr(i);
8508
8509 recursive = FALSE;
8510 }
8511
8512 /*
8513 * This is a new combination of colors and font, add an entry.
8514 */
8515 if (ga_grow(table, 1) == FAIL)
8516 return 0;
8517
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008518 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8519 vim_memset(taep, 0, sizeof(attrentry_T));
8520 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008521#ifdef FEAT_GUI
8522 if (table == &gui_attr_table)
8523 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008524 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8525 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8526 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8527 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008528# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008529 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008530# endif
8531 }
8532#endif
8533 if (table == &term_attr_table)
8534 {
8535 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008536 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008537 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008538 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008539 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008540 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008541 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008542 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008543 }
8544 else if (table == &cterm_attr_table)
8545 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008546 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8547 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008548 }
8549 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008550 return (table->ga_len - 1 + ATTR_OFF);
8551}
8552
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008553/*
8554 * Clear all highlight tables.
8555 */
8556 void
8557clear_hl_tables()
8558{
8559 int i;
8560 attrentry_T *taep;
8561
8562#ifdef FEAT_GUI
8563 ga_clear(&gui_attr_table);
8564#endif
8565 for (i = 0; i < term_attr_table.ga_len; ++i)
8566 {
8567 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8568 vim_free(taep->ae_u.term.start);
8569 vim_free(taep->ae_u.term.stop);
8570 }
8571 ga_clear(&term_attr_table);
8572 ga_clear(&cterm_attr_table);
8573}
8574
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008575#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008576/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008577 * Combine special attributes (e.g., for spelling) with other attributes
8578 * (e.g., for syntax highlighting).
8579 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008580 * This creates a new group when required.
8581 * Since we expect there to be few spelling mistakes we don't cache the
8582 * result.
8583 * Return the resulting attributes.
8584 */
8585 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008586hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008587 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008588 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008589{
8590 attrentry_T *char_aep = NULL;
8591 attrentry_T *spell_aep;
8592 attrentry_T new_en;
8593
8594 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008595 return prim_attr;
8596 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8597 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008598#ifdef FEAT_GUI
8599 if (gui.in_use)
8600 {
8601 if (char_attr > HL_ALL)
8602 char_aep = syn_gui_attr2entry(char_attr);
8603 if (char_aep != NULL)
8604 new_en = *char_aep;
8605 else
8606 {
8607 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008608 new_en.ae_u.gui.fg_color = INVALCOLOR;
8609 new_en.ae_u.gui.bg_color = INVALCOLOR;
8610 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008611 if (char_attr <= HL_ALL)
8612 new_en.ae_attr = char_attr;
8613 }
8614
Bram Moolenaar30abd282005-06-22 22:35:10 +00008615 if (prim_attr <= HL_ALL)
8616 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008617 else
8618 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008619 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008620 if (spell_aep != NULL)
8621 {
8622 new_en.ae_attr |= spell_aep->ae_attr;
8623 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8624 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8625 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8626 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8627 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8628 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8629 if (spell_aep->ae_u.gui.font != NOFONT)
8630 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8631# ifdef FEAT_XFONTSET
8632 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8633 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8634# endif
8635 }
8636 }
8637 return get_attr_entry(&gui_attr_table, &new_en);
8638 }
8639#endif
8640
8641 if (t_colors > 1)
8642 {
8643 if (char_attr > HL_ALL)
8644 char_aep = syn_cterm_attr2entry(char_attr);
8645 if (char_aep != NULL)
8646 new_en = *char_aep;
8647 else
8648 {
8649 vim_memset(&new_en, 0, sizeof(new_en));
8650 if (char_attr <= HL_ALL)
8651 new_en.ae_attr = char_attr;
8652 }
8653
Bram Moolenaar30abd282005-06-22 22:35:10 +00008654 if (prim_attr <= HL_ALL)
8655 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008656 else
8657 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008658 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008659 if (spell_aep != NULL)
8660 {
8661 new_en.ae_attr |= spell_aep->ae_attr;
8662 if (spell_aep->ae_u.cterm.fg_color > 0)
8663 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8664 if (spell_aep->ae_u.cterm.bg_color > 0)
8665 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8666 }
8667 }
8668 return get_attr_entry(&cterm_attr_table, &new_en);
8669 }
8670
8671 if (char_attr > HL_ALL)
8672 char_aep = syn_term_attr2entry(char_attr);
8673 if (char_aep != NULL)
8674 new_en = *char_aep;
8675 else
8676 {
8677 vim_memset(&new_en, 0, sizeof(new_en));
8678 if (char_attr <= HL_ALL)
8679 new_en.ae_attr = char_attr;
8680 }
8681
Bram Moolenaar30abd282005-06-22 22:35:10 +00008682 if (prim_attr <= HL_ALL)
8683 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008684 else
8685 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008686 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008687 if (spell_aep != NULL)
8688 {
8689 new_en.ae_attr |= spell_aep->ae_attr;
8690 if (spell_aep->ae_u.term.start != NULL)
8691 {
8692 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8693 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8694 }
8695 }
8696 }
8697 return get_attr_entry(&term_attr_table, &new_en);
8698}
8699#endif
8700
Bram Moolenaar071d4272004-06-13 20:20:40 +00008701#ifdef FEAT_GUI
8702
8703 attrentry_T *
8704syn_gui_attr2entry(attr)
8705 int attr;
8706{
8707 attr -= ATTR_OFF;
8708 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8709 return NULL;
8710 return &(GUI_ATTR_ENTRY(attr));
8711}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008712#endif /* FEAT_GUI */
8713
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008714/*
8715 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8716 * Only to be used when "attr" > HL_ALL.
8717 */
8718 int
8719syn_attr2attr(attr)
8720 int attr;
8721{
8722 attrentry_T *aep;
8723
8724#ifdef FEAT_GUI
8725 if (gui.in_use)
8726 aep = syn_gui_attr2entry(attr);
8727 else
8728#endif
8729 if (t_colors > 1)
8730 aep = syn_cterm_attr2entry(attr);
8731 else
8732 aep = syn_term_attr2entry(attr);
8733
8734 if (aep == NULL) /* highlighting not set */
8735 return 0;
8736 return aep->ae_attr;
8737}
8738
8739
Bram Moolenaar071d4272004-06-13 20:20:40 +00008740 attrentry_T *
8741syn_term_attr2entry(attr)
8742 int attr;
8743{
8744 attr -= ATTR_OFF;
8745 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8746 return NULL;
8747 return &(TERM_ATTR_ENTRY(attr));
8748}
8749
8750 attrentry_T *
8751syn_cterm_attr2entry(attr)
8752 int attr;
8753{
8754 attr -= ATTR_OFF;
8755 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8756 return NULL;
8757 return &(CTERM_ATTR_ENTRY(attr));
8758}
8759
8760#define LIST_ATTR 1
8761#define LIST_STRING 2
8762#define LIST_INT 3
8763
8764 static void
8765highlight_list_one(id)
8766 int id;
8767{
8768 struct hl_group *sgp;
8769 int didh = FALSE;
8770
8771 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8772
8773 didh = highlight_list_arg(id, didh, LIST_ATTR,
8774 sgp->sg_term, NULL, "term");
8775 didh = highlight_list_arg(id, didh, LIST_STRING,
8776 0, sgp->sg_start, "start");
8777 didh = highlight_list_arg(id, didh, LIST_STRING,
8778 0, sgp->sg_stop, "stop");
8779
8780 didh = highlight_list_arg(id, didh, LIST_ATTR,
8781 sgp->sg_cterm, NULL, "cterm");
8782 didh = highlight_list_arg(id, didh, LIST_INT,
8783 sgp->sg_cterm_fg, NULL, "ctermfg");
8784 didh = highlight_list_arg(id, didh, LIST_INT,
8785 sgp->sg_cterm_bg, NULL, "ctermbg");
8786
Bram Moolenaar61623362010-07-14 22:04:22 +02008787#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008788 didh = highlight_list_arg(id, didh, LIST_ATTR,
8789 sgp->sg_gui, NULL, "gui");
8790 didh = highlight_list_arg(id, didh, LIST_STRING,
8791 0, sgp->sg_gui_fg_name, "guifg");
8792 didh = highlight_list_arg(id, didh, LIST_STRING,
8793 0, sgp->sg_gui_bg_name, "guibg");
8794 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008795 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008796#endif
8797#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008798 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008799 0, sgp->sg_font_name, "font");
8800#endif
8801
Bram Moolenaar661b1822005-07-28 22:36:45 +00008802 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008803 {
8804 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008805 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008806 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8807 msg_putchar(' ');
8808 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8809 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008810
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008811 if (!didh)
8812 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008813#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008814 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008815 last_set_msg(sgp->sg_scriptID);
8816#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008817}
8818
8819 static int
8820highlight_list_arg(id, didh, type, iarg, sarg, name)
8821 int id;
8822 int didh;
8823 int type;
8824 int iarg;
8825 char_u *sarg;
8826 char *name;
8827{
8828 char_u buf[100];
8829 char_u *ts;
8830 int i;
8831
Bram Moolenaar661b1822005-07-28 22:36:45 +00008832 if (got_int)
8833 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008834 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8835 {
8836 ts = buf;
8837 if (type == LIST_INT)
8838 sprintf((char *)buf, "%d", iarg - 1);
8839 else if (type == LIST_STRING)
8840 ts = sarg;
8841 else /* type == LIST_ATTR */
8842 {
8843 buf[0] = NUL;
8844 for (i = 0; hl_attr_table[i] != 0; ++i)
8845 {
8846 if (iarg & hl_attr_table[i])
8847 {
8848 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008849 vim_strcat(buf, (char_u *)",", 100);
8850 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008851 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8852 }
8853 }
8854 }
8855
8856 (void)syn_list_header(didh,
8857 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8858 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008859 if (!got_int)
8860 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008861 if (*name != NUL)
8862 {
8863 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8864 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8865 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008866 msg_outtrans(ts);
8867 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008868 }
8869 return didh;
8870}
8871
8872#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8873/*
8874 * Return "1" if highlight group "id" has attribute "flag".
8875 * Return NULL otherwise.
8876 */
8877 char_u *
8878highlight_has_attr(id, flag, modec)
8879 int id;
8880 int flag;
8881 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8882{
8883 int attr;
8884
8885 if (id <= 0 || id > highlight_ga.ga_len)
8886 return NULL;
8887
Bram Moolenaar61623362010-07-14 22:04:22 +02008888#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008889 if (modec == 'g')
8890 attr = HL_TABLE()[id - 1].sg_gui;
8891 else
8892#endif
8893 if (modec == 'c')
8894 attr = HL_TABLE()[id - 1].sg_cterm;
8895 else
8896 attr = HL_TABLE()[id - 1].sg_term;
8897
8898 if (attr & flag)
8899 return (char_u *)"1";
8900 return NULL;
8901}
8902#endif
8903
8904#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8905/*
8906 * Return color name of highlight group "id".
8907 */
8908 char_u *
8909highlight_color(id, what, modec)
8910 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008911 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008912 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8913{
8914 static char_u name[20];
8915 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008916 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008917 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008918 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008919
8920 if (id <= 0 || id > highlight_ga.ga_len)
8921 return NULL;
8922
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008923 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008924 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008925 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008926 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008927 font = TRUE;
8928 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008929 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008930 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8931 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008932 if (modec == 'g')
8933 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008934# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008935 /* return font name */
8936 if (font)
8937 return HL_TABLE()[id - 1].sg_font_name;
8938
Bram Moolenaar071d4272004-06-13 20:20:40 +00008939 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008940 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008941 {
8942 guicolor_T color;
8943 long_u rgb;
8944 static char_u buf[10];
8945
8946 if (fg)
8947 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008948 else if (sp)
8949 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008950 else
8951 color = HL_TABLE()[id - 1].sg_gui_bg;
8952 if (color == INVALCOLOR)
8953 return NULL;
8954 rgb = gui_mch_get_rgb(color);
8955 sprintf((char *)buf, "#%02x%02x%02x",
8956 (unsigned)(rgb >> 16),
8957 (unsigned)(rgb >> 8) & 255,
8958 (unsigned)rgb & 255);
8959 return buf;
8960 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008961#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008962 if (fg)
8963 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008964 if (sp)
8965 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008966 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8967 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008968 if (font || sp)
8969 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008970 if (modec == 'c')
8971 {
8972 if (fg)
8973 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8974 else
8975 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8976 sprintf((char *)name, "%d", n);
8977 return name;
8978 }
8979 /* term doesn't have color */
8980 return NULL;
8981}
8982#endif
8983
8984#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8985 || defined(PROTO)
8986/*
8987 * Return color name of highlight group "id" as RGB value.
8988 */
8989 long_u
8990highlight_gui_color_rgb(id, fg)
8991 int id;
8992 int fg; /* TRUE = fg, FALSE = bg */
8993{
8994 guicolor_T color;
8995
8996 if (id <= 0 || id > highlight_ga.ga_len)
8997 return 0L;
8998
8999 if (fg)
9000 color = HL_TABLE()[id - 1].sg_gui_fg;
9001 else
9002 color = HL_TABLE()[id - 1].sg_gui_bg;
9003
9004 if (color == INVALCOLOR)
9005 return 0L;
9006
9007 return gui_mch_get_rgb(color);
9008}
9009#endif
9010
9011/*
9012 * Output the syntax list header.
9013 * Return TRUE when started a new line.
9014 */
9015 static int
9016syn_list_header(did_header, outlen, id)
9017 int did_header; /* did header already */
9018 int outlen; /* length of string that comes */
9019 int id; /* highlight group id */
9020{
9021 int endcol = 19;
9022 int newline = TRUE;
9023
9024 if (!did_header)
9025 {
9026 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009027 if (got_int)
9028 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009029 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9030 endcol = 15;
9031 }
9032 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009033 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009034 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009035 if (got_int)
9036 return TRUE;
9037 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009038 else
9039 {
9040 if (msg_col >= endcol) /* wrap around is like starting a new line */
9041 newline = FALSE;
9042 }
9043
9044 if (msg_col >= endcol) /* output at least one space */
9045 endcol = msg_col + 1;
9046 if (Columns <= endcol) /* avoid hang for tiny window */
9047 endcol = Columns - 1;
9048
9049 msg_advance(endcol);
9050
9051 /* Show "xxx" with the attributes. */
9052 if (!did_header)
9053 {
9054 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9055 msg_putchar(' ');
9056 }
9057
9058 return newline;
9059}
9060
9061/*
9062 * Set the attribute numbers for a highlight group.
9063 * Called after one of the attributes has changed.
9064 */
9065 static void
9066set_hl_attr(idx)
9067 int idx; /* index in array */
9068{
9069 attrentry_T at_en;
9070 struct hl_group *sgp = HL_TABLE() + idx;
9071
9072 /* The "Normal" group doesn't need an attribute number */
9073 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9074 return;
9075
9076#ifdef FEAT_GUI
9077 /*
9078 * For the GUI mode: If there are other than "normal" highlighting
9079 * attributes, need to allocate an attr number.
9080 */
9081 if (sgp->sg_gui_fg == INVALCOLOR
9082 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009083 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009084 && sgp->sg_font == NOFONT
9085# ifdef FEAT_XFONTSET
9086 && sgp->sg_fontset == NOFONTSET
9087# endif
9088 )
9089 {
9090 sgp->sg_gui_attr = sgp->sg_gui;
9091 }
9092 else
9093 {
9094 at_en.ae_attr = sgp->sg_gui;
9095 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9096 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009097 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009098 at_en.ae_u.gui.font = sgp->sg_font;
9099# ifdef FEAT_XFONTSET
9100 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9101# endif
9102 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9103 }
9104#endif
9105 /*
9106 * For the term mode: If there are other than "normal" highlighting
9107 * attributes, need to allocate an attr number.
9108 */
9109 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9110 sgp->sg_term_attr = sgp->sg_term;
9111 else
9112 {
9113 at_en.ae_attr = sgp->sg_term;
9114 at_en.ae_u.term.start = sgp->sg_start;
9115 at_en.ae_u.term.stop = sgp->sg_stop;
9116 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9117 }
9118
9119 /*
9120 * For the color term mode: If there are other than "normal"
9121 * highlighting attributes, need to allocate an attr number.
9122 */
9123 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
9124 sgp->sg_cterm_attr = sgp->sg_cterm;
9125 else
9126 {
9127 at_en.ae_attr = sgp->sg_cterm;
9128 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9129 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
9130 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9131 }
9132}
9133
9134/*
9135 * Lookup a highlight group name and return it's ID.
9136 * If it is not found, 0 is returned.
9137 */
9138 int
9139syn_name2id(name)
9140 char_u *name;
9141{
9142 int i;
9143 char_u name_u[200];
9144
9145 /* Avoid using stricmp() too much, it's slow on some systems */
9146 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9147 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009148 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009149 vim_strup(name_u);
9150 for (i = highlight_ga.ga_len; --i >= 0; )
9151 if (HL_TABLE()[i].sg_name_u != NULL
9152 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9153 break;
9154 return i + 1;
9155}
9156
9157#if defined(FEAT_EVAL) || defined(PROTO)
9158/*
9159 * Return TRUE if highlight group "name" exists.
9160 */
9161 int
9162highlight_exists(name)
9163 char_u *name;
9164{
9165 return (syn_name2id(name) > 0);
9166}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009167
9168# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9169/*
9170 * Return the name of highlight group "id".
9171 * When not a valid ID return an empty string.
9172 */
9173 char_u *
9174syn_id2name(id)
9175 int id;
9176{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009177 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009178 return (char_u *)"";
9179 return HL_TABLE()[id - 1].sg_name;
9180}
9181# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009182#endif
9183
9184/*
9185 * Like syn_name2id(), but take a pointer + length argument.
9186 */
9187 int
9188syn_namen2id(linep, len)
9189 char_u *linep;
9190 int len;
9191{
9192 char_u *name;
9193 int id = 0;
9194
9195 name = vim_strnsave(linep, len);
9196 if (name != NULL)
9197 {
9198 id = syn_name2id(name);
9199 vim_free(name);
9200 }
9201 return id;
9202}
9203
9204/*
9205 * Find highlight group name in the table and return it's ID.
9206 * The argument is a pointer to the name and the length of the name.
9207 * If it doesn't exist yet, a new entry is created.
9208 * Return 0 for failure.
9209 */
9210 int
9211syn_check_group(pp, len)
9212 char_u *pp;
9213 int len;
9214{
9215 int id;
9216 char_u *name;
9217
9218 name = vim_strnsave(pp, len);
9219 if (name == NULL)
9220 return 0;
9221
9222 id = syn_name2id(name);
9223 if (id == 0) /* doesn't exist yet */
9224 id = syn_add_group(name);
9225 else
9226 vim_free(name);
9227 return id;
9228}
9229
9230/*
9231 * Add new highlight group and return it's ID.
9232 * "name" must be an allocated string, it will be consumed.
9233 * Return 0 for failure.
9234 */
9235 static int
9236syn_add_group(name)
9237 char_u *name;
9238{
9239 char_u *p;
9240
9241 /* Check that the name is ASCII letters, digits and underscore. */
9242 for (p = name; *p != NUL; ++p)
9243 {
9244 if (!vim_isprintc(*p))
9245 {
9246 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009247 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009248 return 0;
9249 }
9250 else if (!ASCII_ISALNUM(*p) && *p != '_')
9251 {
9252 /* This is an error, but since there previously was no check only
9253 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009254 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009255 MSG(_("W18: Invalid character in group name"));
9256 break;
9257 }
9258 }
9259
9260 /*
9261 * First call for this growarray: init growing array.
9262 */
9263 if (highlight_ga.ga_data == NULL)
9264 {
9265 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9266 highlight_ga.ga_growsize = 10;
9267 }
9268
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009269 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009270 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009271 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009272 vim_free(name);
9273 return 0;
9274 }
9275
Bram Moolenaar071d4272004-06-13 20:20:40 +00009276 /*
9277 * Make room for at least one other syntax_highlight entry.
9278 */
9279 if (ga_grow(&highlight_ga, 1) == FAIL)
9280 {
9281 vim_free(name);
9282 return 0;
9283 }
9284
9285 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9286 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9287 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9288#ifdef FEAT_GUI
9289 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9290 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009291 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009292#endif
9293 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009294
9295 return highlight_ga.ga_len; /* ID is index plus one */
9296}
9297
9298/*
9299 * When, just after calling syn_add_group(), an error is discovered, this
9300 * function deletes the new name.
9301 */
9302 static void
9303syn_unadd_group()
9304{
9305 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009306 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9307 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9308}
9309
9310/*
9311 * Translate a group ID to highlight attributes.
9312 */
9313 int
9314syn_id2attr(hl_id)
9315 int hl_id;
9316{
9317 int attr;
9318 struct hl_group *sgp;
9319
9320 hl_id = syn_get_final_id(hl_id);
9321 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9322
9323#ifdef FEAT_GUI
9324 /*
9325 * Only use GUI attr when the GUI is being used.
9326 */
9327 if (gui.in_use)
9328 attr = sgp->sg_gui_attr;
9329 else
9330#endif
9331 if (t_colors > 1)
9332 attr = sgp->sg_cterm_attr;
9333 else
9334 attr = sgp->sg_term_attr;
9335
9336 return attr;
9337}
9338
9339#ifdef FEAT_GUI
9340/*
9341 * Get the GUI colors and attributes for a group ID.
9342 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9343 */
9344 int
9345syn_id2colors(hl_id, fgp, bgp)
9346 int hl_id;
9347 guicolor_T *fgp;
9348 guicolor_T *bgp;
9349{
9350 struct hl_group *sgp;
9351
9352 hl_id = syn_get_final_id(hl_id);
9353 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9354
9355 *fgp = sgp->sg_gui_fg;
9356 *bgp = sgp->sg_gui_bg;
9357 return sgp->sg_gui;
9358}
9359#endif
9360
9361/*
9362 * Translate a group ID to the final group ID (following links).
9363 */
9364 int
9365syn_get_final_id(hl_id)
9366 int hl_id;
9367{
9368 int count;
9369 struct hl_group *sgp;
9370
9371 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9372 return 0; /* Can be called from eval!! */
9373
9374 /*
9375 * Follow links until there is no more.
9376 * Look out for loops! Break after 100 links.
9377 */
9378 for (count = 100; --count >= 0; )
9379 {
9380 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9381 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9382 break;
9383 hl_id = sgp->sg_link;
9384 }
9385
9386 return hl_id;
9387}
9388
9389#ifdef FEAT_GUI
9390/*
9391 * Call this function just after the GUI has started.
9392 * It finds the font and color handles for the highlighting groups.
9393 */
9394 void
9395highlight_gui_started()
9396{
9397 int idx;
9398
9399 /* First get the colors from the "Normal" and "Menu" group, if set */
9400 set_normal_colors();
9401
9402 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9403 gui_do_one_color(idx, FALSE, FALSE);
9404
9405 highlight_changed();
9406}
9407
9408 static void
9409gui_do_one_color(idx, do_menu, do_tooltip)
9410 int idx;
9411 int do_menu; /* TRUE: might set the menu font */
9412 int do_tooltip; /* TRUE: might set the tooltip font */
9413{
9414 int didit = FALSE;
9415
9416 if (HL_TABLE()[idx].sg_font_name != NULL)
9417 {
9418 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009419 do_tooltip, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009420 didit = TRUE;
9421 }
9422 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9423 {
9424 HL_TABLE()[idx].sg_gui_fg =
9425 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9426 didit = TRUE;
9427 }
9428 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9429 {
9430 HL_TABLE()[idx].sg_gui_bg =
9431 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9432 didit = TRUE;
9433 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009434 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9435 {
9436 HL_TABLE()[idx].sg_gui_sp =
9437 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9438 didit = TRUE;
9439 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009440 if (didit) /* need to get a new attr number */
9441 set_hl_attr(idx);
9442}
9443
9444#endif
9445
9446/*
9447 * Translate the 'highlight' option into attributes in highlight_attr[] and
9448 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9449 * corresponding highlights to use on top of HLF_SNC is computed.
9450 * Called only when the 'highlight' option has been changed and upon first
9451 * screen redraw after any :highlight command.
9452 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9453 */
9454 int
9455highlight_changed()
9456{
9457 int hlf;
9458 int i;
9459 char_u *p;
9460 int attr;
9461 char_u *end;
9462 int id;
9463#ifdef USER_HIGHLIGHT
9464 char_u userhl[10];
9465# ifdef FEAT_STL_OPT
9466 int id_SNC = -1;
9467 int id_S = -1;
9468 int hlcnt;
9469# endif
9470#endif
9471 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9472
9473 need_highlight_changed = FALSE;
9474
9475 /*
9476 * Clear all attributes.
9477 */
9478 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9479 highlight_attr[hlf] = 0;
9480
9481 /*
9482 * First set all attributes to their default value.
9483 * Then use the attributes from the 'highlight' option.
9484 */
9485 for (i = 0; i < 2; ++i)
9486 {
9487 if (i)
9488 p = p_hl;
9489 else
9490 p = get_highlight_default();
9491 if (p == NULL) /* just in case */
9492 continue;
9493
9494 while (*p)
9495 {
9496 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9497 if (hl_flags[hlf] == *p)
9498 break;
9499 ++p;
9500 if (hlf == (int)HLF_COUNT || *p == NUL)
9501 return FAIL;
9502
9503 /*
9504 * Allow several hl_flags to be combined, like "bu" for
9505 * bold-underlined.
9506 */
9507 attr = 0;
9508 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9509 {
9510 if (vim_iswhite(*p)) /* ignore white space */
9511 continue;
9512
9513 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9514 return FAIL;
9515
9516 switch (*p)
9517 {
9518 case 'b': attr |= HL_BOLD;
9519 break;
9520 case 'i': attr |= HL_ITALIC;
9521 break;
9522 case '-':
9523 case 'n': /* no highlighting */
9524 break;
9525 case 'r': attr |= HL_INVERSE;
9526 break;
9527 case 's': attr |= HL_STANDOUT;
9528 break;
9529 case 'u': attr |= HL_UNDERLINE;
9530 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009531 case 'c': attr |= HL_UNDERCURL;
9532 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009533 case ':': ++p; /* highlight group name */
9534 if (attr || *p == NUL) /* no combinations */
9535 return FAIL;
9536 end = vim_strchr(p, ',');
9537 if (end == NULL)
9538 end = p + STRLEN(p);
9539 id = syn_check_group(p, (int)(end - p));
9540 if (id == 0)
9541 return FAIL;
9542 attr = syn_id2attr(id);
9543 p = end - 1;
9544#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9545 if (hlf == (int)HLF_SNC)
9546 id_SNC = syn_get_final_id(id);
9547 else if (hlf == (int)HLF_S)
9548 id_S = syn_get_final_id(id);
9549#endif
9550 break;
9551 default: return FAIL;
9552 }
9553 }
9554 highlight_attr[hlf] = attr;
9555
9556 p = skip_to_option_part(p); /* skip comma and spaces */
9557 }
9558 }
9559
9560#ifdef USER_HIGHLIGHT
9561 /* Setup the user highlights
9562 *
9563 * Temporarily utilize 10 more hl entries. Have to be in there
9564 * simultaneously in case of table overflows in get_attr_entry()
9565 */
9566# ifdef FEAT_STL_OPT
9567 if (ga_grow(&highlight_ga, 10) == FAIL)
9568 return FAIL;
9569 hlcnt = highlight_ga.ga_len;
9570 if (id_S == 0)
9571 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009572 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009573 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9574 id_S = hlcnt + 10;
9575 }
9576# endif
9577 for (i = 0; i < 9; i++)
9578 {
9579 sprintf((char *)userhl, "User%d", i + 1);
9580 id = syn_name2id(userhl);
9581 if (id == 0)
9582 {
9583 highlight_user[i] = 0;
9584# ifdef FEAT_STL_OPT
9585 highlight_stlnc[i] = 0;
9586# endif
9587 }
9588 else
9589 {
9590# ifdef FEAT_STL_OPT
9591 struct hl_group *hlt = HL_TABLE();
9592# endif
9593
9594 highlight_user[i] = syn_id2attr(id);
9595# ifdef FEAT_STL_OPT
9596 if (id_SNC == 0)
9597 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009598 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009599 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9600 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009601# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009602 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9603# endif
9604 }
9605 else
9606 mch_memmove(&hlt[hlcnt + i],
9607 &hlt[id_SNC - 1],
9608 sizeof(struct hl_group));
9609 hlt[hlcnt + i].sg_link = 0;
9610
9611 /* Apply difference between UserX and HLF_S to HLF_SNC */
9612 hlt[hlcnt + i].sg_term ^=
9613 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9614 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9615 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9616 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9617 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9618 hlt[hlcnt + i].sg_cterm ^=
9619 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9620 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9621 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9622 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9623 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009624# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009625 hlt[hlcnt + i].sg_gui ^=
9626 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009627# endif
9628# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009629 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9630 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9631 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9632 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009633 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9634 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009635 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9636 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9637# ifdef FEAT_XFONTSET
9638 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9639 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9640# endif
9641# endif
9642 highlight_ga.ga_len = hlcnt + i + 1;
9643 set_hl_attr(hlcnt + i); /* At long last we can apply */
9644 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9645# endif
9646 }
9647 }
9648# ifdef FEAT_STL_OPT
9649 highlight_ga.ga_len = hlcnt;
9650# endif
9651
9652#endif /* USER_HIGHLIGHT */
9653
9654 return OK;
9655}
9656
Bram Moolenaar4f688582007-07-24 12:34:30 +00009657#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009658
9659static void highlight_list __ARGS((void));
9660static void highlight_list_two __ARGS((int cnt, int attr));
9661
9662/*
9663 * Handle command line completion for :highlight command.
9664 */
9665 void
9666set_context_in_highlight_cmd(xp, arg)
9667 expand_T *xp;
9668 char_u *arg;
9669{
9670 char_u *p;
9671
9672 /* Default: expand group names */
9673 xp->xp_context = EXPAND_HIGHLIGHT;
9674 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009675 include_link = 2;
9676 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009677
9678 /* (part of) subcommand already typed */
9679 if (*arg != NUL)
9680 {
9681 p = skiptowhite(arg);
9682 if (*p != NUL) /* past "default" or group name */
9683 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009684 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009685 if (STRNCMP("default", arg, p - arg) == 0)
9686 {
9687 arg = skipwhite(p);
9688 xp->xp_pattern = arg;
9689 p = skiptowhite(arg);
9690 }
9691 if (*p != NUL) /* past group name */
9692 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009693 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009694 if (arg[1] == 'i' && arg[0] == 'N')
9695 highlight_list();
9696 if (STRNCMP("link", arg, p - arg) == 0
9697 || STRNCMP("clear", arg, p - arg) == 0)
9698 {
9699 xp->xp_pattern = skipwhite(p);
9700 p = skiptowhite(xp->xp_pattern);
9701 if (*p != NUL) /* past first group name */
9702 {
9703 xp->xp_pattern = skipwhite(p);
9704 p = skiptowhite(xp->xp_pattern);
9705 }
9706 }
9707 if (*p != NUL) /* past group name(s) */
9708 xp->xp_context = EXPAND_NOTHING;
9709 }
9710 }
9711 }
9712}
9713
9714/*
9715 * List highlighting matches in a nice way.
9716 */
9717 static void
9718highlight_list()
9719{
9720 int i;
9721
9722 for (i = 10; --i >= 0; )
9723 highlight_list_two(i, hl_attr(HLF_D));
9724 for (i = 40; --i >= 0; )
9725 highlight_list_two(99, 0);
9726}
9727
9728 static void
9729highlight_list_two(cnt, attr)
9730 int cnt;
9731 int attr;
9732{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009733 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009734 msg_clr_eos();
9735 out_flush();
9736 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9737}
9738
9739#endif /* FEAT_CMDL_COMPL */
9740
9741#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9742 || defined(FEAT_SIGNS) || defined(PROTO)
9743/*
9744 * Function given to ExpandGeneric() to obtain the list of group names.
9745 * Also used for synIDattr() function.
9746 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009747 char_u *
9748get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009749 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009750 int idx;
9751{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009752#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009753 if (idx == highlight_ga.ga_len && include_none != 0)
9754 return (char_u *)"none";
9755 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009756 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009757 if (idx == highlight_ga.ga_len + include_none + include_default
9758 && include_link != 0)
9759 return (char_u *)"link";
9760 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9761 && include_link != 0)
9762 return (char_u *)"clear";
9763#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009764 if (idx < 0 || idx >= highlight_ga.ga_len)
9765 return NULL;
9766 return HL_TABLE()[idx].sg_name;
9767}
9768#endif
9769
Bram Moolenaar4f688582007-07-24 12:34:30 +00009770#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009771/*
9772 * Free all the highlight group fonts.
9773 * Used when quitting for systems which need it.
9774 */
9775 void
9776free_highlight_fonts()
9777{
9778 int idx;
9779
9780 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9781 {
9782 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9783 HL_TABLE()[idx].sg_font = NOFONT;
9784# ifdef FEAT_XFONTSET
9785 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9786 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9787# endif
9788 }
9789
9790 gui_mch_free_font(gui.norm_font);
9791# ifdef FEAT_XFONTSET
9792 gui_mch_free_fontset(gui.fontset);
9793# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009794# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009795 gui_mch_free_font(gui.bold_font);
9796 gui_mch_free_font(gui.ital_font);
9797 gui_mch_free_font(gui.boldital_font);
9798# endif
9799}
9800#endif
9801
9802/**************************************
9803 * End of Highlighting stuff *
9804 **************************************/