blob: ae3a88a61ab22ec50947472842138938e3006c99 [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
Bram Moolenaare7154eb2015-03-21 21:46:13 +0100314static int next_seqnr = 1; /* value to use for si_seqnr */
Bram Moolenaarffbbcb52010-07-24 17:29:03 +0200315#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;
Bram Moolenaardffa5b82014-11-19 16:38:07 +0100995 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +0000996
Bram Moolenaar860cae12010-06-05 23:22:07 +0200997 if (syn_block->b_syn_linecont_prog != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +0000998 {
Bram Moolenaar860cae12010-06-05 23:22:07 +0200999 regmatch.rmm_ic = syn_block->b_syn_linecont_ic;
1000 regmatch.regprog = syn_block->b_syn_linecont_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001001 r = syn_regexec(&regmatch, lnum, (colnr_T)0,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02001002 IF_SYN_TIME(&syn_block->b_syn_linecont_time));
Bram Moolenaardffa5b82014-11-19 16:38:07 +01001003 syn_block->b_syn_linecont_prog = regmatch.regprog;
1004 return r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001005 }
1006 return FALSE;
1007}
1008
1009/*
1010 * Prepare the current state for the start of a line.
1011 */
1012 static void
1013syn_start_line()
1014{
1015 current_finished = FALSE;
1016 current_col = 0;
1017
1018 /*
1019 * Need to update the end of a start/skip/end that continues from the
1020 * previous line and regions that have "keepend".
1021 */
1022 if (current_state.ga_len > 0)
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001023 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00001024 syn_update_ends(TRUE);
Bram Moolenaar6fa46362011-05-25 17:56:27 +02001025 check_state_ends();
1026 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00001027
1028 next_match_idx = -1;
1029 ++current_line_id;
1030}
1031
1032/*
1033 * Check for items in the stack that need their end updated.
1034 * When "startofline" is TRUE the last item is always updated.
1035 * When "startofline" is FALSE the item with "keepend" is forcefully updated.
1036 */
1037 static void
1038syn_update_ends(startofline)
1039 int startofline;
1040{
1041 stateitem_T *cur_si;
1042 int i;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001043 int seen_keepend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001044
1045 if (startofline)
1046 {
1047 /* Check for a match carried over from a previous line with a
1048 * contained region. The match ends as soon as the region ends. */
1049 for (i = 0; i < current_state.ga_len; ++i)
1050 {
1051 cur_si = &CUR_STATE(i);
1052 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001053 && (SYN_ITEMS(syn_block)[cur_si->si_idx]).sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00001054 == SPTYPE_MATCH
1055 && cur_si->si_m_endpos.lnum < current_lnum)
1056 {
1057 cur_si->si_flags |= HL_MATCHCONT;
1058 cur_si->si_m_endpos.lnum = 0;
1059 cur_si->si_m_endpos.col = 0;
1060 cur_si->si_h_endpos = cur_si->si_m_endpos;
1061 cur_si->si_ends = TRUE;
1062 }
1063 }
1064 }
1065
1066 /*
1067 * Need to update the end of a start/skip/end that continues from the
1068 * previous line. And regions that have "keepend", because they may
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001069 * influence contained items. If we've just removed "extend"
1070 * (startofline == 0) then we should update ends of normal regions
1071 * contained inside "keepend" because "extend" could have extended
1072 * these "keepend" regions as well as contained normal regions.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001073 * Then check for items ending in column 0.
1074 */
1075 i = current_state.ga_len - 1;
1076 if (keepend_level >= 0)
1077 for ( ; i > keepend_level; --i)
1078 if (CUR_STATE(i).si_flags & HL_EXTEND)
1079 break;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001080
1081 seen_keepend = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001082 for ( ; i < current_state.ga_len; ++i)
1083 {
1084 cur_si = &CUR_STATE(i);
1085 if ((cur_si->si_flags & HL_KEEPEND)
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001086 || (seen_keepend && !startofline)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001087 || (i == current_state.ga_len - 1 && startofline))
1088 {
1089 cur_si->si_h_startpos.col = 0; /* start highl. in col 0 */
1090 cur_si->si_h_startpos.lnum = current_lnum;
1091
1092 if (!(cur_si->si_flags & HL_MATCHCONT))
1093 update_si_end(cur_si, (int)current_col, !startofline);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00001094
1095 if (!startofline && (cur_si->si_flags & HL_KEEPEND))
1096 seen_keepend = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001097 }
1098 }
1099 check_keepend();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001100}
1101
1102/****************************************
1103 * Handling of the state stack cache.
1104 */
1105
1106/*
1107 * EXPLANATION OF THE SYNTAX STATE STACK CACHE
1108 *
1109 * To speed up syntax highlighting, the state stack for the start of some
1110 * lines is cached. These entries can be used to start parsing at that point.
1111 *
1112 * The stack is kept in b_sst_array[] for each buffer. There is a list of
1113 * valid entries. b_sst_first points to the first one, then follow sst_next.
1114 * The entries are sorted on line number. The first entry is often for line 2
1115 * (line 1 always starts with an empty stack).
1116 * There is also a list for free entries. This construction is used to avoid
1117 * having to allocate and free memory blocks too often.
1118 *
1119 * When making changes to the buffer, this is logged in b_mod_*. When calling
1120 * update_screen() to update the display, it will call
1121 * syn_stack_apply_changes() for each displayed buffer to adjust the cached
1122 * entries. The entries which are inside the changed area are removed,
1123 * because they must be recomputed. Entries below the changed have their line
1124 * number adjusted for deleted/inserted lines, and have their sst_change_lnum
1125 * set to indicate that a check must be made if the changed lines would change
1126 * the cached entry.
1127 *
1128 * When later displaying lines, an entry is stored for each line. Displayed
1129 * lines are likely to be displayed again, in which case the state at the
1130 * start of the line is needed.
1131 * For not displayed lines, an entry is stored for every so many lines. These
1132 * entries will be used e.g., when scrolling backwards. The distance between
1133 * entries depends on the number of lines in the buffer. For small buffers
1134 * the distance is fixed at SST_DIST, for large buffers there is a fixed
1135 * number of entries SST_MAX_ENTRIES, and the distance is computed.
1136 */
1137
Bram Moolenaar860cae12010-06-05 23:22:07 +02001138 static void
1139syn_stack_free_block(block)
1140 synblock_T *block;
1141{
1142 synstate_T *p;
1143
1144 if (block->b_sst_array != NULL)
1145 {
1146 for (p = block->b_sst_first; p != NULL; p = p->sst_next)
1147 clear_syn_state(p);
1148 vim_free(block->b_sst_array);
1149 block->b_sst_array = NULL;
1150 block->b_sst_len = 0;
1151 }
1152}
Bram Moolenaar071d4272004-06-13 20:20:40 +00001153/*
1154 * Free b_sst_array[] for buffer "buf".
1155 * Used when syntax items changed to force resyncing everywhere.
1156 */
1157 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001158syn_stack_free_all(block)
1159 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001160{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001161 win_T *wp;
1162
Bram Moolenaar860cae12010-06-05 23:22:07 +02001163 syn_stack_free_block(block);
1164
1165
Bram Moolenaar071d4272004-06-13 20:20:40 +00001166#ifdef FEAT_FOLDING
1167 /* When using "syntax" fold method, must update all folds. */
1168 FOR_ALL_WINDOWS(wp)
1169 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001170 if (wp->w_s == block && foldmethodIsSyntax(wp))
Bram Moolenaar071d4272004-06-13 20:20:40 +00001171 foldUpdateAll(wp);
1172 }
1173#endif
1174}
1175
1176/*
1177 * Allocate the syntax state stack for syn_buf when needed.
1178 * If the number of entries in b_sst_array[] is much too big or a bit too
1179 * small, reallocate it.
1180 * Also used to allocate b_sst_array[] for the first time.
1181 */
1182 static void
1183syn_stack_alloc()
1184{
1185 long len;
1186 synstate_T *to, *from;
1187 synstate_T *sstp;
1188
1189 len = syn_buf->b_ml.ml_line_count / SST_DIST + Rows * 2;
1190 if (len < SST_MIN_ENTRIES)
1191 len = SST_MIN_ENTRIES;
1192 else if (len > SST_MAX_ENTRIES)
1193 len = SST_MAX_ENTRIES;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001194 if (syn_block->b_sst_len > len * 2 || syn_block->b_sst_len < len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001195 {
1196 /* Allocate 50% too much, to avoid reallocating too often. */
1197 len = syn_buf->b_ml.ml_line_count;
1198 len = (len + len / 2) / SST_DIST + Rows * 2;
1199 if (len < SST_MIN_ENTRIES)
1200 len = SST_MIN_ENTRIES;
1201 else if (len > SST_MAX_ENTRIES)
1202 len = SST_MAX_ENTRIES;
1203
Bram Moolenaar860cae12010-06-05 23:22:07 +02001204 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001205 {
1206 /* When shrinking the array, cleanup the existing stack.
1207 * Make sure that all valid entries fit in the new array. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001208 while (syn_block->b_sst_len - syn_block->b_sst_freecount + 2 > len
Bram Moolenaar071d4272004-06-13 20:20:40 +00001209 && syn_stack_cleanup())
1210 ;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001211 if (len < syn_block->b_sst_len - syn_block->b_sst_freecount + 2)
1212 len = syn_block->b_sst_len - syn_block->b_sst_freecount + 2;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001213 }
1214
1215 sstp = (synstate_T *)alloc_clear((unsigned)(len * sizeof(synstate_T)));
1216 if (sstp == NULL) /* out of memory! */
1217 return;
1218
1219 to = sstp - 1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001220 if (syn_block->b_sst_array != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001221 {
1222 /* Move the states from the old array to the new one. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001223 for (from = syn_block->b_sst_first; from != NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001224 from = from->sst_next)
1225 {
1226 ++to;
1227 *to = *from;
1228 to->sst_next = to + 1;
1229 }
1230 }
1231 if (to != sstp - 1)
1232 {
1233 to->sst_next = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001234 syn_block->b_sst_first = sstp;
1235 syn_block->b_sst_freecount = len - (int)(to - sstp) - 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001236 }
1237 else
1238 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001239 syn_block->b_sst_first = NULL;
1240 syn_block->b_sst_freecount = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001241 }
1242
1243 /* Create the list of free entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001244 syn_block->b_sst_firstfree = to + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001245 while (++to < sstp + len)
1246 to->sst_next = to + 1;
1247 (sstp + len - 1)->sst_next = NULL;
1248
Bram Moolenaar860cae12010-06-05 23:22:07 +02001249 vim_free(syn_block->b_sst_array);
1250 syn_block->b_sst_array = sstp;
1251 syn_block->b_sst_len = len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001252 }
1253}
1254
1255/*
1256 * Check for changes in a buffer to affect stored syntax states. Uses the
1257 * b_mod_* fields.
1258 * Called from update_screen(), before screen is being updated, once for each
1259 * displayed buffer.
1260 */
1261 void
1262syn_stack_apply_changes(buf)
1263 buf_T *buf;
1264{
Bram Moolenaar860cae12010-06-05 23:22:07 +02001265 win_T *wp;
1266
1267 syn_stack_apply_changes_block(&buf->b_s, buf);
1268
1269 FOR_ALL_WINDOWS(wp)
1270 {
1271 if ((wp->w_buffer == buf) && (wp->w_s != &buf->b_s))
1272 syn_stack_apply_changes_block(wp->w_s, buf);
1273 }
1274}
1275
1276 static void
1277syn_stack_apply_changes_block(block, buf)
1278 synblock_T *block;
1279 buf_T *buf;
1280{
Bram Moolenaar071d4272004-06-13 20:20:40 +00001281 synstate_T *p, *prev, *np;
1282 linenr_T n;
1283
Bram Moolenaar860cae12010-06-05 23:22:07 +02001284 if (block->b_sst_array == NULL) /* nothing to do */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001285 return;
1286
1287 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001288 for (p = block->b_sst_first; p != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00001289 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001290 if (p->sst_lnum + block->b_syn_sync_linebreaks > buf->b_mod_top)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001291 {
1292 n = p->sst_lnum + buf->b_mod_xlines;
1293 if (n <= buf->b_mod_bot)
1294 {
1295 /* this state is inside the changed area, remove it */
1296 np = p->sst_next;
1297 if (prev == NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02001298 block->b_sst_first = np;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001299 else
1300 prev->sst_next = np;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001301 syn_stack_free_entry(block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001302 p = np;
1303 continue;
1304 }
1305 /* This state is below the changed area. Remember the line
1306 * that needs to be parsed before this entry can be made valid
1307 * again. */
1308 if (p->sst_change_lnum != 0 && p->sst_change_lnum > buf->b_mod_top)
1309 {
1310 if (p->sst_change_lnum + buf->b_mod_xlines > buf->b_mod_top)
1311 p->sst_change_lnum += buf->b_mod_xlines;
1312 else
1313 p->sst_change_lnum = buf->b_mod_top;
1314 }
1315 if (p->sst_change_lnum == 0
1316 || p->sst_change_lnum < buf->b_mod_bot)
1317 p->sst_change_lnum = buf->b_mod_bot;
1318
1319 p->sst_lnum = n;
1320 }
1321 prev = p;
1322 p = p->sst_next;
1323 }
1324}
1325
1326/*
1327 * Reduce the number of entries in the state stack for syn_buf.
1328 * Returns TRUE if at least one entry was freed.
1329 */
1330 static int
1331syn_stack_cleanup()
1332{
1333 synstate_T *p, *prev;
1334 disptick_T tick;
1335 int above;
1336 int dist;
1337 int retval = FALSE;
1338
Bram Moolenaar860cae12010-06-05 23:22:07 +02001339 if (syn_block->b_sst_array == NULL || syn_block->b_sst_first == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001340 return retval;
1341
1342 /* Compute normal distance between non-displayed entries. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001343 if (syn_block->b_sst_len <= Rows)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00001344 dist = 999999;
1345 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02001346 dist = syn_buf->b_ml.ml_line_count / (syn_block->b_sst_len - Rows) + 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001347
1348 /*
Bram Moolenaarf5b63862009-12-16 17:13:44 +00001349 * Go through the list to find the "tick" for the oldest entry that can
Bram Moolenaar071d4272004-06-13 20:20:40 +00001350 * be removed. Set "above" when the "tick" for the oldest entry is above
1351 * "b_sst_lasttick" (the display tick wraps around).
1352 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001353 tick = syn_block->b_sst_lasttick;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001354 above = FALSE;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001355 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001356 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1357 {
1358 if (prev->sst_lnum + dist > p->sst_lnum)
1359 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02001360 if (p->sst_tick > syn_block->b_sst_lasttick)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001361 {
1362 if (!above || p->sst_tick < tick)
1363 tick = p->sst_tick;
1364 above = TRUE;
1365 }
1366 else if (!above && p->sst_tick < tick)
1367 tick = p->sst_tick;
1368 }
1369 }
1370
1371 /*
1372 * Go through the list to make the entries for the oldest tick at an
1373 * interval of several lines.
1374 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001375 prev = syn_block->b_sst_first;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001376 for (p = prev->sst_next; p != NULL; prev = p, p = p->sst_next)
1377 {
1378 if (p->sst_tick == tick && prev->sst_lnum + dist > p->sst_lnum)
1379 {
1380 /* Move this entry from used list to free list */
1381 prev->sst_next = p->sst_next;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001382 syn_stack_free_entry(syn_block, p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001383 p = prev;
1384 retval = TRUE;
1385 }
1386 }
1387 return retval;
1388}
1389
1390/*
1391 * Free the allocated memory for a syn_state item.
1392 * Move the entry into the free list.
1393 */
1394 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02001395syn_stack_free_entry(block, p)
1396 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001397 synstate_T *p;
1398{
1399 clear_syn_state(p);
Bram Moolenaar860cae12010-06-05 23:22:07 +02001400 p->sst_next = block->b_sst_firstfree;
1401 block->b_sst_firstfree = p;
1402 ++block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001403}
1404
1405/*
1406 * Find an entry in the list of state stacks at or before "lnum".
1407 * Returns NULL when there is no entry or the first entry is after "lnum".
1408 */
1409 static synstate_T *
1410syn_stack_find_entry(lnum)
1411 linenr_T lnum;
1412{
1413 synstate_T *p, *prev;
1414
1415 prev = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001416 for (p = syn_block->b_sst_first; p != NULL; prev = p, p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001417 {
1418 if (p->sst_lnum == lnum)
1419 return p;
1420 if (p->sst_lnum > lnum)
1421 break;
1422 }
1423 return prev;
1424}
1425
1426/*
1427 * Try saving the current state in b_sst_array[].
1428 * The current state must be valid for the start of the current_lnum line!
1429 */
1430 static synstate_T *
Bram Moolenaardbe31752008-01-13 16:40:19 +00001431store_current_state()
Bram Moolenaar071d4272004-06-13 20:20:40 +00001432{
1433 int i;
1434 synstate_T *p;
1435 bufstate_T *bp;
1436 stateitem_T *cur_si;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001437 synstate_T *sp = syn_stack_find_entry(current_lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001438
1439 /*
1440 * If the current state contains a start or end pattern that continues
1441 * from the previous line, we can't use it. Don't store it then.
1442 */
1443 for (i = current_state.ga_len - 1; i >= 0; --i)
1444 {
1445 cur_si = &CUR_STATE(i);
1446 if (cur_si->si_h_startpos.lnum >= current_lnum
1447 || cur_si->si_m_endpos.lnum >= current_lnum
1448 || cur_si->si_h_endpos.lnum >= current_lnum
1449 || (cur_si->si_end_idx
1450 && cur_si->si_eoe_pos.lnum >= current_lnum))
1451 break;
1452 }
1453 if (i >= 0)
1454 {
1455 if (sp != NULL)
1456 {
1457 /* find "sp" in the list and remove it */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001458 if (syn_block->b_sst_first == sp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001459 /* it's the first entry */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001460 syn_block->b_sst_first = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001461 else
1462 {
1463 /* find the entry just before this one to adjust sst_next */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001464 for (p = syn_block->b_sst_first; p != NULL; p = p->sst_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001465 if (p->sst_next == sp)
1466 break;
Bram Moolenaareb3593b2006-04-22 22:33:57 +00001467 if (p != NULL) /* just in case */
1468 p->sst_next = sp->sst_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001469 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02001470 syn_stack_free_entry(syn_block, sp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001471 sp = NULL;
1472 }
1473 }
1474 else if (sp == NULL || sp->sst_lnum != current_lnum)
1475 {
1476 /*
1477 * Add a new entry
1478 */
1479 /* If no free items, cleanup the array first. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001480 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001481 {
1482 (void)syn_stack_cleanup();
1483 /* "sp" may have been moved to the freelist now */
1484 sp = syn_stack_find_entry(current_lnum);
1485 }
1486 /* Still no free items? Must be a strange problem... */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001487 if (syn_block->b_sst_freecount == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001488 sp = NULL;
1489 else
1490 {
1491 /* Take the first item from the free list and put it in the used
1492 * list, after *sp */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001493 p = syn_block->b_sst_firstfree;
1494 syn_block->b_sst_firstfree = p->sst_next;
1495 --syn_block->b_sst_freecount;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001496 if (sp == NULL)
1497 {
1498 /* Insert in front of the list */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001499 p->sst_next = syn_block->b_sst_first;
1500 syn_block->b_sst_first = p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001501 }
1502 else
1503 {
1504 /* insert in list after *sp */
1505 p->sst_next = sp->sst_next;
1506 sp->sst_next = p;
1507 }
1508 sp = p;
1509 sp->sst_stacksize = 0;
1510 sp->sst_lnum = current_lnum;
1511 }
1512 }
1513 if (sp != NULL)
1514 {
1515 /* When overwriting an existing state stack, clear it first */
1516 clear_syn_state(sp);
1517 sp->sst_stacksize = current_state.ga_len;
1518 if (current_state.ga_len > SST_FIX_STATES)
1519 {
1520 /* Need to clear it, might be something remaining from when the
1521 * length was less than SST_FIX_STATES. */
1522 ga_init2(&sp->sst_union.sst_ga, (int)sizeof(bufstate_T), 1);
1523 if (ga_grow(&sp->sst_union.sst_ga, current_state.ga_len) == FAIL)
1524 sp->sst_stacksize = 0;
1525 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00001526 sp->sst_union.sst_ga.ga_len = current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001527 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1528 }
1529 else
1530 bp = sp->sst_union.sst_stack;
1531 for (i = 0; i < sp->sst_stacksize; ++i)
1532 {
1533 bp[i].bs_idx = CUR_STATE(i).si_idx;
1534 bp[i].bs_flags = CUR_STATE(i).si_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001535#ifdef FEAT_CONCEAL
1536 bp[i].bs_seqnr = CUR_STATE(i).si_seqnr;
1537 bp[i].bs_cchar = CUR_STATE(i).si_cchar;
1538#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001539 bp[i].bs_extmatch = ref_extmatch(CUR_STATE(i).si_extmatch);
1540 }
1541 sp->sst_next_flags = current_next_flags;
1542 sp->sst_next_list = current_next_list;
1543 sp->sst_tick = display_tick;
1544 sp->sst_change_lnum = 0;
1545 }
1546 current_state_stored = TRUE;
1547 return sp;
1548}
1549
1550/*
1551 * Copy a state stack from "from" in b_sst_array[] to current_state;
1552 */
1553 static void
1554load_current_state(from)
1555 synstate_T *from;
1556{
1557 int i;
1558 bufstate_T *bp;
1559
1560 clear_current_state();
1561 validate_current_state();
1562 keepend_level = -1;
1563 if (from->sst_stacksize
1564 && ga_grow(&current_state, from->sst_stacksize) != FAIL)
1565 {
1566 if (from->sst_stacksize > SST_FIX_STATES)
1567 bp = SYN_STATE_P(&(from->sst_union.sst_ga));
1568 else
1569 bp = from->sst_union.sst_stack;
1570 for (i = 0; i < from->sst_stacksize; ++i)
1571 {
1572 CUR_STATE(i).si_idx = bp[i].bs_idx;
1573 CUR_STATE(i).si_flags = bp[i].bs_flags;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02001574#ifdef FEAT_CONCEAL
1575 CUR_STATE(i).si_seqnr = bp[i].bs_seqnr;
1576 CUR_STATE(i).si_cchar = bp[i].bs_cchar;
1577#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00001578 CUR_STATE(i).si_extmatch = ref_extmatch(bp[i].bs_extmatch);
1579 if (keepend_level < 0 && (CUR_STATE(i).si_flags & HL_KEEPEND))
1580 keepend_level = i;
1581 CUR_STATE(i).si_ends = FALSE;
1582 CUR_STATE(i).si_m_lnum = 0;
1583 if (CUR_STATE(i).si_idx >= 0)
1584 CUR_STATE(i).si_next_list =
Bram Moolenaar860cae12010-06-05 23:22:07 +02001585 (SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001586 else
1587 CUR_STATE(i).si_next_list = NULL;
1588 update_si_attr(i);
1589 }
1590 current_state.ga_len = from->sst_stacksize;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001591 }
1592 current_next_list = from->sst_next_list;
1593 current_next_flags = from->sst_next_flags;
1594 current_lnum = from->sst_lnum;
1595}
1596
1597/*
1598 * Compare saved state stack "*sp" with the current state.
1599 * Return TRUE when they are equal.
1600 */
1601 static int
1602syn_stack_equal(sp)
1603 synstate_T *sp;
1604{
1605 int i, j;
1606 bufstate_T *bp;
1607 reg_extmatch_T *six, *bsx;
1608
1609 /* First a quick check if the stacks have the same size end nextlist. */
1610 if (sp->sst_stacksize == current_state.ga_len
1611 && sp->sst_next_list == current_next_list)
1612 {
1613 /* Need to compare all states on both stacks. */
1614 if (sp->sst_stacksize > SST_FIX_STATES)
1615 bp = SYN_STATE_P(&(sp->sst_union.sst_ga));
1616 else
1617 bp = sp->sst_union.sst_stack;
1618
1619 for (i = current_state.ga_len; --i >= 0; )
1620 {
1621 /* If the item has another index the state is different. */
1622 if (bp[i].bs_idx != CUR_STATE(i).si_idx)
1623 break;
1624 if (bp[i].bs_extmatch != CUR_STATE(i).si_extmatch)
1625 {
1626 /* When the extmatch pointers are different, the strings in
1627 * them can still be the same. Check if the extmatch
1628 * references are equal. */
1629 bsx = bp[i].bs_extmatch;
1630 six = CUR_STATE(i).si_extmatch;
1631 /* If one of the extmatch pointers is NULL the states are
1632 * different. */
1633 if (bsx == NULL || six == NULL)
1634 break;
1635 for (j = 0; j < NSUBEXP; ++j)
1636 {
1637 /* Check each referenced match string. They must all be
1638 * equal. */
1639 if (bsx->matches[j] != six->matches[j])
1640 {
1641 /* If the pointer is different it can still be the
1642 * same text. Compare the strings, ignore case when
1643 * the start item has the sp_ic flag set. */
1644 if (bsx->matches[j] == NULL
1645 || six->matches[j] == NULL)
1646 break;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001647 if ((SYN_ITEMS(syn_block)[CUR_STATE(i).si_idx]).sp_ic
Bram Moolenaar071d4272004-06-13 20:20:40 +00001648 ? MB_STRICMP(bsx->matches[j],
1649 six->matches[j]) != 0
1650 : STRCMP(bsx->matches[j], six->matches[j]) != 0)
1651 break;
1652 }
1653 }
1654 if (j != NSUBEXP)
1655 break;
1656 }
1657 }
1658 if (i < 0)
1659 return TRUE;
1660 }
1661 return FALSE;
1662}
1663
1664/*
1665 * We stop parsing syntax above line "lnum". If the stored state at or below
1666 * this line depended on a change before it, it now depends on the line below
1667 * the last parsed line.
1668 * The window looks like this:
1669 * line which changed
1670 * displayed line
1671 * displayed line
1672 * lnum -> line below window
1673 */
1674 void
1675syntax_end_parsing(lnum)
1676 linenr_T lnum;
1677{
1678 synstate_T *sp;
1679
1680 sp = syn_stack_find_entry(lnum);
1681 if (sp != NULL && sp->sst_lnum < lnum)
1682 sp = sp->sst_next;
1683
1684 if (sp != NULL && sp->sst_change_lnum != 0)
1685 sp->sst_change_lnum = lnum;
1686}
1687
1688/*
1689 * End of handling of the state stack.
1690 ****************************************/
1691
1692 static void
1693invalidate_current_state()
1694{
1695 clear_current_state();
1696 current_state.ga_itemsize = 0; /* mark current_state invalid */
1697 current_next_list = NULL;
1698 keepend_level = -1;
1699}
1700
1701 static void
1702validate_current_state()
1703{
1704 current_state.ga_itemsize = sizeof(stateitem_T);
1705 current_state.ga_growsize = 3;
1706}
1707
1708/*
1709 * Return TRUE if the syntax at start of lnum changed since last time.
1710 * This will only be called just after get_syntax_attr() for the previous
1711 * line, to check if the next line needs to be redrawn too.
1712 */
1713 int
1714syntax_check_changed(lnum)
1715 linenr_T lnum;
1716{
1717 int retval = TRUE;
1718 synstate_T *sp;
1719
Bram Moolenaar071d4272004-06-13 20:20:40 +00001720 /*
1721 * Check the state stack when:
1722 * - lnum is just below the previously syntaxed line.
1723 * - lnum is not before the lines with saved states.
1724 * - lnum is not past the lines with saved states.
1725 * - lnum is at or before the last changed line.
1726 */
1727 if (VALID_STATE(&current_state) && lnum == current_lnum + 1)
1728 {
1729 sp = syn_stack_find_entry(lnum);
1730 if (sp != NULL && sp->sst_lnum == lnum)
1731 {
1732 /*
1733 * finish the previous line (needed when not all of the line was
1734 * drawn)
1735 */
1736 (void)syn_finish_line(FALSE);
1737
1738 /*
1739 * Compare the current state with the previously saved state of
1740 * the line.
1741 */
1742 if (syn_stack_equal(sp))
1743 retval = FALSE;
1744
1745 /*
1746 * Store the current state in b_sst_array[] for later use.
1747 */
1748 ++current_lnum;
Bram Moolenaardbe31752008-01-13 16:40:19 +00001749 (void)store_current_state();
Bram Moolenaar071d4272004-06-13 20:20:40 +00001750 }
1751 }
1752
Bram Moolenaar071d4272004-06-13 20:20:40 +00001753 return retval;
1754}
1755
1756/*
1757 * Finish the current line.
1758 * This doesn't return any attributes, it only gets the state at the end of
1759 * the line. It can start anywhere in the line, as long as the current state
1760 * is valid.
1761 */
1762 static int
1763syn_finish_line(syncing)
1764 int syncing; /* called for syncing */
1765{
1766 stateitem_T *cur_si;
Bram Moolenaar81366db2005-07-24 21:16:51 +00001767 colnr_T prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001768
1769 if (!current_finished)
1770 {
1771 while (!current_finished)
1772 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001773 (void)syn_current_attr(syncing, FALSE, NULL, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001774 /*
1775 * When syncing, and found some item, need to check the item.
1776 */
1777 if (syncing && current_state.ga_len)
1778 {
1779 /*
1780 * Check for match with sync item.
1781 */
1782 cur_si = &CUR_STATE(current_state.ga_len - 1);
1783 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02001784 && (SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags
Bram Moolenaar071d4272004-06-13 20:20:40 +00001785 & (HL_SYNC_HERE|HL_SYNC_THERE)))
1786 return TRUE;
1787
1788 /* syn_current_attr() will have skipped the check for an item
Bram Moolenaar81366db2005-07-24 21:16:51 +00001789 * that ends here, need to do that now. Be careful not to go
1790 * past the NUL. */
1791 prev_current_col = current_col;
1792 if (syn_getcurline()[current_col] != NUL)
1793 ++current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001794 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00001795 current_col = prev_current_col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001796 }
1797 ++current_col;
1798 }
1799 }
1800 return FALSE;
1801}
1802
1803/*
1804 * Return highlight attributes for next character.
1805 * Must first call syntax_start() once for the line.
1806 * "col" is normally 0 for the first use in a line, and increments by one each
1807 * time. It's allowed to skip characters and to stop before the end of the
1808 * line. But only a "col" after a previously used column is allowed.
Bram Moolenaar217ad922005-03-20 22:37:15 +00001809 * When "can_spell" is not NULL set it to TRUE when spell-checking should be
1810 * done.
Bram Moolenaar071d4272004-06-13 20:20:40 +00001811 */
1812 int
Bram Moolenaar27c735b2010-07-22 22:16:29 +02001813get_syntax_attr(col, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001814 colnr_T col;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001815 int *can_spell;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001816 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001817{
1818 int attr = 0;
1819
Bram Moolenaar349955a2007-08-14 21:07:36 +00001820 if (can_spell != NULL)
1821 /* Default: Only do spelling when there is no @Spell cluster or when
1822 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001823 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
1824 ? (syn_block->b_spell_cluster_id == 0)
1825 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar349955a2007-08-14 21:07:36 +00001826
Bram Moolenaar071d4272004-06-13 20:20:40 +00001827 /* check for out of memory situation */
Bram Moolenaar860cae12010-06-05 23:22:07 +02001828 if (syn_block->b_sst_array == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001829 return 0;
1830
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001831 /* After 'synmaxcol' the attribute is always zero. */
Bram Moolenaar84fb85a2005-07-20 22:02:14 +00001832 if (syn_buf->b_p_smc > 0 && col >= (colnr_T)syn_buf->b_p_smc)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001833 {
1834 clear_current_state();
1835#ifdef FEAT_EVAL
1836 current_id = 0;
1837 current_trans_id = 0;
1838#endif
Bram Moolenaar7510fe72010-07-25 12:46:44 +02001839#ifdef FEAT_CONCEAL
1840 current_flags = 0;
1841#endif
Bram Moolenaarce0842a2005-07-18 21:58:11 +00001842 return 0;
1843 }
1844
Bram Moolenaar071d4272004-06-13 20:20:40 +00001845 /* Make sure current_state is valid */
1846 if (INVALID_STATE(&current_state))
1847 validate_current_state();
1848
1849 /*
1850 * Skip from the current column to "col", get the attributes for "col".
1851 */
1852 while (current_col <= col)
1853 {
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001854 attr = syn_current_attr(FALSE, TRUE, can_spell,
1855 current_col == col ? keep_state : FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001856 ++current_col;
1857 }
1858
Bram Moolenaar071d4272004-06-13 20:20:40 +00001859 return attr;
1860}
1861
1862/*
1863 * Get syntax attributes for current_lnum, current_col.
1864 */
1865 static int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001866syn_current_attr(syncing, displaying, can_spell, keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001867 int syncing; /* When 1: called for syncing */
1868 int displaying; /* result will be displayed */
Bram Moolenaar217ad922005-03-20 22:37:15 +00001869 int *can_spell; /* return: do spell checking */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00001870 int keep_state; /* keep syntax stack afterwards */
Bram Moolenaar071d4272004-06-13 20:20:40 +00001871{
1872 int syn_id;
1873 lpos_T endpos; /* was: char_u *endp; */
1874 lpos_T hl_startpos; /* was: int hl_startcol; */
1875 lpos_T hl_endpos;
1876 lpos_T eos_pos; /* end-of-start match (start region) */
1877 lpos_T eoe_pos; /* end-of-end pattern */
1878 int end_idx; /* group ID for end pattern */
1879 int idx;
1880 synpat_T *spp;
Bram Moolenaar217ad922005-03-20 22:37:15 +00001881 stateitem_T *cur_si, *sip = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001882 int startcol;
1883 int endcol;
1884 long flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02001885 int cchar;
Bram Moolenaar071d4272004-06-13 20:20:40 +00001886 short *next_list;
1887 int found_match; /* found usable match */
1888 static int try_next_column = FALSE; /* must try in next col */
1889 int do_keywords;
1890 regmmatch_T regmatch;
1891 lpos_T pos;
1892 int lc_col;
1893 reg_extmatch_T *cur_extmatch = NULL;
1894 char_u *line; /* current line. NOTE: becomes invalid after
1895 looking for a pattern match! */
1896
1897 /* variables for zero-width matches that have a "nextgroup" argument */
1898 int keep_next_list;
1899 int zero_width_next_list = FALSE;
1900 garray_T zero_width_next_ga;
1901
1902 /*
1903 * No character, no attributes! Past end of line?
1904 * Do try matching with an empty line (could be the start of a region).
1905 */
1906 line = syn_getcurline();
1907 if (line[current_col] == NUL && current_col != 0)
1908 {
1909 /*
1910 * If we found a match after the last column, use it.
1911 */
1912 if (next_match_idx >= 0 && next_match_col >= (int)current_col
1913 && next_match_col != MAXCOL)
1914 (void)push_next_match(NULL);
1915
1916 current_finished = TRUE;
1917 current_state_stored = FALSE;
1918 return 0;
1919 }
1920
1921 /* if the current or next character is NUL, we will finish the line now */
1922 if (line[current_col] == NUL || line[current_col + 1] == NUL)
1923 {
1924 current_finished = TRUE;
1925 current_state_stored = FALSE;
1926 }
1927
1928 /*
1929 * When in the previous column there was a match but it could not be used
1930 * (empty match or already matched in this column) need to try again in
1931 * the next column.
1932 */
1933 if (try_next_column)
1934 {
1935 next_match_idx = -1;
1936 try_next_column = FALSE;
1937 }
1938
1939 /* Only check for keywords when not syncing and there are some. */
1940 do_keywords = !syncing
Bram Moolenaar860cae12010-06-05 23:22:07 +02001941 && (syn_block->b_keywtab.ht_used > 0
1942 || syn_block->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00001943
1944 /* Init the list of zero-width matches with a nextlist. This is used to
1945 * avoid matching the same item in the same position twice. */
1946 ga_init2(&zero_width_next_ga, (int)sizeof(int), 10);
1947
1948 /*
1949 * Repeat matching keywords and patterns, to find contained items at the
1950 * same column. This stops when there are no extra matches at the current
1951 * column.
1952 */
1953 do
1954 {
1955 found_match = FALSE;
1956 keep_next_list = FALSE;
1957 syn_id = 0;
1958
1959 /*
1960 * 1. Check for a current state.
1961 * Only when there is no current state, or if the current state may
1962 * contain other things, we need to check for keywords and patterns.
1963 * Always need to check for contained items if some item has the
1964 * "containedin" argument (takes extra time!).
1965 */
1966 if (current_state.ga_len)
1967 cur_si = &CUR_STATE(current_state.ga_len - 1);
1968 else
1969 cur_si = NULL;
1970
Bram Moolenaar860cae12010-06-05 23:22:07 +02001971 if (syn_block->b_syn_containedin || cur_si == NULL
Bram Moolenaar071d4272004-06-13 20:20:40 +00001972 || cur_si->si_cont_list != NULL)
1973 {
1974 /*
1975 * 2. Check for keywords, if on a keyword char after a non-keyword
1976 * char. Don't do this when syncing.
1977 */
1978 if (do_keywords)
1979 {
1980 line = syn_getcurline();
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001981 if (vim_iswordp_buf(line + current_col, syn_buf)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001982 && (current_col == 0
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01001983 || !vim_iswordp_buf(line + current_col - 1
Bram Moolenaar071d4272004-06-13 20:20:40 +00001984#ifdef FEAT_MBYTE
1985 - (has_mbyte
1986 ? (*mb_head_off)(line, line + current_col - 1)
1987 : 0)
1988#endif
1989 , syn_buf)))
1990 {
1991 syn_id = check_keyword_id(line, (int)current_col,
Bram Moolenaar860cae12010-06-05 23:22:07 +02001992 &endcol, &flags, &next_list, cur_si,
1993 &cchar);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00001994 if (syn_id != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00001995 {
1996 if (push_current_state(KEYWORD_IDX) == OK)
1997 {
1998 cur_si = &CUR_STATE(current_state.ga_len - 1);
1999 cur_si->si_m_startcol = current_col;
2000 cur_si->si_h_startpos.lnum = current_lnum;
2001 cur_si->si_h_startpos.col = 0; /* starts right away */
2002 cur_si->si_m_endpos.lnum = current_lnum;
2003 cur_si->si_m_endpos.col = endcol;
2004 cur_si->si_h_endpos.lnum = current_lnum;
2005 cur_si->si_h_endpos.col = endcol;
2006 cur_si->si_ends = TRUE;
2007 cur_si->si_end_idx = 0;
2008 cur_si->si_flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002009#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002010 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002011 cur_si->si_cchar = cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002012 if (current_state.ga_len > 1)
2013 cur_si->si_flags |=
2014 CUR_STATE(current_state.ga_len - 2).si_flags
2015 & HL_CONCEAL;
2016#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002017 cur_si->si_id = syn_id;
2018 cur_si->si_trans_id = syn_id;
2019 if (flags & HL_TRANSP)
2020 {
2021 if (current_state.ga_len < 2)
2022 {
2023 cur_si->si_attr = 0;
2024 cur_si->si_trans_id = 0;
2025 }
2026 else
2027 {
2028 cur_si->si_attr = CUR_STATE(
2029 current_state.ga_len - 2).si_attr;
2030 cur_si->si_trans_id = CUR_STATE(
2031 current_state.ga_len - 2).si_trans_id;
2032 }
2033 }
2034 else
2035 cur_si->si_attr = syn_id2attr(syn_id);
2036 cur_si->si_cont_list = NULL;
2037 cur_si->si_next_list = next_list;
2038 check_keepend();
2039 }
2040 else
2041 vim_free(next_list);
2042 }
2043 }
2044 }
2045
2046 /*
Bram Moolenaare2cc9702005-03-15 22:43:58 +00002047 * 3. Check for patterns (only if no keyword found).
Bram Moolenaar071d4272004-06-13 20:20:40 +00002048 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002049 if (syn_id == 0 && syn_block->b_syn_patterns.ga_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002050 {
2051 /*
2052 * If we didn't check for a match yet, or we are past it, check
2053 * for any match with a pattern.
2054 */
2055 if (next_match_idx < 0 || next_match_col < (int)current_col)
2056 {
2057 /*
2058 * Check all relevant patterns for a match at this
2059 * position. This is complicated, because matching with a
2060 * pattern takes quite a bit of time, thus we want to
2061 * avoid doing it when it's not needed.
2062 */
2063 next_match_idx = 0; /* no match in this line yet */
2064 next_match_col = MAXCOL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002065 for (idx = syn_block->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00002066 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002067 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002068 if ( spp->sp_syncing == syncing
2069 && (displaying || !(spp->sp_flags & HL_DISPLAY))
2070 && (spp->sp_type == SPTYPE_MATCH
2071 || spp->sp_type == SPTYPE_START)
2072 && (current_next_list != NULL
2073 ? in_id_list(NULL, current_next_list,
2074 &spp->sp_syn, 0)
2075 : (cur_si == NULL
2076 ? !(spp->sp_flags & HL_CONTAINED)
2077 : in_id_list(cur_si,
2078 cur_si->si_cont_list, &spp->sp_syn,
2079 spp->sp_flags & HL_CONTAINED))))
2080 {
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002081 int r;
2082
Bram Moolenaar071d4272004-06-13 20:20:40 +00002083 /* If we already tried matching in this line, and
2084 * there isn't a match before next_match_col, skip
2085 * this item. */
2086 if (spp->sp_line_id == current_line_id
2087 && spp->sp_startcol >= next_match_col)
2088 continue;
2089 spp->sp_line_id = current_line_id;
2090
2091 lc_col = current_col - spp->sp_offsets[SPO_LC_OFF];
2092 if (lc_col < 0)
2093 lc_col = 0;
2094
2095 regmatch.rmm_ic = spp->sp_ic;
2096 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002097 r = syn_regexec(&regmatch,
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02002098 current_lnum,
2099 (colnr_T)lc_col,
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002100 IF_SYN_TIME(&spp->sp_time));
2101 spp->sp_prog = regmatch.regprog;
2102 if (!r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002103 {
2104 /* no match in this line, try another one */
2105 spp->sp_startcol = MAXCOL;
2106 continue;
2107 }
2108
2109 /*
2110 * Compute the first column of the match.
2111 */
2112 syn_add_start_off(&pos, &regmatch,
2113 spp, SPO_MS_OFF, -1);
2114 if (pos.lnum > current_lnum)
2115 {
2116 /* must have used end of match in a next line,
2117 * we can't handle that */
2118 spp->sp_startcol = MAXCOL;
2119 continue;
2120 }
2121 startcol = pos.col;
2122
2123 /* remember the next column where this pattern
2124 * matches in the current line */
2125 spp->sp_startcol = startcol;
2126
2127 /*
2128 * If a previously found match starts at a lower
2129 * column number, don't use this one.
2130 */
2131 if (startcol >= next_match_col)
2132 continue;
2133
2134 /*
2135 * If we matched this pattern at this position
2136 * before, skip it. Must retry in the next
2137 * column, because it may match from there.
2138 */
2139 if (did_match_already(idx, &zero_width_next_ga))
2140 {
2141 try_next_column = TRUE;
2142 continue;
2143 }
2144
2145 endpos.lnum = regmatch.endpos[0].lnum;
2146 endpos.col = regmatch.endpos[0].col;
2147
2148 /* Compute the highlight start. */
2149 syn_add_start_off(&hl_startpos, &regmatch,
2150 spp, SPO_HS_OFF, -1);
2151
2152 /* Compute the region start. */
2153 /* Default is to use the end of the match. */
2154 syn_add_end_off(&eos_pos, &regmatch,
2155 spp, SPO_RS_OFF, 0);
2156
2157 /*
2158 * Grab the external submatches before they get
2159 * overwritten. Reference count doesn't change.
2160 */
2161 unref_extmatch(cur_extmatch);
2162 cur_extmatch = re_extmatch_out;
2163 re_extmatch_out = NULL;
2164
2165 flags = 0;
2166 eoe_pos.lnum = 0; /* avoid warning */
2167 eoe_pos.col = 0;
2168 end_idx = 0;
2169 hl_endpos.lnum = 0;
2170
2171 /*
2172 * For a "oneline" the end must be found in the
2173 * same line too. Search for it after the end of
2174 * the match with the start pattern. Set the
2175 * resulting end positions at the same time.
2176 */
2177 if (spp->sp_type == SPTYPE_START
2178 && (spp->sp_flags & HL_ONELINE))
2179 {
2180 lpos_T startpos;
2181
2182 startpos = endpos;
2183 find_endpos(idx, &startpos, &endpos, &hl_endpos,
2184 &flags, &eoe_pos, &end_idx, cur_extmatch);
2185 if (endpos.lnum == 0)
2186 continue; /* not found */
2187 }
2188
2189 /*
2190 * For a "match" the size must be > 0 after the
2191 * end offset needs has been added. Except when
2192 * syncing.
2193 */
2194 else if (spp->sp_type == SPTYPE_MATCH)
2195 {
2196 syn_add_end_off(&hl_endpos, &regmatch, spp,
2197 SPO_HE_OFF, 0);
2198 syn_add_end_off(&endpos, &regmatch, spp,
2199 SPO_ME_OFF, 0);
2200 if (endpos.lnum == current_lnum
2201 && (int)endpos.col + syncing < startcol)
2202 {
2203 /*
2204 * If an empty string is matched, may need
2205 * to try matching again at next column.
2206 */
2207 if (regmatch.startpos[0].col
2208 == regmatch.endpos[0].col)
2209 try_next_column = TRUE;
2210 continue;
2211 }
2212 }
2213
2214 /*
2215 * keep the best match so far in next_match_*
2216 */
2217 /* Highlighting must start after startpos and end
2218 * before endpos. */
2219 if (hl_startpos.lnum == current_lnum
2220 && (int)hl_startpos.col < startcol)
2221 hl_startpos.col = startcol;
2222 limit_pos_zero(&hl_endpos, &endpos);
2223
2224 next_match_idx = idx;
2225 next_match_col = startcol;
2226 next_match_m_endpos = endpos;
2227 next_match_h_endpos = hl_endpos;
2228 next_match_h_startpos = hl_startpos;
2229 next_match_flags = flags;
2230 next_match_eos_pos = eos_pos;
2231 next_match_eoe_pos = eoe_pos;
2232 next_match_end_idx = end_idx;
2233 unref_extmatch(next_match_extmatch);
2234 next_match_extmatch = cur_extmatch;
2235 cur_extmatch = NULL;
2236 }
2237 }
2238 }
2239
2240 /*
2241 * If we found a match at the current column, use it.
2242 */
2243 if (next_match_idx >= 0 && next_match_col == (int)current_col)
2244 {
2245 synpat_T *lspp;
2246
2247 /* When a zero-width item matched which has a nextgroup,
2248 * don't push the item but set nextgroup. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002249 lspp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002250 if (next_match_m_endpos.lnum == current_lnum
2251 && next_match_m_endpos.col == current_col
2252 && lspp->sp_next_list != NULL)
2253 {
2254 current_next_list = lspp->sp_next_list;
2255 current_next_flags = lspp->sp_flags;
2256 keep_next_list = TRUE;
2257 zero_width_next_list = TRUE;
2258
2259 /* Add the index to a list, so that we can check
2260 * later that we don't match it again (and cause an
2261 * endless loop). */
2262 if (ga_grow(&zero_width_next_ga, 1) == OK)
2263 {
2264 ((int *)(zero_width_next_ga.ga_data))
2265 [zero_width_next_ga.ga_len++] = next_match_idx;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002266 }
2267 next_match_idx = -1;
2268 }
2269 else
2270 cur_si = push_next_match(cur_si);
2271 found_match = TRUE;
2272 }
2273 }
2274 }
2275
2276 /*
2277 * Handle searching for nextgroup match.
2278 */
2279 if (current_next_list != NULL && !keep_next_list)
2280 {
2281 /*
2282 * If a nextgroup was not found, continue looking for one if:
2283 * - this is an empty line and the "skipempty" option was given
2284 * - we are on white space and the "skipwhite" option was given
2285 */
2286 if (!found_match)
2287 {
2288 line = syn_getcurline();
2289 if (((current_next_flags & HL_SKIPWHITE)
2290 && vim_iswhite(line[current_col]))
2291 || ((current_next_flags & HL_SKIPEMPTY)
2292 && *line == NUL))
2293 break;
2294 }
2295
2296 /*
2297 * If a nextgroup was found: Use it, and continue looking for
2298 * contained matches.
2299 * If a nextgroup was not found: Continue looking for a normal
2300 * match.
2301 * When did set current_next_list for a zero-width item and no
2302 * match was found don't loop (would get stuck).
2303 */
2304 current_next_list = NULL;
2305 next_match_idx = -1;
2306 if (!zero_width_next_list)
2307 found_match = TRUE;
2308 }
2309
2310 } while (found_match);
2311
2312 /*
2313 * Use attributes from the current state, if within its highlighting.
2314 * If not, use attributes from the current-but-one state, etc.
2315 */
2316 current_attr = 0;
2317#ifdef FEAT_EVAL
2318 current_id = 0;
2319 current_trans_id = 0;
2320#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02002321#ifdef FEAT_CONCEAL
2322 current_flags = 0;
2323#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002324 if (cur_si != NULL)
2325 {
Bram Moolenaar217ad922005-03-20 22:37:15 +00002326#ifndef FEAT_EVAL
2327 int current_trans_id = 0;
2328#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002329 for (idx = current_state.ga_len - 1; idx >= 0; --idx)
2330 {
2331 sip = &CUR_STATE(idx);
2332 if ((current_lnum > sip->si_h_startpos.lnum
2333 || (current_lnum == sip->si_h_startpos.lnum
2334 && current_col >= sip->si_h_startpos.col))
2335 && (sip->si_h_endpos.lnum == 0
2336 || current_lnum < sip->si_h_endpos.lnum
2337 || (current_lnum == sip->si_h_endpos.lnum
2338 && current_col < sip->si_h_endpos.col)))
2339 {
2340 current_attr = sip->si_attr;
2341#ifdef FEAT_EVAL
2342 current_id = sip->si_id;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002343#endif
Bram Moolenaar217ad922005-03-20 22:37:15 +00002344 current_trans_id = sip->si_trans_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002345#ifdef FEAT_CONCEAL
2346 current_flags = sip->si_flags;
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002347 current_seqnr = sip->si_seqnr;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002348 current_sub_char = sip->si_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002349#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002350 break;
2351 }
2352 }
2353
Bram Moolenaar217ad922005-03-20 22:37:15 +00002354 if (can_spell != NULL)
2355 {
2356 struct sp_syn sps;
2357
2358 /*
2359 * set "can_spell" to TRUE if spell checking is supposed to be
2360 * done in the current item.
2361 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002362 if (syn_block->b_spell_cluster_id == 0)
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002363 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002364 /* There is no @Spell cluster: Do spelling for items without
2365 * @NoSpell cluster. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002366 if (syn_block->b_nospell_cluster_id == 0
2367 || current_trans_id == 0)
2368 *can_spell = (syn_block->b_syn_spell != SYNSPL_NOTOP);
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002369 else
2370 {
2371 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002372 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00002373 sps.cont_in_list = NULL;
2374 *can_spell = !in_id_list(sip, sip->si_cont_list, &sps, 0);
2375 }
2376 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002377 else
2378 {
Bram Moolenaar3638c682005-06-08 22:05:14 +00002379 /* The @Spell cluster is defined: Do spelling in items with
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002380 * the @Spell cluster. But not when @NoSpell is also there.
2381 * At the toplevel only spell check when ":syn spell toplevel"
2382 * was used. */
Bram Moolenaar3638c682005-06-08 22:05:14 +00002383 if (current_trans_id == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02002384 *can_spell = (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar3638c682005-06-08 22:05:14 +00002385 else
2386 {
2387 sps.inc_tag = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002388 sps.id = syn_block->b_spell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002389 sps.cont_in_list = NULL;
2390 *can_spell = in_id_list(sip, sip->si_cont_list, &sps, 0);
2391
Bram Moolenaar860cae12010-06-05 23:22:07 +02002392 if (syn_block->b_nospell_cluster_id != 0)
Bram Moolenaar3638c682005-06-08 22:05:14 +00002393 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002394 sps.id = syn_block->b_nospell_cluster_id;
Bram Moolenaar3638c682005-06-08 22:05:14 +00002395 if (in_id_list(sip, sip->si_cont_list, &sps, 0))
2396 *can_spell = FALSE;
2397 }
2398 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002399 }
2400 }
2401
2402
Bram Moolenaar071d4272004-06-13 20:20:40 +00002403 /*
2404 * Check for end of current state (and the states before it) at the
2405 * next column. Don't do this for syncing, because we would miss a
2406 * single character match.
2407 * First check if the current state ends at the current column. It
2408 * may be for an empty match and a containing item might end in the
2409 * current column.
2410 */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00002411 if (!syncing && !keep_state)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002412 {
2413 check_state_ends();
Bram Moolenaar81366db2005-07-24 21:16:51 +00002414 if (current_state.ga_len > 0
2415 && syn_getcurline()[current_col] != NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002416 {
2417 ++current_col;
2418 check_state_ends();
2419 --current_col;
2420 }
2421 }
2422 }
Bram Moolenaar217ad922005-03-20 22:37:15 +00002423 else if (can_spell != NULL)
Bram Moolenaarce0842a2005-07-18 21:58:11 +00002424 /* Default: Only do spelling when there is no @Spell cluster or when
2425 * ":syn spell toplevel" was used. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002426 *can_spell = syn_block->b_syn_spell == SYNSPL_DEFAULT
2427 ? (syn_block->b_spell_cluster_id == 0)
2428 : (syn_block->b_syn_spell == SYNSPL_TOP);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002429
Bram Moolenaarf5b63862009-12-16 17:13:44 +00002430 /* nextgroup ends at end of line, unless "skipnl" or "skipempty" present */
Bram Moolenaar071d4272004-06-13 20:20:40 +00002431 if (current_next_list != NULL
2432 && syn_getcurline()[current_col + 1] == NUL
2433 && !(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY)))
2434 current_next_list = NULL;
2435
2436 if (zero_width_next_ga.ga_len > 0)
2437 ga_clear(&zero_width_next_ga);
2438
2439 /* No longer need external matches. But keep next_match_extmatch. */
2440 unref_extmatch(re_extmatch_out);
2441 re_extmatch_out = NULL;
2442 unref_extmatch(cur_extmatch);
2443
2444 return current_attr;
2445}
2446
2447
2448/*
2449 * Check if we already matched pattern "idx" at the current column.
2450 */
2451 static int
2452did_match_already(idx, gap)
2453 int idx;
2454 garray_T *gap;
2455{
2456 int i;
2457
2458 for (i = current_state.ga_len; --i >= 0; )
2459 if (CUR_STATE(i).si_m_startcol == (int)current_col
2460 && CUR_STATE(i).si_m_lnum == (int)current_lnum
2461 && CUR_STATE(i).si_idx == idx)
2462 return TRUE;
2463
2464 /* Zero-width matches with a nextgroup argument are not put on the syntax
2465 * stack, and can only be matched once anyway. */
2466 for (i = gap->ga_len; --i >= 0; )
2467 if (((int *)(gap->ga_data))[i] == idx)
2468 return TRUE;
2469
2470 return FALSE;
2471}
2472
2473/*
2474 * Push the next match onto the stack.
2475 */
2476 static stateitem_T *
2477push_next_match(cur_si)
2478 stateitem_T *cur_si;
2479{
2480 synpat_T *spp;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002481#ifdef FEAT_CONCEAL
2482 int save_flags;
2483#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002484
Bram Moolenaar860cae12010-06-05 23:22:07 +02002485 spp = &(SYN_ITEMS(syn_block)[next_match_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002486
2487 /*
2488 * Push the item in current_state stack;
2489 */
2490 if (push_current_state(next_match_idx) == OK)
2491 {
2492 /*
2493 * If it's a start-skip-end type that crosses lines, figure out how
2494 * much it continues in this line. Otherwise just fill in the length.
2495 */
2496 cur_si = &CUR_STATE(current_state.ga_len - 1);
2497 cur_si->si_h_startpos = next_match_h_startpos;
2498 cur_si->si_m_startcol = current_col;
2499 cur_si->si_m_lnum = current_lnum;
2500 cur_si->si_flags = spp->sp_flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002501#ifdef FEAT_CONCEAL
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02002502 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar6e202e52010-07-28 18:14:45 +02002503 cur_si->si_cchar = spp->sp_cchar;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002504 if (current_state.ga_len > 1)
2505 cur_si->si_flags |=
2506 CUR_STATE(current_state.ga_len - 2).si_flags & HL_CONCEAL;
2507#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002508 cur_si->si_next_list = spp->sp_next_list;
2509 cur_si->si_extmatch = ref_extmatch(next_match_extmatch);
2510 if (spp->sp_type == SPTYPE_START && !(spp->sp_flags & HL_ONELINE))
2511 {
2512 /* Try to find the end pattern in the current line */
2513 update_si_end(cur_si, (int)(next_match_m_endpos.col), TRUE);
2514 check_keepend();
2515 }
2516 else
2517 {
2518 cur_si->si_m_endpos = next_match_m_endpos;
2519 cur_si->si_h_endpos = next_match_h_endpos;
2520 cur_si->si_ends = TRUE;
2521 cur_si->si_flags |= next_match_flags;
2522 cur_si->si_eoe_pos = next_match_eoe_pos;
2523 cur_si->si_end_idx = next_match_end_idx;
2524 }
2525 if (keepend_level < 0 && (cur_si->si_flags & HL_KEEPEND))
2526 keepend_level = current_state.ga_len - 1;
2527 check_keepend();
2528 update_si_attr(current_state.ga_len - 1);
2529
Bram Moolenaar860cae12010-06-05 23:22:07 +02002530#ifdef FEAT_CONCEAL
2531 save_flags = cur_si->si_flags & (HL_CONCEAL | HL_CONCEALENDS);
2532#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002533 /*
2534 * If the start pattern has another highlight group, push another item
2535 * on the stack for the start pattern.
2536 */
2537 if ( spp->sp_type == SPTYPE_START
2538 && spp->sp_syn_match_id != 0
2539 && push_current_state(next_match_idx) == OK)
2540 {
2541 cur_si = &CUR_STATE(current_state.ga_len - 1);
2542 cur_si->si_h_startpos = next_match_h_startpos;
2543 cur_si->si_m_startcol = current_col;
2544 cur_si->si_m_lnum = current_lnum;
2545 cur_si->si_m_endpos = next_match_eos_pos;
2546 cur_si->si_h_endpos = next_match_eos_pos;
2547 cur_si->si_ends = TRUE;
2548 cur_si->si_end_idx = 0;
2549 cur_si->si_flags = HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002550#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002551 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002552 cur_si->si_flags |= save_flags;
2553 if (cur_si->si_flags & HL_CONCEALENDS)
2554 cur_si->si_flags |= HL_CONCEAL;
2555#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002556 cur_si->si_next_list = NULL;
2557 check_keepend();
2558 update_si_attr(current_state.ga_len - 1);
2559 }
2560 }
2561
2562 next_match_idx = -1; /* try other match next time */
2563
2564 return cur_si;
2565}
2566
2567/*
2568 * Check for end of current state (and the states before it).
2569 */
2570 static void
2571check_state_ends()
2572{
2573 stateitem_T *cur_si;
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002574 int had_extend;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002575
2576 cur_si = &CUR_STATE(current_state.ga_len - 1);
2577 for (;;)
2578 {
2579 if (cur_si->si_ends
2580 && (cur_si->si_m_endpos.lnum < current_lnum
2581 || (cur_si->si_m_endpos.lnum == current_lnum
2582 && cur_si->si_m_endpos.col <= current_col)))
2583 {
2584 /*
2585 * If there is an end pattern group ID, highlight the end pattern
2586 * now. No need to pop the current item from the stack.
2587 * Only do this if the end pattern continues beyond the current
2588 * position.
2589 */
2590 if (cur_si->si_end_idx
2591 && (cur_si->si_eoe_pos.lnum > current_lnum
2592 || (cur_si->si_eoe_pos.lnum == current_lnum
2593 && cur_si->si_eoe_pos.col > current_col)))
2594 {
2595 cur_si->si_idx = cur_si->si_end_idx;
2596 cur_si->si_end_idx = 0;
2597 cur_si->si_m_endpos = cur_si->si_eoe_pos;
2598 cur_si->si_h_endpos = cur_si->si_eoe_pos;
2599 cur_si->si_flags |= HL_MATCH;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002600#ifdef FEAT_CONCEAL
Bram Moolenaar3b953892010-07-27 20:47:25 +02002601 cur_si->si_seqnr = next_seqnr++;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002602 if (cur_si->si_flags & HL_CONCEALENDS)
2603 cur_si->si_flags |= HL_CONCEAL;
2604#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00002605 update_si_attr(current_state.ga_len - 1);
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002606
Bram Moolenaar3a7d8c32011-05-19 12:14:10 +02002607 /* nextgroup= should not match in the end pattern */
2608 current_next_list = NULL;
2609
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002610 /* what matches next may be different now, clear it */
2611 next_match_idx = 0;
2612 next_match_col = MAXCOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002613 break;
2614 }
2615 else
2616 {
2617 /* handle next_list, unless at end of line and no "skipnl" or
2618 * "skipempty" */
2619 current_next_list = cur_si->si_next_list;
2620 current_next_flags = cur_si->si_flags;
2621 if (!(current_next_flags & (HL_SKIPNL | HL_SKIPEMPTY))
2622 && syn_getcurline()[current_col] == NUL)
2623 current_next_list = NULL;
2624
2625 /* When the ended item has "extend", another item with
2626 * "keepend" now needs to check for its end. */
Bram Moolenaar6fa46362011-05-25 17:56:27 +02002627 had_extend = (cur_si->si_flags & HL_EXTEND);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002628
2629 pop_current_state();
2630
2631 if (current_state.ga_len == 0)
2632 break;
2633
Bram Moolenaar81993f42008-01-11 20:27:45 +00002634 if (had_extend && keepend_level >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002635 {
2636 syn_update_ends(FALSE);
2637 if (current_state.ga_len == 0)
2638 break;
2639 }
2640
2641 cur_si = &CUR_STATE(current_state.ga_len - 1);
2642
2643 /*
2644 * Only for a region the search for the end continues after
2645 * the end of the contained item. If the contained match
2646 * included the end-of-line, break here, the region continues.
2647 * Don't do this when:
2648 * - "keepend" is used for the contained item
2649 * - not at the end of the line (could be end="x$"me=e-1).
2650 * - "excludenl" is used (HL_HAS_EOL won't be set)
2651 */
2652 if (cur_si->si_idx >= 0
Bram Moolenaar860cae12010-06-05 23:22:07 +02002653 && SYN_ITEMS(syn_block)[cur_si->si_idx].sp_type
Bram Moolenaar071d4272004-06-13 20:20:40 +00002654 == SPTYPE_START
2655 && !(cur_si->si_flags & (HL_MATCH | HL_KEEPEND)))
2656 {
2657 update_si_end(cur_si, (int)current_col, TRUE);
2658 check_keepend();
2659 if ((current_next_flags & HL_HAS_EOL)
2660 && keepend_level < 0
2661 && syn_getcurline()[current_col] == NUL)
2662 break;
2663 }
2664 }
2665 }
2666 else
2667 break;
2668 }
2669}
2670
2671/*
2672 * Update an entry in the current_state stack for a match or region. This
2673 * fills in si_attr, si_next_list and si_cont_list.
2674 */
2675 static void
2676update_si_attr(idx)
2677 int idx;
2678{
2679 stateitem_T *sip = &CUR_STATE(idx);
2680 synpat_T *spp;
2681
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002682 /* This should not happen... */
2683 if (sip->si_idx < 0)
2684 return;
2685
Bram Moolenaar860cae12010-06-05 23:22:07 +02002686 spp = &(SYN_ITEMS(syn_block)[sip->si_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002687 if (sip->si_flags & HL_MATCH)
2688 sip->si_id = spp->sp_syn_match_id;
2689 else
2690 sip->si_id = spp->sp_syn.id;
2691 sip->si_attr = syn_id2attr(sip->si_id);
2692 sip->si_trans_id = sip->si_id;
2693 if (sip->si_flags & HL_MATCH)
2694 sip->si_cont_list = NULL;
2695 else
2696 sip->si_cont_list = spp->sp_cont_list;
2697
2698 /*
2699 * For transparent items, take attr from outer item.
2700 * Also take cont_list, if there is none.
2701 * Don't do this for the matchgroup of a start or end pattern.
2702 */
2703 if ((spp->sp_flags & HL_TRANSP) && !(sip->si_flags & HL_MATCH))
2704 {
2705 if (idx == 0)
2706 {
2707 sip->si_attr = 0;
2708 sip->si_trans_id = 0;
2709 if (sip->si_cont_list == NULL)
2710 sip->si_cont_list = ID_LIST_ALL;
2711 }
2712 else
2713 {
2714 sip->si_attr = CUR_STATE(idx - 1).si_attr;
2715 sip->si_trans_id = CUR_STATE(idx - 1).si_trans_id;
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00002716 sip->si_h_startpos = CUR_STATE(idx - 1).si_h_startpos;
2717 sip->si_h_endpos = CUR_STATE(idx - 1).si_h_endpos;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002718 if (sip->si_cont_list == NULL)
2719 {
2720 sip->si_flags |= HL_TRANS_CONT;
2721 sip->si_cont_list = CUR_STATE(idx - 1).si_cont_list;
2722 }
2723 }
2724 }
2725}
2726
2727/*
2728 * Check the current stack for patterns with "keepend" flag.
2729 * Propagate the match-end to contained items, until a "skipend" item is found.
2730 */
2731 static void
2732check_keepend()
2733{
2734 int i;
2735 lpos_T maxpos;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002736 lpos_T maxpos_h;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002737 stateitem_T *sip;
2738
2739 /*
2740 * This check can consume a lot of time; only do it from the level where
2741 * there really is a keepend.
2742 */
2743 if (keepend_level < 0)
2744 return;
2745
2746 /*
2747 * Find the last index of an "extend" item. "keepend" items before that
2748 * won't do anything. If there is no "extend" item "i" will be
2749 * "keepend_level" and all "keepend" items will work normally.
2750 */
2751 for (i = current_state.ga_len - 1; i > keepend_level; --i)
2752 if (CUR_STATE(i).si_flags & HL_EXTEND)
2753 break;
2754
2755 maxpos.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002756 maxpos.col = 0;
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002757 maxpos_h.lnum = 0;
Bram Moolenaared39e1d2008-08-09 17:55:22 +00002758 maxpos_h.col = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002759 for ( ; i < current_state.ga_len; ++i)
2760 {
2761 sip = &CUR_STATE(i);
2762 if (maxpos.lnum != 0)
2763 {
2764 limit_pos_zero(&sip->si_m_endpos, &maxpos);
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002765 limit_pos_zero(&sip->si_h_endpos, &maxpos_h);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002766 limit_pos_zero(&sip->si_eoe_pos, &maxpos);
2767 sip->si_ends = TRUE;
2768 }
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002769 if (sip->si_ends && (sip->si_flags & HL_KEEPEND))
2770 {
2771 if (maxpos.lnum == 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00002772 || maxpos.lnum > sip->si_m_endpos.lnum
2773 || (maxpos.lnum == sip->si_m_endpos.lnum
Bram Moolenaar7bd2cd82006-10-03 15:04:36 +00002774 && maxpos.col > sip->si_m_endpos.col))
2775 maxpos = sip->si_m_endpos;
2776 if (maxpos_h.lnum == 0
2777 || maxpos_h.lnum > sip->si_h_endpos.lnum
2778 || (maxpos_h.lnum == sip->si_h_endpos.lnum
2779 && maxpos_h.col > sip->si_h_endpos.col))
2780 maxpos_h = sip->si_h_endpos;
2781 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00002782 }
2783}
2784
2785/*
2786 * Update an entry in the current_state stack for a start-skip-end pattern.
2787 * This finds the end of the current item, if it's in the current line.
2788 *
2789 * Return the flags for the matched END.
2790 */
2791 static void
2792update_si_end(sip, startcol, force)
2793 stateitem_T *sip;
2794 int startcol; /* where to start searching for the end */
2795 int force; /* when TRUE overrule a previous end */
2796{
2797 lpos_T startpos;
2798 lpos_T endpos;
2799 lpos_T hl_endpos;
2800 lpos_T end_endpos;
2801 int end_idx;
2802
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002803 /* return quickly for a keyword */
2804 if (sip->si_idx < 0)
2805 return;
2806
Bram Moolenaar071d4272004-06-13 20:20:40 +00002807 /* Don't update when it's already done. Can be a match of an end pattern
2808 * that started in a previous line. Watch out: can also be a "keepend"
2809 * from a containing item. */
2810 if (!force && sip->si_m_endpos.lnum >= current_lnum)
2811 return;
2812
2813 /*
2814 * We need to find the end of the region. It may continue in the next
2815 * line.
2816 */
2817 end_idx = 0;
2818 startpos.lnum = current_lnum;
2819 startpos.col = startcol;
2820 find_endpos(sip->si_idx, &startpos, &endpos, &hl_endpos,
2821 &(sip->si_flags), &end_endpos, &end_idx, sip->si_extmatch);
2822
2823 if (endpos.lnum == 0)
2824 {
2825 /* No end pattern matched. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002826 if (SYN_ITEMS(syn_block)[sip->si_idx].sp_flags & HL_ONELINE)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002827 {
2828 /* a "oneline" never continues in the next line */
2829 sip->si_ends = TRUE;
2830 sip->si_m_endpos.lnum = current_lnum;
2831 sip->si_m_endpos.col = (colnr_T)STRLEN(syn_getcurline());
2832 }
2833 else
2834 {
2835 /* continues in the next line */
2836 sip->si_ends = FALSE;
2837 sip->si_m_endpos.lnum = 0;
2838 }
2839 sip->si_h_endpos = sip->si_m_endpos;
2840 }
2841 else
2842 {
2843 /* match within this line */
2844 sip->si_m_endpos = endpos;
2845 sip->si_h_endpos = hl_endpos;
2846 sip->si_eoe_pos = end_endpos;
2847 sip->si_ends = TRUE;
2848 sip->si_end_idx = end_idx;
2849 }
2850}
2851
2852/*
2853 * Add a new state to the current state stack.
2854 * It is cleared and the index set to "idx".
2855 * Return FAIL if it's not possible (out of memory).
2856 */
2857 static int
2858push_current_state(idx)
2859 int idx;
2860{
2861 if (ga_grow(&current_state, 1) == FAIL)
2862 return FAIL;
2863 vim_memset(&CUR_STATE(current_state.ga_len), 0, sizeof(stateitem_T));
2864 CUR_STATE(current_state.ga_len).si_idx = idx;
2865 ++current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002866 return OK;
2867}
2868
2869/*
2870 * Remove a state from the current_state stack.
2871 */
2872 static void
2873pop_current_state()
2874{
2875 if (current_state.ga_len)
2876 {
2877 unref_extmatch(CUR_STATE(current_state.ga_len - 1).si_extmatch);
2878 --current_state.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002879 }
2880 /* after the end of a pattern, try matching a keyword or pattern */
2881 next_match_idx = -1;
2882
2883 /* if first state with "keepend" is popped, reset keepend_level */
2884 if (keepend_level >= current_state.ga_len)
2885 keepend_level = -1;
2886}
2887
2888/*
2889 * Find the end of a start/skip/end syntax region after "startpos".
2890 * Only checks one line.
2891 * Also handles a match item that continued from a previous line.
2892 * If not found, the syntax item continues in the next line. m_endpos->lnum
2893 * will be 0.
2894 * If found, the end of the region and the end of the highlighting is
2895 * computed.
2896 */
2897 static void
2898find_endpos(idx, startpos, m_endpos, hl_endpos, flagsp, end_endpos,
2899 end_idx, start_ext)
2900 int idx; /* index of the pattern */
2901 lpos_T *startpos; /* where to start looking for an END match */
2902 lpos_T *m_endpos; /* return: end of match */
2903 lpos_T *hl_endpos; /* return: end of highlighting */
2904 long *flagsp; /* return: flags of matching END */
2905 lpos_T *end_endpos; /* return: end of end pattern match */
2906 int *end_idx; /* return: group ID for end pat. match, or 0 */
2907 reg_extmatch_T *start_ext; /* submatches from the start pattern */
2908{
2909 colnr_T matchcol;
2910 synpat_T *spp, *spp_skip;
2911 int start_idx;
2912 int best_idx;
2913 regmmatch_T regmatch;
2914 regmmatch_T best_regmatch; /* startpos/endpos of best match */
2915 lpos_T pos;
2916 char_u *line;
2917 int had_match = FALSE;
2918
Bram Moolenaar3a36cf72007-08-21 15:29:56 +00002919 /* just in case we are invoked for a keyword */
2920 if (idx < 0)
2921 return;
2922
Bram Moolenaar071d4272004-06-13 20:20:40 +00002923 /*
2924 * Check for being called with a START pattern.
2925 * Can happen with a match that continues to the next line, because it
2926 * contained a region.
2927 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02002928 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002929 if (spp->sp_type != SPTYPE_START)
2930 {
2931 *hl_endpos = *startpos;
2932 return;
2933 }
2934
2935 /*
2936 * Find the SKIP or first END pattern after the last START pattern.
2937 */
2938 for (;;)
2939 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02002940 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002941 if (spp->sp_type != SPTYPE_START)
2942 break;
2943 ++idx;
2944 }
2945
2946 /*
2947 * Lookup the SKIP pattern (if present)
2948 */
2949 if (spp->sp_type == SPTYPE_SKIP)
2950 {
2951 spp_skip = spp;
2952 ++idx;
2953 }
2954 else
2955 spp_skip = NULL;
2956
2957 /* Setup external matches for syn_regexec(). */
2958 unref_extmatch(re_extmatch_in);
2959 re_extmatch_in = ref_extmatch(start_ext);
2960
2961 matchcol = startpos->col; /* start looking for a match at sstart */
2962 start_idx = idx; /* remember the first END pattern. */
2963 best_regmatch.startpos[0].col = 0; /* avoid compiler warning */
2964 for (;;)
2965 {
2966 /*
2967 * Find end pattern that matches first after "matchcol".
2968 */
2969 best_idx = -1;
Bram Moolenaar860cae12010-06-05 23:22:07 +02002970 for (idx = start_idx; idx < syn_block->b_syn_patterns.ga_len; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002971 {
2972 int lc_col = matchcol;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002973 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00002974
Bram Moolenaar860cae12010-06-05 23:22:07 +02002975 spp = &(SYN_ITEMS(syn_block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00002976 if (spp->sp_type != SPTYPE_END) /* past last END pattern */
2977 break;
2978 lc_col -= spp->sp_offsets[SPO_LC_OFF];
2979 if (lc_col < 0)
2980 lc_col = 0;
2981
2982 regmatch.rmm_ic = spp->sp_ic;
2983 regmatch.regprog = spp->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01002984 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
2985 IF_SYN_TIME(&spp->sp_time));
2986 spp->sp_prog = regmatch.regprog;
2987 if (r)
Bram Moolenaar071d4272004-06-13 20:20:40 +00002988 {
2989 if (best_idx == -1 || regmatch.startpos[0].col
2990 < best_regmatch.startpos[0].col)
2991 {
2992 best_idx = idx;
2993 best_regmatch.startpos[0] = regmatch.startpos[0];
2994 best_regmatch.endpos[0] = regmatch.endpos[0];
2995 }
2996 }
2997 }
2998
2999 /*
3000 * If all end patterns have been tried, and there is no match, the
3001 * item continues until end-of-line.
3002 */
3003 if (best_idx == -1)
3004 break;
3005
3006 /*
3007 * If the skip pattern matches before the end pattern,
3008 * continue searching after the skip pattern.
3009 */
3010 if (spp_skip != NULL)
3011 {
3012 int lc_col = matchcol - spp_skip->sp_offsets[SPO_LC_OFF];
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003013 int r;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003014
3015 if (lc_col < 0)
3016 lc_col = 0;
3017 regmatch.rmm_ic = spp_skip->sp_ic;
3018 regmatch.regprog = spp_skip->sp_prog;
Bram Moolenaardffa5b82014-11-19 16:38:07 +01003019 r = syn_regexec(&regmatch, startpos->lnum, lc_col,
3020 IF_SYN_TIME(&spp_skip->sp_time));
3021 spp_skip->sp_prog = regmatch.regprog;
3022 if (r && regmatch.startpos[0].col
Bram Moolenaar071d4272004-06-13 20:20:40 +00003023 <= best_regmatch.startpos[0].col)
3024 {
3025 /* Add offset to skip pattern match */
3026 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3027
3028 /* If the skip pattern goes on to the next line, there is no
3029 * match with an end pattern in this line. */
3030 if (pos.lnum > startpos->lnum)
3031 break;
3032
3033 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
3034
3035 /* take care of an empty match or negative offset */
3036 if (pos.col <= matchcol)
3037 ++matchcol;
3038 else if (pos.col <= regmatch.endpos[0].col)
3039 matchcol = pos.col;
3040 else
3041 /* Be careful not to jump over the NUL at the end-of-line */
3042 for (matchcol = regmatch.endpos[0].col;
3043 line[matchcol] != NUL && matchcol < pos.col;
3044 ++matchcol)
3045 ;
3046
3047 /* if the skip pattern includes end-of-line, break here */
3048 if (line[matchcol] == NUL)
3049 break;
3050
3051 continue; /* start with first end pattern again */
3052 }
3053 }
3054
3055 /*
3056 * Match from start pattern to end pattern.
3057 * Correct for match and highlight offset of end pattern.
3058 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003059 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003060 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3061 /* can't end before the start */
3062 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3063 m_endpos->col = startpos->col;
3064
3065 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3066 /* can't end before the start */
3067 if (end_endpos->lnum == startpos->lnum
3068 && end_endpos->col < startpos->col)
3069 end_endpos->col = startpos->col;
3070 /* can't end after the match */
3071 limit_pos(end_endpos, m_endpos);
3072
3073 /*
3074 * If the end group is highlighted differently, adjust the pointers.
3075 */
3076 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3077 {
3078 *end_idx = best_idx;
3079 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3080 {
3081 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3082 hl_endpos->col = best_regmatch.endpos[0].col;
3083 }
3084 else
3085 {
3086 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3087 hl_endpos->col = best_regmatch.startpos[0].col;
3088 }
3089 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3090
3091 /* can't end before the start */
3092 if (hl_endpos->lnum == startpos->lnum
3093 && hl_endpos->col < startpos->col)
3094 hl_endpos->col = startpos->col;
3095 limit_pos(hl_endpos, m_endpos);
3096
3097 /* now the match ends where the highlighting ends, it is turned
3098 * into the matchgroup for the end */
3099 *m_endpos = *hl_endpos;
3100 }
3101 else
3102 {
3103 *end_idx = 0;
3104 *hl_endpos = *end_endpos;
3105 }
3106
3107 *flagsp = spp->sp_flags;
3108
3109 had_match = TRUE;
3110 break;
3111 }
3112
3113 /* no match for an END pattern in this line */
3114 if (!had_match)
3115 m_endpos->lnum = 0;
3116
3117 /* Remove external matches. */
3118 unref_extmatch(re_extmatch_in);
3119 re_extmatch_in = NULL;
3120}
3121
3122/*
3123 * Limit "pos" not to be after "limit".
3124 */
3125 static void
3126limit_pos(pos, limit)
3127 lpos_T *pos;
3128 lpos_T *limit;
3129{
3130 if (pos->lnum > limit->lnum)
3131 *pos = *limit;
3132 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3133 pos->col = limit->col;
3134}
3135
3136/*
3137 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3138 */
3139 static void
3140limit_pos_zero(pos, limit)
3141 lpos_T *pos;
3142 lpos_T *limit;
3143{
3144 if (pos->lnum == 0)
3145 *pos = *limit;
3146 else
3147 limit_pos(pos, limit);
3148}
3149
3150/*
3151 * Add offset to matched text for end of match or highlight.
3152 */
3153 static void
3154syn_add_end_off(result, regmatch, spp, idx, extra)
3155 lpos_T *result; /* returned position */
3156 regmmatch_T *regmatch; /* start/end of match */
3157 synpat_T *spp; /* matched pattern */
3158 int idx; /* index of offset */
3159 int extra; /* extra chars for offset to start */
3160{
3161 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003162 int off;
3163 char_u *base;
3164 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003165
3166 if (spp->sp_off_flags & (1 << idx))
3167 {
3168 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003169 col = regmatch->startpos[0].col;
3170 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003171 }
3172 else
3173 {
3174 result->lnum = regmatch->endpos[0].lnum;
3175 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003176 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003177 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003178 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3179 * is a matchgroup. Watch out for match with last NL in the buffer. */
3180 if (result->lnum > syn_buf->b_ml.ml_line_count)
3181 col = 0;
3182 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003183 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003184 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3185 p = base + col;
3186 if (off > 0)
3187 {
3188 while (off-- > 0 && *p != NUL)
3189 mb_ptr_adv(p);
3190 }
3191 else if (off < 0)
3192 {
3193 while (off++ < 0 && base < p)
3194 mb_ptr_back(base, p);
3195 }
3196 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003197 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003198 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003199}
3200
3201/*
3202 * Add offset to matched text for start of match or highlight.
3203 * Avoid resulting column to become negative.
3204 */
3205 static void
3206syn_add_start_off(result, regmatch, spp, idx, extra)
3207 lpos_T *result; /* returned position */
3208 regmmatch_T *regmatch; /* start/end of match */
3209 synpat_T *spp;
3210 int idx;
3211 int extra; /* extra chars for offset to end */
3212{
3213 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003214 int off;
3215 char_u *base;
3216 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003217
3218 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3219 {
3220 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003221 col = regmatch->endpos[0].col;
3222 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003223 }
3224 else
3225 {
3226 result->lnum = regmatch->startpos[0].lnum;
3227 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003228 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003229 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003230 if (result->lnum > syn_buf->b_ml.ml_line_count)
3231 {
3232 /* a "\n" at the end of the pattern may take us below the last line */
3233 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003234 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003235 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003236 if (off != 0)
3237 {
3238 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3239 p = base + col;
3240 if (off > 0)
3241 {
3242 while (off-- && *p != NUL)
3243 mb_ptr_adv(p);
3244 }
3245 else if (off < 0)
3246 {
3247 while (off++ && base < p)
3248 mb_ptr_back(base, p);
3249 }
3250 col = (int)(p - base);
3251 }
3252 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003253}
3254
3255/*
3256 * Get current line in syntax buffer.
3257 */
3258 static char_u *
3259syn_getcurline()
3260{
3261 return ml_get_buf(syn_buf, current_lnum, FALSE);
3262}
3263
3264/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003265 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003266 * Returns TRUE when there is a match.
3267 */
3268 static int
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003269syn_regexec(rmp, lnum, col, st)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003270 regmmatch_T *rmp;
3271 linenr_T lnum;
3272 colnr_T col;
Bram Moolenaar4e312962013-06-06 21:19:51 +02003273 syn_time_T *st UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003274{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003275 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003276#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003277 proftime_T pt;
3278
3279 if (syn_time_on)
3280 profile_start(&pt);
3281#endif
3282
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003283 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003284 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3285
Bram Moolenaarf7512552013-06-06 14:55:19 +02003286#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003287 if (syn_time_on)
3288 {
3289 profile_end(&pt);
3290 profile_add(&st->total, &pt);
3291 if (profile_cmp(&pt, &st->slowest) < 0)
3292 st->slowest = pt;
3293 ++st->count;
3294 if (r > 0)
3295 ++st->match;
3296 }
3297#endif
3298
3299 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003300 {
3301 rmp->startpos[0].lnum += lnum;
3302 rmp->endpos[0].lnum += lnum;
3303 return TRUE;
3304 }
3305 return FALSE;
3306}
3307
3308/*
3309 * Check one position in a line for a matching keyword.
3310 * The caller must check if a keyword can start at startcol.
3311 * Return it's ID if found, 0 otherwise.
3312 */
3313 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003314check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003315 char_u *line;
3316 int startcol; /* position in line to check for keyword */
3317 int *endcolp; /* return: character after found keyword */
3318 long *flagsp; /* return: flags of matching keyword */
3319 short **next_listp; /* return: next_list of matching keyword */
3320 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003321 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003322{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003323 keyentry_T *kp;
3324 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003325 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003326 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003327 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003328 hashtab_T *ht;
3329 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003330
3331 /* Find first character after the keyword. First character was already
3332 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003333 kwp = line + startcol;
3334 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003335 do
3336 {
3337#ifdef FEAT_MBYTE
3338 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003339 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003340 else
3341#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003342 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003344 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003345
Bram Moolenaardad6b692005-01-25 22:14:34 +00003346 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003347 return 0;
3348
3349 /*
3350 * Must make a copy of the keyword, so we can add a NUL and make it
3351 * lowercase.
3352 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003353 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003354
3355 /*
3356 * Try twice:
3357 * 1. matching case
3358 * 2. ignoring case
3359 */
3360 for (round = 1; round <= 2; ++round)
3361 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003362 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003363 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003364 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003365 if (round == 2) /* ignore case */
3366 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367
3368 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003369 * Find keywords that match. There can be several with different
3370 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003371 * When current_next_list is non-zero accept only that group, otherwise:
3372 * Accept a not-contained keyword at toplevel.
3373 * Accept a keyword at other levels only if it is in the contains list.
3374 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003375 hi = hash_find(ht, keyword);
3376 if (!HASHITEM_EMPTY(hi))
3377 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003378 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003379 if (current_next_list != 0
3380 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3381 : (cur_si == NULL
3382 ? !(kp->flags & HL_CONTAINED)
3383 : in_id_list(cur_si, cur_si->si_cont_list,
3384 &kp->k_syn, kp->flags & HL_CONTAINED)))
3385 {
3386 *endcolp = startcol + kwlen;
3387 *flagsp = kp->flags;
3388 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003389#ifdef FEAT_CONCEAL
3390 *ccharp = kp->k_char;
3391#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003392 return kp->k_syn.id;
3393 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003394 }
3395 }
3396 return 0;
3397}
3398
3399/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003400 * Handle ":syntax conceal" command.
3401 */
3402 static void
3403syn_cmd_conceal(eap, syncing)
3404 exarg_T *eap UNUSED;
3405 int syncing UNUSED;
3406{
3407#ifdef FEAT_CONCEAL
3408 char_u *arg = eap->arg;
3409 char_u *next;
3410
3411 eap->nextcmd = find_nextcmd(arg);
3412 if (eap->skip)
3413 return;
3414
3415 next = skiptowhite(arg);
3416 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3417 curwin->w_s->b_syn_conceal = TRUE;
3418 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3419 curwin->w_s->b_syn_conceal = FALSE;
3420 else
3421 EMSG2(_("E390: Illegal argument: %s"), arg);
3422#endif
3423}
3424
3425/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003426 * Handle ":syntax case" command.
3427 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003428 static void
3429syn_cmd_case(eap, syncing)
3430 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003431 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003432{
3433 char_u *arg = eap->arg;
3434 char_u *next;
3435
3436 eap->nextcmd = find_nextcmd(arg);
3437 if (eap->skip)
3438 return;
3439
3440 next = skiptowhite(arg);
3441 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003442 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003443 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003444 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003445 else
3446 EMSG2(_("E390: Illegal argument: %s"), arg);
3447}
3448
3449/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003450 * Handle ":syntax spell" command.
3451 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003452 static void
3453syn_cmd_spell(eap, syncing)
3454 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003455 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003456{
3457 char_u *arg = eap->arg;
3458 char_u *next;
3459
3460 eap->nextcmd = find_nextcmd(arg);
3461 if (eap->skip)
3462 return;
3463
3464 next = skiptowhite(arg);
3465 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003466 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003467 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003468 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003469 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003470 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003471 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003472 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003473 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003474 return;
3475 }
3476
3477 /* assume spell checking changed, force a redraw */
3478 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003479}
3480
3481/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003482 * Clear all syntax info for one buffer.
3483 */
3484 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003485syntax_clear(block)
3486 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003487{
3488 int i;
3489
Bram Moolenaar860cae12010-06-05 23:22:07 +02003490 block->b_syn_error = FALSE; /* clear previous error */
3491 block->b_syn_ic = FALSE; /* Use case, by default */
3492 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3493 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003494
3495 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003496 clear_keywtab(&block->b_keywtab);
3497 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003498
3499 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003500 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3501 syn_clear_pattern(block, i);
3502 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003503
3504 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003505 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3506 syn_clear_cluster(block, i);
3507 ga_clear(&block->b_syn_clusters);
3508 block->b_spell_cluster_id = 0;
3509 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003510
Bram Moolenaar860cae12010-06-05 23:22:07 +02003511 block->b_syn_sync_flags = 0;
3512 block->b_syn_sync_minlines = 0;
3513 block->b_syn_sync_maxlines = 0;
3514 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003515
Bram Moolenaar473de612013-06-08 18:19:48 +02003516 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003517 block->b_syn_linecont_prog = NULL;
3518 vim_free(block->b_syn_linecont_pat);
3519 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003520#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003521 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003522#endif
3523
3524 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003525 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003526 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003527
3528 /* Reset the counter for ":syn include" */
3529 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003530}
3531
3532/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003533 * Get rid of ownsyntax for window "wp".
3534 */
3535 void
3536reset_synblock(wp)
3537 win_T *wp;
3538{
3539 if (wp->w_s != &wp->w_buffer->b_s)
3540 {
3541 syntax_clear(wp->w_s);
3542 vim_free(wp->w_s);
3543 wp->w_s = &wp->w_buffer->b_s;
3544 }
3545}
3546
3547/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003548 * Clear syncing info for one buffer.
3549 */
3550 static void
3551syntax_sync_clear()
3552{
3553 int i;
3554
3555 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003556 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3557 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3558 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003559
Bram Moolenaar860cae12010-06-05 23:22:07 +02003560 curwin->w_s->b_syn_sync_flags = 0;
3561 curwin->w_s->b_syn_sync_minlines = 0;
3562 curwin->w_s->b_syn_sync_maxlines = 0;
3563 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003564
Bram Moolenaar473de612013-06-08 18:19:48 +02003565 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003566 curwin->w_s->b_syn_linecont_prog = NULL;
3567 vim_free(curwin->w_s->b_syn_linecont_pat);
3568 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003569
Bram Moolenaar860cae12010-06-05 23:22:07 +02003570 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003571}
3572
3573/*
3574 * Remove one pattern from the buffer's pattern list.
3575 */
3576 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003577syn_remove_pattern(block, idx)
3578 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003579 int idx;
3580{
3581 synpat_T *spp;
3582
Bram Moolenaar860cae12010-06-05 23:22:07 +02003583 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003584#ifdef FEAT_FOLDING
3585 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003586 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003588 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003589 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003590 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3591 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592}
3593
3594/*
3595 * Clear and free one syntax pattern. When clearing all, must be called from
3596 * last to first!
3597 */
3598 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003599syn_clear_pattern(block, i)
3600 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003601 int i;
3602{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003603 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003604 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003605 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003606 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003607 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003608 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3609 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3610 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003611 }
3612}
3613
3614/*
3615 * Clear and free one syntax cluster.
3616 */
3617 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003618syn_clear_cluster(block, i)
3619 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003620 int i;
3621{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003622 vim_free(SYN_CLSTR(block)[i].scl_name);
3623 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3624 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003625}
3626
3627/*
3628 * Handle ":syntax clear" command.
3629 */
3630 static void
3631syn_cmd_clear(eap, syncing)
3632 exarg_T *eap;
3633 int syncing;
3634{
3635 char_u *arg = eap->arg;
3636 char_u *arg_end;
3637 int id;
3638
3639 eap->nextcmd = find_nextcmd(arg);
3640 if (eap->skip)
3641 return;
3642
3643 /*
3644 * We have to disable this within ":syn include @group filename",
3645 * because otherwise @group would get deleted.
3646 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3647 * clear".
3648 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003649 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003650 return;
3651
3652 if (ends_excmd(*arg))
3653 {
3654 /*
3655 * No argument: Clear all syntax items.
3656 */
3657 if (syncing)
3658 syntax_sync_clear();
3659 else
3660 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003661 syntax_clear(curwin->w_s);
3662 if (curwin->w_s == &curwin->w_buffer->b_s)
3663 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003664 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003665 }
3666 }
3667 else
3668 {
3669 /*
3670 * Clear the group IDs that are in the argument.
3671 */
3672 while (!ends_excmd(*arg))
3673 {
3674 arg_end = skiptowhite(arg);
3675 if (*arg == '@')
3676 {
3677 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3678 if (id == 0)
3679 {
3680 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3681 break;
3682 }
3683 else
3684 {
3685 /*
3686 * We can't physically delete a cluster without changing
3687 * the IDs of other clusters, so we do the next best thing
3688 * and make it empty.
3689 */
3690 short scl_id = id - SYNID_CLUSTER;
3691
Bram Moolenaar860cae12010-06-05 23:22:07 +02003692 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3693 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003694 }
3695 }
3696 else
3697 {
3698 id = syn_namen2id(arg, (int)(arg_end - arg));
3699 if (id == 0)
3700 {
3701 EMSG2(_(e_nogroup), arg);
3702 break;
3703 }
3704 else
3705 syn_clear_one(id, syncing);
3706 }
3707 arg = skipwhite(arg_end);
3708 }
3709 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003710 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003711 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003712}
3713
3714/*
3715 * Clear one syntax group for the current buffer.
3716 */
3717 static void
3718syn_clear_one(id, syncing)
3719 int id;
3720 int syncing;
3721{
3722 synpat_T *spp;
3723 int idx;
3724
3725 /* Clear keywords only when not ":syn sync clear group-name" */
3726 if (!syncing)
3727 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003728 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3729 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003730 }
3731
3732 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003733 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003734 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003735 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003736 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3737 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003738 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739 }
3740}
3741
3742/*
3743 * Handle ":syntax on" command.
3744 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003745 static void
3746syn_cmd_on(eap, syncing)
3747 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003748 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003749{
3750 syn_cmd_onoff(eap, "syntax");
3751}
3752
3753/*
3754 * Handle ":syntax enable" command.
3755 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003756 static void
3757syn_cmd_enable(eap, syncing)
3758 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003759 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003760{
3761 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3762 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003763 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003764}
3765
3766/*
3767 * Handle ":syntax reset" command.
3768 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003769 static void
3770syn_cmd_reset(eap, syncing)
3771 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003772 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003773{
3774 eap->nextcmd = check_nextcmd(eap->arg);
3775 if (!eap->skip)
3776 {
3777 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3778 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003779 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003780 }
3781}
3782
3783/*
3784 * Handle ":syntax manual" command.
3785 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003786 static void
3787syn_cmd_manual(eap, syncing)
3788 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003789 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003790{
3791 syn_cmd_onoff(eap, "manual");
3792}
3793
3794/*
3795 * Handle ":syntax off" command.
3796 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003797 static void
3798syn_cmd_off(eap, syncing)
3799 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003800 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003801{
3802 syn_cmd_onoff(eap, "nosyntax");
3803}
3804
3805 static void
3806syn_cmd_onoff(eap, name)
3807 exarg_T *eap;
3808 char *name;
3809{
3810 char_u buf[100];
3811
3812 eap->nextcmd = check_nextcmd(eap->arg);
3813 if (!eap->skip)
3814 {
3815 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003816 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003817 do_cmdline_cmd(buf);
3818 }
3819}
3820
3821/*
3822 * Handle ":syntax [list]" command: list current syntax words.
3823 */
3824 static void
3825syn_cmd_list(eap, syncing)
3826 exarg_T *eap;
3827 int syncing; /* when TRUE: list syncing items */
3828{
3829 char_u *arg = eap->arg;
3830 int id;
3831 char_u *arg_end;
3832
3833 eap->nextcmd = find_nextcmd(arg);
3834 if (eap->skip)
3835 return;
3836
Bram Moolenaar860cae12010-06-05 23:22:07 +02003837 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003838 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003839 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003840 return;
3841 }
3842
3843 if (syncing)
3844 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003845 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003846 {
3847 MSG_PUTS(_("syncing on C-style comments"));
3848 syn_lines_msg();
3849 syn_match_msg();
3850 return;
3851 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003852 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003853 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003854 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003855 MSG_PUTS(_("no syncing"));
3856 else
3857 {
3858 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003859 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003860 MSG_PUTS(_(" lines before top line"));
3861 syn_match_msg();
3862 }
3863 return;
3864 }
3865 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003866 if (curwin->w_s->b_syn_sync_minlines > 0
3867 || curwin->w_s->b_syn_sync_maxlines > 0
3868 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003869 {
3870 MSG_PUTS(_("\nsyncing on items"));
3871 syn_lines_msg();
3872 syn_match_msg();
3873 }
3874 }
3875 else
3876 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3877 if (ends_excmd(*arg))
3878 {
3879 /*
3880 * No argument: List all group IDs and all syntax clusters.
3881 */
3882 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3883 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003884 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003885 syn_list_cluster(id);
3886 }
3887 else
3888 {
3889 /*
3890 * List the group IDs and syntax clusters that are in the argument.
3891 */
3892 while (!ends_excmd(*arg) && !got_int)
3893 {
3894 arg_end = skiptowhite(arg);
3895 if (*arg == '@')
3896 {
3897 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3898 if (id == 0)
3899 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3900 else
3901 syn_list_cluster(id - SYNID_CLUSTER);
3902 }
3903 else
3904 {
3905 id = syn_namen2id(arg, (int)(arg_end - arg));
3906 if (id == 0)
3907 EMSG2(_(e_nogroup), arg);
3908 else
3909 syn_list_one(id, syncing, TRUE);
3910 }
3911 arg = skipwhite(arg_end);
3912 }
3913 }
3914 eap->nextcmd = check_nextcmd(arg);
3915}
3916
3917 static void
3918syn_lines_msg()
3919{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003920 if (curwin->w_s->b_syn_sync_maxlines > 0
3921 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003922 {
3923 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003924 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925 {
3926 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003927 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3928 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003929 MSG_PUTS(", ");
3930 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003931 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932 {
3933 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003934 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 }
3936 MSG_PUTS(_(" lines before top line"));
3937 }
3938}
3939
3940 static void
3941syn_match_msg()
3942{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003943 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003944 {
3945 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003946 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947 MSG_PUTS(_(" line breaks"));
3948 }
3949}
3950
3951static int last_matchgroup;
3952
3953struct name_list
3954{
3955 int flag;
3956 char *name;
3957};
3958
3959static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3960
3961/*
3962 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3963 */
3964 static void
3965syn_list_one(id, syncing, link_only)
3966 int id;
3967 int syncing; /* when TRUE: list syncing items */
3968 int link_only; /* when TRUE; list link-only too */
3969{
3970 int attr;
3971 int idx;
3972 int did_header = FALSE;
3973 synpat_T *spp;
3974 static struct name_list namelist1[] =
3975 {
3976 {HL_DISPLAY, "display"},
3977 {HL_CONTAINED, "contained"},
3978 {HL_ONELINE, "oneline"},
3979 {HL_KEEPEND, "keepend"},
3980 {HL_EXTEND, "extend"},
3981 {HL_EXCLUDENL, "excludenl"},
3982 {HL_TRANSP, "transparent"},
3983 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003984#ifdef FEAT_CONCEAL
3985 {HL_CONCEAL, "conceal"},
3986 {HL_CONCEALENDS, "concealends"},
3987#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003988 {0, NULL}
3989 };
3990 static struct name_list namelist2[] =
3991 {
3992 {HL_SKIPWHITE, "skipwhite"},
3993 {HL_SKIPNL, "skipnl"},
3994 {HL_SKIPEMPTY, "skipempty"},
3995 {0, NULL}
3996 };
3997
3998 attr = hl_attr(HLF_D); /* highlight like directories */
3999
4000 /* list the keywords for "id" */
4001 if (!syncing)
4002 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004003 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4004 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004005 did_header, attr);
4006 }
4007
4008 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004009 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004010 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004011 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004012 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4013 continue;
4014
4015 (void)syn_list_header(did_header, 999, id);
4016 did_header = TRUE;
4017 last_matchgroup = 0;
4018 if (spp->sp_type == SPTYPE_MATCH)
4019 {
4020 put_pattern("match", ' ', spp, attr);
4021 msg_putchar(' ');
4022 }
4023 else if (spp->sp_type == SPTYPE_START)
4024 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004025 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4026 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4027 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4028 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4029 while (idx < curwin->w_s->b_syn_patterns.ga_len
4030 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4031 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004032 --idx;
4033 msg_putchar(' ');
4034 }
4035 syn_list_flags(namelist1, spp->sp_flags, attr);
4036
4037 if (spp->sp_cont_list != NULL)
4038 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4039
4040 if (spp->sp_syn.cont_in_list != NULL)
4041 put_id_list((char_u *)"containedin",
4042 spp->sp_syn.cont_in_list, attr);
4043
4044 if (spp->sp_next_list != NULL)
4045 {
4046 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4047 syn_list_flags(namelist2, spp->sp_flags, attr);
4048 }
4049 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4050 {
4051 if (spp->sp_flags & HL_SYNC_HERE)
4052 msg_puts_attr((char_u *)"grouphere", attr);
4053 else
4054 msg_puts_attr((char_u *)"groupthere", attr);
4055 msg_putchar(' ');
4056 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004057 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004058 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4059 else
4060 MSG_PUTS("NONE");
4061 msg_putchar(' ');
4062 }
4063 }
4064
4065 /* list the link, if there is one */
4066 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4067 {
4068 (void)syn_list_header(did_header, 999, id);
4069 msg_puts_attr((char_u *)"links to", attr);
4070 msg_putchar(' ');
4071 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4072 }
4073}
4074
4075 static void
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004076syn_list_flags(nlist, flags, attr)
4077 struct name_list *nlist;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004078 int flags;
4079 int attr;
4080{
4081 int i;
4082
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004083 for (i = 0; nlist[i].flag != 0; ++i)
4084 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004085 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004086 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004087 msg_putchar(' ');
4088 }
4089}
4090
4091/*
4092 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4093 */
4094 static void
4095syn_list_cluster(id)
4096 int id;
4097{
4098 int endcol = 15;
4099
4100 /* slight hack: roughly duplicate the guts of syn_list_header() */
4101 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004102 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004103
4104 if (msg_col >= endcol) /* output at least one space */
4105 endcol = msg_col + 1;
4106 if (Columns <= endcol) /* avoid hang for tiny window */
4107 endcol = Columns - 1;
4108
4109 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004110 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004111 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004112 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004113 hl_attr(HLF_D));
4114 }
4115 else
4116 {
4117 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4118 msg_puts((char_u *)"=NONE");
4119 }
4120}
4121
4122 static void
4123put_id_list(name, list, attr)
4124 char_u *name;
4125 short *list;
4126 int attr;
4127{
4128 short *p;
4129
4130 msg_puts_attr(name, attr);
4131 msg_putchar('=');
4132 for (p = list; *p; ++p)
4133 {
4134 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4135 {
4136 if (p[1])
4137 MSG_PUTS("ALLBUT");
4138 else
4139 MSG_PUTS("ALL");
4140 }
4141 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4142 {
4143 MSG_PUTS("TOP");
4144 }
4145 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4146 {
4147 MSG_PUTS("CONTAINED");
4148 }
4149 else if (*p >= SYNID_CLUSTER)
4150 {
4151 short scl_id = *p - SYNID_CLUSTER;
4152
4153 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004154 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004155 }
4156 else
4157 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4158 if (p[1])
4159 msg_putchar(',');
4160 }
4161 msg_putchar(' ');
4162}
4163
4164 static void
4165put_pattern(s, c, spp, attr)
4166 char *s;
4167 int c;
4168 synpat_T *spp;
4169 int attr;
4170{
4171 long n;
4172 int mask;
4173 int first;
4174 static char *sepchars = "/+=-#@\"|'^&";
4175 int i;
4176
4177 /* May have to write "matchgroup=group" */
4178 if (last_matchgroup != spp->sp_syn_match_id)
4179 {
4180 last_matchgroup = spp->sp_syn_match_id;
4181 msg_puts_attr((char_u *)"matchgroup", attr);
4182 msg_putchar('=');
4183 if (last_matchgroup == 0)
4184 msg_outtrans((char_u *)"NONE");
4185 else
4186 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4187 msg_putchar(' ');
4188 }
4189
4190 /* output the name of the pattern and an '=' or ' ' */
4191 msg_puts_attr((char_u *)s, attr);
4192 msg_putchar(c);
4193
4194 /* output the pattern, in between a char that is not in the pattern */
4195 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4196 if (sepchars[++i] == NUL)
4197 {
4198 i = 0; /* no good char found, just use the first one */
4199 break;
4200 }
4201 msg_putchar(sepchars[i]);
4202 msg_outtrans(spp->sp_pattern);
4203 msg_putchar(sepchars[i]);
4204
4205 /* output any pattern options */
4206 first = TRUE;
4207 for (i = 0; i < SPO_COUNT; ++i)
4208 {
4209 mask = (1 << i);
4210 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4211 {
4212 if (!first)
4213 msg_putchar(','); /* separate with commas */
4214 msg_puts((char_u *)spo_name_tab[i]);
4215 n = spp->sp_offsets[i];
4216 if (i != SPO_LC_OFF)
4217 {
4218 if (spp->sp_off_flags & mask)
4219 msg_putchar('s');
4220 else
4221 msg_putchar('e');
4222 if (n > 0)
4223 msg_putchar('+');
4224 }
4225 if (n || i == SPO_LC_OFF)
4226 msg_outnum(n);
4227 first = FALSE;
4228 }
4229 }
4230 msg_putchar(' ');
4231}
4232
4233/*
4234 * List or clear the keywords for one syntax group.
4235 * Return TRUE if the header has been printed.
4236 */
4237 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004238syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004239 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004240 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004241 int did_header; /* header has already been printed */
4242 int attr;
4243{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004244 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004245 hashitem_T *hi;
4246 keyentry_T *kp;
4247 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004248 int prev_contained = 0;
4249 short *prev_next_list = NULL;
4250 short *prev_cont_in_list = NULL;
4251 int prev_skipnl = 0;
4252 int prev_skipwhite = 0;
4253 int prev_skipempty = 0;
4254
Bram Moolenaar071d4272004-06-13 20:20:40 +00004255 /*
4256 * Unfortunately, this list of keywords is not sorted on alphabet but on
4257 * hash value...
4258 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004259 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004260 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004261 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004262 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004263 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004264 --todo;
4265 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004267 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004268 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004269 if (prev_contained != (kp->flags & HL_CONTAINED)
4270 || prev_skipnl != (kp->flags & HL_SKIPNL)
4271 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4272 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4273 || prev_cont_in_list != kp->k_syn.cont_in_list
4274 || prev_next_list != kp->next_list)
4275 outlen = 9999;
4276 else
4277 outlen = (int)STRLEN(kp->keyword);
4278 /* output "contained" and "nextgroup" on each line */
4279 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004280 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004281 prev_contained = 0;
4282 prev_next_list = NULL;
4283 prev_cont_in_list = NULL;
4284 prev_skipnl = 0;
4285 prev_skipwhite = 0;
4286 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004287 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004288 did_header = TRUE;
4289 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004290 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004291 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004292 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004293 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004294 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004295 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004296 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004297 put_id_list((char_u *)"containedin",
4298 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004299 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004301 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004302 if (kp->next_list != prev_next_list)
4303 {
4304 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4305 msg_putchar(' ');
4306 prev_next_list = kp->next_list;
4307 if (kp->flags & HL_SKIPNL)
4308 {
4309 msg_puts_attr((char_u *)"skipnl", attr);
4310 msg_putchar(' ');
4311 prev_skipnl = (kp->flags & HL_SKIPNL);
4312 }
4313 if (kp->flags & HL_SKIPWHITE)
4314 {
4315 msg_puts_attr((char_u *)"skipwhite", attr);
4316 msg_putchar(' ');
4317 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4318 }
4319 if (kp->flags & HL_SKIPEMPTY)
4320 {
4321 msg_puts_attr((char_u *)"skipempty", attr);
4322 msg_putchar(' ');
4323 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4324 }
4325 }
4326 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004327 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004328 }
4329 }
4330 }
4331
4332 return did_header;
4333}
4334
4335 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004336syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004337 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004338 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004339{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004340 hashitem_T *hi;
4341 keyentry_T *kp;
4342 keyentry_T *kp_prev;
4343 keyentry_T *kp_next;
4344 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004345
Bram Moolenaardad6b692005-01-25 22:14:34 +00004346 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004347 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004348 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004349 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004350 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004351 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004352 --todo;
4353 kp_prev = NULL;
4354 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004355 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004356 if (kp->k_syn.id == id)
4357 {
4358 kp_next = kp->ke_next;
4359 if (kp_prev == NULL)
4360 {
4361 if (kp_next == NULL)
4362 hash_remove(ht, hi);
4363 else
4364 hi->hi_key = KE2HIKEY(kp_next);
4365 }
4366 else
4367 kp_prev->ke_next = kp_next;
4368 vim_free(kp->next_list);
4369 vim_free(kp->k_syn.cont_in_list);
4370 vim_free(kp);
4371 kp = kp_next;
4372 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004373 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004374 {
4375 kp_prev = kp;
4376 kp = kp->ke_next;
4377 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004378 }
4379 }
4380 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004381 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004382}
4383
4384/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004385 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004386 */
4387 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388clear_keywtab(ht)
4389 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004390{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004391 hashitem_T *hi;
4392 int todo;
4393 keyentry_T *kp;
4394 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004395
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004396 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004397 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004398 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004399 if (!HASHITEM_EMPTY(hi))
4400 {
4401 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004402 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004403 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004404 kp_next = kp->ke_next;
4405 vim_free(kp->next_list);
4406 vim_free(kp->k_syn.cont_in_list);
4407 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004408 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004409 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004410 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004411 hash_clear(ht);
4412 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004413}
4414
4415/*
4416 * Add a keyword to the list of keywords.
4417 */
4418 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004419add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004420 char_u *name; /* name of keyword */
4421 int id; /* group ID for this keyword */
4422 int flags; /* flags for this keyword */
4423 short *cont_in_list; /* containedin for this keyword */
4424 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004425 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004426{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004427 keyentry_T *kp;
4428 hashtab_T *ht;
4429 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004430 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004431 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004432 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004433
Bram Moolenaar860cae12010-06-05 23:22:07 +02004434 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004435 name_ic = str_foldcase(name, (int)STRLEN(name),
4436 name_folded, MAXKEYWLEN + 1);
4437 else
4438 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004439 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4440 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004441 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004442 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004443 kp->k_syn.id = id;
4444 kp->k_syn.inc_tag = current_syn_inc_tag;
4445 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004446 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004447 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004448 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004449 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004450 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004451
Bram Moolenaar860cae12010-06-05 23:22:07 +02004452 if (curwin->w_s->b_syn_ic)
4453 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004454 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004455 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004456
Bram Moolenaardad6b692005-01-25 22:14:34 +00004457 hash = hash_hash(kp->keyword);
4458 hi = hash_lookup(ht, kp->keyword, hash);
4459 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004460 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004461 /* new keyword, add to hashtable */
4462 kp->ke_next = NULL;
4463 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004464 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004465 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004466 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004467 /* keyword already exists, prepend to list */
4468 kp->ke_next = HI2KE(hi);
4469 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004470 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004471}
4472
4473/*
4474 * Get the start and end of the group name argument.
4475 * Return a pointer to the first argument.
4476 * Return NULL if the end of the command was found instead of further args.
4477 */
4478 static char_u *
4479get_group_name(arg, name_end)
4480 char_u *arg; /* start of the argument */
4481 char_u **name_end; /* pointer to end of the name */
4482{
4483 char_u *rest;
4484
4485 *name_end = skiptowhite(arg);
4486 rest = skipwhite(*name_end);
4487
4488 /*
4489 * Check if there are enough arguments. The first argument may be a
4490 * pattern, where '|' is allowed, so only check for NUL.
4491 */
4492 if (ends_excmd(*arg) || *rest == NUL)
4493 return NULL;
4494 return rest;
4495}
4496
4497/*
4498 * Check for syntax command option arguments.
4499 * This can be called at any place in the list of arguments, and just picks
4500 * out the arguments that are known. Can be called several times in a row to
4501 * collect all options in between other arguments.
4502 * Return a pointer to the next argument (which isn't an option).
4503 * Return NULL for any error;
4504 */
4505 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004506get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004507 char_u *arg; /* next argument to be checked */
4508 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004509 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004510{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004511 char_u *gname_start, *gname;
4512 int syn_id;
4513 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004514 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004515 int i;
4516 int fidx;
4517 static struct flag
4518 {
4519 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004520 int argtype;
4521 int flags;
4522 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4523 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4524 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4525 {"eExXtTeEnNdD", 0, HL_EXTEND},
4526 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4527 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4528 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4529 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4530 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4531 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4532 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4533 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4534 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004535 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4536 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4537 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004538 {"cCoOnNtTaAiInNsS", 1, 0},
4539 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4540 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004541 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004542 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004543
4544 if (arg == NULL) /* already detected error */
4545 return NULL;
4546
Bram Moolenaar860cae12010-06-05 23:22:07 +02004547#ifdef FEAT_CONCEAL
4548 if (curwin->w_s->b_syn_conceal)
4549 opt->flags |= HL_CONCEAL;
4550#endif
4551
Bram Moolenaar071d4272004-06-13 20:20:40 +00004552 for (;;)
4553 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004554 /*
4555 * This is used very often when a large number of keywords is defined.
4556 * Need to skip quickly when no option name is found.
4557 * Also avoid tolower(), it's slow.
4558 */
4559 if (strchr(first_letters, *arg) == NULL)
4560 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004561
4562 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4563 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004564 p = flagtab[fidx].name;
4565 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4566 if (arg[len] != p[i] && arg[len] != p[i + 1])
4567 break;
4568 if (p[i] == NUL && (vim_iswhite(arg[len])
4569 || (flagtab[fidx].argtype > 0
4570 ? arg[len] == '='
4571 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004572 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004573 if (opt->keyword
4574 && (flagtab[fidx].flags == HL_DISPLAY
4575 || flagtab[fidx].flags == HL_FOLD
4576 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004577 /* treat "display", "fold" and "extend" as a keyword */
4578 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004579 break;
4580 }
4581 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004582 if (fidx < 0) /* no match found */
4583 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004584
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004585 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004586 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004587 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004588 {
4589 EMSG(_("E395: contains argument not accepted here"));
4590 return NULL;
4591 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004592 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004593 return NULL;
4594 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004595 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004596 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004597 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004598 return NULL;
4599 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004600 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004601 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004602 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004603 return NULL;
4604 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004605 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4606 {
4607#ifdef FEAT_MBYTE
4608 /* cchar=? */
4609 if (has_mbyte)
4610 {
4611# ifdef FEAT_CONCEAL
4612 *conceal_char = mb_ptr2char(arg + 6);
4613# endif
4614 arg += mb_ptr2len(arg + 6) - 1;
4615 }
4616 else
4617#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004618 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004619#ifdef FEAT_CONCEAL
4620 *conceal_char = arg[6];
4621#else
4622 ;
4623#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004624 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004625#ifdef FEAT_CONCEAL
4626 if (!vim_isprintc_strict(*conceal_char))
4627 {
4628 EMSG(_("E844: invalid cchar value"));
4629 return NULL;
4630 }
4631#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004632 arg = skipwhite(arg + 7);
4633 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004634 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004635 {
4636 opt->flags |= flagtab[fidx].flags;
4637 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004638
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004639 if (flagtab[fidx].flags == HL_SYNC_HERE
4640 || flagtab[fidx].flags == HL_SYNC_THERE)
4641 {
4642 if (opt->sync_idx == NULL)
4643 {
4644 EMSG(_("E393: group[t]here not accepted here"));
4645 return NULL;
4646 }
4647 gname_start = arg;
4648 arg = skiptowhite(arg);
4649 if (gname_start == arg)
4650 return NULL;
4651 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4652 if (gname == NULL)
4653 return NULL;
4654 if (STRCMP(gname, "NONE") == 0)
4655 *opt->sync_idx = NONE_IDX;
4656 else
4657 {
4658 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004659 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4660 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4661 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004662 {
4663 *opt->sync_idx = i;
4664 break;
4665 }
4666 if (i < 0)
4667 {
4668 EMSG2(_("E394: Didn't find region item for %s"), gname);
4669 vim_free(gname);
4670 return NULL;
4671 }
4672 }
4673
4674 vim_free(gname);
4675 arg = skipwhite(arg);
4676 }
4677#ifdef FEAT_FOLDING
4678 else if (flagtab[fidx].flags == HL_FOLD
4679 && foldmethodIsSyntax(curwin))
4680 /* Need to update folds later. */
4681 foldUpdateAll(curwin);
4682#endif
4683 }
4684 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004685
4686 return arg;
4687}
4688
4689/*
4690 * Adjustments to syntax item when declared in a ":syn include"'d file.
4691 * Set the contained flag, and if the item is not already contained, add it
4692 * to the specified top-level group, if any.
4693 */
4694 static void
4695syn_incl_toplevel(id, flagsp)
4696 int id;
4697 int *flagsp;
4698{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004699 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004700 return;
4701 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004702 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703 {
4704 /* We have to alloc this, because syn_combine_list() will free it. */
4705 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004706 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004707
4708 if (grp_list != NULL)
4709 {
4710 grp_list[0] = id;
4711 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004712 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004713 CLUSTER_ADD);
4714 }
4715 }
4716}
4717
4718/*
4719 * Handle ":syntax include [@{group-name}] filename" command.
4720 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004721 static void
4722syn_cmd_include(eap, syncing)
4723 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004724 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004725{
4726 char_u *arg = eap->arg;
4727 int sgl_id = 1;
4728 char_u *group_name_end;
4729 char_u *rest;
4730 char_u *errormsg = NULL;
4731 int prev_toplvl_grp;
4732 int prev_syn_inc_tag;
4733 int source = FALSE;
4734
4735 eap->nextcmd = find_nextcmd(arg);
4736 if (eap->skip)
4737 return;
4738
4739 if (arg[0] == '@')
4740 {
4741 ++arg;
4742 rest = get_group_name(arg, &group_name_end);
4743 if (rest == NULL)
4744 {
4745 EMSG((char_u *)_("E397: Filename required"));
4746 return;
4747 }
4748 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004749 if (sgl_id == 0)
4750 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004751 /* separate_nextcmd() and expand_filename() depend on this */
4752 eap->arg = rest;
4753 }
4754
4755 /*
4756 * Everything that's left, up to the next command, should be the
4757 * filename to include.
4758 */
4759 eap->argt |= (XFILE | NOSPC);
4760 separate_nextcmd(eap);
4761 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4762 {
4763 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4764 * file. Need to expand the file name first. In other cases
4765 * ":runtime!" is used. */
4766 source = TRUE;
4767 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4768 {
4769 if (errormsg != NULL)
4770 EMSG(errormsg);
4771 return;
4772 }
4773 }
4774
4775 /*
4776 * Save and restore the existing top-level grouplist id and ":syn
4777 * include" tag around the actual inclusion.
4778 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004779 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4780 {
4781 EMSG((char_u *)_("E847: Too many syntax includes"));
4782 return;
4783 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004784 prev_syn_inc_tag = current_syn_inc_tag;
4785 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004786 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4787 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004788 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4789 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004790 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004791 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004792 current_syn_inc_tag = prev_syn_inc_tag;
4793}
4794
4795/*
4796 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4797 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004798 static void
4799syn_cmd_keyword(eap, syncing)
4800 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004801 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004802{
4803 char_u *arg = eap->arg;
4804 char_u *group_name_end;
4805 int syn_id;
4806 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004807 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004808 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004809 char_u *kw;
4810 syn_opt_arg_T syn_opt_arg;
4811 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004812 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004813
4814 rest = get_group_name(arg, &group_name_end);
4815
4816 if (rest != NULL)
4817 {
4818 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004819 if (syn_id != 0)
4820 /* allocate a buffer, for removing backslashes in the keyword */
4821 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004822 if (keyword_copy != NULL)
4823 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004824 syn_opt_arg.flags = 0;
4825 syn_opt_arg.keyword = TRUE;
4826 syn_opt_arg.sync_idx = NULL;
4827 syn_opt_arg.has_cont_list = FALSE;
4828 syn_opt_arg.cont_in_list = NULL;
4829 syn_opt_arg.next_list = NULL;
4830
Bram Moolenaar071d4272004-06-13 20:20:40 +00004831 /*
4832 * The options given apply to ALL keywords, so all options must be
4833 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004834 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004835 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004836 cnt = 0;
4837 p = keyword_copy;
4838 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004839 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004840 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004841 if (rest == NULL || ends_excmd(*rest))
4842 break;
4843 /* Copy the keyword, removing backslashes, and add a NUL. */
4844 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004845 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004846 if (*rest == '\\' && rest[1] != NUL)
4847 ++rest;
4848 *p++ = *rest++;
4849 }
4850 *p++ = NUL;
4851 ++cnt;
4852 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004853
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004854 if (!eap->skip)
4855 {
4856 /* Adjust flags for use of ":syn include". */
4857 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4858
4859 /*
4860 * 2: Add an entry for each keyword.
4861 */
4862 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4863 {
4864 for (p = vim_strchr(kw, '['); ; )
4865 {
4866 if (p != NULL)
4867 *p = NUL;
4868 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004869 syn_opt_arg.cont_in_list,
4870 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004871 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004872 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004873 if (p[1] == NUL)
4874 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004875 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004876 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004877 }
4878 if (p[1] == ']')
4879 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004880 if (p[2] != NUL)
4881 {
4882 EMSG3(_("E890: trailing char after ']': %s]%s"),
4883 kw, &p[2]);
4884 goto error;
4885 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004886 kw = p + 1; /* skip over the "]" */
4887 break;
4888 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004889#ifdef FEAT_MBYTE
4890 if (has_mbyte)
4891 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004892 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004893
4894 mch_memmove(p, p + 1, l);
4895 p += l;
4896 }
4897 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004898#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004899 {
4900 p[0] = p[1];
4901 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004902 }
4903 }
4904 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004906error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004907 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004908 vim_free(syn_opt_arg.cont_in_list);
4909 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004910 }
4911 }
4912
4913 if (rest != NULL)
4914 eap->nextcmd = check_nextcmd(rest);
4915 else
4916 EMSG2(_(e_invarg2), arg);
4917
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004918 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004919 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004920}
4921
4922/*
4923 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4924 *
4925 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4926 */
4927 static void
4928syn_cmd_match(eap, syncing)
4929 exarg_T *eap;
4930 int syncing; /* TRUE for ":syntax sync match .. " */
4931{
4932 char_u *arg = eap->arg;
4933 char_u *group_name_end;
4934 char_u *rest;
4935 synpat_T item; /* the item found in the line */
4936 int syn_id;
4937 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004938 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004939 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004940 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004941
4942 /* Isolate the group name, check for validity */
4943 rest = get_group_name(arg, &group_name_end);
4944
4945 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004946 syn_opt_arg.flags = 0;
4947 syn_opt_arg.keyword = FALSE;
4948 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4949 syn_opt_arg.has_cont_list = TRUE;
4950 syn_opt_arg.cont_list = NULL;
4951 syn_opt_arg.cont_in_list = NULL;
4952 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004953 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004954
4955 /* get the pattern. */
4956 init_syn_patterns();
4957 vim_memset(&item, 0, sizeof(item));
4958 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004959 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4960 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004961
4962 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004963 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004964
4965 if (rest != NULL) /* all arguments are valid */
4966 {
4967 /*
4968 * Check for trailing command and illegal trailing arguments.
4969 */
4970 eap->nextcmd = check_nextcmd(rest);
4971 if (!ends_excmd(*rest) || eap->skip)
4972 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004973 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004974 && (syn_id = syn_check_group(arg,
4975 (int)(group_name_end - arg))) != 0)
4976 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004977 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004978 /*
4979 * Store the pattern in the syn_items list
4980 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004981 idx = curwin->w_s->b_syn_patterns.ga_len;
4982 SYN_ITEMS(curwin->w_s)[idx] = item;
4983 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4984 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4985 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4986 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4987 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4988 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4989 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4990 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004991 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004992#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004993 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004994#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004995 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004996 curwin->w_s->b_syn_containedin = TRUE;
4997 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
4998 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004999
5000 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005001 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005002 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005003#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005004 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005005 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006#endif
5007
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005008 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005009 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005010 return; /* don't free the progs and patterns now */
5011 }
5012 }
5013
5014 /*
5015 * Something failed, free the allocated memory.
5016 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005017 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005018 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005019 vim_free(syn_opt_arg.cont_list);
5020 vim_free(syn_opt_arg.cont_in_list);
5021 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005022
5023 if (rest == NULL)
5024 EMSG2(_(e_invarg2), arg);
5025}
5026
5027/*
5028 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5029 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5030 */
5031 static void
5032syn_cmd_region(eap, syncing)
5033 exarg_T *eap;
5034 int syncing; /* TRUE for ":syntax sync region .." */
5035{
5036 char_u *arg = eap->arg;
5037 char_u *group_name_end;
5038 char_u *rest; /* next arg, NULL on error */
5039 char_u *key_end;
5040 char_u *key = NULL;
5041 char_u *p;
5042 int item;
5043#define ITEM_START 0
5044#define ITEM_SKIP 1
5045#define ITEM_END 2
5046#define ITEM_MATCHGROUP 3
5047 struct pat_ptr
5048 {
5049 synpat_T *pp_synp; /* pointer to syn_pattern */
5050 int pp_matchgroup_id; /* matchgroup ID */
5051 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5052 } *(pat_ptrs[3]);
5053 /* patterns found in the line */
5054 struct pat_ptr *ppp;
5055 struct pat_ptr *ppp_next;
5056 int pat_count = 0; /* nr of syn_patterns found */
5057 int syn_id;
5058 int matchgroup_id = 0;
5059 int not_enough = FALSE; /* not enough arguments */
5060 int illegal = FALSE; /* illegal arguments */
5061 int success = FALSE;
5062 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005063 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005064 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005065
5066 /* Isolate the group name, check for validity */
5067 rest = get_group_name(arg, &group_name_end);
5068
5069 pat_ptrs[0] = NULL;
5070 pat_ptrs[1] = NULL;
5071 pat_ptrs[2] = NULL;
5072
5073 init_syn_patterns();
5074
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005075 syn_opt_arg.flags = 0;
5076 syn_opt_arg.keyword = FALSE;
5077 syn_opt_arg.sync_idx = NULL;
5078 syn_opt_arg.has_cont_list = TRUE;
5079 syn_opt_arg.cont_list = NULL;
5080 syn_opt_arg.cont_in_list = NULL;
5081 syn_opt_arg.next_list = NULL;
5082
Bram Moolenaar071d4272004-06-13 20:20:40 +00005083 /*
5084 * get the options, patterns and matchgroup.
5085 */
5086 while (rest != NULL && !ends_excmd(*rest))
5087 {
5088 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005089 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005090 if (rest == NULL || ends_excmd(*rest))
5091 break;
5092
5093 /* must be a pattern or matchgroup then */
5094 key_end = rest;
5095 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5096 ++key_end;
5097 vim_free(key);
5098 key = vim_strnsave_up(rest, (int)(key_end - rest));
5099 if (key == NULL) /* out of memory */
5100 {
5101 rest = NULL;
5102 break;
5103 }
5104 if (STRCMP(key, "MATCHGROUP") == 0)
5105 item = ITEM_MATCHGROUP;
5106 else if (STRCMP(key, "START") == 0)
5107 item = ITEM_START;
5108 else if (STRCMP(key, "END") == 0)
5109 item = ITEM_END;
5110 else if (STRCMP(key, "SKIP") == 0)
5111 {
5112 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5113 {
5114 illegal = TRUE;
5115 break;
5116 }
5117 item = ITEM_SKIP;
5118 }
5119 else
5120 break;
5121 rest = skipwhite(key_end);
5122 if (*rest != '=')
5123 {
5124 rest = NULL;
5125 EMSG2(_("E398: Missing '=': %s"), arg);
5126 break;
5127 }
5128 rest = skipwhite(rest + 1);
5129 if (*rest == NUL)
5130 {
5131 not_enough = TRUE;
5132 break;
5133 }
5134
5135 if (item == ITEM_MATCHGROUP)
5136 {
5137 p = skiptowhite(rest);
5138 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5139 matchgroup_id = 0;
5140 else
5141 {
5142 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5143 if (matchgroup_id == 0)
5144 {
5145 illegal = TRUE;
5146 break;
5147 }
5148 }
5149 rest = skipwhite(p);
5150 }
5151 else
5152 {
5153 /*
5154 * Allocate room for a syn_pattern, and link it in the list of
5155 * syn_patterns for this item, at the start (because the list is
5156 * used from end to start).
5157 */
5158 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5159 if (ppp == NULL)
5160 {
5161 rest = NULL;
5162 break;
5163 }
5164 ppp->pp_next = pat_ptrs[item];
5165 pat_ptrs[item] = ppp;
5166 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5167 if (ppp->pp_synp == NULL)
5168 {
5169 rest = NULL;
5170 break;
5171 }
5172
5173 /*
5174 * Get the syntax pattern and the following offset(s).
5175 */
5176 /* Enable the appropriate \z specials. */
5177 if (item == ITEM_START)
5178 reg_do_extmatch = REX_SET;
5179 else if (item == ITEM_SKIP || item == ITEM_END)
5180 reg_do_extmatch = REX_USE;
5181 rest = get_syn_pattern(rest, ppp->pp_synp);
5182 reg_do_extmatch = 0;
5183 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005184 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005185 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5186 ppp->pp_matchgroup_id = matchgroup_id;
5187 ++pat_count;
5188 }
5189 }
5190 vim_free(key);
5191 if (illegal || not_enough)
5192 rest = NULL;
5193
5194 /*
5195 * Must have a "start" and "end" pattern.
5196 */
5197 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5198 pat_ptrs[ITEM_END] == NULL))
5199 {
5200 not_enough = TRUE;
5201 rest = NULL;
5202 }
5203
5204 if (rest != NULL)
5205 {
5206 /*
5207 * Check for trailing garbage or command.
5208 * If OK, add the item.
5209 */
5210 eap->nextcmd = check_nextcmd(rest);
5211 if (!ends_excmd(*rest) || eap->skip)
5212 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005213 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005214 && (syn_id = syn_check_group(arg,
5215 (int)(group_name_end - arg))) != 0)
5216 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005217 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005218 /*
5219 * Store the start/skip/end in the syn_items list
5220 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005221 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005222 for (item = ITEM_START; item <= ITEM_END; ++item)
5223 {
5224 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5225 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005226 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5227 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5228 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005229 (item == ITEM_START) ? SPTYPE_START :
5230 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005231 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5232 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005233 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5234 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005235 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005236 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005237#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005238 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005239#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005240 if (item == ITEM_START)
5241 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005242 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005243 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005244 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005245 syn_opt_arg.cont_in_list;
5246 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005247 curwin->w_s->b_syn_containedin = TRUE;
5248 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005249 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005250 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005251 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005252 ++idx;
5253#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005254 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005255 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005256#endif
5257 }
5258 }
5259
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005260 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005261 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005262 success = TRUE; /* don't free the progs and patterns now */
5263 }
5264 }
5265
5266 /*
5267 * Free the allocated memory.
5268 */
5269 for (item = ITEM_START; item <= ITEM_END; ++item)
5270 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5271 {
5272 if (!success)
5273 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005274 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005275 vim_free(ppp->pp_synp->sp_pattern);
5276 }
5277 vim_free(ppp->pp_synp);
5278 ppp_next = ppp->pp_next;
5279 vim_free(ppp);
5280 }
5281
5282 if (!success)
5283 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005284 vim_free(syn_opt_arg.cont_list);
5285 vim_free(syn_opt_arg.cont_in_list);
5286 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005287 if (not_enough)
5288 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5289 else if (illegal || rest == NULL)
5290 EMSG2(_(e_invarg2), arg);
5291 }
5292}
5293
5294/*
5295 * A simple syntax group ID comparison function suitable for use in qsort()
5296 */
5297 static int
5298#ifdef __BORLANDC__
5299_RTLENTRYF
5300#endif
5301syn_compare_stub(v1, v2)
5302 const void *v1;
5303 const void *v2;
5304{
5305 const short *s1 = v1;
5306 const short *s2 = v2;
5307
5308 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5309}
5310
5311/*
5312 * Combines lists of syntax clusters.
5313 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5314 */
5315 static void
5316syn_combine_list(clstr1, clstr2, list_op)
5317 short **clstr1;
5318 short **clstr2;
5319 int list_op;
5320{
5321 int count1 = 0;
5322 int count2 = 0;
5323 short *g1;
5324 short *g2;
5325 short *clstr = NULL;
5326 int count;
5327 int round;
5328
5329 /*
5330 * Handle degenerate cases.
5331 */
5332 if (*clstr2 == NULL)
5333 return;
5334 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5335 {
5336 if (list_op == CLUSTER_REPLACE)
5337 vim_free(*clstr1);
5338 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5339 *clstr1 = *clstr2;
5340 else
5341 vim_free(*clstr2);
5342 return;
5343 }
5344
5345 for (g1 = *clstr1; *g1; g1++)
5346 ++count1;
5347 for (g2 = *clstr2; *g2; g2++)
5348 ++count2;
5349
5350 /*
5351 * For speed purposes, sort both lists.
5352 */
5353 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5354 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5355
5356 /*
5357 * We proceed in two passes; in round 1, we count the elements to place
5358 * in the new list, and in round 2, we allocate and populate the new
5359 * list. For speed, we use a mergesort-like method, adding the smaller
5360 * of the current elements in each list to the new list.
5361 */
5362 for (round = 1; round <= 2; round++)
5363 {
5364 g1 = *clstr1;
5365 g2 = *clstr2;
5366 count = 0;
5367
5368 /*
5369 * First, loop through the lists until one of them is empty.
5370 */
5371 while (*g1 && *g2)
5372 {
5373 /*
5374 * We always want to add from the first list.
5375 */
5376 if (*g1 < *g2)
5377 {
5378 if (round == 2)
5379 clstr[count] = *g1;
5380 count++;
5381 g1++;
5382 continue;
5383 }
5384 /*
5385 * We only want to add from the second list if we're adding the
5386 * lists.
5387 */
5388 if (list_op == CLUSTER_ADD)
5389 {
5390 if (round == 2)
5391 clstr[count] = *g2;
5392 count++;
5393 }
5394 if (*g1 == *g2)
5395 g1++;
5396 g2++;
5397 }
5398
5399 /*
5400 * Now add the leftovers from whichever list didn't get finished
5401 * first. As before, we only want to add from the second list if
5402 * we're adding the lists.
5403 */
5404 for (; *g1; g1++, count++)
5405 if (round == 2)
5406 clstr[count] = *g1;
5407 if (list_op == CLUSTER_ADD)
5408 for (; *g2; g2++, count++)
5409 if (round == 2)
5410 clstr[count] = *g2;
5411
5412 if (round == 1)
5413 {
5414 /*
5415 * If the group ended up empty, we don't need to allocate any
5416 * space for it.
5417 */
5418 if (count == 0)
5419 {
5420 clstr = NULL;
5421 break;
5422 }
5423 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5424 if (clstr == NULL)
5425 break;
5426 clstr[count] = 0;
5427 }
5428 }
5429
5430 /*
5431 * Finally, put the new list in place.
5432 */
5433 vim_free(*clstr1);
5434 vim_free(*clstr2);
5435 *clstr1 = clstr;
5436}
5437
5438/*
5439 * Lookup a syntax cluster name and return it's ID.
5440 * If it is not found, 0 is returned.
5441 */
5442 static int
5443syn_scl_name2id(name)
5444 char_u *name;
5445{
5446 int i;
5447 char_u *name_u;
5448
5449 /* Avoid using stricmp() too much, it's slow on some systems */
5450 name_u = vim_strsave_up(name);
5451 if (name_u == NULL)
5452 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005453 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5454 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5455 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005456 break;
5457 vim_free(name_u);
5458 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5459}
5460
5461/*
5462 * Like syn_scl_name2id(), but take a pointer + length argument.
5463 */
5464 static int
5465syn_scl_namen2id(linep, len)
5466 char_u *linep;
5467 int len;
5468{
5469 char_u *name;
5470 int id = 0;
5471
5472 name = vim_strnsave(linep, len);
5473 if (name != NULL)
5474 {
5475 id = syn_scl_name2id(name);
5476 vim_free(name);
5477 }
5478 return id;
5479}
5480
5481/*
5482 * Find syntax cluster name in the table and return it's ID.
5483 * The argument is a pointer to the name and the length of the name.
5484 * If it doesn't exist yet, a new entry is created.
5485 * Return 0 for failure.
5486 */
5487 static int
5488syn_check_cluster(pp, len)
5489 char_u *pp;
5490 int len;
5491{
5492 int id;
5493 char_u *name;
5494
5495 name = vim_strnsave(pp, len);
5496 if (name == NULL)
5497 return 0;
5498
5499 id = syn_scl_name2id(name);
5500 if (id == 0) /* doesn't exist yet */
5501 id = syn_add_cluster(name);
5502 else
5503 vim_free(name);
5504 return id;
5505}
5506
5507/*
5508 * Add new syntax cluster and return it's ID.
5509 * "name" must be an allocated string, it will be consumed.
5510 * Return 0 for failure.
5511 */
5512 static int
5513syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005514 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005515{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005516 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005517
5518 /*
5519 * First call for this growarray: init growing array.
5520 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005521 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005522 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005523 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5524 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005525 }
5526
Bram Moolenaar42431a72011-04-01 14:44:59 +02005527 len = curwin->w_s->b_syn_clusters.ga_len;
5528 if (len >= MAX_CLUSTER_ID)
5529 {
5530 EMSG((char_u *)_("E848: Too many syntax clusters"));
5531 vim_free(name);
5532 return 0;
5533 }
5534
Bram Moolenaar071d4272004-06-13 20:20:40 +00005535 /*
5536 * Make room for at least one other cluster entry.
5537 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005538 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005539 {
5540 vim_free(name);
5541 return 0;
5542 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005543
Bram Moolenaar860cae12010-06-05 23:22:07 +02005544 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5545 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5546 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5547 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5548 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005549
Bram Moolenaar217ad922005-03-20 22:37:15 +00005550 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005551 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005552 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005553 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005554
Bram Moolenaar071d4272004-06-13 20:20:40 +00005555 return len + SYNID_CLUSTER;
5556}
5557
5558/*
5559 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5560 * [add={groupname},..] [remove={groupname},..]".
5561 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005562 static void
5563syn_cmd_cluster(eap, syncing)
5564 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005565 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005566{
5567 char_u *arg = eap->arg;
5568 char_u *group_name_end;
5569 char_u *rest;
5570 int scl_id;
5571 short *clstr_list;
5572 int got_clstr = FALSE;
5573 int opt_len;
5574 int list_op;
5575
5576 eap->nextcmd = find_nextcmd(arg);
5577 if (eap->skip)
5578 return;
5579
5580 rest = get_group_name(arg, &group_name_end);
5581
5582 if (rest != NULL)
5583 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005584 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5585 if (scl_id == 0)
5586 return;
5587 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005588
5589 for (;;)
5590 {
5591 if (STRNICMP(rest, "add", 3) == 0
5592 && (vim_iswhite(rest[3]) || rest[3] == '='))
5593 {
5594 opt_len = 3;
5595 list_op = CLUSTER_ADD;
5596 }
5597 else if (STRNICMP(rest, "remove", 6) == 0
5598 && (vim_iswhite(rest[6]) || rest[6] == '='))
5599 {
5600 opt_len = 6;
5601 list_op = CLUSTER_SUBTRACT;
5602 }
5603 else if (STRNICMP(rest, "contains", 8) == 0
5604 && (vim_iswhite(rest[8]) || rest[8] == '='))
5605 {
5606 opt_len = 8;
5607 list_op = CLUSTER_REPLACE;
5608 }
5609 else
5610 break;
5611
5612 clstr_list = NULL;
5613 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5614 {
5615 EMSG2(_(e_invarg2), rest);
5616 break;
5617 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005618 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005619 &clstr_list, list_op);
5620 got_clstr = TRUE;
5621 }
5622
5623 if (got_clstr)
5624 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005625 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005626 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005627 }
5628 }
5629
5630 if (!got_clstr)
5631 EMSG(_("E400: No cluster specified"));
5632 if (rest == NULL || !ends_excmd(*rest))
5633 EMSG2(_(e_invarg2), arg);
5634}
5635
5636/*
5637 * On first call for current buffer: Init growing array.
5638 */
5639 static void
5640init_syn_patterns()
5641{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005642 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5643 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005644}
5645
5646/*
5647 * Get one pattern for a ":syntax match" or ":syntax region" command.
5648 * Stores the pattern and program in a synpat_T.
5649 * Returns a pointer to the next argument, or NULL in case of an error.
5650 */
5651 static char_u *
5652get_syn_pattern(arg, ci)
5653 char_u *arg;
5654 synpat_T *ci;
5655{
5656 char_u *end;
5657 int *p;
5658 int idx;
5659 char_u *cpo_save;
5660
5661 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005662 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005663 return NULL;
5664
5665 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5666 if (*end != *arg) /* end delimiter not found */
5667 {
5668 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5669 return NULL;
5670 }
5671 /* store the pattern and compiled regexp program */
5672 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5673 return NULL;
5674
5675 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5676 cpo_save = p_cpo;
5677 p_cpo = (char_u *)"";
5678 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5679 p_cpo = cpo_save;
5680
5681 if (ci->sp_prog == NULL)
5682 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005683 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005684#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005685 syn_clear_time(&ci->sp_time);
5686#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005687
5688 /*
5689 * Check for a match, highlight or region offset.
5690 */
5691 ++end;
5692 do
5693 {
5694 for (idx = SPO_COUNT; --idx >= 0; )
5695 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5696 break;
5697 if (idx >= 0)
5698 {
5699 p = &(ci->sp_offsets[idx]);
5700 if (idx != SPO_LC_OFF)
5701 switch (end[3])
5702 {
5703 case 's': break;
5704 case 'b': break;
5705 case 'e': idx += SPO_COUNT; break;
5706 default: idx = -1; break;
5707 }
5708 if (idx >= 0)
5709 {
5710 ci->sp_off_flags |= (1 << idx);
5711 if (idx == SPO_LC_OFF) /* lc=99 */
5712 {
5713 end += 3;
5714 *p = getdigits(&end);
5715
5716 /* "lc=" offset automatically sets "ms=" offset */
5717 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5718 {
5719 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5720 ci->sp_offsets[SPO_MS_OFF] = *p;
5721 }
5722 }
5723 else /* yy=x+99 */
5724 {
5725 end += 4;
5726 if (*end == '+')
5727 {
5728 ++end;
5729 *p = getdigits(&end); /* positive offset */
5730 }
5731 else if (*end == '-')
5732 {
5733 ++end;
5734 *p = -getdigits(&end); /* negative offset */
5735 }
5736 }
5737 if (*end != ',')
5738 break;
5739 ++end;
5740 }
5741 }
5742 } while (idx >= 0);
5743
5744 if (!ends_excmd(*end) && !vim_iswhite(*end))
5745 {
5746 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5747 return NULL;
5748 }
5749 return skipwhite(end);
5750}
5751
5752/*
5753 * Handle ":syntax sync .." command.
5754 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005755 static void
5756syn_cmd_sync(eap, syncing)
5757 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005758 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005759{
5760 char_u *arg_start = eap->arg;
5761 char_u *arg_end;
5762 char_u *key = NULL;
5763 char_u *next_arg;
5764 int illegal = FALSE;
5765 int finished = FALSE;
5766 long n;
5767 char_u *cpo_save;
5768
5769 if (ends_excmd(*arg_start))
5770 {
5771 syn_cmd_list(eap, TRUE);
5772 return;
5773 }
5774
5775 while (!ends_excmd(*arg_start))
5776 {
5777 arg_end = skiptowhite(arg_start);
5778 next_arg = skipwhite(arg_end);
5779 vim_free(key);
5780 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5781 if (STRCMP(key, "CCOMMENT") == 0)
5782 {
5783 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005784 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005785 if (!ends_excmd(*next_arg))
5786 {
5787 arg_end = skiptowhite(next_arg);
5788 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005789 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005790 (int)(arg_end - next_arg));
5791 next_arg = skipwhite(arg_end);
5792 }
5793 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005794 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005795 }
5796 else if ( STRNCMP(key, "LINES", 5) == 0
5797 || STRNCMP(key, "MINLINES", 8) == 0
5798 || STRNCMP(key, "MAXLINES", 8) == 0
5799 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5800 {
5801 if (key[4] == 'S')
5802 arg_end = key + 6;
5803 else if (key[0] == 'L')
5804 arg_end = key + 11;
5805 else
5806 arg_end = key + 9;
5807 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5808 {
5809 illegal = TRUE;
5810 break;
5811 }
5812 n = getdigits(&arg_end);
5813 if (!eap->skip)
5814 {
5815 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005816 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005817 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005818 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005819 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005820 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005821 }
5822 }
5823 else if (STRCMP(key, "FROMSTART") == 0)
5824 {
5825 if (!eap->skip)
5826 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005827 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5828 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005829 }
5830 }
5831 else if (STRCMP(key, "LINECONT") == 0)
5832 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005833 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005834 {
5835 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5836 finished = TRUE;
5837 break;
5838 }
5839 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5840 if (*arg_end != *next_arg) /* end delimiter not found */
5841 {
5842 illegal = TRUE;
5843 break;
5844 }
5845
5846 if (!eap->skip)
5847 {
5848 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005849 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005850 (int)(arg_end - next_arg - 1))) == NULL)
5851 {
5852 finished = TRUE;
5853 break;
5854 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005855 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005856
5857 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5858 cpo_save = p_cpo;
5859 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005860 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005861 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005862 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005863#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005864 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5865#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005866
Bram Moolenaar860cae12010-06-05 23:22:07 +02005867 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005868 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005869 vim_free(curwin->w_s->b_syn_linecont_pat);
5870 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005871 finished = TRUE;
5872 break;
5873 }
5874 }
5875 next_arg = skipwhite(arg_end + 1);
5876 }
5877 else
5878 {
5879 eap->arg = next_arg;
5880 if (STRCMP(key, "MATCH") == 0)
5881 syn_cmd_match(eap, TRUE);
5882 else if (STRCMP(key, "REGION") == 0)
5883 syn_cmd_region(eap, TRUE);
5884 else if (STRCMP(key, "CLEAR") == 0)
5885 syn_cmd_clear(eap, TRUE);
5886 else
5887 illegal = TRUE;
5888 finished = TRUE;
5889 break;
5890 }
5891 arg_start = next_arg;
5892 }
5893 vim_free(key);
5894 if (illegal)
5895 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5896 else if (!finished)
5897 {
5898 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005899 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005900 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005901 }
5902}
5903
5904/*
5905 * Convert a line of highlight group names into a list of group ID numbers.
5906 * "arg" should point to the "contains" or "nextgroup" keyword.
5907 * "arg" is advanced to after the last group name.
5908 * Careful: the argument is modified (NULs added).
5909 * returns FAIL for some error, OK for success.
5910 */
5911 static int
5912get_id_list(arg, keylen, list)
5913 char_u **arg;
5914 int keylen; /* length of keyword */
5915 short **list; /* where to store the resulting list, if not
5916 NULL, the list is silently skipped! */
5917{
5918 char_u *p = NULL;
5919 char_u *end;
5920 int round;
5921 int count;
5922 int total_count = 0;
5923 short *retval = NULL;
5924 char_u *name;
5925 regmatch_T regmatch;
5926 int id;
5927 int i;
5928 int failed = FALSE;
5929
5930 /*
5931 * We parse the list twice:
5932 * round == 1: count the number of items, allocate the array.
5933 * round == 2: fill the array with the items.
5934 * In round 1 new groups may be added, causing the number of items to
5935 * grow when a regexp is used. In that case round 1 is done once again.
5936 */
5937 for (round = 1; round <= 2; ++round)
5938 {
5939 /*
5940 * skip "contains"
5941 */
5942 p = skipwhite(*arg + keylen);
5943 if (*p != '=')
5944 {
5945 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5946 break;
5947 }
5948 p = skipwhite(p + 1);
5949 if (ends_excmd(*p))
5950 {
5951 EMSG2(_("E406: Empty argument: %s"), *arg);
5952 break;
5953 }
5954
5955 /*
5956 * parse the arguments after "contains"
5957 */
5958 count = 0;
5959 while (!ends_excmd(*p))
5960 {
5961 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5962 ;
5963 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5964 if (name == NULL)
5965 {
5966 failed = TRUE;
5967 break;
5968 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005969 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005970 if ( STRCMP(name + 1, "ALLBUT") == 0
5971 || STRCMP(name + 1, "ALL") == 0
5972 || STRCMP(name + 1, "TOP") == 0
5973 || STRCMP(name + 1, "CONTAINED") == 0)
5974 {
5975 if (TOUPPER_ASC(**arg) != 'C')
5976 {
5977 EMSG2(_("E407: %s not allowed here"), name + 1);
5978 failed = TRUE;
5979 vim_free(name);
5980 break;
5981 }
5982 if (count != 0)
5983 {
5984 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5985 failed = TRUE;
5986 vim_free(name);
5987 break;
5988 }
5989 if (name[1] == 'A')
5990 id = SYNID_ALLBUT;
5991 else if (name[1] == 'T')
5992 id = SYNID_TOP;
5993 else
5994 id = SYNID_CONTAINED;
5995 id += current_syn_inc_tag;
5996 }
5997 else if (name[1] == '@')
5998 {
5999 id = syn_check_cluster(name + 2, (int)(end - p - 1));
6000 }
6001 else
6002 {
6003 /*
6004 * Handle full group name.
6005 */
6006 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6007 id = syn_check_group(name + 1, (int)(end - p));
6008 else
6009 {
6010 /*
6011 * Handle match of regexp with group names.
6012 */
6013 *name = '^';
6014 STRCAT(name, "$");
6015 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6016 if (regmatch.regprog == NULL)
6017 {
6018 failed = TRUE;
6019 vim_free(name);
6020 break;
6021 }
6022
6023 regmatch.rm_ic = TRUE;
6024 id = 0;
6025 for (i = highlight_ga.ga_len; --i >= 0; )
6026 {
6027 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6028 (colnr_T)0))
6029 {
6030 if (round == 2)
6031 {
6032 /* Got more items than expected; can happen
6033 * when adding items that match:
6034 * "contains=a.*b,axb".
6035 * Go back to first round */
6036 if (count >= total_count)
6037 {
6038 vim_free(retval);
6039 round = 1;
6040 }
6041 else
6042 retval[count] = i + 1;
6043 }
6044 ++count;
6045 id = -1; /* remember that we found one */
6046 }
6047 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006048 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006049 }
6050 }
6051 vim_free(name);
6052 if (id == 0)
6053 {
6054 EMSG2(_("E409: Unknown group name: %s"), p);
6055 failed = TRUE;
6056 break;
6057 }
6058 if (id > 0)
6059 {
6060 if (round == 2)
6061 {
6062 /* Got more items than expected, go back to first round */
6063 if (count >= total_count)
6064 {
6065 vim_free(retval);
6066 round = 1;
6067 }
6068 else
6069 retval[count] = id;
6070 }
6071 ++count;
6072 }
6073 p = skipwhite(end);
6074 if (*p != ',')
6075 break;
6076 p = skipwhite(p + 1); /* skip comma in between arguments */
6077 }
6078 if (failed)
6079 break;
6080 if (round == 1)
6081 {
6082 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6083 if (retval == NULL)
6084 break;
6085 retval[count] = 0; /* zero means end of the list */
6086 total_count = count;
6087 }
6088 }
6089
6090 *arg = p;
6091 if (failed || retval == NULL)
6092 {
6093 vim_free(retval);
6094 return FAIL;
6095 }
6096
6097 if (*list == NULL)
6098 *list = retval;
6099 else
6100 vim_free(retval); /* list already found, don't overwrite it */
6101
6102 return OK;
6103}
6104
6105/*
6106 * Make a copy of an ID list.
6107 */
6108 static short *
6109copy_id_list(list)
6110 short *list;
6111{
6112 int len;
6113 int count;
6114 short *retval;
6115
6116 if (list == NULL)
6117 return NULL;
6118
6119 for (count = 0; list[count]; ++count)
6120 ;
6121 len = (count + 1) * sizeof(short);
6122 retval = (short *)alloc((unsigned)len);
6123 if (retval != NULL)
6124 mch_memmove(retval, list, (size_t)len);
6125
6126 return retval;
6127}
6128
6129/*
6130 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6131 * "cur_si" can be NULL if not checking the "containedin" list.
6132 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6133 * the current item.
6134 * This function is called very often, keep it fast!!
6135 */
6136 static int
6137in_id_list(cur_si, list, ssp, contained)
6138 stateitem_T *cur_si; /* current item or NULL */
6139 short *list; /* id list */
6140 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6141 int contained; /* group id is contained */
6142{
6143 int retval;
6144 short *scl_list;
6145 short item;
6146 short id = ssp->id;
6147 static int depth = 0;
6148 int r;
6149
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006150 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006151 if (cur_si != NULL && ssp->cont_in_list != NULL
6152 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006153 {
6154 /* Ignore transparent items without a contains argument. Double check
6155 * that we don't go back past the first one. */
6156 while ((cur_si->si_flags & HL_TRANS_CONT)
6157 && cur_si > (stateitem_T *)(current_state.ga_data))
6158 --cur_si;
6159 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6160 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006161 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6162 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006163 return TRUE;
6164 }
6165
6166 if (list == NULL)
6167 return FALSE;
6168
6169 /*
6170 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6171 * inside anything. Only allow not-contained groups.
6172 */
6173 if (list == ID_LIST_ALL)
6174 return !contained;
6175
6176 /*
6177 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6178 * contains list. We also require that "id" is at the same ":syn include"
6179 * level as the list.
6180 */
6181 item = *list;
6182 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6183 {
6184 if (item < SYNID_TOP)
6185 {
6186 /* ALL or ALLBUT: accept all groups in the same file */
6187 if (item - SYNID_ALLBUT != ssp->inc_tag)
6188 return FALSE;
6189 }
6190 else if (item < SYNID_CONTAINED)
6191 {
6192 /* TOP: accept all not-contained groups in the same file */
6193 if (item - SYNID_TOP != ssp->inc_tag || contained)
6194 return FALSE;
6195 }
6196 else
6197 {
6198 /* CONTAINED: accept all contained groups in the same file */
6199 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6200 return FALSE;
6201 }
6202 item = *++list;
6203 retval = FALSE;
6204 }
6205 else
6206 retval = TRUE;
6207
6208 /*
6209 * Return "retval" if id is in the contains list.
6210 */
6211 while (item != 0)
6212 {
6213 if (item == id)
6214 return retval;
6215 if (item >= SYNID_CLUSTER)
6216 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006217 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006218 /* restrict recursiveness to 30 to avoid an endless loop for a
6219 * cluster that includes itself (indirectly) */
6220 if (scl_list != NULL && depth < 30)
6221 {
6222 ++depth;
6223 r = in_id_list(NULL, scl_list, ssp, contained);
6224 --depth;
6225 if (r)
6226 return retval;
6227 }
6228 }
6229 item = *++list;
6230 }
6231 return !retval;
6232}
6233
6234struct subcommand
6235{
6236 char *name; /* subcommand name */
6237 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6238};
6239
6240static struct subcommand subcommands[] =
6241{
6242 {"case", syn_cmd_case},
6243 {"clear", syn_cmd_clear},
6244 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006245 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006246 {"enable", syn_cmd_enable},
6247 {"include", syn_cmd_include},
6248 {"keyword", syn_cmd_keyword},
6249 {"list", syn_cmd_list},
6250 {"manual", syn_cmd_manual},
6251 {"match", syn_cmd_match},
6252 {"on", syn_cmd_on},
6253 {"off", syn_cmd_off},
6254 {"region", syn_cmd_region},
6255 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006256 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006257 {"sync", syn_cmd_sync},
6258 {"", syn_cmd_list},
6259 {NULL, NULL}
6260};
6261
6262/*
6263 * ":syntax".
6264 * This searches the subcommands[] table for the subcommand name, and calls a
6265 * syntax_subcommand() function to do the rest.
6266 */
6267 void
6268ex_syntax(eap)
6269 exarg_T *eap;
6270{
6271 char_u *arg = eap->arg;
6272 char_u *subcmd_end;
6273 char_u *subcmd_name;
6274 int i;
6275
6276 syn_cmdlinep = eap->cmdlinep;
6277
6278 /* isolate subcommand name */
6279 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6280 ;
6281 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6282 if (subcmd_name != NULL)
6283 {
6284 if (eap->skip) /* skip error messages for all subcommands */
6285 ++emsg_skip;
6286 for (i = 0; ; ++i)
6287 {
6288 if (subcommands[i].name == NULL)
6289 {
6290 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6291 break;
6292 }
6293 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6294 {
6295 eap->arg = skipwhite(subcmd_end);
6296 (subcommands[i].func)(eap, FALSE);
6297 break;
6298 }
6299 }
6300 vim_free(subcmd_name);
6301 if (eap->skip)
6302 --emsg_skip;
6303 }
6304}
6305
Bram Moolenaar860cae12010-06-05 23:22:07 +02006306 void
6307ex_ownsyntax(eap)
6308 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006309{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006310 char_u *old_value;
6311 char_u *new_value;
6312
Bram Moolenaar860cae12010-06-05 23:22:07 +02006313 if (curwin->w_s == &curwin->w_buffer->b_s)
6314 {
6315 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6316 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006317 hash_init(&curwin->w_s->b_keywtab);
6318 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006319#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006320 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006321 curwin->w_p_spell = FALSE; /* No spell checking */
6322 clear_string_option(&curwin->w_s->b_p_spc);
6323 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006324 clear_string_option(&curwin->w_s->b_p_spl);
6325#endif
6326 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006327
6328 /* save value of b:current_syntax */
6329 old_value = get_var_value((char_u *)"b:current_syntax");
6330 if (old_value != NULL)
6331 old_value = vim_strsave(old_value);
6332
6333 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6334 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006335 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006336
6337 /* move value of b:current_syntax to w:current_syntax */
6338 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006339 if (new_value != NULL)
6340 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006341
6342 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006343 if (old_value == NULL)
6344 do_unlet((char_u *)"b:current_syntax", TRUE);
6345 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006346 {
6347 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6348 vim_free(old_value);
6349 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006350}
6351
6352 int
6353syntax_present(win)
6354 win_T *win;
6355{
6356 return (win->w_s->b_syn_patterns.ga_len != 0
6357 || win->w_s->b_syn_clusters.ga_len != 0
6358 || win->w_s->b_keywtab.ht_used > 0
6359 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006360}
6361
6362#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6363
6364static enum
6365{
6366 EXP_SUBCMD, /* expand ":syn" sub-commands */
6367 EXP_CASE /* expand ":syn case" arguments */
6368} expand_what;
6369
Bram Moolenaar4f688582007-07-24 12:34:30 +00006370/*
6371 * Reset include_link, include_default, include_none to 0.
6372 * Called when we are done expanding.
6373 */
6374 void
6375reset_expand_highlight()
6376{
6377 include_link = include_default = include_none = 0;
6378}
6379
6380/*
6381 * Handle command line completion for :match and :echohl command: Add "None"
6382 * as highlight group.
6383 */
6384 void
6385set_context_in_echohl_cmd(xp, arg)
6386 expand_T *xp;
6387 char_u *arg;
6388{
6389 xp->xp_context = EXPAND_HIGHLIGHT;
6390 xp->xp_pattern = arg;
6391 include_none = 1;
6392}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006393
6394/*
6395 * Handle command line completion for :syntax command.
6396 */
6397 void
6398set_context_in_syntax_cmd(xp, arg)
6399 expand_T *xp;
6400 char_u *arg;
6401{
6402 char_u *p;
6403
6404 /* Default: expand subcommands */
6405 xp->xp_context = EXPAND_SYNTAX;
6406 expand_what = EXP_SUBCMD;
6407 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006408 include_link = 0;
6409 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006410
6411 /* (part of) subcommand already typed */
6412 if (*arg != NUL)
6413 {
6414 p = skiptowhite(arg);
6415 if (*p != NUL) /* past first word */
6416 {
6417 xp->xp_pattern = skipwhite(p);
6418 if (*skiptowhite(xp->xp_pattern) != NUL)
6419 xp->xp_context = EXPAND_NOTHING;
6420 else if (STRNICMP(arg, "case", p - arg) == 0)
6421 expand_what = EXP_CASE;
6422 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6423 || STRNICMP(arg, "region", p - arg) == 0
6424 || STRNICMP(arg, "match", p - arg) == 0
6425 || STRNICMP(arg, "list", p - arg) == 0)
6426 xp->xp_context = EXPAND_HIGHLIGHT;
6427 else
6428 xp->xp_context = EXPAND_NOTHING;
6429 }
6430 }
6431}
6432
6433static char *(case_args[]) = {"match", "ignore", NULL};
6434
6435/*
6436 * Function given to ExpandGeneric() to obtain the list syntax names for
6437 * expansion.
6438 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006439 char_u *
6440get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006441 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006442 int idx;
6443{
6444 if (expand_what == EXP_SUBCMD)
6445 return (char_u *)subcommands[idx].name;
6446 return (char_u *)case_args[idx];
6447}
6448
6449#endif /* FEAT_CMDL_COMPL */
6450
Bram Moolenaar071d4272004-06-13 20:20:40 +00006451/*
6452 * Function called for expression evaluation: get syntax ID at file position.
6453 */
6454 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006455syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006456 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006457 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006458 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006459 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006460 int *spellp; /* return: can do spell checking */
6461 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006462{
6463 /* When the position is not after the current position and in the same
6464 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006465 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006466 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006467 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006468 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006469
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006470 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006471
6472 return (trans ? current_trans_id : current_id);
6473}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006474
Bram Moolenaar860cae12010-06-05 23:22:07 +02006475#if defined(FEAT_CONCEAL) || defined(PROTO)
6476/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006477 * Get extra information about the syntax item. Must be called right after
6478 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006479 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006480 * Returns the current flags.
6481 */
6482 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006483get_syntax_info(seqnrp)
6484 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006485{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006486 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006487 return current_flags;
6488}
6489
6490/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006491 * Return conceal substitution character
6492 */
6493 int
6494syn_get_sub_char()
6495{
6496 return current_sub_char;
6497}
6498#endif
6499
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006500#if defined(FEAT_EVAL) || defined(PROTO)
6501/*
6502 * Return the syntax ID at position "i" in the current stack.
6503 * The caller must have called syn_get_id() before to fill the stack.
6504 * Returns -1 when "i" is out of range.
6505 */
6506 int
6507syn_get_stack_item(i)
6508 int i;
6509{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006510 if (i >= current_state.ga_len)
6511 {
6512 /* Need to invalidate the state, because we didn't properly finish it
6513 * for the last character, "keep_state" was TRUE. */
6514 invalidate_current_state();
6515 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006516 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006517 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006518 return CUR_STATE(i).si_id;
6519}
6520#endif
6521
Bram Moolenaar071d4272004-06-13 20:20:40 +00006522#if defined(FEAT_FOLDING) || defined(PROTO)
6523/*
6524 * Function called to get folding level for line "lnum" in window "wp".
6525 */
6526 int
6527syn_get_foldlevel(wp, lnum)
6528 win_T *wp;
6529 long lnum;
6530{
6531 int level = 0;
6532 int i;
6533
6534 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006535 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006536 {
6537 syntax_start(wp, lnum);
6538
6539 for (i = 0; i < current_state.ga_len; ++i)
6540 if (CUR_STATE(i).si_flags & HL_FOLD)
6541 ++level;
6542 }
6543 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006544 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006545 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006546 if (level < 0)
6547 level = 0;
6548 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006549 return level;
6550}
6551#endif
6552
Bram Moolenaar01615492015-02-03 13:00:38 +01006553#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006554/*
6555 * ":syntime".
6556 */
6557 void
6558ex_syntime(eap)
6559 exarg_T *eap;
6560{
6561 if (STRCMP(eap->arg, "on") == 0)
6562 syn_time_on = TRUE;
6563 else if (STRCMP(eap->arg, "off") == 0)
6564 syn_time_on = FALSE;
6565 else if (STRCMP(eap->arg, "clear") == 0)
6566 syntime_clear();
6567 else if (STRCMP(eap->arg, "report") == 0)
6568 syntime_report();
6569 else
6570 EMSG2(_(e_invarg2), eap->arg);
6571}
6572
6573 static void
6574syn_clear_time(st)
6575 syn_time_T *st;
6576{
6577 profile_zero(&st->total);
6578 profile_zero(&st->slowest);
6579 st->count = 0;
6580 st->match = 0;
6581}
6582
6583/*
6584 * Clear the syntax timing for the current buffer.
6585 */
6586 static void
6587syntime_clear()
6588{
6589 int idx;
6590 synpat_T *spp;
6591
6592 if (!syntax_present(curwin))
6593 {
6594 MSG(_(msg_no_items));
6595 return;
6596 }
6597 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6598 {
6599 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6600 syn_clear_time(&spp->sp_time);
6601 }
6602}
6603
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006604#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6605/*
6606 * Function given to ExpandGeneric() to obtain the possible arguments of the
6607 * ":syntime {on,off,clear,report}" command.
6608 */
6609 char_u *
6610get_syntime_arg(xp, idx)
6611 expand_T *xp UNUSED;
6612 int idx;
6613{
6614 switch (idx)
6615 {
6616 case 0: return (char_u *)"on";
6617 case 1: return (char_u *)"off";
6618 case 2: return (char_u *)"clear";
6619 case 3: return (char_u *)"report";
6620 }
6621 return NULL;
6622}
6623#endif
6624
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006625typedef struct
6626{
6627 proftime_T total;
6628 int count;
6629 int match;
6630 proftime_T slowest;
6631 proftime_T average;
6632 int id;
6633 char_u *pattern;
6634} time_entry_T;
6635
6636 static int
6637#ifdef __BORLANDC__
6638_RTLENTRYF
6639#endif
6640syn_compare_syntime(v1, v2)
6641 const void *v1;
6642 const void *v2;
6643{
6644 const time_entry_T *s1 = v1;
6645 const time_entry_T *s2 = v2;
6646
6647 return profile_cmp(&s1->total, &s2->total);
6648}
6649
6650/*
6651 * Clear the syntax timing for the current buffer.
6652 */
6653 static void
6654syntime_report()
6655{
6656 int idx;
6657 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006658# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006659 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006660# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006661 int len;
6662 proftime_T total_total;
6663 int total_count = 0;
6664 garray_T ga;
6665 time_entry_T *p;
6666
6667 if (!syntax_present(curwin))
6668 {
6669 MSG(_(msg_no_items));
6670 return;
6671 }
6672
6673 ga_init2(&ga, sizeof(time_entry_T), 50);
6674 profile_zero(&total_total);
6675 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6676 {
6677 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6678 if (spp->sp_time.count > 0)
6679 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006680 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006681 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6682 p->total = spp->sp_time.total;
6683 profile_add(&total_total, &spp->sp_time.total);
6684 p->count = spp->sp_time.count;
6685 p->match = spp->sp_time.match;
6686 total_count += spp->sp_time.count;
6687 p->slowest = spp->sp_time.slowest;
6688# ifdef FEAT_FLOAT
6689 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6690 p->average = tm;
6691# endif
6692 p->id = spp->sp_syn.id;
6693 p->pattern = spp->sp_pattern;
6694 ++ga.ga_len;
6695 }
6696 }
6697
6698 /* sort on total time */
Bram Moolenaar4e312962013-06-06 21:19:51 +02006699 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
6700 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006701
6702 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6703 MSG_PUTS("\n");
6704 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6705 {
6706 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6707 p = ((time_entry_T *)ga.ga_data) + idx;
6708
6709 MSG_PUTS(profile_msg(&p->total));
6710 MSG_PUTS(" "); /* make sure there is always a separating space */
6711 msg_advance(13);
6712 msg_outnum(p->count);
6713 MSG_PUTS(" ");
6714 msg_advance(20);
6715 msg_outnum(p->match);
6716 MSG_PUTS(" ");
6717 msg_advance(26);
6718 MSG_PUTS(profile_msg(&p->slowest));
6719 MSG_PUTS(" ");
6720 msg_advance(38);
6721# ifdef FEAT_FLOAT
6722 MSG_PUTS(profile_msg(&p->average));
6723 MSG_PUTS(" ");
6724# endif
6725 msg_advance(50);
6726 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6727 MSG_PUTS(" ");
6728
6729 msg_advance(69);
6730 if (Columns < 80)
6731 len = 20; /* will wrap anyway */
6732 else
6733 len = Columns - 70;
6734 if (len > (int)STRLEN(p->pattern))
6735 len = (int)STRLEN(p->pattern);
6736 msg_outtrans_len(p->pattern, len);
6737 MSG_PUTS("\n");
6738 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006739 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006740 if (!got_int)
6741 {
6742 MSG_PUTS("\n");
6743 MSG_PUTS(profile_msg(&total_total));
6744 msg_advance(13);
6745 msg_outnum(total_count);
6746 MSG_PUTS("\n");
6747 }
6748}
6749#endif
6750
Bram Moolenaar071d4272004-06-13 20:20:40 +00006751#endif /* FEAT_SYN_HL */
6752
Bram Moolenaar071d4272004-06-13 20:20:40 +00006753/**************************************
6754 * Highlighting stuff *
6755 **************************************/
6756
6757/*
6758 * The default highlight groups. These are compiled-in for fast startup and
6759 * they still work when the runtime files can't be found.
6760 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006761 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6762 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006763 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006764#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006765# define CENT(a, b) b
6766#else
6767# define CENT(a, b) a
6768#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006769static char *(highlight_init_both[]) =
6770 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006771 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6772 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6773 CENT("IncSearch term=reverse cterm=reverse",
6774 "IncSearch term=reverse cterm=reverse gui=reverse"),
6775 CENT("ModeMsg term=bold cterm=bold",
6776 "ModeMsg term=bold cterm=bold gui=bold"),
6777 CENT("NonText term=bold ctermfg=Blue",
6778 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6779 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6780 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6781 CENT("StatusLineNC term=reverse cterm=reverse",
6782 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006783#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006784 CENT("VertSplit term=reverse cterm=reverse",
6785 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006786#endif
6787#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006788 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6789 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006790#endif
6791#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006792 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6793 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006794#endif
6795#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006796 CENT("PmenuSbar ctermbg=Grey",
6797 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006798#endif
6799#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006800 CENT("TabLineSel term=bold cterm=bold",
6801 "TabLineSel term=bold cterm=bold gui=bold"),
6802 CENT("TabLineFill term=reverse cterm=reverse",
6803 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006804#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006805#ifdef FEAT_GUI
6806 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006807 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006808#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006809 NULL
6810 };
6811
6812static char *(highlight_init_light[]) =
6813 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006814 CENT("Directory term=bold ctermfg=DarkBlue",
6815 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6816 CENT("LineNr term=underline ctermfg=Brown",
6817 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006818 CENT("CursorLineNr term=bold ctermfg=Brown",
6819 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006820 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6821 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6822 CENT("Question term=standout ctermfg=DarkGreen",
6823 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6824 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6825 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006826#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006827 CENT("SpellBad term=reverse ctermbg=LightRed",
6828 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6829 CENT("SpellCap term=reverse ctermbg=LightBlue",
6830 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6831 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6832 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6833 CENT("SpellLocal term=underline ctermbg=Cyan",
6834 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006835#endif
6836#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006837 CENT("PmenuThumb ctermbg=Black",
6838 "PmenuThumb ctermbg=Black guibg=Black"),
6839 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6840 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6841 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6842 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006843#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006844 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6845 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6846 CENT("Title term=bold ctermfg=DarkMagenta",
6847 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6848 CENT("WarningMsg term=standout ctermfg=DarkRed",
6849 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006850#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006851 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6852 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006853#endif
6854#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006855 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6856 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6857 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6858 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006859#endif
6860#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006861 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6862 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006863#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006864 CENT("Visual term=reverse",
6865 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006866#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006867 CENT("DiffAdd term=bold ctermbg=LightBlue",
6868 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6869 CENT("DiffChange term=bold ctermbg=LightMagenta",
6870 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6871 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6872 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006873#endif
6874#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006875 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6876 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006877#endif
6878#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006879 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006880 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006881 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006882 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006883 CENT("ColorColumn term=reverse ctermbg=LightRed",
6884 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006885#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006886#ifdef FEAT_CONCEAL
6887 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6888 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6889#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006890#ifdef FEAT_AUTOCMD
6891 CENT("MatchParen term=reverse ctermbg=Cyan",
6892 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6893#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006894#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006895 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006896#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006897 NULL
6898 };
6899
6900static char *(highlight_init_dark[]) =
6901 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006902 CENT("Directory term=bold ctermfg=LightCyan",
6903 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6904 CENT("LineNr term=underline ctermfg=Yellow",
6905 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006906 CENT("CursorLineNr term=bold ctermfg=Yellow",
6907 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006908 CENT("MoreMsg term=bold ctermfg=LightGreen",
6909 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6910 CENT("Question term=standout ctermfg=LightGreen",
6911 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6912 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6913 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6914 CENT("SpecialKey term=bold ctermfg=LightBlue",
6915 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006916#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006917 CENT("SpellBad term=reverse ctermbg=Red",
6918 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6919 CENT("SpellCap term=reverse ctermbg=Blue",
6920 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6921 CENT("SpellRare term=reverse ctermbg=Magenta",
6922 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6923 CENT("SpellLocal term=underline ctermbg=Cyan",
6924 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006925#endif
6926#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006927 CENT("PmenuThumb ctermbg=White",
6928 "PmenuThumb ctermbg=White guibg=White"),
6929 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6930 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02006931 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6932 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006933#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006934 CENT("Title term=bold ctermfg=LightMagenta",
6935 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6936 CENT("WarningMsg term=standout ctermfg=LightRed",
6937 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006938#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006939 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6940 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006941#endif
6942#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006943 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6944 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6945 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6946 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006947#endif
6948#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006949 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6950 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006951#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006952 CENT("Visual term=reverse",
6953 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006954#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006955 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6956 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6957 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6958 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6959 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6960 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006961#endif
6962#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006963 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6964 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006965#endif
6966#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006967 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006968 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006969 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006970 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006971 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6972 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006973#endif
6974#ifdef FEAT_AUTOCMD
6975 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6976 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006977#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006978#ifdef FEAT_CONCEAL
6979 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6980 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6981#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006982#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006983 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006984#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006985 NULL
6986 };
6987
6988 void
6989init_highlight(both, reset)
6990 int both; /* include groups where 'bg' doesn't matter */
6991 int reset; /* clear group first */
6992{
6993 int i;
6994 char **pp;
6995 static int had_both = FALSE;
6996#ifdef FEAT_EVAL
6997 char_u *p;
6998
6999 /*
7000 * Try finding the color scheme file. Used when a color file was loaded
7001 * and 'background' or 't_Co' is changed.
7002 */
7003 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007004 if (p != NULL)
7005 {
7006 /* The value of g:colors_name could be freed when sourcing the script,
7007 * making "p" invalid, so copy it. */
7008 char_u *copy_p = vim_strsave(p);
7009 int r;
7010
7011 if (copy_p != NULL)
7012 {
7013 r = load_colors(copy_p);
7014 vim_free(copy_p);
7015 if (r == OK)
7016 return;
7017 }
7018 }
7019
Bram Moolenaar071d4272004-06-13 20:20:40 +00007020#endif
7021
7022 /*
7023 * Didn't use a color file, use the compiled-in colors.
7024 */
7025 if (both)
7026 {
7027 had_both = TRUE;
7028 pp = highlight_init_both;
7029 for (i = 0; pp[i] != NULL; ++i)
7030 do_highlight((char_u *)pp[i], reset, TRUE);
7031 }
7032 else if (!had_both)
7033 /* Don't do anything before the call with both == TRUE from main().
7034 * Not everything has been setup then, and that call will overrule
7035 * everything anyway. */
7036 return;
7037
7038 if (*p_bg == 'l')
7039 pp = highlight_init_light;
7040 else
7041 pp = highlight_init_dark;
7042 for (i = 0; pp[i] != NULL; ++i)
7043 do_highlight((char_u *)pp[i], reset, TRUE);
7044
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007045 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007046 * depend on the number of colors available.
7047 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007048 * to avoid Statement highlighted text disappears.
7049 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007050 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007051 do_highlight((char_u *)(*p_bg == 'l'
7052 ? "Visual cterm=NONE ctermbg=LightGrey"
7053 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007054 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007055 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007056 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7057 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007058 if (*p_bg == 'l')
7059 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7060 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007061
Bram Moolenaar071d4272004-06-13 20:20:40 +00007062#ifdef FEAT_SYN_HL
7063 /*
7064 * If syntax highlighting is enabled load the highlighting for it.
7065 */
7066 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007067 {
7068 static int recursive = 0;
7069
7070 if (recursive >= 5)
7071 EMSG(_("E679: recursive loop loading syncolor.vim"));
7072 else
7073 {
7074 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007075 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007076 --recursive;
7077 }
7078 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007079#endif
7080}
7081
7082/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007083 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007084 * Return OK for success, FAIL for failure.
7085 */
7086 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007087load_colors(name)
7088 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007089{
7090 char_u *buf;
7091 int retval = FAIL;
7092 static int recursive = FALSE;
7093
7094 /* When being called recursively, this is probably because setting
7095 * 'background' caused the highlighting to be reloaded. This means it is
7096 * working, thus we should return OK. */
7097 if (recursive)
7098 return OK;
7099
7100 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007101 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007102 if (buf != NULL)
7103 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007104 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007105 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007106 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007107#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007108 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007109#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110 }
7111 recursive = FALSE;
7112
7113 return retval;
7114}
7115
7116/*
7117 * Handle the ":highlight .." command.
7118 * When using ":hi clear" this is called recursively for each group with
7119 * "forceit" and "init" both TRUE.
7120 */
7121 void
7122do_highlight(line, forceit, init)
7123 char_u *line;
7124 int forceit;
7125 int init; /* TRUE when called for initializing */
7126{
7127 char_u *name_end;
7128 char_u *p;
7129 char_u *linep;
7130 char_u *key_start;
7131 char_u *arg_start;
7132 char_u *key = NULL, *arg = NULL;
7133 long i;
7134 int off;
7135 int len;
7136 int attr;
7137 int id;
7138 int idx;
7139 int dodefault = FALSE;
7140 int doclear = FALSE;
7141 int dolink = FALSE;
7142 int error = FALSE;
7143 int color;
7144 int is_normal_group = FALSE; /* "Normal" group */
7145#ifdef FEAT_GUI_X11
7146 int is_menu_group = FALSE; /* "Menu" group */
7147 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7148 int is_tooltip_group = FALSE; /* "Tooltip" group */
7149 int do_colors = FALSE; /* need to update colors? */
7150#else
7151# define is_menu_group 0
7152# define is_tooltip_group 0
7153#endif
7154
7155 /*
7156 * If no argument, list current highlighting.
7157 */
7158 if (ends_excmd(*line))
7159 {
7160 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7161 /* TODO: only call when the group has attributes set */
7162 highlight_list_one((int)i);
7163 return;
7164 }
7165
7166 /*
7167 * Isolate the name.
7168 */
7169 name_end = skiptowhite(line);
7170 linep = skipwhite(name_end);
7171
7172 /*
7173 * Check for "default" argument.
7174 */
7175 if (STRNCMP(line, "default", name_end - line) == 0)
7176 {
7177 dodefault = TRUE;
7178 line = linep;
7179 name_end = skiptowhite(line);
7180 linep = skipwhite(name_end);
7181 }
7182
7183 /*
7184 * Check for "clear" or "link" argument.
7185 */
7186 if (STRNCMP(line, "clear", name_end - line) == 0)
7187 doclear = TRUE;
7188 if (STRNCMP(line, "link", name_end - line) == 0)
7189 dolink = TRUE;
7190
7191 /*
7192 * ":highlight {group-name}": list highlighting for one group.
7193 */
7194 if (!doclear && !dolink && ends_excmd(*linep))
7195 {
7196 id = syn_namen2id(line, (int)(name_end - line));
7197 if (id == 0)
7198 EMSG2(_("E411: highlight group not found: %s"), line);
7199 else
7200 highlight_list_one(id);
7201 return;
7202 }
7203
7204 /*
7205 * Handle ":highlight link {from} {to}" command.
7206 */
7207 if (dolink)
7208 {
7209 char_u *from_start = linep;
7210 char_u *from_end;
7211 char_u *to_start;
7212 char_u *to_end;
7213 int from_id;
7214 int to_id;
7215
7216 from_end = skiptowhite(from_start);
7217 to_start = skipwhite(from_end);
7218 to_end = skiptowhite(to_start);
7219
7220 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7221 {
7222 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7223 from_start);
7224 return;
7225 }
7226
7227 if (!ends_excmd(*skipwhite(to_end)))
7228 {
7229 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7230 return;
7231 }
7232
7233 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7234 if (STRNCMP(to_start, "NONE", 4) == 0)
7235 to_id = 0;
7236 else
7237 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7238
7239 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7240 {
7241 /*
7242 * Don't allow a link when there already is some highlighting
7243 * for the group, unless '!' is used
7244 */
7245 if (to_id > 0 && !forceit && !init
7246 && hl_has_settings(from_id - 1, dodefault))
7247 {
7248 if (sourcing_name == NULL && !dodefault)
7249 EMSG(_("E414: group has settings, highlight link ignored"));
7250 }
7251 else
7252 {
7253 if (!init)
7254 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7255 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007256#ifdef FEAT_EVAL
7257 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7258#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007259 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007260 }
7261 }
7262
7263 /* Only call highlight_changed() once, after sourcing a syntax file */
7264 need_highlight_changed = TRUE;
7265
7266 return;
7267 }
7268
7269 if (doclear)
7270 {
7271 /*
7272 * ":highlight clear [group]" command.
7273 */
7274 line = linep;
7275 if (ends_excmd(*line))
7276 {
7277#ifdef FEAT_GUI
7278 /* First, we do not destroy the old values, but allocate the new
7279 * ones and update the display. THEN we destroy the old values.
7280 * If we destroy the old values first, then the old values
7281 * (such as GuiFont's or GuiFontset's) will still be displayed but
7282 * invalid because they were free'd.
7283 */
7284 if (gui.in_use)
7285 {
7286# ifdef FEAT_BEVAL_TIP
7287 gui_init_tooltip_font();
7288# endif
7289# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7290 gui_init_menu_font();
7291# endif
7292 }
7293# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7294 gui_mch_def_colors();
7295# endif
7296# ifdef FEAT_GUI_X11
7297# ifdef FEAT_MENU
7298
7299 /* This only needs to be done when there is no Menu highlight
7300 * group defined by default, which IS currently the case.
7301 */
7302 gui_mch_new_menu_colors();
7303# endif
7304 if (gui.in_use)
7305 {
7306 gui_new_scrollbar_colors();
7307# ifdef FEAT_BEVAL
7308 gui_mch_new_tooltip_colors();
7309# endif
7310# ifdef FEAT_MENU
7311 gui_mch_new_menu_font();
7312# endif
7313 }
7314# endif
7315
7316 /* Ok, we're done allocating the new default graphics items.
7317 * The screen should already be refreshed at this point.
7318 * It is now Ok to clear out the old data.
7319 */
7320#endif
7321#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007322 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007323#endif
7324 restore_cterm_colors();
7325
7326 /*
7327 * Clear all default highlight groups and load the defaults.
7328 */
7329 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7330 highlight_clear(idx);
7331 init_highlight(TRUE, TRUE);
7332#ifdef FEAT_GUI
7333 if (gui.in_use)
7334 highlight_gui_started();
7335#endif
7336 highlight_changed();
7337 redraw_later_clear();
7338 return;
7339 }
7340 name_end = skiptowhite(line);
7341 linep = skipwhite(name_end);
7342 }
7343
7344 /*
7345 * Find the group name in the table. If it does not exist yet, add it.
7346 */
7347 id = syn_check_group(line, (int)(name_end - line));
7348 if (id == 0) /* failed (out of memory) */
7349 return;
7350 idx = id - 1; /* index is ID minus one */
7351
7352 /* Return if "default" was used and the group already has settings. */
7353 if (dodefault && hl_has_settings(idx, TRUE))
7354 return;
7355
7356 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7357 is_normal_group = TRUE;
7358#ifdef FEAT_GUI_X11
7359 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7360 is_menu_group = TRUE;
7361 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7362 is_scrollbar_group = TRUE;
7363 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7364 is_tooltip_group = TRUE;
7365#endif
7366
7367 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7368 if (doclear || (forceit && init))
7369 {
7370 highlight_clear(idx);
7371 if (!doclear)
7372 HL_TABLE()[idx].sg_set = 0;
7373 }
7374
7375 if (!doclear)
7376 while (!ends_excmd(*linep))
7377 {
7378 key_start = linep;
7379 if (*linep == '=')
7380 {
7381 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7382 error = TRUE;
7383 break;
7384 }
7385
7386 /*
7387 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7388 * "guibg").
7389 */
7390 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7391 ++linep;
7392 vim_free(key);
7393 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7394 if (key == NULL)
7395 {
7396 error = TRUE;
7397 break;
7398 }
7399 linep = skipwhite(linep);
7400
7401 if (STRCMP(key, "NONE") == 0)
7402 {
7403 if (!init || HL_TABLE()[idx].sg_set == 0)
7404 {
7405 if (!init)
7406 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7407 highlight_clear(idx);
7408 }
7409 continue;
7410 }
7411
7412 /*
7413 * Check for the equal sign.
7414 */
7415 if (*linep != '=')
7416 {
7417 EMSG2(_("E416: missing equal sign: %s"), key_start);
7418 error = TRUE;
7419 break;
7420 }
7421 ++linep;
7422
7423 /*
7424 * Isolate the argument.
7425 */
7426 linep = skipwhite(linep);
7427 if (*linep == '\'') /* guifg='color name' */
7428 {
7429 arg_start = ++linep;
7430 linep = vim_strchr(linep, '\'');
7431 if (linep == NULL)
7432 {
7433 EMSG2(_(e_invarg2), key_start);
7434 error = TRUE;
7435 break;
7436 }
7437 }
7438 else
7439 {
7440 arg_start = linep;
7441 linep = skiptowhite(linep);
7442 }
7443 if (linep == arg_start)
7444 {
7445 EMSG2(_("E417: missing argument: %s"), key_start);
7446 error = TRUE;
7447 break;
7448 }
7449 vim_free(arg);
7450 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7451 if (arg == NULL)
7452 {
7453 error = TRUE;
7454 break;
7455 }
7456 if (*linep == '\'')
7457 ++linep;
7458
7459 /*
7460 * Store the argument.
7461 */
7462 if ( STRCMP(key, "TERM") == 0
7463 || STRCMP(key, "CTERM") == 0
7464 || STRCMP(key, "GUI") == 0)
7465 {
7466 attr = 0;
7467 off = 0;
7468 while (arg[off] != NUL)
7469 {
7470 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7471 {
7472 len = (int)STRLEN(hl_name_table[i]);
7473 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7474 {
7475 attr |= hl_attr_table[i];
7476 off += len;
7477 break;
7478 }
7479 }
7480 if (i < 0)
7481 {
7482 EMSG2(_("E418: Illegal value: %s"), arg);
7483 error = TRUE;
7484 break;
7485 }
7486 if (arg[off] == ',') /* another one follows */
7487 ++off;
7488 }
7489 if (error)
7490 break;
7491 if (*key == 'T')
7492 {
7493 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7494 {
7495 if (!init)
7496 HL_TABLE()[idx].sg_set |= SG_TERM;
7497 HL_TABLE()[idx].sg_term = attr;
7498 }
7499 }
7500 else if (*key == 'C')
7501 {
7502 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7503 {
7504 if (!init)
7505 HL_TABLE()[idx].sg_set |= SG_CTERM;
7506 HL_TABLE()[idx].sg_cterm = attr;
7507 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7508 }
7509 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007510#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007511 else
7512 {
7513 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7514 {
7515 if (!init)
7516 HL_TABLE()[idx].sg_set |= SG_GUI;
7517 HL_TABLE()[idx].sg_gui = attr;
7518 }
7519 }
7520#endif
7521 }
7522 else if (STRCMP(key, "FONT") == 0)
7523 {
7524 /* in non-GUI fonts are simply ignored */
7525#ifdef FEAT_GUI
7526 if (!gui.shell_created)
7527 {
7528 /* GUI not started yet, always accept the name. */
7529 vim_free(HL_TABLE()[idx].sg_font_name);
7530 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7531 }
7532 else
7533 {
7534 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7535# ifdef FEAT_XFONTSET
7536 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7537# endif
7538 /* First, save the current font/fontset.
7539 * Then try to allocate the font/fontset.
7540 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7541 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7542 */
7543
7544 HL_TABLE()[idx].sg_font = NOFONT;
7545# ifdef FEAT_XFONTSET
7546 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7547# endif
7548 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007549 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007550
7551# ifdef FEAT_XFONTSET
7552 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7553 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007554 /* New fontset was accepted. Free the old one, if there
7555 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007556 gui_mch_free_fontset(temp_sg_fontset);
7557 vim_free(HL_TABLE()[idx].sg_font_name);
7558 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7559 }
7560 else
7561 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7562# endif
7563 if (HL_TABLE()[idx].sg_font != NOFONT)
7564 {
7565 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007566 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007567 gui_mch_free_font(temp_sg_font);
7568 vim_free(HL_TABLE()[idx].sg_font_name);
7569 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7570 }
7571 else
7572 HL_TABLE()[idx].sg_font = temp_sg_font;
7573 }
7574#endif
7575 }
7576 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7577 {
7578 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7579 {
7580 if (!init)
7581 HL_TABLE()[idx].sg_set |= SG_CTERM;
7582
7583 /* When setting the foreground color, and previously the "bold"
7584 * flag was set for a light color, reset it now */
7585 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7586 {
7587 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7588 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7589 }
7590
7591 if (VIM_ISDIGIT(*arg))
7592 color = atoi((char *)arg);
7593 else if (STRICMP(arg, "fg") == 0)
7594 {
7595 if (cterm_normal_fg_color)
7596 color = cterm_normal_fg_color - 1;
7597 else
7598 {
7599 EMSG(_("E419: FG color unknown"));
7600 error = TRUE;
7601 break;
7602 }
7603 }
7604 else if (STRICMP(arg, "bg") == 0)
7605 {
7606 if (cterm_normal_bg_color > 0)
7607 color = cterm_normal_bg_color - 1;
7608 else
7609 {
7610 EMSG(_("E420: BG color unknown"));
7611 error = TRUE;
7612 break;
7613 }
7614 }
7615 else
7616 {
7617 static char *(color_names[28]) = {
7618 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7619 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7620 "Gray", "Grey",
7621 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7622 "Blue", "LightBlue", "Green", "LightGreen",
7623 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7624 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7625 static int color_numbers_16[28] = {0, 1, 2, 3,
7626 4, 5, 6, 6,
7627 7, 7,
7628 7, 7, 8, 8,
7629 9, 9, 10, 10,
7630 11, 11, 12, 12, 13,
7631 13, 14, 14, 15, -1};
7632 /* for xterm with 88 colors... */
7633 static int color_numbers_88[28] = {0, 4, 2, 6,
7634 1, 5, 32, 72,
7635 84, 84,
7636 7, 7, 82, 82,
7637 12, 43, 10, 61,
7638 14, 63, 9, 74, 13,
7639 75, 11, 78, 15, -1};
7640 /* for xterm with 256 colors... */
7641 static int color_numbers_256[28] = {0, 4, 2, 6,
7642 1, 5, 130, 130,
7643 248, 248,
7644 7, 7, 242, 242,
7645 12, 81, 10, 121,
7646 14, 159, 9, 224, 13,
7647 225, 11, 229, 15, -1};
7648 /* for terminals with less than 16 colors... */
7649 static int color_numbers_8[28] = {0, 4, 2, 6,
7650 1, 5, 3, 3,
7651 7, 7,
7652 7, 7, 0+8, 0+8,
7653 4+8, 4+8, 2+8, 2+8,
7654 6+8, 6+8, 1+8, 1+8, 5+8,
7655 5+8, 3+8, 3+8, 7+8, -1};
7656#if defined(__QNXNTO__)
7657 static int *color_numbers_8_qansi = color_numbers_8;
7658 /* On qnx, the 8 & 16 color arrays are the same */
7659 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7660 color_numbers_8_qansi = color_numbers_16;
7661#endif
7662
7663 /* reduce calls to STRICMP a bit, it can be slow */
7664 off = TOUPPER_ASC(*arg);
7665 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7666 if (off == color_names[i][0]
7667 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7668 break;
7669 if (i < 0)
7670 {
7671 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7672 error = TRUE;
7673 break;
7674 }
7675
7676 /* Use the _16 table to check if its a valid color name. */
7677 color = color_numbers_16[i];
7678 if (color >= 0)
7679 {
7680 if (t_colors == 8)
7681 {
7682 /* t_Co is 8: use the 8 colors table */
7683#if defined(__QNXNTO__)
7684 color = color_numbers_8_qansi[i];
7685#else
7686 color = color_numbers_8[i];
7687#endif
7688 if (key[5] == 'F')
7689 {
7690 /* set/reset bold attribute to get light foreground
7691 * colors (on some terminals, e.g. "linux") */
7692 if (color & 8)
7693 {
7694 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7695 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7696 }
7697 else
7698 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7699 }
7700 color &= 7; /* truncate to 8 colors */
7701 }
7702 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007703 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007704 {
7705 /*
7706 * Guess: if the termcap entry ends in 'm', it is
7707 * probably an xterm-like terminal. Use the changed
7708 * order for colors.
7709 */
7710 if (*T_CAF != NUL)
7711 p = T_CAF;
7712 else
7713 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007714 if (*p != NUL && (t_colors > 256
7715 || *(p + STRLEN(p) - 1) == 'm'))
7716 {
7717 if (t_colors == 88)
7718 color = color_numbers_88[i];
7719 else if (t_colors >= 256)
7720 color = color_numbers_256[i];
7721 else
7722 color = color_numbers_8[i];
7723 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007724 }
7725 }
7726 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007727 /* Add one to the argument, to avoid zero. Zero is used for
7728 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007729 if (key[5] == 'F')
7730 {
7731 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7732 if (is_normal_group)
7733 {
7734 cterm_normal_fg_color = color + 1;
7735 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7736#ifdef FEAT_GUI
7737 /* Don't do this if the GUI is used. */
7738 if (!gui.in_use && !gui.starting)
7739#endif
7740 {
7741 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007742 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007743 term_fg_color(color);
7744 }
7745 }
7746 }
7747 else
7748 {
7749 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7750 if (is_normal_group)
7751 {
7752 cterm_normal_bg_color = color + 1;
7753#ifdef FEAT_GUI
7754 /* Don't mess with 'background' if the GUI is used. */
7755 if (!gui.in_use && !gui.starting)
7756#endif
7757 {
7758 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007759 if (color >= 0)
7760 {
7761 if (termcap_active)
7762 term_bg_color(color);
7763 if (t_colors < 16)
7764 i = (color == 0 || color == 4);
7765 else
7766 i = (color < 7 || color == 8);
7767 /* Set the 'background' option if the value is
7768 * wrong. */
7769 if (i != (*p_bg == 'd'))
7770 set_option_value((char_u *)"bg", 0L,
7771 i ? (char_u *)"dark"
7772 : (char_u *)"light", 0);
7773 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007774 }
7775 }
7776 }
7777 }
7778 }
7779 else if (STRCMP(key, "GUIFG") == 0)
7780 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007781#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007782 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007783 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007784 if (!init)
7785 HL_TABLE()[idx].sg_set |= SG_GUI;
7786
Bram Moolenaar61623362010-07-14 22:04:22 +02007787# ifdef FEAT_GUI
7788 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007789 i = color_name2handle(arg);
7790 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7791 {
7792 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007793# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007794 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7795 if (STRCMP(arg, "NONE"))
7796 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7797 else
7798 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007799# ifdef FEAT_GUI
7800# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007801 if (is_menu_group)
7802 gui.menu_fg_pixel = i;
7803 if (is_scrollbar_group)
7804 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007805# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007806 if (is_tooltip_group)
7807 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007808# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007809 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007810# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007811 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007812# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007813 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007814#endif
7815 }
7816 else if (STRCMP(key, "GUIBG") == 0)
7817 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007818#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007819 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007820 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007821 if (!init)
7822 HL_TABLE()[idx].sg_set |= SG_GUI;
7823
Bram Moolenaar61623362010-07-14 22:04:22 +02007824# ifdef FEAT_GUI
7825 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007826 i = color_name2handle(arg);
7827 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7828 {
7829 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007830# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007831 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7832 if (STRCMP(arg, "NONE") != 0)
7833 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7834 else
7835 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007836# ifdef FEAT_GUI
7837# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007838 if (is_menu_group)
7839 gui.menu_bg_pixel = i;
7840 if (is_scrollbar_group)
7841 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007842# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007843 if (is_tooltip_group)
7844 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007845# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007846 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007847# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007848 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007849# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007850 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007851#endif
7852 }
7853 else if (STRCMP(key, "GUISP") == 0)
7854 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007855#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007856 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7857 {
7858 if (!init)
7859 HL_TABLE()[idx].sg_set |= SG_GUI;
7860
Bram Moolenaar61623362010-07-14 22:04:22 +02007861# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007862 i = color_name2handle(arg);
7863 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7864 {
7865 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007866# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007867 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7868 if (STRCMP(arg, "NONE") != 0)
7869 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7870 else
7871 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007872# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007873 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007874# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007875 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007876#endif
7877 }
7878 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7879 {
7880 char_u buf[100];
7881 char_u *tname;
7882
7883 if (!init)
7884 HL_TABLE()[idx].sg_set |= SG_TERM;
7885
7886 /*
7887 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007888 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007889 */
7890 if (STRNCMP(arg, "t_", 2) == 0)
7891 {
7892 off = 0;
7893 buf[0] = 0;
7894 while (arg[off] != NUL)
7895 {
7896 /* Isolate one termcap name */
7897 for (len = 0; arg[off + len] &&
7898 arg[off + len] != ','; ++len)
7899 ;
7900 tname = vim_strnsave(arg + off, len);
7901 if (tname == NULL) /* out of memory */
7902 {
7903 error = TRUE;
7904 break;
7905 }
7906 /* lookup the escape sequence for the item */
7907 p = get_term_code(tname);
7908 vim_free(tname);
7909 if (p == NULL) /* ignore non-existing things */
7910 p = (char_u *)"";
7911
7912 /* Append it to the already found stuff */
7913 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7914 {
7915 EMSG2(_("E422: terminal code too long: %s"), arg);
7916 error = TRUE;
7917 break;
7918 }
7919 STRCAT(buf, p);
7920
7921 /* Advance to the next item */
7922 off += len;
7923 if (arg[off] == ',') /* another one follows */
7924 ++off;
7925 }
7926 }
7927 else
7928 {
7929 /*
7930 * Copy characters from arg[] to buf[], translating <> codes.
7931 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007932 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007933 {
7934 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007935 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007936 off += len;
7937 else /* copy as normal char */
7938 buf[off++] = *p++;
7939 }
7940 buf[off] = NUL;
7941 }
7942 if (error)
7943 break;
7944
7945 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7946 p = NULL;
7947 else
7948 p = vim_strsave(buf);
7949 if (key[2] == 'A')
7950 {
7951 vim_free(HL_TABLE()[idx].sg_start);
7952 HL_TABLE()[idx].sg_start = p;
7953 }
7954 else
7955 {
7956 vim_free(HL_TABLE()[idx].sg_stop);
7957 HL_TABLE()[idx].sg_stop = p;
7958 }
7959 }
7960 else
7961 {
7962 EMSG2(_("E423: Illegal argument: %s"), key_start);
7963 error = TRUE;
7964 break;
7965 }
7966
7967 /*
7968 * When highlighting has been given for a group, don't link it.
7969 */
7970 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7971 HL_TABLE()[idx].sg_link = 0;
7972
7973 /*
7974 * Continue with next argument.
7975 */
7976 linep = skipwhite(linep);
7977 }
7978
7979 /*
7980 * If there is an error, and it's a new entry, remove it from the table.
7981 */
7982 if (error && idx == highlight_ga.ga_len)
7983 syn_unadd_group();
7984 else
7985 {
7986 if (is_normal_group)
7987 {
7988 HL_TABLE()[idx].sg_term_attr = 0;
7989 HL_TABLE()[idx].sg_cterm_attr = 0;
7990#ifdef FEAT_GUI
7991 HL_TABLE()[idx].sg_gui_attr = 0;
7992 /*
7993 * Need to update all groups, because they might be using "bg"
7994 * and/or "fg", which have been changed now.
7995 */
7996 if (gui.in_use)
7997 highlight_gui_started();
7998#endif
7999 }
8000#ifdef FEAT_GUI_X11
8001# ifdef FEAT_MENU
8002 else if (is_menu_group)
8003 {
8004 if (gui.in_use && do_colors)
8005 gui_mch_new_menu_colors();
8006 }
8007# endif
8008 else if (is_scrollbar_group)
8009 {
8010 if (gui.in_use && do_colors)
8011 gui_new_scrollbar_colors();
8012 }
8013# ifdef FEAT_BEVAL
8014 else if (is_tooltip_group)
8015 {
8016 if (gui.in_use && do_colors)
8017 gui_mch_new_tooltip_colors();
8018 }
8019# endif
8020#endif
8021 else
8022 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008023#ifdef FEAT_EVAL
8024 HL_TABLE()[idx].sg_scriptID = current_SID;
8025#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008026 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008027 }
8028 vim_free(key);
8029 vim_free(arg);
8030
8031 /* Only call highlight_changed() once, after sourcing a syntax file */
8032 need_highlight_changed = TRUE;
8033}
8034
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008035#if defined(EXITFREE) || defined(PROTO)
8036 void
8037free_highlight()
8038{
8039 int i;
8040
8041 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008042 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008043 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008044 vim_free(HL_TABLE()[i].sg_name);
8045 vim_free(HL_TABLE()[i].sg_name_u);
8046 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008047 ga_clear(&highlight_ga);
8048}
8049#endif
8050
Bram Moolenaar071d4272004-06-13 20:20:40 +00008051/*
8052 * Reset the cterm colors to what they were before Vim was started, if
8053 * possible. Otherwise reset them to zero.
8054 */
8055 void
8056restore_cterm_colors()
8057{
8058#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
8059 /* Since t_me has been set, this probably means that the user
8060 * wants to use this as default colors. Need to reset default
8061 * background/foreground colors. */
8062 mch_set_normal_colors();
8063#else
8064 cterm_normal_fg_color = 0;
8065 cterm_normal_fg_bold = 0;
8066 cterm_normal_bg_color = 0;
8067#endif
8068}
8069
8070/*
8071 * Return TRUE if highlight group "idx" has any settings.
8072 * When "check_link" is TRUE also check for an existing link.
8073 */
8074 static int
8075hl_has_settings(idx, check_link)
8076 int idx;
8077 int check_link;
8078{
8079 return ( HL_TABLE()[idx].sg_term_attr != 0
8080 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008081 || HL_TABLE()[idx].sg_cterm_fg != 0
8082 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008083#ifdef FEAT_GUI
8084 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008085 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8086 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8087 || HL_TABLE()[idx].sg_gui_sp_name != NULL
8088 || HL_TABLE()[idx].sg_font_name != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008089#endif
8090 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8091}
8092
8093/*
8094 * Clear highlighting for one group.
8095 */
8096 static void
8097highlight_clear(idx)
8098 int idx;
8099{
8100 HL_TABLE()[idx].sg_term = 0;
8101 vim_free(HL_TABLE()[idx].sg_start);
8102 HL_TABLE()[idx].sg_start = NULL;
8103 vim_free(HL_TABLE()[idx].sg_stop);
8104 HL_TABLE()[idx].sg_stop = NULL;
8105 HL_TABLE()[idx].sg_term_attr = 0;
8106 HL_TABLE()[idx].sg_cterm = 0;
8107 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8108 HL_TABLE()[idx].sg_cterm_fg = 0;
8109 HL_TABLE()[idx].sg_cterm_bg = 0;
8110 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008111#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008112 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008113 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8114 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008115 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8116 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008117 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8118 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008119#endif
8120#ifdef FEAT_GUI
8121 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8122 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
8123 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008124 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8125 HL_TABLE()[idx].sg_font = NOFONT;
8126# ifdef FEAT_XFONTSET
8127 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8128 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8129# endif
8130 vim_free(HL_TABLE()[idx].sg_font_name);
8131 HL_TABLE()[idx].sg_font_name = NULL;
8132 HL_TABLE()[idx].sg_gui_attr = 0;
8133#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008134#ifdef FEAT_EVAL
8135 /* Clear the script ID only when there is no link, since that is not
8136 * cleared. */
8137 if (HL_TABLE()[idx].sg_link == 0)
8138 HL_TABLE()[idx].sg_scriptID = 0;
8139#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008140}
8141
8142#if defined(FEAT_GUI) || defined(PROTO)
8143/*
8144 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008145 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008146 * "Tooltip" colors.
8147 */
8148 void
8149set_normal_colors()
8150{
8151 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008152 &gui.norm_pixel, &gui.back_pixel,
8153 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008154 {
8155 gui_mch_new_colors();
8156 must_redraw = CLEAR;
8157 }
8158#ifdef FEAT_GUI_X11
8159 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008160 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8161 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008162 {
8163# ifdef FEAT_MENU
8164 gui_mch_new_menu_colors();
8165# endif
8166 must_redraw = CLEAR;
8167 }
8168# ifdef FEAT_BEVAL
8169 if (set_group_colors((char_u *)"Tooltip",
8170 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8171 FALSE, FALSE, TRUE))
8172 {
8173# ifdef FEAT_TOOLBAR
8174 gui_mch_new_tooltip_colors();
8175# endif
8176 must_redraw = CLEAR;
8177 }
8178#endif
8179 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008180 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8181 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008182 {
8183 gui_new_scrollbar_colors();
8184 must_redraw = CLEAR;
8185 }
8186#endif
8187}
8188
8189/*
8190 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8191 */
8192 static int
8193set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
8194 char_u *name;
8195 guicolor_T *fgp;
8196 guicolor_T *bgp;
8197 int do_menu;
8198 int use_norm;
8199 int do_tooltip;
8200{
8201 int idx;
8202
8203 idx = syn_name2id(name) - 1;
8204 if (idx >= 0)
8205 {
8206 gui_do_one_color(idx, do_menu, do_tooltip);
8207
8208 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8209 *fgp = HL_TABLE()[idx].sg_gui_fg;
8210 else if (use_norm)
8211 *fgp = gui.def_norm_pixel;
8212 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8213 *bgp = HL_TABLE()[idx].sg_gui_bg;
8214 else if (use_norm)
8215 *bgp = gui.def_back_pixel;
8216 return TRUE;
8217 }
8218 return FALSE;
8219}
8220
8221/*
8222 * Get the font of the "Normal" group.
8223 * Returns "" when it's not found or not set.
8224 */
8225 char_u *
8226hl_get_font_name()
8227{
8228 int id;
8229 char_u *s;
8230
8231 id = syn_name2id((char_u *)"Normal");
8232 if (id > 0)
8233 {
8234 s = HL_TABLE()[id - 1].sg_font_name;
8235 if (s != NULL)
8236 return s;
8237 }
8238 return (char_u *)"";
8239}
8240
8241/*
8242 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8243 * actually chosen to be used.
8244 */
8245 void
8246hl_set_font_name(font_name)
8247 char_u *font_name;
8248{
8249 int id;
8250
8251 id = syn_name2id((char_u *)"Normal");
8252 if (id > 0)
8253 {
8254 vim_free(HL_TABLE()[id - 1].sg_font_name);
8255 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8256 }
8257}
8258
8259/*
8260 * Set background color for "Normal" group. Called by gui_set_bg_color()
8261 * when the color is known.
8262 */
8263 void
8264hl_set_bg_color_name(name)
8265 char_u *name; /* must have been allocated */
8266{
8267 int id;
8268
8269 if (name != NULL)
8270 {
8271 id = syn_name2id((char_u *)"Normal");
8272 if (id > 0)
8273 {
8274 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8275 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8276 }
8277 }
8278}
8279
8280/*
8281 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8282 * when the color is known.
8283 */
8284 void
8285hl_set_fg_color_name(name)
8286 char_u *name; /* must have been allocated */
8287{
8288 int id;
8289
8290 if (name != NULL)
8291 {
8292 id = syn_name2id((char_u *)"Normal");
8293 if (id > 0)
8294 {
8295 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8296 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8297 }
8298 }
8299}
8300
8301/*
8302 * Return the handle for a color name.
8303 * Returns INVALCOLOR when failed.
8304 */
8305 static guicolor_T
8306color_name2handle(name)
8307 char_u *name;
8308{
8309 if (STRCMP(name, "NONE") == 0)
8310 return INVALCOLOR;
8311
8312 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8313 return gui.norm_pixel;
8314 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8315 return gui.back_pixel;
8316
8317 return gui_get_color(name);
8318}
8319
8320/*
8321 * Return the handle for a font name.
8322 * Returns NOFONT when failed.
8323 */
8324 static GuiFont
8325font_name2handle(name)
8326 char_u *name;
8327{
8328 if (STRCMP(name, "NONE") == 0)
8329 return NOFONT;
8330
8331 return gui_mch_get_font(name, TRUE);
8332}
8333
8334# ifdef FEAT_XFONTSET
8335/*
8336 * Return the handle for a fontset name.
8337 * Returns NOFONTSET when failed.
8338 */
8339 static GuiFontset
8340fontset_name2handle(name, fixed_width)
8341 char_u *name;
8342 int fixed_width;
8343{
8344 if (STRCMP(name, "NONE") == 0)
8345 return NOFONTSET;
8346
8347 return gui_mch_get_fontset(name, TRUE, fixed_width);
8348}
8349# endif
8350
8351/*
8352 * Get the font or fontset for one highlight group.
8353 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008354 static void
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008355hl_do_font(idx, arg, do_normal, do_menu, do_tooltip, free_font)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008356 int idx;
8357 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008358 int do_normal; /* set normal font */
8359 int do_menu UNUSED; /* set menu font */
8360 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008361 int free_font; /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008362{
8363# ifdef FEAT_XFONTSET
8364 /* If 'guifontset' is not empty, first try using the name as a
8365 * fontset. If that doesn't work, use it as a font name. */
8366 if (*p_guifontset != NUL
8367# ifdef FONTSET_ALWAYS
8368 || do_menu
8369# endif
8370# ifdef FEAT_BEVAL_TIP
8371 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8372 || do_tooltip
8373# endif
8374 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008375 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008376 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008377 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008378 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8379# ifdef FONTSET_ALWAYS
8380 || do_menu
8381# endif
8382# ifdef FEAT_BEVAL_TIP
8383 || do_tooltip
8384# endif
8385 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008386 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008387 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8388 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008389 /* If it worked and it's the Normal group, use it as the normal
8390 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008391 if (do_normal)
8392 gui_init_font(arg, TRUE);
8393# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8394 if (do_menu)
8395 {
8396# ifdef FONTSET_ALWAYS
8397 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8398# else
8399 /* YIKES! This is a bug waiting to crash the program */
8400 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8401# endif
8402 gui_mch_new_menu_font();
8403 }
8404# ifdef FEAT_BEVAL
8405 if (do_tooltip)
8406 {
8407 /* The Athena widget set cannot currently handle switching between
8408 * displaying a single font and a fontset.
8409 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008410 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008411 * XFontStruct is used.
8412 */
8413 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8414 gui_mch_new_tooltip_font();
8415 }
8416# endif
8417# endif
8418 }
8419 else
8420# endif
8421 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008422 if (free_font)
8423 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008424 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8425 /* If it worked and it's the Normal group, use it as the
8426 * normal font. Same for the Menu group. */
8427 if (HL_TABLE()[idx].sg_font != NOFONT)
8428 {
8429 if (do_normal)
8430 gui_init_font(arg, FALSE);
8431#ifndef FONTSET_ALWAYS
8432# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8433 if (do_menu)
8434 {
8435 gui.menu_font = HL_TABLE()[idx].sg_font;
8436 gui_mch_new_menu_font();
8437 }
8438# endif
8439#endif
8440 }
8441 }
8442}
8443
8444#endif /* FEAT_GUI */
8445
8446/*
8447 * Table with the specifications for an attribute number.
8448 * Note that this table is used by ALL buffers. This is required because the
8449 * GUI can redraw at any time for any buffer.
8450 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008451static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008452
8453#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8454
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008455static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008456
8457#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8458
8459#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008460static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008461
8462#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8463#endif
8464
8465/*
8466 * Return the attr number for a set of colors and font.
8467 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8468 * if the combination is new.
8469 * Return 0 for error (no more room).
8470 */
8471 static int
8472get_attr_entry(table, aep)
8473 garray_T *table;
8474 attrentry_T *aep;
8475{
8476 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008477 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008478 static int recursive = FALSE;
8479
8480 /*
8481 * Init the table, in case it wasn't done yet.
8482 */
8483 table->ga_itemsize = sizeof(attrentry_T);
8484 table->ga_growsize = 7;
8485
8486 /*
8487 * Try to find an entry with the same specifications.
8488 */
8489 for (i = 0; i < table->ga_len; ++i)
8490 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008491 taep = &(((attrentry_T *)table->ga_data)[i]);
8492 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008493 && (
8494#ifdef FEAT_GUI
8495 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008496 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8497 && aep->ae_u.gui.bg_color
8498 == taep->ae_u.gui.bg_color
8499 && aep->ae_u.gui.sp_color
8500 == taep->ae_u.gui.sp_color
8501 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008502# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008503 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008504# endif
8505 ))
8506 ||
8507#endif
8508 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008509 && (aep->ae_u.term.start == NULL)
8510 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008511 && (aep->ae_u.term.start == NULL
8512 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008513 taep->ae_u.term.start) == 0)
8514 && (aep->ae_u.term.stop == NULL)
8515 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008516 && (aep->ae_u.term.stop == NULL
8517 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008518 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008519 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008520 && aep->ae_u.cterm.fg_color
8521 == taep->ae_u.cterm.fg_color
8522 && aep->ae_u.cterm.bg_color
8523 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008524 ))
8525
8526 return i + ATTR_OFF;
8527 }
8528
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008529 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008530 {
8531 /*
8532 * Running out of attribute entries! remove all attributes, and
8533 * compute new ones for all groups.
8534 * When called recursively, we are really out of numbers.
8535 */
8536 if (recursive)
8537 {
8538 EMSG(_("E424: Too many different highlighting attributes in use"));
8539 return 0;
8540 }
8541 recursive = TRUE;
8542
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008543 clear_hl_tables();
8544
Bram Moolenaar071d4272004-06-13 20:20:40 +00008545 must_redraw = CLEAR;
8546
8547 for (i = 0; i < highlight_ga.ga_len; ++i)
8548 set_hl_attr(i);
8549
8550 recursive = FALSE;
8551 }
8552
8553 /*
8554 * This is a new combination of colors and font, add an entry.
8555 */
8556 if (ga_grow(table, 1) == FAIL)
8557 return 0;
8558
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008559 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8560 vim_memset(taep, 0, sizeof(attrentry_T));
8561 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008562#ifdef FEAT_GUI
8563 if (table == &gui_attr_table)
8564 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008565 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8566 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8567 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8568 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008569# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008570 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008571# endif
8572 }
8573#endif
8574 if (table == &term_attr_table)
8575 {
8576 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008577 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008578 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008579 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008580 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008581 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008582 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008583 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008584 }
8585 else if (table == &cterm_attr_table)
8586 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008587 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8588 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008589 }
8590 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008591 return (table->ga_len - 1 + ATTR_OFF);
8592}
8593
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008594/*
8595 * Clear all highlight tables.
8596 */
8597 void
8598clear_hl_tables()
8599{
8600 int i;
8601 attrentry_T *taep;
8602
8603#ifdef FEAT_GUI
8604 ga_clear(&gui_attr_table);
8605#endif
8606 for (i = 0; i < term_attr_table.ga_len; ++i)
8607 {
8608 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8609 vim_free(taep->ae_u.term.start);
8610 vim_free(taep->ae_u.term.stop);
8611 }
8612 ga_clear(&term_attr_table);
8613 ga_clear(&cterm_attr_table);
8614}
8615
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008616#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008617/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008618 * Combine special attributes (e.g., for spelling) with other attributes
8619 * (e.g., for syntax highlighting).
8620 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008621 * This creates a new group when required.
8622 * Since we expect there to be few spelling mistakes we don't cache the
8623 * result.
8624 * Return the resulting attributes.
8625 */
8626 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008627hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008628 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008629 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008630{
8631 attrentry_T *char_aep = NULL;
8632 attrentry_T *spell_aep;
8633 attrentry_T new_en;
8634
8635 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008636 return prim_attr;
8637 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8638 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008639#ifdef FEAT_GUI
8640 if (gui.in_use)
8641 {
8642 if (char_attr > HL_ALL)
8643 char_aep = syn_gui_attr2entry(char_attr);
8644 if (char_aep != NULL)
8645 new_en = *char_aep;
8646 else
8647 {
8648 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008649 new_en.ae_u.gui.fg_color = INVALCOLOR;
8650 new_en.ae_u.gui.bg_color = INVALCOLOR;
8651 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008652 if (char_attr <= HL_ALL)
8653 new_en.ae_attr = char_attr;
8654 }
8655
Bram Moolenaar30abd282005-06-22 22:35:10 +00008656 if (prim_attr <= HL_ALL)
8657 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008658 else
8659 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008660 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008661 if (spell_aep != NULL)
8662 {
8663 new_en.ae_attr |= spell_aep->ae_attr;
8664 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8665 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8666 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8667 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8668 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8669 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8670 if (spell_aep->ae_u.gui.font != NOFONT)
8671 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8672# ifdef FEAT_XFONTSET
8673 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8674 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8675# endif
8676 }
8677 }
8678 return get_attr_entry(&gui_attr_table, &new_en);
8679 }
8680#endif
8681
8682 if (t_colors > 1)
8683 {
8684 if (char_attr > HL_ALL)
8685 char_aep = syn_cterm_attr2entry(char_attr);
8686 if (char_aep != NULL)
8687 new_en = *char_aep;
8688 else
8689 {
8690 vim_memset(&new_en, 0, sizeof(new_en));
8691 if (char_attr <= HL_ALL)
8692 new_en.ae_attr = char_attr;
8693 }
8694
Bram Moolenaar30abd282005-06-22 22:35:10 +00008695 if (prim_attr <= HL_ALL)
8696 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008697 else
8698 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008699 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008700 if (spell_aep != NULL)
8701 {
8702 new_en.ae_attr |= spell_aep->ae_attr;
8703 if (spell_aep->ae_u.cterm.fg_color > 0)
8704 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8705 if (spell_aep->ae_u.cterm.bg_color > 0)
8706 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8707 }
8708 }
8709 return get_attr_entry(&cterm_attr_table, &new_en);
8710 }
8711
8712 if (char_attr > HL_ALL)
8713 char_aep = syn_term_attr2entry(char_attr);
8714 if (char_aep != NULL)
8715 new_en = *char_aep;
8716 else
8717 {
8718 vim_memset(&new_en, 0, sizeof(new_en));
8719 if (char_attr <= HL_ALL)
8720 new_en.ae_attr = char_attr;
8721 }
8722
Bram Moolenaar30abd282005-06-22 22:35:10 +00008723 if (prim_attr <= HL_ALL)
8724 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008725 else
8726 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008727 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008728 if (spell_aep != NULL)
8729 {
8730 new_en.ae_attr |= spell_aep->ae_attr;
8731 if (spell_aep->ae_u.term.start != NULL)
8732 {
8733 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8734 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8735 }
8736 }
8737 }
8738 return get_attr_entry(&term_attr_table, &new_en);
8739}
8740#endif
8741
Bram Moolenaar071d4272004-06-13 20:20:40 +00008742#ifdef FEAT_GUI
8743
8744 attrentry_T *
8745syn_gui_attr2entry(attr)
8746 int attr;
8747{
8748 attr -= ATTR_OFF;
8749 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8750 return NULL;
8751 return &(GUI_ATTR_ENTRY(attr));
8752}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008753#endif /* FEAT_GUI */
8754
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008755/*
8756 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8757 * Only to be used when "attr" > HL_ALL.
8758 */
8759 int
8760syn_attr2attr(attr)
8761 int attr;
8762{
8763 attrentry_T *aep;
8764
8765#ifdef FEAT_GUI
8766 if (gui.in_use)
8767 aep = syn_gui_attr2entry(attr);
8768 else
8769#endif
8770 if (t_colors > 1)
8771 aep = syn_cterm_attr2entry(attr);
8772 else
8773 aep = syn_term_attr2entry(attr);
8774
8775 if (aep == NULL) /* highlighting not set */
8776 return 0;
8777 return aep->ae_attr;
8778}
8779
8780
Bram Moolenaar071d4272004-06-13 20:20:40 +00008781 attrentry_T *
8782syn_term_attr2entry(attr)
8783 int attr;
8784{
8785 attr -= ATTR_OFF;
8786 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8787 return NULL;
8788 return &(TERM_ATTR_ENTRY(attr));
8789}
8790
8791 attrentry_T *
8792syn_cterm_attr2entry(attr)
8793 int attr;
8794{
8795 attr -= ATTR_OFF;
8796 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8797 return NULL;
8798 return &(CTERM_ATTR_ENTRY(attr));
8799}
8800
8801#define LIST_ATTR 1
8802#define LIST_STRING 2
8803#define LIST_INT 3
8804
8805 static void
8806highlight_list_one(id)
8807 int id;
8808{
8809 struct hl_group *sgp;
8810 int didh = FALSE;
8811
8812 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8813
8814 didh = highlight_list_arg(id, didh, LIST_ATTR,
8815 sgp->sg_term, NULL, "term");
8816 didh = highlight_list_arg(id, didh, LIST_STRING,
8817 0, sgp->sg_start, "start");
8818 didh = highlight_list_arg(id, didh, LIST_STRING,
8819 0, sgp->sg_stop, "stop");
8820
8821 didh = highlight_list_arg(id, didh, LIST_ATTR,
8822 sgp->sg_cterm, NULL, "cterm");
8823 didh = highlight_list_arg(id, didh, LIST_INT,
8824 sgp->sg_cterm_fg, NULL, "ctermfg");
8825 didh = highlight_list_arg(id, didh, LIST_INT,
8826 sgp->sg_cterm_bg, NULL, "ctermbg");
8827
Bram Moolenaar61623362010-07-14 22:04:22 +02008828#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008829 didh = highlight_list_arg(id, didh, LIST_ATTR,
8830 sgp->sg_gui, NULL, "gui");
8831 didh = highlight_list_arg(id, didh, LIST_STRING,
8832 0, sgp->sg_gui_fg_name, "guifg");
8833 didh = highlight_list_arg(id, didh, LIST_STRING,
8834 0, sgp->sg_gui_bg_name, "guibg");
8835 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008836 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008837#endif
8838#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008839 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008840 0, sgp->sg_font_name, "font");
8841#endif
8842
Bram Moolenaar661b1822005-07-28 22:36:45 +00008843 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008844 {
8845 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008846 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008847 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8848 msg_putchar(' ');
8849 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8850 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008851
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008852 if (!didh)
8853 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008854#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008855 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008856 last_set_msg(sgp->sg_scriptID);
8857#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008858}
8859
8860 static int
8861highlight_list_arg(id, didh, type, iarg, sarg, name)
8862 int id;
8863 int didh;
8864 int type;
8865 int iarg;
8866 char_u *sarg;
8867 char *name;
8868{
8869 char_u buf[100];
8870 char_u *ts;
8871 int i;
8872
Bram Moolenaar661b1822005-07-28 22:36:45 +00008873 if (got_int)
8874 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008875 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8876 {
8877 ts = buf;
8878 if (type == LIST_INT)
8879 sprintf((char *)buf, "%d", iarg - 1);
8880 else if (type == LIST_STRING)
8881 ts = sarg;
8882 else /* type == LIST_ATTR */
8883 {
8884 buf[0] = NUL;
8885 for (i = 0; hl_attr_table[i] != 0; ++i)
8886 {
8887 if (iarg & hl_attr_table[i])
8888 {
8889 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008890 vim_strcat(buf, (char_u *)",", 100);
8891 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008892 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8893 }
8894 }
8895 }
8896
8897 (void)syn_list_header(didh,
8898 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8899 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008900 if (!got_int)
8901 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008902 if (*name != NUL)
8903 {
8904 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8905 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8906 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008907 msg_outtrans(ts);
8908 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008909 }
8910 return didh;
8911}
8912
8913#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8914/*
8915 * Return "1" if highlight group "id" has attribute "flag".
8916 * Return NULL otherwise.
8917 */
8918 char_u *
8919highlight_has_attr(id, flag, modec)
8920 int id;
8921 int flag;
8922 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8923{
8924 int attr;
8925
8926 if (id <= 0 || id > highlight_ga.ga_len)
8927 return NULL;
8928
Bram Moolenaar61623362010-07-14 22:04:22 +02008929#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008930 if (modec == 'g')
8931 attr = HL_TABLE()[id - 1].sg_gui;
8932 else
8933#endif
8934 if (modec == 'c')
8935 attr = HL_TABLE()[id - 1].sg_cterm;
8936 else
8937 attr = HL_TABLE()[id - 1].sg_term;
8938
8939 if (attr & flag)
8940 return (char_u *)"1";
8941 return NULL;
8942}
8943#endif
8944
8945#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8946/*
8947 * Return color name of highlight group "id".
8948 */
8949 char_u *
8950highlight_color(id, what, modec)
8951 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008952 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008953 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8954{
8955 static char_u name[20];
8956 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008957 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008958 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008959 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008960
8961 if (id <= 0 || id > highlight_ga.ga_len)
8962 return NULL;
8963
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008964 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008965 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008966 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008967 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008968 font = TRUE;
8969 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008970 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008971 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8972 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008973 if (modec == 'g')
8974 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008975# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008976 /* return font name */
8977 if (font)
8978 return HL_TABLE()[id - 1].sg_font_name;
8979
Bram Moolenaar071d4272004-06-13 20:20:40 +00008980 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008981 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008982 {
8983 guicolor_T color;
8984 long_u rgb;
8985 static char_u buf[10];
8986
8987 if (fg)
8988 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008989 else if (sp)
8990 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008991 else
8992 color = HL_TABLE()[id - 1].sg_gui_bg;
8993 if (color == INVALCOLOR)
8994 return NULL;
8995 rgb = gui_mch_get_rgb(color);
8996 sprintf((char *)buf, "#%02x%02x%02x",
8997 (unsigned)(rgb >> 16),
8998 (unsigned)(rgb >> 8) & 255,
8999 (unsigned)rgb & 255);
9000 return buf;
9001 }
Bram Moolenaar61623362010-07-14 22:04:22 +02009002#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009003 if (fg)
9004 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009005 if (sp)
9006 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009007 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9008 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009009 if (font || sp)
9010 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009011 if (modec == 'c')
9012 {
9013 if (fg)
9014 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9015 else
9016 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
9017 sprintf((char *)name, "%d", n);
9018 return name;
9019 }
9020 /* term doesn't have color */
9021 return NULL;
9022}
9023#endif
9024
9025#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
9026 || defined(PROTO)
9027/*
9028 * Return color name of highlight group "id" as RGB value.
9029 */
9030 long_u
9031highlight_gui_color_rgb(id, fg)
9032 int id;
9033 int fg; /* TRUE = fg, FALSE = bg */
9034{
9035 guicolor_T color;
9036
9037 if (id <= 0 || id > highlight_ga.ga_len)
9038 return 0L;
9039
9040 if (fg)
9041 color = HL_TABLE()[id - 1].sg_gui_fg;
9042 else
9043 color = HL_TABLE()[id - 1].sg_gui_bg;
9044
9045 if (color == INVALCOLOR)
9046 return 0L;
9047
9048 return gui_mch_get_rgb(color);
9049}
9050#endif
9051
9052/*
9053 * Output the syntax list header.
9054 * Return TRUE when started a new line.
9055 */
9056 static int
9057syn_list_header(did_header, outlen, id)
9058 int did_header; /* did header already */
9059 int outlen; /* length of string that comes */
9060 int id; /* highlight group id */
9061{
9062 int endcol = 19;
9063 int newline = TRUE;
9064
9065 if (!did_header)
9066 {
9067 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009068 if (got_int)
9069 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009070 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9071 endcol = 15;
9072 }
9073 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009074 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009075 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009076 if (got_int)
9077 return TRUE;
9078 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009079 else
9080 {
9081 if (msg_col >= endcol) /* wrap around is like starting a new line */
9082 newline = FALSE;
9083 }
9084
9085 if (msg_col >= endcol) /* output at least one space */
9086 endcol = msg_col + 1;
9087 if (Columns <= endcol) /* avoid hang for tiny window */
9088 endcol = Columns - 1;
9089
9090 msg_advance(endcol);
9091
9092 /* Show "xxx" with the attributes. */
9093 if (!did_header)
9094 {
9095 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9096 msg_putchar(' ');
9097 }
9098
9099 return newline;
9100}
9101
9102/*
9103 * Set the attribute numbers for a highlight group.
9104 * Called after one of the attributes has changed.
9105 */
9106 static void
9107set_hl_attr(idx)
9108 int idx; /* index in array */
9109{
9110 attrentry_T at_en;
9111 struct hl_group *sgp = HL_TABLE() + idx;
9112
9113 /* The "Normal" group doesn't need an attribute number */
9114 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9115 return;
9116
9117#ifdef FEAT_GUI
9118 /*
9119 * For the GUI mode: If there are other than "normal" highlighting
9120 * attributes, need to allocate an attr number.
9121 */
9122 if (sgp->sg_gui_fg == INVALCOLOR
9123 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009124 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009125 && sgp->sg_font == NOFONT
9126# ifdef FEAT_XFONTSET
9127 && sgp->sg_fontset == NOFONTSET
9128# endif
9129 )
9130 {
9131 sgp->sg_gui_attr = sgp->sg_gui;
9132 }
9133 else
9134 {
9135 at_en.ae_attr = sgp->sg_gui;
9136 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9137 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009138 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009139 at_en.ae_u.gui.font = sgp->sg_font;
9140# ifdef FEAT_XFONTSET
9141 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9142# endif
9143 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9144 }
9145#endif
9146 /*
9147 * For the term mode: If there are other than "normal" highlighting
9148 * attributes, need to allocate an attr number.
9149 */
9150 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9151 sgp->sg_term_attr = sgp->sg_term;
9152 else
9153 {
9154 at_en.ae_attr = sgp->sg_term;
9155 at_en.ae_u.term.start = sgp->sg_start;
9156 at_en.ae_u.term.stop = sgp->sg_stop;
9157 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9158 }
9159
9160 /*
9161 * For the color term mode: If there are other than "normal"
9162 * highlighting attributes, need to allocate an attr number.
9163 */
9164 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
9165 sgp->sg_cterm_attr = sgp->sg_cterm;
9166 else
9167 {
9168 at_en.ae_attr = sgp->sg_cterm;
9169 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9170 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
9171 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9172 }
9173}
9174
9175/*
9176 * Lookup a highlight group name and return it's ID.
9177 * If it is not found, 0 is returned.
9178 */
9179 int
9180syn_name2id(name)
9181 char_u *name;
9182{
9183 int i;
9184 char_u name_u[200];
9185
9186 /* Avoid using stricmp() too much, it's slow on some systems */
9187 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9188 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009189 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009190 vim_strup(name_u);
9191 for (i = highlight_ga.ga_len; --i >= 0; )
9192 if (HL_TABLE()[i].sg_name_u != NULL
9193 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9194 break;
9195 return i + 1;
9196}
9197
9198#if defined(FEAT_EVAL) || defined(PROTO)
9199/*
9200 * Return TRUE if highlight group "name" exists.
9201 */
9202 int
9203highlight_exists(name)
9204 char_u *name;
9205{
9206 return (syn_name2id(name) > 0);
9207}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009208
9209# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9210/*
9211 * Return the name of highlight group "id".
9212 * When not a valid ID return an empty string.
9213 */
9214 char_u *
9215syn_id2name(id)
9216 int id;
9217{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009218 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009219 return (char_u *)"";
9220 return HL_TABLE()[id - 1].sg_name;
9221}
9222# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009223#endif
9224
9225/*
9226 * Like syn_name2id(), but take a pointer + length argument.
9227 */
9228 int
9229syn_namen2id(linep, len)
9230 char_u *linep;
9231 int len;
9232{
9233 char_u *name;
9234 int id = 0;
9235
9236 name = vim_strnsave(linep, len);
9237 if (name != NULL)
9238 {
9239 id = syn_name2id(name);
9240 vim_free(name);
9241 }
9242 return id;
9243}
9244
9245/*
9246 * Find highlight group name in the table and return it's ID.
9247 * The argument is a pointer to the name and the length of the name.
9248 * If it doesn't exist yet, a new entry is created.
9249 * Return 0 for failure.
9250 */
9251 int
9252syn_check_group(pp, len)
9253 char_u *pp;
9254 int len;
9255{
9256 int id;
9257 char_u *name;
9258
9259 name = vim_strnsave(pp, len);
9260 if (name == NULL)
9261 return 0;
9262
9263 id = syn_name2id(name);
9264 if (id == 0) /* doesn't exist yet */
9265 id = syn_add_group(name);
9266 else
9267 vim_free(name);
9268 return id;
9269}
9270
9271/*
9272 * Add new highlight group and return it's ID.
9273 * "name" must be an allocated string, it will be consumed.
9274 * Return 0 for failure.
9275 */
9276 static int
9277syn_add_group(name)
9278 char_u *name;
9279{
9280 char_u *p;
9281
9282 /* Check that the name is ASCII letters, digits and underscore. */
9283 for (p = name; *p != NUL; ++p)
9284 {
9285 if (!vim_isprintc(*p))
9286 {
9287 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009288 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009289 return 0;
9290 }
9291 else if (!ASCII_ISALNUM(*p) && *p != '_')
9292 {
9293 /* This is an error, but since there previously was no check only
9294 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009295 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009296 MSG(_("W18: Invalid character in group name"));
9297 break;
9298 }
9299 }
9300
9301 /*
9302 * First call for this growarray: init growing array.
9303 */
9304 if (highlight_ga.ga_data == NULL)
9305 {
9306 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9307 highlight_ga.ga_growsize = 10;
9308 }
9309
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009310 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009311 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009312 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009313 vim_free(name);
9314 return 0;
9315 }
9316
Bram Moolenaar071d4272004-06-13 20:20:40 +00009317 /*
9318 * Make room for at least one other syntax_highlight entry.
9319 */
9320 if (ga_grow(&highlight_ga, 1) == FAIL)
9321 {
9322 vim_free(name);
9323 return 0;
9324 }
9325
9326 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9327 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9328 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9329#ifdef FEAT_GUI
9330 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9331 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009332 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009333#endif
9334 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009335
9336 return highlight_ga.ga_len; /* ID is index plus one */
9337}
9338
9339/*
9340 * When, just after calling syn_add_group(), an error is discovered, this
9341 * function deletes the new name.
9342 */
9343 static void
9344syn_unadd_group()
9345{
9346 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009347 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9348 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9349}
9350
9351/*
9352 * Translate a group ID to highlight attributes.
9353 */
9354 int
9355syn_id2attr(hl_id)
9356 int hl_id;
9357{
9358 int attr;
9359 struct hl_group *sgp;
9360
9361 hl_id = syn_get_final_id(hl_id);
9362 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9363
9364#ifdef FEAT_GUI
9365 /*
9366 * Only use GUI attr when the GUI is being used.
9367 */
9368 if (gui.in_use)
9369 attr = sgp->sg_gui_attr;
9370 else
9371#endif
9372 if (t_colors > 1)
9373 attr = sgp->sg_cterm_attr;
9374 else
9375 attr = sgp->sg_term_attr;
9376
9377 return attr;
9378}
9379
9380#ifdef FEAT_GUI
9381/*
9382 * Get the GUI colors and attributes for a group ID.
9383 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9384 */
9385 int
9386syn_id2colors(hl_id, fgp, bgp)
9387 int hl_id;
9388 guicolor_T *fgp;
9389 guicolor_T *bgp;
9390{
9391 struct hl_group *sgp;
9392
9393 hl_id = syn_get_final_id(hl_id);
9394 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9395
9396 *fgp = sgp->sg_gui_fg;
9397 *bgp = sgp->sg_gui_bg;
9398 return sgp->sg_gui;
9399}
9400#endif
9401
9402/*
9403 * Translate a group ID to the final group ID (following links).
9404 */
9405 int
9406syn_get_final_id(hl_id)
9407 int hl_id;
9408{
9409 int count;
9410 struct hl_group *sgp;
9411
9412 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9413 return 0; /* Can be called from eval!! */
9414
9415 /*
9416 * Follow links until there is no more.
9417 * Look out for loops! Break after 100 links.
9418 */
9419 for (count = 100; --count >= 0; )
9420 {
9421 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9422 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9423 break;
9424 hl_id = sgp->sg_link;
9425 }
9426
9427 return hl_id;
9428}
9429
9430#ifdef FEAT_GUI
9431/*
9432 * Call this function just after the GUI has started.
9433 * It finds the font and color handles for the highlighting groups.
9434 */
9435 void
9436highlight_gui_started()
9437{
9438 int idx;
9439
9440 /* First get the colors from the "Normal" and "Menu" group, if set */
9441 set_normal_colors();
9442
9443 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9444 gui_do_one_color(idx, FALSE, FALSE);
9445
9446 highlight_changed();
9447}
9448
9449 static void
9450gui_do_one_color(idx, do_menu, do_tooltip)
9451 int idx;
9452 int do_menu; /* TRUE: might set the menu font */
9453 int do_tooltip; /* TRUE: might set the tooltip font */
9454{
9455 int didit = FALSE;
9456
9457 if (HL_TABLE()[idx].sg_font_name != NULL)
9458 {
9459 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009460 do_tooltip, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009461 didit = TRUE;
9462 }
9463 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9464 {
9465 HL_TABLE()[idx].sg_gui_fg =
9466 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9467 didit = TRUE;
9468 }
9469 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9470 {
9471 HL_TABLE()[idx].sg_gui_bg =
9472 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9473 didit = TRUE;
9474 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009475 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9476 {
9477 HL_TABLE()[idx].sg_gui_sp =
9478 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9479 didit = TRUE;
9480 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009481 if (didit) /* need to get a new attr number */
9482 set_hl_attr(idx);
9483}
9484
9485#endif
9486
9487/*
9488 * Translate the 'highlight' option into attributes in highlight_attr[] and
9489 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9490 * corresponding highlights to use on top of HLF_SNC is computed.
9491 * Called only when the 'highlight' option has been changed and upon first
9492 * screen redraw after any :highlight command.
9493 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9494 */
9495 int
9496highlight_changed()
9497{
9498 int hlf;
9499 int i;
9500 char_u *p;
9501 int attr;
9502 char_u *end;
9503 int id;
9504#ifdef USER_HIGHLIGHT
9505 char_u userhl[10];
9506# ifdef FEAT_STL_OPT
9507 int id_SNC = -1;
9508 int id_S = -1;
9509 int hlcnt;
9510# endif
9511#endif
9512 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9513
9514 need_highlight_changed = FALSE;
9515
9516 /*
9517 * Clear all attributes.
9518 */
9519 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9520 highlight_attr[hlf] = 0;
9521
9522 /*
9523 * First set all attributes to their default value.
9524 * Then use the attributes from the 'highlight' option.
9525 */
9526 for (i = 0; i < 2; ++i)
9527 {
9528 if (i)
9529 p = p_hl;
9530 else
9531 p = get_highlight_default();
9532 if (p == NULL) /* just in case */
9533 continue;
9534
9535 while (*p)
9536 {
9537 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9538 if (hl_flags[hlf] == *p)
9539 break;
9540 ++p;
9541 if (hlf == (int)HLF_COUNT || *p == NUL)
9542 return FAIL;
9543
9544 /*
9545 * Allow several hl_flags to be combined, like "bu" for
9546 * bold-underlined.
9547 */
9548 attr = 0;
9549 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9550 {
9551 if (vim_iswhite(*p)) /* ignore white space */
9552 continue;
9553
9554 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9555 return FAIL;
9556
9557 switch (*p)
9558 {
9559 case 'b': attr |= HL_BOLD;
9560 break;
9561 case 'i': attr |= HL_ITALIC;
9562 break;
9563 case '-':
9564 case 'n': /* no highlighting */
9565 break;
9566 case 'r': attr |= HL_INVERSE;
9567 break;
9568 case 's': attr |= HL_STANDOUT;
9569 break;
9570 case 'u': attr |= HL_UNDERLINE;
9571 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009572 case 'c': attr |= HL_UNDERCURL;
9573 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009574 case ':': ++p; /* highlight group name */
9575 if (attr || *p == NUL) /* no combinations */
9576 return FAIL;
9577 end = vim_strchr(p, ',');
9578 if (end == NULL)
9579 end = p + STRLEN(p);
9580 id = syn_check_group(p, (int)(end - p));
9581 if (id == 0)
9582 return FAIL;
9583 attr = syn_id2attr(id);
9584 p = end - 1;
9585#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9586 if (hlf == (int)HLF_SNC)
9587 id_SNC = syn_get_final_id(id);
9588 else if (hlf == (int)HLF_S)
9589 id_S = syn_get_final_id(id);
9590#endif
9591 break;
9592 default: return FAIL;
9593 }
9594 }
9595 highlight_attr[hlf] = attr;
9596
9597 p = skip_to_option_part(p); /* skip comma and spaces */
9598 }
9599 }
9600
9601#ifdef USER_HIGHLIGHT
9602 /* Setup the user highlights
9603 *
9604 * Temporarily utilize 10 more hl entries. Have to be in there
9605 * simultaneously in case of table overflows in get_attr_entry()
9606 */
9607# ifdef FEAT_STL_OPT
9608 if (ga_grow(&highlight_ga, 10) == FAIL)
9609 return FAIL;
9610 hlcnt = highlight_ga.ga_len;
9611 if (id_S == 0)
9612 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009613 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009614 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9615 id_S = hlcnt + 10;
9616 }
9617# endif
9618 for (i = 0; i < 9; i++)
9619 {
9620 sprintf((char *)userhl, "User%d", i + 1);
9621 id = syn_name2id(userhl);
9622 if (id == 0)
9623 {
9624 highlight_user[i] = 0;
9625# ifdef FEAT_STL_OPT
9626 highlight_stlnc[i] = 0;
9627# endif
9628 }
9629 else
9630 {
9631# ifdef FEAT_STL_OPT
9632 struct hl_group *hlt = HL_TABLE();
9633# endif
9634
9635 highlight_user[i] = syn_id2attr(id);
9636# ifdef FEAT_STL_OPT
9637 if (id_SNC == 0)
9638 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009639 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009640 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9641 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009642# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009643 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9644# endif
9645 }
9646 else
9647 mch_memmove(&hlt[hlcnt + i],
9648 &hlt[id_SNC - 1],
9649 sizeof(struct hl_group));
9650 hlt[hlcnt + i].sg_link = 0;
9651
9652 /* Apply difference between UserX and HLF_S to HLF_SNC */
9653 hlt[hlcnt + i].sg_term ^=
9654 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9655 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9656 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9657 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9658 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9659 hlt[hlcnt + i].sg_cterm ^=
9660 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9661 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9662 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9663 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9664 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009665# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009666 hlt[hlcnt + i].sg_gui ^=
9667 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009668# endif
9669# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009670 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9671 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9672 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9673 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009674 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9675 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009676 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9677 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9678# ifdef FEAT_XFONTSET
9679 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9680 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9681# endif
9682# endif
9683 highlight_ga.ga_len = hlcnt + i + 1;
9684 set_hl_attr(hlcnt + i); /* At long last we can apply */
9685 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9686# endif
9687 }
9688 }
9689# ifdef FEAT_STL_OPT
9690 highlight_ga.ga_len = hlcnt;
9691# endif
9692
9693#endif /* USER_HIGHLIGHT */
9694
9695 return OK;
9696}
9697
Bram Moolenaar4f688582007-07-24 12:34:30 +00009698#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009699
9700static void highlight_list __ARGS((void));
9701static void highlight_list_two __ARGS((int cnt, int attr));
9702
9703/*
9704 * Handle command line completion for :highlight command.
9705 */
9706 void
9707set_context_in_highlight_cmd(xp, arg)
9708 expand_T *xp;
9709 char_u *arg;
9710{
9711 char_u *p;
9712
9713 /* Default: expand group names */
9714 xp->xp_context = EXPAND_HIGHLIGHT;
9715 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009716 include_link = 2;
9717 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009718
9719 /* (part of) subcommand already typed */
9720 if (*arg != NUL)
9721 {
9722 p = skiptowhite(arg);
9723 if (*p != NUL) /* past "default" or group name */
9724 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009725 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009726 if (STRNCMP("default", arg, p - arg) == 0)
9727 {
9728 arg = skipwhite(p);
9729 xp->xp_pattern = arg;
9730 p = skiptowhite(arg);
9731 }
9732 if (*p != NUL) /* past group name */
9733 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009734 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009735 if (arg[1] == 'i' && arg[0] == 'N')
9736 highlight_list();
9737 if (STRNCMP("link", arg, p - arg) == 0
9738 || STRNCMP("clear", arg, p - arg) == 0)
9739 {
9740 xp->xp_pattern = skipwhite(p);
9741 p = skiptowhite(xp->xp_pattern);
9742 if (*p != NUL) /* past first group name */
9743 {
9744 xp->xp_pattern = skipwhite(p);
9745 p = skiptowhite(xp->xp_pattern);
9746 }
9747 }
9748 if (*p != NUL) /* past group name(s) */
9749 xp->xp_context = EXPAND_NOTHING;
9750 }
9751 }
9752 }
9753}
9754
9755/*
9756 * List highlighting matches in a nice way.
9757 */
9758 static void
9759highlight_list()
9760{
9761 int i;
9762
9763 for (i = 10; --i >= 0; )
9764 highlight_list_two(i, hl_attr(HLF_D));
9765 for (i = 40; --i >= 0; )
9766 highlight_list_two(99, 0);
9767}
9768
9769 static void
9770highlight_list_two(cnt, attr)
9771 int cnt;
9772 int attr;
9773{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009774 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009775 msg_clr_eos();
9776 out_flush();
9777 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9778}
9779
9780#endif /* FEAT_CMDL_COMPL */
9781
9782#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9783 || defined(FEAT_SIGNS) || defined(PROTO)
9784/*
9785 * Function given to ExpandGeneric() to obtain the list of group names.
9786 * Also used for synIDattr() function.
9787 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009788 char_u *
9789get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009790 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009791 int idx;
9792{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009793#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009794 if (idx == highlight_ga.ga_len && include_none != 0)
9795 return (char_u *)"none";
9796 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009797 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009798 if (idx == highlight_ga.ga_len + include_none + include_default
9799 && include_link != 0)
9800 return (char_u *)"link";
9801 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9802 && include_link != 0)
9803 return (char_u *)"clear";
9804#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009805 if (idx < 0 || idx >= highlight_ga.ga_len)
9806 return NULL;
9807 return HL_TABLE()[idx].sg_name;
9808}
9809#endif
9810
Bram Moolenaar4f688582007-07-24 12:34:30 +00009811#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009812/*
9813 * Free all the highlight group fonts.
9814 * Used when quitting for systems which need it.
9815 */
9816 void
9817free_highlight_fonts()
9818{
9819 int idx;
9820
9821 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9822 {
9823 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9824 HL_TABLE()[idx].sg_font = NOFONT;
9825# ifdef FEAT_XFONTSET
9826 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9827 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9828# endif
9829 }
9830
9831 gui_mch_free_font(gui.norm_font);
9832# ifdef FEAT_XFONTSET
9833 gui_mch_free_fontset(gui.fontset);
9834# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009835# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009836 gui_mch_free_font(gui.bold_font);
9837 gui_mch_free_font(gui.ital_font);
9838 gui_mch_free_font(gui.boldital_font);
9839# endif
9840}
9841#endif
9842
9843/**************************************
9844 * End of Highlighting stuff *
9845 **************************************/