blob: 90b54afdbcce62f17190b4ebd0da8852e49e381b [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 Moolenaar8a7f5a22013-06-06 14:01:46 +02003261 syn_time_T *st;
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 Moolenaar860cae12010-06-05 23:22:07 +02003498 vim_free(block->b_syn_linecont_prog);
3499 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 Moolenaar860cae12010-06-05 23:22:07 +02003547 vim_free(curwin->w_s->b_syn_linecont_prog);
3548 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);
3586 vim_free(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 */
4994 vim_free(item.sp_prog);
4995 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 {
5251 vim_free(ppp->pp_synp->sp_prog);
5252 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 }
6025 vim_free(regmatch.regprog);
6026 }
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);
6298 vim_free(curwin->w_s->b_cap_prog);
6299 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
6580typedef struct
6581{
6582 proftime_T total;
6583 int count;
6584 int match;
6585 proftime_T slowest;
6586 proftime_T average;
6587 int id;
6588 char_u *pattern;
6589} time_entry_T;
6590
6591 static int
6592#ifdef __BORLANDC__
6593_RTLENTRYF
6594#endif
6595syn_compare_syntime(v1, v2)
6596 const void *v1;
6597 const void *v2;
6598{
6599 const time_entry_T *s1 = v1;
6600 const time_entry_T *s2 = v2;
6601
6602 return profile_cmp(&s1->total, &s2->total);
6603}
6604
6605/*
6606 * Clear the syntax timing for the current buffer.
6607 */
6608 static void
6609syntime_report()
6610{
6611 int idx;
6612 synpat_T *spp;
6613 proftime_T tm;
6614 int len;
6615 proftime_T total_total;
6616 int total_count = 0;
6617 garray_T ga;
6618 time_entry_T *p;
6619
6620 if (!syntax_present(curwin))
6621 {
6622 MSG(_(msg_no_items));
6623 return;
6624 }
6625
6626 ga_init2(&ga, sizeof(time_entry_T), 50);
6627 profile_zero(&total_total);
6628 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6629 {
6630 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6631 if (spp->sp_time.count > 0)
6632 {
6633 ga_grow(&ga, 1);
6634 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6635 p->total = spp->sp_time.total;
6636 profile_add(&total_total, &spp->sp_time.total);
6637 p->count = spp->sp_time.count;
6638 p->match = spp->sp_time.match;
6639 total_count += spp->sp_time.count;
6640 p->slowest = spp->sp_time.slowest;
6641# ifdef FEAT_FLOAT
6642 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6643 p->average = tm;
6644# endif
6645 p->id = spp->sp_syn.id;
6646 p->pattern = spp->sp_pattern;
6647 ++ga.ga_len;
6648 }
6649 }
6650
6651 /* sort on total time */
6652 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T), syn_compare_syntime);
6653
6654 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6655 MSG_PUTS("\n");
6656 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6657 {
6658 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6659 p = ((time_entry_T *)ga.ga_data) + idx;
6660
6661 MSG_PUTS(profile_msg(&p->total));
6662 MSG_PUTS(" "); /* make sure there is always a separating space */
6663 msg_advance(13);
6664 msg_outnum(p->count);
6665 MSG_PUTS(" ");
6666 msg_advance(20);
6667 msg_outnum(p->match);
6668 MSG_PUTS(" ");
6669 msg_advance(26);
6670 MSG_PUTS(profile_msg(&p->slowest));
6671 MSG_PUTS(" ");
6672 msg_advance(38);
6673# ifdef FEAT_FLOAT
6674 MSG_PUTS(profile_msg(&p->average));
6675 MSG_PUTS(" ");
6676# endif
6677 msg_advance(50);
6678 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6679 MSG_PUTS(" ");
6680
6681 msg_advance(69);
6682 if (Columns < 80)
6683 len = 20; /* will wrap anyway */
6684 else
6685 len = Columns - 70;
6686 if (len > (int)STRLEN(p->pattern))
6687 len = (int)STRLEN(p->pattern);
6688 msg_outtrans_len(p->pattern, len);
6689 MSG_PUTS("\n");
6690 }
6691 if (!got_int)
6692 {
6693 MSG_PUTS("\n");
6694 MSG_PUTS(profile_msg(&total_total));
6695 msg_advance(13);
6696 msg_outnum(total_count);
6697 MSG_PUTS("\n");
6698 }
6699}
6700#endif
6701
Bram Moolenaar071d4272004-06-13 20:20:40 +00006702#endif /* FEAT_SYN_HL */
6703
Bram Moolenaar071d4272004-06-13 20:20:40 +00006704/**************************************
6705 * Highlighting stuff *
6706 **************************************/
6707
6708/*
6709 * The default highlight groups. These are compiled-in for fast startup and
6710 * they still work when the runtime files can't be found.
6711 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006712 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6713 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006714 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006715#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006716# define CENT(a, b) b
6717#else
6718# define CENT(a, b) a
6719#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006720static char *(highlight_init_both[]) =
6721 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006722 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6723 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6724 CENT("IncSearch term=reverse cterm=reverse",
6725 "IncSearch term=reverse cterm=reverse gui=reverse"),
6726 CENT("ModeMsg term=bold cterm=bold",
6727 "ModeMsg term=bold cterm=bold gui=bold"),
6728 CENT("NonText term=bold ctermfg=Blue",
6729 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6730 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6731 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6732 CENT("StatusLineNC term=reverse cterm=reverse",
6733 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006734#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006735 CENT("VertSplit term=reverse cterm=reverse",
6736 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006737#endif
6738#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006739 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6740 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006741#endif
6742#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006743 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6744 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006745#endif
6746#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006747 CENT("PmenuSbar ctermbg=Grey",
6748 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006749#endif
6750#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006751 CENT("TabLineSel term=bold cterm=bold",
6752 "TabLineSel term=bold cterm=bold gui=bold"),
6753 CENT("TabLineFill term=reverse cterm=reverse",
6754 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006755#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006756#ifdef FEAT_GUI
6757 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006758 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006759#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006760 NULL
6761 };
6762
6763static char *(highlight_init_light[]) =
6764 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006765 CENT("Directory term=bold ctermfg=DarkBlue",
6766 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6767 CENT("LineNr term=underline ctermfg=Brown",
6768 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006769 CENT("CursorLineNr term=bold ctermfg=Brown",
6770 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006771 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6772 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6773 CENT("Question term=standout ctermfg=DarkGreen",
6774 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6775 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6776 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006777#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006778 CENT("SpellBad term=reverse ctermbg=LightRed",
6779 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6780 CENT("SpellCap term=reverse ctermbg=LightBlue",
6781 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6782 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6783 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6784 CENT("SpellLocal term=underline ctermbg=Cyan",
6785 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006786#endif
6787#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006788 CENT("PmenuThumb ctermbg=Black",
6789 "PmenuThumb ctermbg=Black guibg=Black"),
6790 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6791 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6792 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6793 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006794#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006795 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6796 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6797 CENT("Title term=bold ctermfg=DarkMagenta",
6798 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6799 CENT("WarningMsg term=standout ctermfg=DarkRed",
6800 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006801#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006802 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6803 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006804#endif
6805#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006806 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6807 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6808 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6809 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006810#endif
6811#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006812 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6813 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006814#endif
6815#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006816 CENT("Visual term=reverse",
6817 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006818#endif
6819#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006820 CENT("DiffAdd term=bold ctermbg=LightBlue",
6821 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6822 CENT("DiffChange term=bold ctermbg=LightMagenta",
6823 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6824 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6825 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006826#endif
6827#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006828 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6829 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006830#endif
6831#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006832 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006833 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006834 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006835 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006836 CENT("ColorColumn term=reverse ctermbg=LightRed",
6837 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006838#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006839#ifdef FEAT_CONCEAL
6840 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6841 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6842#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006843#ifdef FEAT_AUTOCMD
6844 CENT("MatchParen term=reverse ctermbg=Cyan",
6845 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6846#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006847#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006848 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006849#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006850 NULL
6851 };
6852
6853static char *(highlight_init_dark[]) =
6854 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006855 CENT("Directory term=bold ctermfg=LightCyan",
6856 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6857 CENT("LineNr term=underline ctermfg=Yellow",
6858 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006859 CENT("CursorLineNr term=bold ctermfg=Yellow",
6860 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006861 CENT("MoreMsg term=bold ctermfg=LightGreen",
6862 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6863 CENT("Question term=standout ctermfg=LightGreen",
6864 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6865 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6866 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6867 CENT("SpecialKey term=bold ctermfg=LightBlue",
6868 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006869#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006870 CENT("SpellBad term=reverse ctermbg=Red",
6871 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6872 CENT("SpellCap term=reverse ctermbg=Blue",
6873 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6874 CENT("SpellRare term=reverse ctermbg=Magenta",
6875 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6876 CENT("SpellLocal term=underline ctermbg=Cyan",
6877 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006878#endif
6879#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006880 CENT("PmenuThumb ctermbg=White",
6881 "PmenuThumb ctermbg=White guibg=White"),
6882 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6883 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02006884 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6885 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006886#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006887 CENT("Title term=bold ctermfg=LightMagenta",
6888 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6889 CENT("WarningMsg term=standout ctermfg=LightRed",
6890 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006891#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006892 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6893 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006894#endif
6895#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006896 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6897 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6898 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6899 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006900#endif
6901#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006902 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6903 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006904#endif
6905#ifdef FEAT_VISUAL
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006906 CENT("Visual term=reverse",
6907 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006908#endif
6909#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006910 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6911 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6912 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6913 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6914 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6915 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006916#endif
6917#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006918 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6919 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006920#endif
6921#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006922 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006923 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006924 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006925 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006926 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6927 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006928#endif
6929#ifdef FEAT_AUTOCMD
6930 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6931 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006932#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006933#ifdef FEAT_CONCEAL
6934 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6935 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6936#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006937#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006938 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006939#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006940 NULL
6941 };
6942
6943 void
6944init_highlight(both, reset)
6945 int both; /* include groups where 'bg' doesn't matter */
6946 int reset; /* clear group first */
6947{
6948 int i;
6949 char **pp;
6950 static int had_both = FALSE;
6951#ifdef FEAT_EVAL
6952 char_u *p;
6953
6954 /*
6955 * Try finding the color scheme file. Used when a color file was loaded
6956 * and 'background' or 't_Co' is changed.
6957 */
6958 p = get_var_value((char_u *)"g:colors_name");
6959 if (p != NULL && load_colors(p) == OK)
6960 return;
6961#endif
6962
6963 /*
6964 * Didn't use a color file, use the compiled-in colors.
6965 */
6966 if (both)
6967 {
6968 had_both = TRUE;
6969 pp = highlight_init_both;
6970 for (i = 0; pp[i] != NULL; ++i)
6971 do_highlight((char_u *)pp[i], reset, TRUE);
6972 }
6973 else if (!had_both)
6974 /* Don't do anything before the call with both == TRUE from main().
6975 * Not everything has been setup then, and that call will overrule
6976 * everything anyway. */
6977 return;
6978
6979 if (*p_bg == 'l')
6980 pp = highlight_init_light;
6981 else
6982 pp = highlight_init_dark;
6983 for (i = 0; pp[i] != NULL; ++i)
6984 do_highlight((char_u *)pp[i], reset, TRUE);
6985
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006986 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006987 * depend on the number of colors available.
6988 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00006989 * to avoid Statement highlighted text disappears.
6990 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00006991 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00006992 do_highlight((char_u *)(*p_bg == 'l'
6993 ? "Visual cterm=NONE ctermbg=LightGrey"
6994 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006995 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006996 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00006997 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
6998 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00006999 if (*p_bg == 'l')
7000 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7001 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007002
Bram Moolenaar071d4272004-06-13 20:20:40 +00007003#ifdef FEAT_SYN_HL
7004 /*
7005 * If syntax highlighting is enabled load the highlighting for it.
7006 */
7007 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007008 {
7009 static int recursive = 0;
7010
7011 if (recursive >= 5)
7012 EMSG(_("E679: recursive loop loading syncolor.vim"));
7013 else
7014 {
7015 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007016 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007017 --recursive;
7018 }
7019 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007020#endif
7021}
7022
7023/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007024 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007025 * Return OK for success, FAIL for failure.
7026 */
7027 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007028load_colors(name)
7029 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007030{
7031 char_u *buf;
7032 int retval = FAIL;
7033 static int recursive = FALSE;
7034
7035 /* When being called recursively, this is probably because setting
7036 * 'background' caused the highlighting to be reloaded. This means it is
7037 * working, thus we should return OK. */
7038 if (recursive)
7039 return OK;
7040
7041 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007042 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007043 if (buf != NULL)
7044 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007045 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007046 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007047 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007048#ifdef FEAT_AUTOCMD
7049 apply_autocmds(EVENT_COLORSCHEME, NULL, NULL, FALSE, curbuf);
7050#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007051 }
7052 recursive = FALSE;
7053
7054 return retval;
7055}
7056
7057/*
7058 * Handle the ":highlight .." command.
7059 * When using ":hi clear" this is called recursively for each group with
7060 * "forceit" and "init" both TRUE.
7061 */
7062 void
7063do_highlight(line, forceit, init)
7064 char_u *line;
7065 int forceit;
7066 int init; /* TRUE when called for initializing */
7067{
7068 char_u *name_end;
7069 char_u *p;
7070 char_u *linep;
7071 char_u *key_start;
7072 char_u *arg_start;
7073 char_u *key = NULL, *arg = NULL;
7074 long i;
7075 int off;
7076 int len;
7077 int attr;
7078 int id;
7079 int idx;
7080 int dodefault = FALSE;
7081 int doclear = FALSE;
7082 int dolink = FALSE;
7083 int error = FALSE;
7084 int color;
7085 int is_normal_group = FALSE; /* "Normal" group */
7086#ifdef FEAT_GUI_X11
7087 int is_menu_group = FALSE; /* "Menu" group */
7088 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7089 int is_tooltip_group = FALSE; /* "Tooltip" group */
7090 int do_colors = FALSE; /* need to update colors? */
7091#else
7092# define is_menu_group 0
7093# define is_tooltip_group 0
7094#endif
7095
7096 /*
7097 * If no argument, list current highlighting.
7098 */
7099 if (ends_excmd(*line))
7100 {
7101 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7102 /* TODO: only call when the group has attributes set */
7103 highlight_list_one((int)i);
7104 return;
7105 }
7106
7107 /*
7108 * Isolate the name.
7109 */
7110 name_end = skiptowhite(line);
7111 linep = skipwhite(name_end);
7112
7113 /*
7114 * Check for "default" argument.
7115 */
7116 if (STRNCMP(line, "default", name_end - line) == 0)
7117 {
7118 dodefault = TRUE;
7119 line = linep;
7120 name_end = skiptowhite(line);
7121 linep = skipwhite(name_end);
7122 }
7123
7124 /*
7125 * Check for "clear" or "link" argument.
7126 */
7127 if (STRNCMP(line, "clear", name_end - line) == 0)
7128 doclear = TRUE;
7129 if (STRNCMP(line, "link", name_end - line) == 0)
7130 dolink = TRUE;
7131
7132 /*
7133 * ":highlight {group-name}": list highlighting for one group.
7134 */
7135 if (!doclear && !dolink && ends_excmd(*linep))
7136 {
7137 id = syn_namen2id(line, (int)(name_end - line));
7138 if (id == 0)
7139 EMSG2(_("E411: highlight group not found: %s"), line);
7140 else
7141 highlight_list_one(id);
7142 return;
7143 }
7144
7145 /*
7146 * Handle ":highlight link {from} {to}" command.
7147 */
7148 if (dolink)
7149 {
7150 char_u *from_start = linep;
7151 char_u *from_end;
7152 char_u *to_start;
7153 char_u *to_end;
7154 int from_id;
7155 int to_id;
7156
7157 from_end = skiptowhite(from_start);
7158 to_start = skipwhite(from_end);
7159 to_end = skiptowhite(to_start);
7160
7161 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7162 {
7163 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7164 from_start);
7165 return;
7166 }
7167
7168 if (!ends_excmd(*skipwhite(to_end)))
7169 {
7170 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7171 return;
7172 }
7173
7174 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7175 if (STRNCMP(to_start, "NONE", 4) == 0)
7176 to_id = 0;
7177 else
7178 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7179
7180 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7181 {
7182 /*
7183 * Don't allow a link when there already is some highlighting
7184 * for the group, unless '!' is used
7185 */
7186 if (to_id > 0 && !forceit && !init
7187 && hl_has_settings(from_id - 1, dodefault))
7188 {
7189 if (sourcing_name == NULL && !dodefault)
7190 EMSG(_("E414: group has settings, highlight link ignored"));
7191 }
7192 else
7193 {
7194 if (!init)
7195 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7196 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007197#ifdef FEAT_EVAL
7198 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7199#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007200 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007201 }
7202 }
7203
7204 /* Only call highlight_changed() once, after sourcing a syntax file */
7205 need_highlight_changed = TRUE;
7206
7207 return;
7208 }
7209
7210 if (doclear)
7211 {
7212 /*
7213 * ":highlight clear [group]" command.
7214 */
7215 line = linep;
7216 if (ends_excmd(*line))
7217 {
7218#ifdef FEAT_GUI
7219 /* First, we do not destroy the old values, but allocate the new
7220 * ones and update the display. THEN we destroy the old values.
7221 * If we destroy the old values first, then the old values
7222 * (such as GuiFont's or GuiFontset's) will still be displayed but
7223 * invalid because they were free'd.
7224 */
7225 if (gui.in_use)
7226 {
7227# ifdef FEAT_BEVAL_TIP
7228 gui_init_tooltip_font();
7229# endif
7230# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7231 gui_init_menu_font();
7232# endif
7233 }
7234# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7235 gui_mch_def_colors();
7236# endif
7237# ifdef FEAT_GUI_X11
7238# ifdef FEAT_MENU
7239
7240 /* This only needs to be done when there is no Menu highlight
7241 * group defined by default, which IS currently the case.
7242 */
7243 gui_mch_new_menu_colors();
7244# endif
7245 if (gui.in_use)
7246 {
7247 gui_new_scrollbar_colors();
7248# ifdef FEAT_BEVAL
7249 gui_mch_new_tooltip_colors();
7250# endif
7251# ifdef FEAT_MENU
7252 gui_mch_new_menu_font();
7253# endif
7254 }
7255# endif
7256
7257 /* Ok, we're done allocating the new default graphics items.
7258 * The screen should already be refreshed at this point.
7259 * It is now Ok to clear out the old data.
7260 */
7261#endif
7262#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007263 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007264#endif
7265 restore_cterm_colors();
7266
7267 /*
7268 * Clear all default highlight groups and load the defaults.
7269 */
7270 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7271 highlight_clear(idx);
7272 init_highlight(TRUE, TRUE);
7273#ifdef FEAT_GUI
7274 if (gui.in_use)
7275 highlight_gui_started();
7276#endif
7277 highlight_changed();
7278 redraw_later_clear();
7279 return;
7280 }
7281 name_end = skiptowhite(line);
7282 linep = skipwhite(name_end);
7283 }
7284
7285 /*
7286 * Find the group name in the table. If it does not exist yet, add it.
7287 */
7288 id = syn_check_group(line, (int)(name_end - line));
7289 if (id == 0) /* failed (out of memory) */
7290 return;
7291 idx = id - 1; /* index is ID minus one */
7292
7293 /* Return if "default" was used and the group already has settings. */
7294 if (dodefault && hl_has_settings(idx, TRUE))
7295 return;
7296
7297 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7298 is_normal_group = TRUE;
7299#ifdef FEAT_GUI_X11
7300 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7301 is_menu_group = TRUE;
7302 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7303 is_scrollbar_group = TRUE;
7304 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7305 is_tooltip_group = TRUE;
7306#endif
7307
7308 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7309 if (doclear || (forceit && init))
7310 {
7311 highlight_clear(idx);
7312 if (!doclear)
7313 HL_TABLE()[idx].sg_set = 0;
7314 }
7315
7316 if (!doclear)
7317 while (!ends_excmd(*linep))
7318 {
7319 key_start = linep;
7320 if (*linep == '=')
7321 {
7322 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7323 error = TRUE;
7324 break;
7325 }
7326
7327 /*
7328 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7329 * "guibg").
7330 */
7331 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7332 ++linep;
7333 vim_free(key);
7334 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7335 if (key == NULL)
7336 {
7337 error = TRUE;
7338 break;
7339 }
7340 linep = skipwhite(linep);
7341
7342 if (STRCMP(key, "NONE") == 0)
7343 {
7344 if (!init || HL_TABLE()[idx].sg_set == 0)
7345 {
7346 if (!init)
7347 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7348 highlight_clear(idx);
7349 }
7350 continue;
7351 }
7352
7353 /*
7354 * Check for the equal sign.
7355 */
7356 if (*linep != '=')
7357 {
7358 EMSG2(_("E416: missing equal sign: %s"), key_start);
7359 error = TRUE;
7360 break;
7361 }
7362 ++linep;
7363
7364 /*
7365 * Isolate the argument.
7366 */
7367 linep = skipwhite(linep);
7368 if (*linep == '\'') /* guifg='color name' */
7369 {
7370 arg_start = ++linep;
7371 linep = vim_strchr(linep, '\'');
7372 if (linep == NULL)
7373 {
7374 EMSG2(_(e_invarg2), key_start);
7375 error = TRUE;
7376 break;
7377 }
7378 }
7379 else
7380 {
7381 arg_start = linep;
7382 linep = skiptowhite(linep);
7383 }
7384 if (linep == arg_start)
7385 {
7386 EMSG2(_("E417: missing argument: %s"), key_start);
7387 error = TRUE;
7388 break;
7389 }
7390 vim_free(arg);
7391 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7392 if (arg == NULL)
7393 {
7394 error = TRUE;
7395 break;
7396 }
7397 if (*linep == '\'')
7398 ++linep;
7399
7400 /*
7401 * Store the argument.
7402 */
7403 if ( STRCMP(key, "TERM") == 0
7404 || STRCMP(key, "CTERM") == 0
7405 || STRCMP(key, "GUI") == 0)
7406 {
7407 attr = 0;
7408 off = 0;
7409 while (arg[off] != NUL)
7410 {
7411 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7412 {
7413 len = (int)STRLEN(hl_name_table[i]);
7414 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7415 {
7416 attr |= hl_attr_table[i];
7417 off += len;
7418 break;
7419 }
7420 }
7421 if (i < 0)
7422 {
7423 EMSG2(_("E418: Illegal value: %s"), arg);
7424 error = TRUE;
7425 break;
7426 }
7427 if (arg[off] == ',') /* another one follows */
7428 ++off;
7429 }
7430 if (error)
7431 break;
7432 if (*key == 'T')
7433 {
7434 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7435 {
7436 if (!init)
7437 HL_TABLE()[idx].sg_set |= SG_TERM;
7438 HL_TABLE()[idx].sg_term = attr;
7439 }
7440 }
7441 else if (*key == 'C')
7442 {
7443 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7444 {
7445 if (!init)
7446 HL_TABLE()[idx].sg_set |= SG_CTERM;
7447 HL_TABLE()[idx].sg_cterm = attr;
7448 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7449 }
7450 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007451#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007452 else
7453 {
7454 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7455 {
7456 if (!init)
7457 HL_TABLE()[idx].sg_set |= SG_GUI;
7458 HL_TABLE()[idx].sg_gui = attr;
7459 }
7460 }
7461#endif
7462 }
7463 else if (STRCMP(key, "FONT") == 0)
7464 {
7465 /* in non-GUI fonts are simply ignored */
7466#ifdef FEAT_GUI
7467 if (!gui.shell_created)
7468 {
7469 /* GUI not started yet, always accept the name. */
7470 vim_free(HL_TABLE()[idx].sg_font_name);
7471 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7472 }
7473 else
7474 {
7475 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7476# ifdef FEAT_XFONTSET
7477 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7478# endif
7479 /* First, save the current font/fontset.
7480 * Then try to allocate the font/fontset.
7481 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7482 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7483 */
7484
7485 HL_TABLE()[idx].sg_font = NOFONT;
7486# ifdef FEAT_XFONTSET
7487 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7488# endif
7489 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007490 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007491
7492# ifdef FEAT_XFONTSET
7493 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7494 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007495 /* New fontset was accepted. Free the old one, if there
7496 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007497 gui_mch_free_fontset(temp_sg_fontset);
7498 vim_free(HL_TABLE()[idx].sg_font_name);
7499 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7500 }
7501 else
7502 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7503# endif
7504 if (HL_TABLE()[idx].sg_font != NOFONT)
7505 {
7506 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007507 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007508 gui_mch_free_font(temp_sg_font);
7509 vim_free(HL_TABLE()[idx].sg_font_name);
7510 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7511 }
7512 else
7513 HL_TABLE()[idx].sg_font = temp_sg_font;
7514 }
7515#endif
7516 }
7517 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7518 {
7519 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7520 {
7521 if (!init)
7522 HL_TABLE()[idx].sg_set |= SG_CTERM;
7523
7524 /* When setting the foreground color, and previously the "bold"
7525 * flag was set for a light color, reset it now */
7526 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7527 {
7528 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7529 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7530 }
7531
7532 if (VIM_ISDIGIT(*arg))
7533 color = atoi((char *)arg);
7534 else if (STRICMP(arg, "fg") == 0)
7535 {
7536 if (cterm_normal_fg_color)
7537 color = cterm_normal_fg_color - 1;
7538 else
7539 {
7540 EMSG(_("E419: FG color unknown"));
7541 error = TRUE;
7542 break;
7543 }
7544 }
7545 else if (STRICMP(arg, "bg") == 0)
7546 {
7547 if (cterm_normal_bg_color > 0)
7548 color = cterm_normal_bg_color - 1;
7549 else
7550 {
7551 EMSG(_("E420: BG color unknown"));
7552 error = TRUE;
7553 break;
7554 }
7555 }
7556 else
7557 {
7558 static char *(color_names[28]) = {
7559 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7560 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7561 "Gray", "Grey",
7562 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7563 "Blue", "LightBlue", "Green", "LightGreen",
7564 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7565 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7566 static int color_numbers_16[28] = {0, 1, 2, 3,
7567 4, 5, 6, 6,
7568 7, 7,
7569 7, 7, 8, 8,
7570 9, 9, 10, 10,
7571 11, 11, 12, 12, 13,
7572 13, 14, 14, 15, -1};
7573 /* for xterm with 88 colors... */
7574 static int color_numbers_88[28] = {0, 4, 2, 6,
7575 1, 5, 32, 72,
7576 84, 84,
7577 7, 7, 82, 82,
7578 12, 43, 10, 61,
7579 14, 63, 9, 74, 13,
7580 75, 11, 78, 15, -1};
7581 /* for xterm with 256 colors... */
7582 static int color_numbers_256[28] = {0, 4, 2, 6,
7583 1, 5, 130, 130,
7584 248, 248,
7585 7, 7, 242, 242,
7586 12, 81, 10, 121,
7587 14, 159, 9, 224, 13,
7588 225, 11, 229, 15, -1};
7589 /* for terminals with less than 16 colors... */
7590 static int color_numbers_8[28] = {0, 4, 2, 6,
7591 1, 5, 3, 3,
7592 7, 7,
7593 7, 7, 0+8, 0+8,
7594 4+8, 4+8, 2+8, 2+8,
7595 6+8, 6+8, 1+8, 1+8, 5+8,
7596 5+8, 3+8, 3+8, 7+8, -1};
7597#if defined(__QNXNTO__)
7598 static int *color_numbers_8_qansi = color_numbers_8;
7599 /* On qnx, the 8 & 16 color arrays are the same */
7600 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7601 color_numbers_8_qansi = color_numbers_16;
7602#endif
7603
7604 /* reduce calls to STRICMP a bit, it can be slow */
7605 off = TOUPPER_ASC(*arg);
7606 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7607 if (off == color_names[i][0]
7608 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7609 break;
7610 if (i < 0)
7611 {
7612 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7613 error = TRUE;
7614 break;
7615 }
7616
7617 /* Use the _16 table to check if its a valid color name. */
7618 color = color_numbers_16[i];
7619 if (color >= 0)
7620 {
7621 if (t_colors == 8)
7622 {
7623 /* t_Co is 8: use the 8 colors table */
7624#if defined(__QNXNTO__)
7625 color = color_numbers_8_qansi[i];
7626#else
7627 color = color_numbers_8[i];
7628#endif
7629 if (key[5] == 'F')
7630 {
7631 /* set/reset bold attribute to get light foreground
7632 * colors (on some terminals, e.g. "linux") */
7633 if (color & 8)
7634 {
7635 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7636 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7637 }
7638 else
7639 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7640 }
7641 color &= 7; /* truncate to 8 colors */
7642 }
7643 else if (t_colors == 16 || t_colors == 88
7644 || t_colors == 256)
7645 {
7646 /*
7647 * Guess: if the termcap entry ends in 'm', it is
7648 * probably an xterm-like terminal. Use the changed
7649 * order for colors.
7650 */
7651 if (*T_CAF != NUL)
7652 p = T_CAF;
7653 else
7654 p = T_CSF;
7655 if (*p != NUL && *(p + STRLEN(p) - 1) == 'm')
7656 switch (t_colors)
7657 {
7658 case 16:
7659 color = color_numbers_8[i];
7660 break;
7661 case 88:
7662 color = color_numbers_88[i];
7663 break;
7664 case 256:
7665 color = color_numbers_256[i];
7666 break;
7667 }
7668 }
7669 }
7670 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007671 /* Add one to the argument, to avoid zero. Zero is used for
7672 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007673 if (key[5] == 'F')
7674 {
7675 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7676 if (is_normal_group)
7677 {
7678 cterm_normal_fg_color = color + 1;
7679 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7680#ifdef FEAT_GUI
7681 /* Don't do this if the GUI is used. */
7682 if (!gui.in_use && !gui.starting)
7683#endif
7684 {
7685 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007686 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007687 term_fg_color(color);
7688 }
7689 }
7690 }
7691 else
7692 {
7693 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7694 if (is_normal_group)
7695 {
7696 cterm_normal_bg_color = color + 1;
7697#ifdef FEAT_GUI
7698 /* Don't mess with 'background' if the GUI is used. */
7699 if (!gui.in_use && !gui.starting)
7700#endif
7701 {
7702 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007703 if (color >= 0)
7704 {
7705 if (termcap_active)
7706 term_bg_color(color);
7707 if (t_colors < 16)
7708 i = (color == 0 || color == 4);
7709 else
7710 i = (color < 7 || color == 8);
7711 /* Set the 'background' option if the value is
7712 * wrong. */
7713 if (i != (*p_bg == 'd'))
7714 set_option_value((char_u *)"bg", 0L,
7715 i ? (char_u *)"dark"
7716 : (char_u *)"light", 0);
7717 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007718 }
7719 }
7720 }
7721 }
7722 }
7723 else if (STRCMP(key, "GUIFG") == 0)
7724 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007725#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007726 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007727 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007728 if (!init)
7729 HL_TABLE()[idx].sg_set |= SG_GUI;
7730
Bram Moolenaar61623362010-07-14 22:04:22 +02007731# ifdef FEAT_GUI
7732 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007733 i = color_name2handle(arg);
7734 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7735 {
7736 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007737# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007738 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7739 if (STRCMP(arg, "NONE"))
7740 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7741 else
7742 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007743# ifdef FEAT_GUI
7744# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007745 if (is_menu_group)
7746 gui.menu_fg_pixel = i;
7747 if (is_scrollbar_group)
7748 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007749# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007750 if (is_tooltip_group)
7751 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007752# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007753 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007754# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007755 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007756# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007757 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007758#endif
7759 }
7760 else if (STRCMP(key, "GUIBG") == 0)
7761 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007762#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007763 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007764 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007765 if (!init)
7766 HL_TABLE()[idx].sg_set |= SG_GUI;
7767
Bram Moolenaar61623362010-07-14 22:04:22 +02007768# ifdef FEAT_GUI
7769 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007770 i = color_name2handle(arg);
7771 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7772 {
7773 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007774# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007775 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7776 if (STRCMP(arg, "NONE") != 0)
7777 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7778 else
7779 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007780# ifdef FEAT_GUI
7781# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007782 if (is_menu_group)
7783 gui.menu_bg_pixel = i;
7784 if (is_scrollbar_group)
7785 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007786# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007787 if (is_tooltip_group)
7788 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007789# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007790 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007791# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007792 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007793# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007794 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007795#endif
7796 }
7797 else if (STRCMP(key, "GUISP") == 0)
7798 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007799#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007800 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7801 {
7802 if (!init)
7803 HL_TABLE()[idx].sg_set |= SG_GUI;
7804
Bram Moolenaar61623362010-07-14 22:04:22 +02007805# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007806 i = color_name2handle(arg);
7807 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7808 {
7809 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007810# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007811 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7812 if (STRCMP(arg, "NONE") != 0)
7813 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7814 else
7815 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007816# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007817 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007818# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007819 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007820#endif
7821 }
7822 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7823 {
7824 char_u buf[100];
7825 char_u *tname;
7826
7827 if (!init)
7828 HL_TABLE()[idx].sg_set |= SG_TERM;
7829
7830 /*
7831 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007832 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007833 */
7834 if (STRNCMP(arg, "t_", 2) == 0)
7835 {
7836 off = 0;
7837 buf[0] = 0;
7838 while (arg[off] != NUL)
7839 {
7840 /* Isolate one termcap name */
7841 for (len = 0; arg[off + len] &&
7842 arg[off + len] != ','; ++len)
7843 ;
7844 tname = vim_strnsave(arg + off, len);
7845 if (tname == NULL) /* out of memory */
7846 {
7847 error = TRUE;
7848 break;
7849 }
7850 /* lookup the escape sequence for the item */
7851 p = get_term_code(tname);
7852 vim_free(tname);
7853 if (p == NULL) /* ignore non-existing things */
7854 p = (char_u *)"";
7855
7856 /* Append it to the already found stuff */
7857 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7858 {
7859 EMSG2(_("E422: terminal code too long: %s"), arg);
7860 error = TRUE;
7861 break;
7862 }
7863 STRCAT(buf, p);
7864
7865 /* Advance to the next item */
7866 off += len;
7867 if (arg[off] == ',') /* another one follows */
7868 ++off;
7869 }
7870 }
7871 else
7872 {
7873 /*
7874 * Copy characters from arg[] to buf[], translating <> codes.
7875 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007876 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007877 {
7878 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007879 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007880 off += len;
7881 else /* copy as normal char */
7882 buf[off++] = *p++;
7883 }
7884 buf[off] = NUL;
7885 }
7886 if (error)
7887 break;
7888
7889 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7890 p = NULL;
7891 else
7892 p = vim_strsave(buf);
7893 if (key[2] == 'A')
7894 {
7895 vim_free(HL_TABLE()[idx].sg_start);
7896 HL_TABLE()[idx].sg_start = p;
7897 }
7898 else
7899 {
7900 vim_free(HL_TABLE()[idx].sg_stop);
7901 HL_TABLE()[idx].sg_stop = p;
7902 }
7903 }
7904 else
7905 {
7906 EMSG2(_("E423: Illegal argument: %s"), key_start);
7907 error = TRUE;
7908 break;
7909 }
7910
7911 /*
7912 * When highlighting has been given for a group, don't link it.
7913 */
7914 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7915 HL_TABLE()[idx].sg_link = 0;
7916
7917 /*
7918 * Continue with next argument.
7919 */
7920 linep = skipwhite(linep);
7921 }
7922
7923 /*
7924 * If there is an error, and it's a new entry, remove it from the table.
7925 */
7926 if (error && idx == highlight_ga.ga_len)
7927 syn_unadd_group();
7928 else
7929 {
7930 if (is_normal_group)
7931 {
7932 HL_TABLE()[idx].sg_term_attr = 0;
7933 HL_TABLE()[idx].sg_cterm_attr = 0;
7934#ifdef FEAT_GUI
7935 HL_TABLE()[idx].sg_gui_attr = 0;
7936 /*
7937 * Need to update all groups, because they might be using "bg"
7938 * and/or "fg", which have been changed now.
7939 */
7940 if (gui.in_use)
7941 highlight_gui_started();
7942#endif
7943 }
7944#ifdef FEAT_GUI_X11
7945# ifdef FEAT_MENU
7946 else if (is_menu_group)
7947 {
7948 if (gui.in_use && do_colors)
7949 gui_mch_new_menu_colors();
7950 }
7951# endif
7952 else if (is_scrollbar_group)
7953 {
7954 if (gui.in_use && do_colors)
7955 gui_new_scrollbar_colors();
7956 }
7957# ifdef FEAT_BEVAL
7958 else if (is_tooltip_group)
7959 {
7960 if (gui.in_use && do_colors)
7961 gui_mch_new_tooltip_colors();
7962 }
7963# endif
7964#endif
7965 else
7966 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00007967#ifdef FEAT_EVAL
7968 HL_TABLE()[idx].sg_scriptID = current_SID;
7969#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00007970 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007971 }
7972 vim_free(key);
7973 vim_free(arg);
7974
7975 /* Only call highlight_changed() once, after sourcing a syntax file */
7976 need_highlight_changed = TRUE;
7977}
7978
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007979#if defined(EXITFREE) || defined(PROTO)
7980 void
7981free_highlight()
7982{
7983 int i;
7984
7985 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007986 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007987 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00007988 vim_free(HL_TABLE()[i].sg_name);
7989 vim_free(HL_TABLE()[i].sg_name_u);
7990 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00007991 ga_clear(&highlight_ga);
7992}
7993#endif
7994
Bram Moolenaar071d4272004-06-13 20:20:40 +00007995/*
7996 * Reset the cterm colors to what they were before Vim was started, if
7997 * possible. Otherwise reset them to zero.
7998 */
7999 void
8000restore_cterm_colors()
8001{
8002#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
8003 /* Since t_me has been set, this probably means that the user
8004 * wants to use this as default colors. Need to reset default
8005 * background/foreground colors. */
8006 mch_set_normal_colors();
8007#else
8008 cterm_normal_fg_color = 0;
8009 cterm_normal_fg_bold = 0;
8010 cterm_normal_bg_color = 0;
8011#endif
8012}
8013
8014/*
8015 * Return TRUE if highlight group "idx" has any settings.
8016 * When "check_link" is TRUE also check for an existing link.
8017 */
8018 static int
8019hl_has_settings(idx, check_link)
8020 int idx;
8021 int check_link;
8022{
8023 return ( HL_TABLE()[idx].sg_term_attr != 0
8024 || HL_TABLE()[idx].sg_cterm_attr != 0
8025#ifdef FEAT_GUI
8026 || HL_TABLE()[idx].sg_gui_attr != 0
8027#endif
8028 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8029}
8030
8031/*
8032 * Clear highlighting for one group.
8033 */
8034 static void
8035highlight_clear(idx)
8036 int idx;
8037{
8038 HL_TABLE()[idx].sg_term = 0;
8039 vim_free(HL_TABLE()[idx].sg_start);
8040 HL_TABLE()[idx].sg_start = NULL;
8041 vim_free(HL_TABLE()[idx].sg_stop);
8042 HL_TABLE()[idx].sg_stop = NULL;
8043 HL_TABLE()[idx].sg_term_attr = 0;
8044 HL_TABLE()[idx].sg_cterm = 0;
8045 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8046 HL_TABLE()[idx].sg_cterm_fg = 0;
8047 HL_TABLE()[idx].sg_cterm_bg = 0;
8048 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008049#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008050 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008051 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8052 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008053 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8054 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008055 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8056 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008057#endif
8058#ifdef FEAT_GUI
8059 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8060 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
8061 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008062 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8063 HL_TABLE()[idx].sg_font = NOFONT;
8064# ifdef FEAT_XFONTSET
8065 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8066 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8067# endif
8068 vim_free(HL_TABLE()[idx].sg_font_name);
8069 HL_TABLE()[idx].sg_font_name = NULL;
8070 HL_TABLE()[idx].sg_gui_attr = 0;
8071#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008072#ifdef FEAT_EVAL
8073 /* Clear the script ID only when there is no link, since that is not
8074 * cleared. */
8075 if (HL_TABLE()[idx].sg_link == 0)
8076 HL_TABLE()[idx].sg_scriptID = 0;
8077#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008078}
8079
8080#if defined(FEAT_GUI) || defined(PROTO)
8081/*
8082 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008083 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008084 * "Tooltip" colors.
8085 */
8086 void
8087set_normal_colors()
8088{
8089 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008090 &gui.norm_pixel, &gui.back_pixel,
8091 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008092 {
8093 gui_mch_new_colors();
8094 must_redraw = CLEAR;
8095 }
8096#ifdef FEAT_GUI_X11
8097 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008098 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8099 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008100 {
8101# ifdef FEAT_MENU
8102 gui_mch_new_menu_colors();
8103# endif
8104 must_redraw = CLEAR;
8105 }
8106# ifdef FEAT_BEVAL
8107 if (set_group_colors((char_u *)"Tooltip",
8108 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8109 FALSE, FALSE, TRUE))
8110 {
8111# ifdef FEAT_TOOLBAR
8112 gui_mch_new_tooltip_colors();
8113# endif
8114 must_redraw = CLEAR;
8115 }
8116#endif
8117 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008118 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8119 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008120 {
8121 gui_new_scrollbar_colors();
8122 must_redraw = CLEAR;
8123 }
8124#endif
8125}
8126
8127/*
8128 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8129 */
8130 static int
8131set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
8132 char_u *name;
8133 guicolor_T *fgp;
8134 guicolor_T *bgp;
8135 int do_menu;
8136 int use_norm;
8137 int do_tooltip;
8138{
8139 int idx;
8140
8141 idx = syn_name2id(name) - 1;
8142 if (idx >= 0)
8143 {
8144 gui_do_one_color(idx, do_menu, do_tooltip);
8145
8146 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8147 *fgp = HL_TABLE()[idx].sg_gui_fg;
8148 else if (use_norm)
8149 *fgp = gui.def_norm_pixel;
8150 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8151 *bgp = HL_TABLE()[idx].sg_gui_bg;
8152 else if (use_norm)
8153 *bgp = gui.def_back_pixel;
8154 return TRUE;
8155 }
8156 return FALSE;
8157}
8158
8159/*
8160 * Get the font of the "Normal" group.
8161 * Returns "" when it's not found or not set.
8162 */
8163 char_u *
8164hl_get_font_name()
8165{
8166 int id;
8167 char_u *s;
8168
8169 id = syn_name2id((char_u *)"Normal");
8170 if (id > 0)
8171 {
8172 s = HL_TABLE()[id - 1].sg_font_name;
8173 if (s != NULL)
8174 return s;
8175 }
8176 return (char_u *)"";
8177}
8178
8179/*
8180 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8181 * actually chosen to be used.
8182 */
8183 void
8184hl_set_font_name(font_name)
8185 char_u *font_name;
8186{
8187 int id;
8188
8189 id = syn_name2id((char_u *)"Normal");
8190 if (id > 0)
8191 {
8192 vim_free(HL_TABLE()[id - 1].sg_font_name);
8193 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8194 }
8195}
8196
8197/*
8198 * Set background color for "Normal" group. Called by gui_set_bg_color()
8199 * when the color is known.
8200 */
8201 void
8202hl_set_bg_color_name(name)
8203 char_u *name; /* must have been allocated */
8204{
8205 int id;
8206
8207 if (name != NULL)
8208 {
8209 id = syn_name2id((char_u *)"Normal");
8210 if (id > 0)
8211 {
8212 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8213 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8214 }
8215 }
8216}
8217
8218/*
8219 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8220 * when the color is known.
8221 */
8222 void
8223hl_set_fg_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_fg_name);
8234 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8235 }
8236 }
8237}
8238
8239/*
8240 * Return the handle for a color name.
8241 * Returns INVALCOLOR when failed.
8242 */
8243 static guicolor_T
8244color_name2handle(name)
8245 char_u *name;
8246{
8247 if (STRCMP(name, "NONE") == 0)
8248 return INVALCOLOR;
8249
8250 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8251 return gui.norm_pixel;
8252 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8253 return gui.back_pixel;
8254
8255 return gui_get_color(name);
8256}
8257
8258/*
8259 * Return the handle for a font name.
8260 * Returns NOFONT when failed.
8261 */
8262 static GuiFont
8263font_name2handle(name)
8264 char_u *name;
8265{
8266 if (STRCMP(name, "NONE") == 0)
8267 return NOFONT;
8268
8269 return gui_mch_get_font(name, TRUE);
8270}
8271
8272# ifdef FEAT_XFONTSET
8273/*
8274 * Return the handle for a fontset name.
8275 * Returns NOFONTSET when failed.
8276 */
8277 static GuiFontset
8278fontset_name2handle(name, fixed_width)
8279 char_u *name;
8280 int fixed_width;
8281{
8282 if (STRCMP(name, "NONE") == 0)
8283 return NOFONTSET;
8284
8285 return gui_mch_get_fontset(name, TRUE, fixed_width);
8286}
8287# endif
8288
8289/*
8290 * Get the font or fontset for one highlight group.
8291 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008292 static void
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008293hl_do_font(idx, arg, do_normal, do_menu, do_tooltip, free_font)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008294 int idx;
8295 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008296 int do_normal; /* set normal font */
8297 int do_menu UNUSED; /* set menu font */
8298 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008299 int free_font; /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008300{
8301# ifdef FEAT_XFONTSET
8302 /* If 'guifontset' is not empty, first try using the name as a
8303 * fontset. If that doesn't work, use it as a font name. */
8304 if (*p_guifontset != NUL
8305# ifdef FONTSET_ALWAYS
8306 || do_menu
8307# endif
8308# ifdef FEAT_BEVAL_TIP
8309 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8310 || do_tooltip
8311# endif
8312 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008313 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008314 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008315 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008316 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8317# ifdef FONTSET_ALWAYS
8318 || do_menu
8319# endif
8320# ifdef FEAT_BEVAL_TIP
8321 || do_tooltip
8322# endif
8323 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008324 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008325 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8326 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008327 /* If it worked and it's the Normal group, use it as the normal
8328 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008329 if (do_normal)
8330 gui_init_font(arg, TRUE);
8331# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8332 if (do_menu)
8333 {
8334# ifdef FONTSET_ALWAYS
8335 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8336# else
8337 /* YIKES! This is a bug waiting to crash the program */
8338 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8339# endif
8340 gui_mch_new_menu_font();
8341 }
8342# ifdef FEAT_BEVAL
8343 if (do_tooltip)
8344 {
8345 /* The Athena widget set cannot currently handle switching between
8346 * displaying a single font and a fontset.
8347 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008348 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008349 * XFontStruct is used.
8350 */
8351 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8352 gui_mch_new_tooltip_font();
8353 }
8354# endif
8355# endif
8356 }
8357 else
8358# endif
8359 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008360 if (free_font)
8361 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008362 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8363 /* If it worked and it's the Normal group, use it as the
8364 * normal font. Same for the Menu group. */
8365 if (HL_TABLE()[idx].sg_font != NOFONT)
8366 {
8367 if (do_normal)
8368 gui_init_font(arg, FALSE);
8369#ifndef FONTSET_ALWAYS
8370# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8371 if (do_menu)
8372 {
8373 gui.menu_font = HL_TABLE()[idx].sg_font;
8374 gui_mch_new_menu_font();
8375 }
8376# endif
8377#endif
8378 }
8379 }
8380}
8381
8382#endif /* FEAT_GUI */
8383
8384/*
8385 * Table with the specifications for an attribute number.
8386 * Note that this table is used by ALL buffers. This is required because the
8387 * GUI can redraw at any time for any buffer.
8388 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008389static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008390
8391#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8392
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008393static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008394
8395#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8396
8397#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008398static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008399
8400#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8401#endif
8402
8403/*
8404 * Return the attr number for a set of colors and font.
8405 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8406 * if the combination is new.
8407 * Return 0 for error (no more room).
8408 */
8409 static int
8410get_attr_entry(table, aep)
8411 garray_T *table;
8412 attrentry_T *aep;
8413{
8414 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008415 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008416 static int recursive = FALSE;
8417
8418 /*
8419 * Init the table, in case it wasn't done yet.
8420 */
8421 table->ga_itemsize = sizeof(attrentry_T);
8422 table->ga_growsize = 7;
8423
8424 /*
8425 * Try to find an entry with the same specifications.
8426 */
8427 for (i = 0; i < table->ga_len; ++i)
8428 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008429 taep = &(((attrentry_T *)table->ga_data)[i]);
8430 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008431 && (
8432#ifdef FEAT_GUI
8433 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008434 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8435 && aep->ae_u.gui.bg_color
8436 == taep->ae_u.gui.bg_color
8437 && aep->ae_u.gui.sp_color
8438 == taep->ae_u.gui.sp_color
8439 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008440# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008441 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008442# endif
8443 ))
8444 ||
8445#endif
8446 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008447 && (aep->ae_u.term.start == NULL)
8448 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008449 && (aep->ae_u.term.start == NULL
8450 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008451 taep->ae_u.term.start) == 0)
8452 && (aep->ae_u.term.stop == NULL)
8453 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008454 && (aep->ae_u.term.stop == NULL
8455 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008456 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008457 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008458 && aep->ae_u.cterm.fg_color
8459 == taep->ae_u.cterm.fg_color
8460 && aep->ae_u.cterm.bg_color
8461 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008462 ))
8463
8464 return i + ATTR_OFF;
8465 }
8466
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008467 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008468 {
8469 /*
8470 * Running out of attribute entries! remove all attributes, and
8471 * compute new ones for all groups.
8472 * When called recursively, we are really out of numbers.
8473 */
8474 if (recursive)
8475 {
8476 EMSG(_("E424: Too many different highlighting attributes in use"));
8477 return 0;
8478 }
8479 recursive = TRUE;
8480
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008481 clear_hl_tables();
8482
Bram Moolenaar071d4272004-06-13 20:20:40 +00008483 must_redraw = CLEAR;
8484
8485 for (i = 0; i < highlight_ga.ga_len; ++i)
8486 set_hl_attr(i);
8487
8488 recursive = FALSE;
8489 }
8490
8491 /*
8492 * This is a new combination of colors and font, add an entry.
8493 */
8494 if (ga_grow(table, 1) == FAIL)
8495 return 0;
8496
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008497 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8498 vim_memset(taep, 0, sizeof(attrentry_T));
8499 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008500#ifdef FEAT_GUI
8501 if (table == &gui_attr_table)
8502 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008503 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8504 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8505 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8506 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008507# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008508 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008509# endif
8510 }
8511#endif
8512 if (table == &term_attr_table)
8513 {
8514 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008515 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008516 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008517 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008518 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008519 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008520 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008521 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008522 }
8523 else if (table == &cterm_attr_table)
8524 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008525 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8526 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008527 }
8528 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008529 return (table->ga_len - 1 + ATTR_OFF);
8530}
8531
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008532/*
8533 * Clear all highlight tables.
8534 */
8535 void
8536clear_hl_tables()
8537{
8538 int i;
8539 attrentry_T *taep;
8540
8541#ifdef FEAT_GUI
8542 ga_clear(&gui_attr_table);
8543#endif
8544 for (i = 0; i < term_attr_table.ga_len; ++i)
8545 {
8546 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8547 vim_free(taep->ae_u.term.start);
8548 vim_free(taep->ae_u.term.stop);
8549 }
8550 ga_clear(&term_attr_table);
8551 ga_clear(&cterm_attr_table);
8552}
8553
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008554#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008555/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008556 * Combine special attributes (e.g., for spelling) with other attributes
8557 * (e.g., for syntax highlighting).
8558 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008559 * This creates a new group when required.
8560 * Since we expect there to be few spelling mistakes we don't cache the
8561 * result.
8562 * Return the resulting attributes.
8563 */
8564 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008565hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008566 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008567 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008568{
8569 attrentry_T *char_aep = NULL;
8570 attrentry_T *spell_aep;
8571 attrentry_T new_en;
8572
8573 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008574 return prim_attr;
8575 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8576 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008577#ifdef FEAT_GUI
8578 if (gui.in_use)
8579 {
8580 if (char_attr > HL_ALL)
8581 char_aep = syn_gui_attr2entry(char_attr);
8582 if (char_aep != NULL)
8583 new_en = *char_aep;
8584 else
8585 {
8586 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008587 new_en.ae_u.gui.fg_color = INVALCOLOR;
8588 new_en.ae_u.gui.bg_color = INVALCOLOR;
8589 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008590 if (char_attr <= HL_ALL)
8591 new_en.ae_attr = char_attr;
8592 }
8593
Bram Moolenaar30abd282005-06-22 22:35:10 +00008594 if (prim_attr <= HL_ALL)
8595 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008596 else
8597 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008598 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008599 if (spell_aep != NULL)
8600 {
8601 new_en.ae_attr |= spell_aep->ae_attr;
8602 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8603 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8604 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8605 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8606 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8607 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8608 if (spell_aep->ae_u.gui.font != NOFONT)
8609 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8610# ifdef FEAT_XFONTSET
8611 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8612 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8613# endif
8614 }
8615 }
8616 return get_attr_entry(&gui_attr_table, &new_en);
8617 }
8618#endif
8619
8620 if (t_colors > 1)
8621 {
8622 if (char_attr > HL_ALL)
8623 char_aep = syn_cterm_attr2entry(char_attr);
8624 if (char_aep != NULL)
8625 new_en = *char_aep;
8626 else
8627 {
8628 vim_memset(&new_en, 0, sizeof(new_en));
8629 if (char_attr <= HL_ALL)
8630 new_en.ae_attr = char_attr;
8631 }
8632
Bram Moolenaar30abd282005-06-22 22:35:10 +00008633 if (prim_attr <= HL_ALL)
8634 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008635 else
8636 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008637 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008638 if (spell_aep != NULL)
8639 {
8640 new_en.ae_attr |= spell_aep->ae_attr;
8641 if (spell_aep->ae_u.cterm.fg_color > 0)
8642 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8643 if (spell_aep->ae_u.cterm.bg_color > 0)
8644 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8645 }
8646 }
8647 return get_attr_entry(&cterm_attr_table, &new_en);
8648 }
8649
8650 if (char_attr > HL_ALL)
8651 char_aep = syn_term_attr2entry(char_attr);
8652 if (char_aep != NULL)
8653 new_en = *char_aep;
8654 else
8655 {
8656 vim_memset(&new_en, 0, sizeof(new_en));
8657 if (char_attr <= HL_ALL)
8658 new_en.ae_attr = char_attr;
8659 }
8660
Bram Moolenaar30abd282005-06-22 22:35:10 +00008661 if (prim_attr <= HL_ALL)
8662 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008663 else
8664 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008665 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008666 if (spell_aep != NULL)
8667 {
8668 new_en.ae_attr |= spell_aep->ae_attr;
8669 if (spell_aep->ae_u.term.start != NULL)
8670 {
8671 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8672 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8673 }
8674 }
8675 }
8676 return get_attr_entry(&term_attr_table, &new_en);
8677}
8678#endif
8679
Bram Moolenaar071d4272004-06-13 20:20:40 +00008680#ifdef FEAT_GUI
8681
8682 attrentry_T *
8683syn_gui_attr2entry(attr)
8684 int attr;
8685{
8686 attr -= ATTR_OFF;
8687 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8688 return NULL;
8689 return &(GUI_ATTR_ENTRY(attr));
8690}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008691#endif /* FEAT_GUI */
8692
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008693/*
8694 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8695 * Only to be used when "attr" > HL_ALL.
8696 */
8697 int
8698syn_attr2attr(attr)
8699 int attr;
8700{
8701 attrentry_T *aep;
8702
8703#ifdef FEAT_GUI
8704 if (gui.in_use)
8705 aep = syn_gui_attr2entry(attr);
8706 else
8707#endif
8708 if (t_colors > 1)
8709 aep = syn_cterm_attr2entry(attr);
8710 else
8711 aep = syn_term_attr2entry(attr);
8712
8713 if (aep == NULL) /* highlighting not set */
8714 return 0;
8715 return aep->ae_attr;
8716}
8717
8718
Bram Moolenaar071d4272004-06-13 20:20:40 +00008719 attrentry_T *
8720syn_term_attr2entry(attr)
8721 int attr;
8722{
8723 attr -= ATTR_OFF;
8724 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8725 return NULL;
8726 return &(TERM_ATTR_ENTRY(attr));
8727}
8728
8729 attrentry_T *
8730syn_cterm_attr2entry(attr)
8731 int attr;
8732{
8733 attr -= ATTR_OFF;
8734 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8735 return NULL;
8736 return &(CTERM_ATTR_ENTRY(attr));
8737}
8738
8739#define LIST_ATTR 1
8740#define LIST_STRING 2
8741#define LIST_INT 3
8742
8743 static void
8744highlight_list_one(id)
8745 int id;
8746{
8747 struct hl_group *sgp;
8748 int didh = FALSE;
8749
8750 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8751
8752 didh = highlight_list_arg(id, didh, LIST_ATTR,
8753 sgp->sg_term, NULL, "term");
8754 didh = highlight_list_arg(id, didh, LIST_STRING,
8755 0, sgp->sg_start, "start");
8756 didh = highlight_list_arg(id, didh, LIST_STRING,
8757 0, sgp->sg_stop, "stop");
8758
8759 didh = highlight_list_arg(id, didh, LIST_ATTR,
8760 sgp->sg_cterm, NULL, "cterm");
8761 didh = highlight_list_arg(id, didh, LIST_INT,
8762 sgp->sg_cterm_fg, NULL, "ctermfg");
8763 didh = highlight_list_arg(id, didh, LIST_INT,
8764 sgp->sg_cterm_bg, NULL, "ctermbg");
8765
Bram Moolenaar61623362010-07-14 22:04:22 +02008766#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008767 didh = highlight_list_arg(id, didh, LIST_ATTR,
8768 sgp->sg_gui, NULL, "gui");
8769 didh = highlight_list_arg(id, didh, LIST_STRING,
8770 0, sgp->sg_gui_fg_name, "guifg");
8771 didh = highlight_list_arg(id, didh, LIST_STRING,
8772 0, sgp->sg_gui_bg_name, "guibg");
8773 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008774 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008775#endif
8776#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008777 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008778 0, sgp->sg_font_name, "font");
8779#endif
8780
Bram Moolenaar661b1822005-07-28 22:36:45 +00008781 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008782 {
8783 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008784 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008785 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8786 msg_putchar(' ');
8787 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8788 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008789
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008790 if (!didh)
8791 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008792#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008793 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008794 last_set_msg(sgp->sg_scriptID);
8795#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008796}
8797
8798 static int
8799highlight_list_arg(id, didh, type, iarg, sarg, name)
8800 int id;
8801 int didh;
8802 int type;
8803 int iarg;
8804 char_u *sarg;
8805 char *name;
8806{
8807 char_u buf[100];
8808 char_u *ts;
8809 int i;
8810
Bram Moolenaar661b1822005-07-28 22:36:45 +00008811 if (got_int)
8812 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008813 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8814 {
8815 ts = buf;
8816 if (type == LIST_INT)
8817 sprintf((char *)buf, "%d", iarg - 1);
8818 else if (type == LIST_STRING)
8819 ts = sarg;
8820 else /* type == LIST_ATTR */
8821 {
8822 buf[0] = NUL;
8823 for (i = 0; hl_attr_table[i] != 0; ++i)
8824 {
8825 if (iarg & hl_attr_table[i])
8826 {
8827 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008828 vim_strcat(buf, (char_u *)",", 100);
8829 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008830 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8831 }
8832 }
8833 }
8834
8835 (void)syn_list_header(didh,
8836 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8837 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008838 if (!got_int)
8839 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008840 if (*name != NUL)
8841 {
8842 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8843 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8844 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008845 msg_outtrans(ts);
8846 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008847 }
8848 return didh;
8849}
8850
8851#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8852/*
8853 * Return "1" if highlight group "id" has attribute "flag".
8854 * Return NULL otherwise.
8855 */
8856 char_u *
8857highlight_has_attr(id, flag, modec)
8858 int id;
8859 int flag;
8860 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8861{
8862 int attr;
8863
8864 if (id <= 0 || id > highlight_ga.ga_len)
8865 return NULL;
8866
Bram Moolenaar61623362010-07-14 22:04:22 +02008867#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008868 if (modec == 'g')
8869 attr = HL_TABLE()[id - 1].sg_gui;
8870 else
8871#endif
8872 if (modec == 'c')
8873 attr = HL_TABLE()[id - 1].sg_cterm;
8874 else
8875 attr = HL_TABLE()[id - 1].sg_term;
8876
8877 if (attr & flag)
8878 return (char_u *)"1";
8879 return NULL;
8880}
8881#endif
8882
8883#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8884/*
8885 * Return color name of highlight group "id".
8886 */
8887 char_u *
8888highlight_color(id, what, modec)
8889 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008890 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008891 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8892{
8893 static char_u name[20];
8894 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008895 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008896 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008897 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008898
8899 if (id <= 0 || id > highlight_ga.ga_len)
8900 return NULL;
8901
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008902 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008903 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008904 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008905 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008906 font = TRUE;
8907 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008908 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008909 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8910 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008911 if (modec == 'g')
8912 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008913# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008914 /* return font name */
8915 if (font)
8916 return HL_TABLE()[id - 1].sg_font_name;
8917
Bram Moolenaar071d4272004-06-13 20:20:40 +00008918 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008919 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008920 {
8921 guicolor_T color;
8922 long_u rgb;
8923 static char_u buf[10];
8924
8925 if (fg)
8926 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008927 else if (sp)
8928 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008929 else
8930 color = HL_TABLE()[id - 1].sg_gui_bg;
8931 if (color == INVALCOLOR)
8932 return NULL;
8933 rgb = gui_mch_get_rgb(color);
8934 sprintf((char *)buf, "#%02x%02x%02x",
8935 (unsigned)(rgb >> 16),
8936 (unsigned)(rgb >> 8) & 255,
8937 (unsigned)rgb & 255);
8938 return buf;
8939 }
Bram Moolenaar61623362010-07-14 22:04:22 +02008940#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008941 if (fg)
8942 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008943 if (sp)
8944 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008945 return (HL_TABLE()[id - 1].sg_gui_bg_name);
8946 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008947 if (font || sp)
8948 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008949 if (modec == 'c')
8950 {
8951 if (fg)
8952 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
8953 else
8954 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
8955 sprintf((char *)name, "%d", n);
8956 return name;
8957 }
8958 /* term doesn't have color */
8959 return NULL;
8960}
8961#endif
8962
8963#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
8964 || defined(PROTO)
8965/*
8966 * Return color name of highlight group "id" as RGB value.
8967 */
8968 long_u
8969highlight_gui_color_rgb(id, fg)
8970 int id;
8971 int fg; /* TRUE = fg, FALSE = bg */
8972{
8973 guicolor_T color;
8974
8975 if (id <= 0 || id > highlight_ga.ga_len)
8976 return 0L;
8977
8978 if (fg)
8979 color = HL_TABLE()[id - 1].sg_gui_fg;
8980 else
8981 color = HL_TABLE()[id - 1].sg_gui_bg;
8982
8983 if (color == INVALCOLOR)
8984 return 0L;
8985
8986 return gui_mch_get_rgb(color);
8987}
8988#endif
8989
8990/*
8991 * Output the syntax list header.
8992 * Return TRUE when started a new line.
8993 */
8994 static int
8995syn_list_header(did_header, outlen, id)
8996 int did_header; /* did header already */
8997 int outlen; /* length of string that comes */
8998 int id; /* highlight group id */
8999{
9000 int endcol = 19;
9001 int newline = TRUE;
9002
9003 if (!did_header)
9004 {
9005 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009006 if (got_int)
9007 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009008 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9009 endcol = 15;
9010 }
9011 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009012 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009013 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009014 if (got_int)
9015 return TRUE;
9016 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009017 else
9018 {
9019 if (msg_col >= endcol) /* wrap around is like starting a new line */
9020 newline = FALSE;
9021 }
9022
9023 if (msg_col >= endcol) /* output at least one space */
9024 endcol = msg_col + 1;
9025 if (Columns <= endcol) /* avoid hang for tiny window */
9026 endcol = Columns - 1;
9027
9028 msg_advance(endcol);
9029
9030 /* Show "xxx" with the attributes. */
9031 if (!did_header)
9032 {
9033 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9034 msg_putchar(' ');
9035 }
9036
9037 return newline;
9038}
9039
9040/*
9041 * Set the attribute numbers for a highlight group.
9042 * Called after one of the attributes has changed.
9043 */
9044 static void
9045set_hl_attr(idx)
9046 int idx; /* index in array */
9047{
9048 attrentry_T at_en;
9049 struct hl_group *sgp = HL_TABLE() + idx;
9050
9051 /* The "Normal" group doesn't need an attribute number */
9052 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9053 return;
9054
9055#ifdef FEAT_GUI
9056 /*
9057 * For the GUI mode: If there are other than "normal" highlighting
9058 * attributes, need to allocate an attr number.
9059 */
9060 if (sgp->sg_gui_fg == INVALCOLOR
9061 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009062 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009063 && sgp->sg_font == NOFONT
9064# ifdef FEAT_XFONTSET
9065 && sgp->sg_fontset == NOFONTSET
9066# endif
9067 )
9068 {
9069 sgp->sg_gui_attr = sgp->sg_gui;
9070 }
9071 else
9072 {
9073 at_en.ae_attr = sgp->sg_gui;
9074 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9075 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009076 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009077 at_en.ae_u.gui.font = sgp->sg_font;
9078# ifdef FEAT_XFONTSET
9079 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9080# endif
9081 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9082 }
9083#endif
9084 /*
9085 * For the term mode: If there are other than "normal" highlighting
9086 * attributes, need to allocate an attr number.
9087 */
9088 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9089 sgp->sg_term_attr = sgp->sg_term;
9090 else
9091 {
9092 at_en.ae_attr = sgp->sg_term;
9093 at_en.ae_u.term.start = sgp->sg_start;
9094 at_en.ae_u.term.stop = sgp->sg_stop;
9095 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9096 }
9097
9098 /*
9099 * For the color term mode: If there are other than "normal"
9100 * highlighting attributes, need to allocate an attr number.
9101 */
9102 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
9103 sgp->sg_cterm_attr = sgp->sg_cterm;
9104 else
9105 {
9106 at_en.ae_attr = sgp->sg_cterm;
9107 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9108 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
9109 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9110 }
9111}
9112
9113/*
9114 * Lookup a highlight group name and return it's ID.
9115 * If it is not found, 0 is returned.
9116 */
9117 int
9118syn_name2id(name)
9119 char_u *name;
9120{
9121 int i;
9122 char_u name_u[200];
9123
9124 /* Avoid using stricmp() too much, it's slow on some systems */
9125 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9126 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009127 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009128 vim_strup(name_u);
9129 for (i = highlight_ga.ga_len; --i >= 0; )
9130 if (HL_TABLE()[i].sg_name_u != NULL
9131 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9132 break;
9133 return i + 1;
9134}
9135
9136#if defined(FEAT_EVAL) || defined(PROTO)
9137/*
9138 * Return TRUE if highlight group "name" exists.
9139 */
9140 int
9141highlight_exists(name)
9142 char_u *name;
9143{
9144 return (syn_name2id(name) > 0);
9145}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009146
9147# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9148/*
9149 * Return the name of highlight group "id".
9150 * When not a valid ID return an empty string.
9151 */
9152 char_u *
9153syn_id2name(id)
9154 int id;
9155{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009156 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009157 return (char_u *)"";
9158 return HL_TABLE()[id - 1].sg_name;
9159}
9160# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009161#endif
9162
9163/*
9164 * Like syn_name2id(), but take a pointer + length argument.
9165 */
9166 int
9167syn_namen2id(linep, len)
9168 char_u *linep;
9169 int len;
9170{
9171 char_u *name;
9172 int id = 0;
9173
9174 name = vim_strnsave(linep, len);
9175 if (name != NULL)
9176 {
9177 id = syn_name2id(name);
9178 vim_free(name);
9179 }
9180 return id;
9181}
9182
9183/*
9184 * Find highlight group name in the table and return it's ID.
9185 * The argument is a pointer to the name and the length of the name.
9186 * If it doesn't exist yet, a new entry is created.
9187 * Return 0 for failure.
9188 */
9189 int
9190syn_check_group(pp, len)
9191 char_u *pp;
9192 int len;
9193{
9194 int id;
9195 char_u *name;
9196
9197 name = vim_strnsave(pp, len);
9198 if (name == NULL)
9199 return 0;
9200
9201 id = syn_name2id(name);
9202 if (id == 0) /* doesn't exist yet */
9203 id = syn_add_group(name);
9204 else
9205 vim_free(name);
9206 return id;
9207}
9208
9209/*
9210 * Add new highlight group and return it's ID.
9211 * "name" must be an allocated string, it will be consumed.
9212 * Return 0 for failure.
9213 */
9214 static int
9215syn_add_group(name)
9216 char_u *name;
9217{
9218 char_u *p;
9219
9220 /* Check that the name is ASCII letters, digits and underscore. */
9221 for (p = name; *p != NUL; ++p)
9222 {
9223 if (!vim_isprintc(*p))
9224 {
9225 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009226 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009227 return 0;
9228 }
9229 else if (!ASCII_ISALNUM(*p) && *p != '_')
9230 {
9231 /* This is an error, but since there previously was no check only
9232 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009233 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009234 MSG(_("W18: Invalid character in group name"));
9235 break;
9236 }
9237 }
9238
9239 /*
9240 * First call for this growarray: init growing array.
9241 */
9242 if (highlight_ga.ga_data == NULL)
9243 {
9244 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9245 highlight_ga.ga_growsize = 10;
9246 }
9247
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009248 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009249 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009250 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009251 vim_free(name);
9252 return 0;
9253 }
9254
Bram Moolenaar071d4272004-06-13 20:20:40 +00009255 /*
9256 * Make room for at least one other syntax_highlight entry.
9257 */
9258 if (ga_grow(&highlight_ga, 1) == FAIL)
9259 {
9260 vim_free(name);
9261 return 0;
9262 }
9263
9264 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9265 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9266 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9267#ifdef FEAT_GUI
9268 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9269 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009270 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009271#endif
9272 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009273
9274 return highlight_ga.ga_len; /* ID is index plus one */
9275}
9276
9277/*
9278 * When, just after calling syn_add_group(), an error is discovered, this
9279 * function deletes the new name.
9280 */
9281 static void
9282syn_unadd_group()
9283{
9284 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009285 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9286 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9287}
9288
9289/*
9290 * Translate a group ID to highlight attributes.
9291 */
9292 int
9293syn_id2attr(hl_id)
9294 int hl_id;
9295{
9296 int attr;
9297 struct hl_group *sgp;
9298
9299 hl_id = syn_get_final_id(hl_id);
9300 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9301
9302#ifdef FEAT_GUI
9303 /*
9304 * Only use GUI attr when the GUI is being used.
9305 */
9306 if (gui.in_use)
9307 attr = sgp->sg_gui_attr;
9308 else
9309#endif
9310 if (t_colors > 1)
9311 attr = sgp->sg_cterm_attr;
9312 else
9313 attr = sgp->sg_term_attr;
9314
9315 return attr;
9316}
9317
9318#ifdef FEAT_GUI
9319/*
9320 * Get the GUI colors and attributes for a group ID.
9321 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9322 */
9323 int
9324syn_id2colors(hl_id, fgp, bgp)
9325 int hl_id;
9326 guicolor_T *fgp;
9327 guicolor_T *bgp;
9328{
9329 struct hl_group *sgp;
9330
9331 hl_id = syn_get_final_id(hl_id);
9332 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9333
9334 *fgp = sgp->sg_gui_fg;
9335 *bgp = sgp->sg_gui_bg;
9336 return sgp->sg_gui;
9337}
9338#endif
9339
9340/*
9341 * Translate a group ID to the final group ID (following links).
9342 */
9343 int
9344syn_get_final_id(hl_id)
9345 int hl_id;
9346{
9347 int count;
9348 struct hl_group *sgp;
9349
9350 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9351 return 0; /* Can be called from eval!! */
9352
9353 /*
9354 * Follow links until there is no more.
9355 * Look out for loops! Break after 100 links.
9356 */
9357 for (count = 100; --count >= 0; )
9358 {
9359 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9360 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9361 break;
9362 hl_id = sgp->sg_link;
9363 }
9364
9365 return hl_id;
9366}
9367
9368#ifdef FEAT_GUI
9369/*
9370 * Call this function just after the GUI has started.
9371 * It finds the font and color handles for the highlighting groups.
9372 */
9373 void
9374highlight_gui_started()
9375{
9376 int idx;
9377
9378 /* First get the colors from the "Normal" and "Menu" group, if set */
9379 set_normal_colors();
9380
9381 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9382 gui_do_one_color(idx, FALSE, FALSE);
9383
9384 highlight_changed();
9385}
9386
9387 static void
9388gui_do_one_color(idx, do_menu, do_tooltip)
9389 int idx;
9390 int do_menu; /* TRUE: might set the menu font */
9391 int do_tooltip; /* TRUE: might set the tooltip font */
9392{
9393 int didit = FALSE;
9394
9395 if (HL_TABLE()[idx].sg_font_name != NULL)
9396 {
9397 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009398 do_tooltip, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009399 didit = TRUE;
9400 }
9401 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9402 {
9403 HL_TABLE()[idx].sg_gui_fg =
9404 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9405 didit = TRUE;
9406 }
9407 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9408 {
9409 HL_TABLE()[idx].sg_gui_bg =
9410 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9411 didit = TRUE;
9412 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009413 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9414 {
9415 HL_TABLE()[idx].sg_gui_sp =
9416 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9417 didit = TRUE;
9418 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009419 if (didit) /* need to get a new attr number */
9420 set_hl_attr(idx);
9421}
9422
9423#endif
9424
9425/*
9426 * Translate the 'highlight' option into attributes in highlight_attr[] and
9427 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9428 * corresponding highlights to use on top of HLF_SNC is computed.
9429 * Called only when the 'highlight' option has been changed and upon first
9430 * screen redraw after any :highlight command.
9431 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9432 */
9433 int
9434highlight_changed()
9435{
9436 int hlf;
9437 int i;
9438 char_u *p;
9439 int attr;
9440 char_u *end;
9441 int id;
9442#ifdef USER_HIGHLIGHT
9443 char_u userhl[10];
9444# ifdef FEAT_STL_OPT
9445 int id_SNC = -1;
9446 int id_S = -1;
9447 int hlcnt;
9448# endif
9449#endif
9450 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9451
9452 need_highlight_changed = FALSE;
9453
9454 /*
9455 * Clear all attributes.
9456 */
9457 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9458 highlight_attr[hlf] = 0;
9459
9460 /*
9461 * First set all attributes to their default value.
9462 * Then use the attributes from the 'highlight' option.
9463 */
9464 for (i = 0; i < 2; ++i)
9465 {
9466 if (i)
9467 p = p_hl;
9468 else
9469 p = get_highlight_default();
9470 if (p == NULL) /* just in case */
9471 continue;
9472
9473 while (*p)
9474 {
9475 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9476 if (hl_flags[hlf] == *p)
9477 break;
9478 ++p;
9479 if (hlf == (int)HLF_COUNT || *p == NUL)
9480 return FAIL;
9481
9482 /*
9483 * Allow several hl_flags to be combined, like "bu" for
9484 * bold-underlined.
9485 */
9486 attr = 0;
9487 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9488 {
9489 if (vim_iswhite(*p)) /* ignore white space */
9490 continue;
9491
9492 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9493 return FAIL;
9494
9495 switch (*p)
9496 {
9497 case 'b': attr |= HL_BOLD;
9498 break;
9499 case 'i': attr |= HL_ITALIC;
9500 break;
9501 case '-':
9502 case 'n': /* no highlighting */
9503 break;
9504 case 'r': attr |= HL_INVERSE;
9505 break;
9506 case 's': attr |= HL_STANDOUT;
9507 break;
9508 case 'u': attr |= HL_UNDERLINE;
9509 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009510 case 'c': attr |= HL_UNDERCURL;
9511 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009512 case ':': ++p; /* highlight group name */
9513 if (attr || *p == NUL) /* no combinations */
9514 return FAIL;
9515 end = vim_strchr(p, ',');
9516 if (end == NULL)
9517 end = p + STRLEN(p);
9518 id = syn_check_group(p, (int)(end - p));
9519 if (id == 0)
9520 return FAIL;
9521 attr = syn_id2attr(id);
9522 p = end - 1;
9523#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9524 if (hlf == (int)HLF_SNC)
9525 id_SNC = syn_get_final_id(id);
9526 else if (hlf == (int)HLF_S)
9527 id_S = syn_get_final_id(id);
9528#endif
9529 break;
9530 default: return FAIL;
9531 }
9532 }
9533 highlight_attr[hlf] = attr;
9534
9535 p = skip_to_option_part(p); /* skip comma and spaces */
9536 }
9537 }
9538
9539#ifdef USER_HIGHLIGHT
9540 /* Setup the user highlights
9541 *
9542 * Temporarily utilize 10 more hl entries. Have to be in there
9543 * simultaneously in case of table overflows in get_attr_entry()
9544 */
9545# ifdef FEAT_STL_OPT
9546 if (ga_grow(&highlight_ga, 10) == FAIL)
9547 return FAIL;
9548 hlcnt = highlight_ga.ga_len;
9549 if (id_S == 0)
9550 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009551 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009552 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9553 id_S = hlcnt + 10;
9554 }
9555# endif
9556 for (i = 0; i < 9; i++)
9557 {
9558 sprintf((char *)userhl, "User%d", i + 1);
9559 id = syn_name2id(userhl);
9560 if (id == 0)
9561 {
9562 highlight_user[i] = 0;
9563# ifdef FEAT_STL_OPT
9564 highlight_stlnc[i] = 0;
9565# endif
9566 }
9567 else
9568 {
9569# ifdef FEAT_STL_OPT
9570 struct hl_group *hlt = HL_TABLE();
9571# endif
9572
9573 highlight_user[i] = syn_id2attr(id);
9574# ifdef FEAT_STL_OPT
9575 if (id_SNC == 0)
9576 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009577 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009578 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9579 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009580# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009581 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9582# endif
9583 }
9584 else
9585 mch_memmove(&hlt[hlcnt + i],
9586 &hlt[id_SNC - 1],
9587 sizeof(struct hl_group));
9588 hlt[hlcnt + i].sg_link = 0;
9589
9590 /* Apply difference between UserX and HLF_S to HLF_SNC */
9591 hlt[hlcnt + i].sg_term ^=
9592 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9593 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9594 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9595 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9596 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9597 hlt[hlcnt + i].sg_cterm ^=
9598 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9599 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9600 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9601 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9602 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009603# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009604 hlt[hlcnt + i].sg_gui ^=
9605 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009606# endif
9607# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009608 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9609 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9610 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9611 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009612 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9613 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009614 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9615 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9616# ifdef FEAT_XFONTSET
9617 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9618 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9619# endif
9620# endif
9621 highlight_ga.ga_len = hlcnt + i + 1;
9622 set_hl_attr(hlcnt + i); /* At long last we can apply */
9623 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9624# endif
9625 }
9626 }
9627# ifdef FEAT_STL_OPT
9628 highlight_ga.ga_len = hlcnt;
9629# endif
9630
9631#endif /* USER_HIGHLIGHT */
9632
9633 return OK;
9634}
9635
Bram Moolenaar4f688582007-07-24 12:34:30 +00009636#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009637
9638static void highlight_list __ARGS((void));
9639static void highlight_list_two __ARGS((int cnt, int attr));
9640
9641/*
9642 * Handle command line completion for :highlight command.
9643 */
9644 void
9645set_context_in_highlight_cmd(xp, arg)
9646 expand_T *xp;
9647 char_u *arg;
9648{
9649 char_u *p;
9650
9651 /* Default: expand group names */
9652 xp->xp_context = EXPAND_HIGHLIGHT;
9653 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009654 include_link = 2;
9655 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009656
9657 /* (part of) subcommand already typed */
9658 if (*arg != NUL)
9659 {
9660 p = skiptowhite(arg);
9661 if (*p != NUL) /* past "default" or group name */
9662 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009663 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009664 if (STRNCMP("default", arg, p - arg) == 0)
9665 {
9666 arg = skipwhite(p);
9667 xp->xp_pattern = arg;
9668 p = skiptowhite(arg);
9669 }
9670 if (*p != NUL) /* past group name */
9671 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009672 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009673 if (arg[1] == 'i' && arg[0] == 'N')
9674 highlight_list();
9675 if (STRNCMP("link", arg, p - arg) == 0
9676 || STRNCMP("clear", arg, p - arg) == 0)
9677 {
9678 xp->xp_pattern = skipwhite(p);
9679 p = skiptowhite(xp->xp_pattern);
9680 if (*p != NUL) /* past first group name */
9681 {
9682 xp->xp_pattern = skipwhite(p);
9683 p = skiptowhite(xp->xp_pattern);
9684 }
9685 }
9686 if (*p != NUL) /* past group name(s) */
9687 xp->xp_context = EXPAND_NOTHING;
9688 }
9689 }
9690 }
9691}
9692
9693/*
9694 * List highlighting matches in a nice way.
9695 */
9696 static void
9697highlight_list()
9698{
9699 int i;
9700
9701 for (i = 10; --i >= 0; )
9702 highlight_list_two(i, hl_attr(HLF_D));
9703 for (i = 40; --i >= 0; )
9704 highlight_list_two(99, 0);
9705}
9706
9707 static void
9708highlight_list_two(cnt, attr)
9709 int cnt;
9710 int attr;
9711{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009712 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009713 msg_clr_eos();
9714 out_flush();
9715 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9716}
9717
9718#endif /* FEAT_CMDL_COMPL */
9719
9720#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9721 || defined(FEAT_SIGNS) || defined(PROTO)
9722/*
9723 * Function given to ExpandGeneric() to obtain the list of group names.
9724 * Also used for synIDattr() function.
9725 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009726 char_u *
9727get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009728 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009729 int idx;
9730{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009731#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009732 if (idx == highlight_ga.ga_len && include_none != 0)
9733 return (char_u *)"none";
9734 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009735 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009736 if (idx == highlight_ga.ga_len + include_none + include_default
9737 && include_link != 0)
9738 return (char_u *)"link";
9739 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9740 && include_link != 0)
9741 return (char_u *)"clear";
9742#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009743 if (idx < 0 || idx >= highlight_ga.ga_len)
9744 return NULL;
9745 return HL_TABLE()[idx].sg_name;
9746}
9747#endif
9748
Bram Moolenaar4f688582007-07-24 12:34:30 +00009749#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009750/*
9751 * Free all the highlight group fonts.
9752 * Used when quitting for systems which need it.
9753 */
9754 void
9755free_highlight_fonts()
9756{
9757 int idx;
9758
9759 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9760 {
9761 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9762 HL_TABLE()[idx].sg_font = NOFONT;
9763# ifdef FEAT_XFONTSET
9764 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9765 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9766# endif
9767 }
9768
9769 gui_mch_free_font(gui.norm_font);
9770# ifdef FEAT_XFONTSET
9771 gui_mch_free_fontset(gui.fontset);
9772# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009773# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009774 gui_mch_free_font(gui.bold_font);
9775 gui_mch_free_font(gui.ital_font);
9776 gui_mch_free_font(gui.boldital_font);
9777# endif
9778}
9779#endif
9780
9781/**************************************
9782 * End of Highlighting stuff *
9783 **************************************/