blob: badb2264281bfd6890e5c1205ebdb5c9a427ad6c [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 {
Bram Moolenaar04bff882016-01-05 20:46:16 +01003025 int line_len;
3026
Bram Moolenaar071d4272004-06-13 20:20:40 +00003027 /* Add offset to skip pattern match */
3028 syn_add_end_off(&pos, &regmatch, spp_skip, SPO_ME_OFF, 1);
3029
3030 /* If the skip pattern goes on to the next line, there is no
3031 * match with an end pattern in this line. */
3032 if (pos.lnum > startpos->lnum)
3033 break;
3034
3035 line = ml_get_buf(syn_buf, startpos->lnum, FALSE);
Bram Moolenaar04bff882016-01-05 20:46:16 +01003036 line_len = (int)STRLEN(line);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003037
3038 /* take care of an empty match or negative offset */
3039 if (pos.col <= matchcol)
3040 ++matchcol;
3041 else if (pos.col <= regmatch.endpos[0].col)
3042 matchcol = pos.col;
3043 else
3044 /* Be careful not to jump over the NUL at the end-of-line */
3045 for (matchcol = regmatch.endpos[0].col;
Bram Moolenaar04bff882016-01-05 20:46:16 +01003046 matchcol < line_len && matchcol < pos.col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003047 ++matchcol)
3048 ;
3049
3050 /* if the skip pattern includes end-of-line, break here */
Bram Moolenaar04bff882016-01-05 20:46:16 +01003051 if (matchcol >= line_len)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003052 break;
3053
3054 continue; /* start with first end pattern again */
3055 }
3056 }
3057
3058 /*
3059 * Match from start pattern to end pattern.
3060 * Correct for match and highlight offset of end pattern.
3061 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003062 spp = &(SYN_ITEMS(syn_block)[best_idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003063 syn_add_end_off(m_endpos, &best_regmatch, spp, SPO_ME_OFF, 1);
3064 /* can't end before the start */
3065 if (m_endpos->lnum == startpos->lnum && m_endpos->col < startpos->col)
3066 m_endpos->col = startpos->col;
3067
3068 syn_add_end_off(end_endpos, &best_regmatch, spp, SPO_HE_OFF, 1);
3069 /* can't end before the start */
3070 if (end_endpos->lnum == startpos->lnum
3071 && end_endpos->col < startpos->col)
3072 end_endpos->col = startpos->col;
3073 /* can't end after the match */
3074 limit_pos(end_endpos, m_endpos);
3075
3076 /*
3077 * If the end group is highlighted differently, adjust the pointers.
3078 */
3079 if (spp->sp_syn_match_id != spp->sp_syn.id && spp->sp_syn_match_id != 0)
3080 {
3081 *end_idx = best_idx;
3082 if (spp->sp_off_flags & (1 << (SPO_RE_OFF + SPO_COUNT)))
3083 {
3084 hl_endpos->lnum = best_regmatch.endpos[0].lnum;
3085 hl_endpos->col = best_regmatch.endpos[0].col;
3086 }
3087 else
3088 {
3089 hl_endpos->lnum = best_regmatch.startpos[0].lnum;
3090 hl_endpos->col = best_regmatch.startpos[0].col;
3091 }
3092 hl_endpos->col += spp->sp_offsets[SPO_RE_OFF];
3093
3094 /* can't end before the start */
3095 if (hl_endpos->lnum == startpos->lnum
3096 && hl_endpos->col < startpos->col)
3097 hl_endpos->col = startpos->col;
3098 limit_pos(hl_endpos, m_endpos);
3099
3100 /* now the match ends where the highlighting ends, it is turned
3101 * into the matchgroup for the end */
3102 *m_endpos = *hl_endpos;
3103 }
3104 else
3105 {
3106 *end_idx = 0;
3107 *hl_endpos = *end_endpos;
3108 }
3109
3110 *flagsp = spp->sp_flags;
3111
3112 had_match = TRUE;
3113 break;
3114 }
3115
3116 /* no match for an END pattern in this line */
3117 if (!had_match)
3118 m_endpos->lnum = 0;
3119
3120 /* Remove external matches. */
3121 unref_extmatch(re_extmatch_in);
3122 re_extmatch_in = NULL;
3123}
3124
3125/*
3126 * Limit "pos" not to be after "limit".
3127 */
3128 static void
3129limit_pos(pos, limit)
3130 lpos_T *pos;
3131 lpos_T *limit;
3132{
3133 if (pos->lnum > limit->lnum)
3134 *pos = *limit;
3135 else if (pos->lnum == limit->lnum && pos->col > limit->col)
3136 pos->col = limit->col;
3137}
3138
3139/*
3140 * Limit "pos" not to be after "limit", unless pos->lnum is zero.
3141 */
3142 static void
3143limit_pos_zero(pos, limit)
3144 lpos_T *pos;
3145 lpos_T *limit;
3146{
3147 if (pos->lnum == 0)
3148 *pos = *limit;
3149 else
3150 limit_pos(pos, limit);
3151}
3152
3153/*
3154 * Add offset to matched text for end of match or highlight.
3155 */
3156 static void
3157syn_add_end_off(result, regmatch, spp, idx, extra)
3158 lpos_T *result; /* returned position */
3159 regmmatch_T *regmatch; /* start/end of match */
3160 synpat_T *spp; /* matched pattern */
3161 int idx; /* index of offset */
3162 int extra; /* extra chars for offset to start */
3163{
3164 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003165 int off;
3166 char_u *base;
3167 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003168
3169 if (spp->sp_off_flags & (1 << idx))
3170 {
3171 result->lnum = regmatch->startpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003172 col = regmatch->startpos[0].col;
3173 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003174 }
3175 else
3176 {
3177 result->lnum = regmatch->endpos[0].lnum;
3178 col = regmatch->endpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003179 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003180 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003181 /* Don't go past the end of the line. Matters for "rs=e+2" when there
3182 * is a matchgroup. Watch out for match with last NL in the buffer. */
3183 if (result->lnum > syn_buf->b_ml.ml_line_count)
3184 col = 0;
3185 else if (off != 0)
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003186 {
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003187 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3188 p = base + col;
3189 if (off > 0)
3190 {
3191 while (off-- > 0 && *p != NUL)
3192 mb_ptr_adv(p);
3193 }
3194 else if (off < 0)
3195 {
3196 while (off++ < 0 && base < p)
3197 mb_ptr_back(base, p);
3198 }
3199 col = (int)(p - base);
Bram Moolenaara40ceaf2006-01-13 22:35:40 +00003200 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003201 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003202}
3203
3204/*
3205 * Add offset to matched text for start of match or highlight.
3206 * Avoid resulting column to become negative.
3207 */
3208 static void
3209syn_add_start_off(result, regmatch, spp, idx, extra)
3210 lpos_T *result; /* returned position */
3211 regmmatch_T *regmatch; /* start/end of match */
3212 synpat_T *spp;
3213 int idx;
3214 int extra; /* extra chars for offset to end */
3215{
3216 int col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003217 int off;
3218 char_u *base;
3219 char_u *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003220
3221 if (spp->sp_off_flags & (1 << (idx + SPO_COUNT)))
3222 {
3223 result->lnum = regmatch->endpos[0].lnum;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003224 col = regmatch->endpos[0].col;
3225 off = spp->sp_offsets[idx] + extra;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003226 }
3227 else
3228 {
3229 result->lnum = regmatch->startpos[0].lnum;
3230 col = regmatch->startpos[0].col;
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003231 off = spp->sp_offsets[idx];
Bram Moolenaar071d4272004-06-13 20:20:40 +00003232 }
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003233 if (result->lnum > syn_buf->b_ml.ml_line_count)
3234 {
3235 /* a "\n" at the end of the pattern may take us below the last line */
3236 result->lnum = syn_buf->b_ml.ml_line_count;
Bram Moolenaar8b9c05f2010-03-02 17:54:33 +01003237 col = (int)STRLEN(ml_get_buf(syn_buf, result->lnum, FALSE));
Bram Moolenaar72b73c12010-02-24 17:22:20 +01003238 }
Bram Moolenaar8c8de832008-06-24 22:58:06 +00003239 if (off != 0)
3240 {
3241 base = ml_get_buf(syn_buf, result->lnum, FALSE);
3242 p = base + col;
3243 if (off > 0)
3244 {
3245 while (off-- && *p != NUL)
3246 mb_ptr_adv(p);
3247 }
3248 else if (off < 0)
3249 {
3250 while (off++ && base < p)
3251 mb_ptr_back(base, p);
3252 }
3253 col = (int)(p - base);
3254 }
3255 result->col = col;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003256}
3257
3258/*
3259 * Get current line in syntax buffer.
3260 */
3261 static char_u *
3262syn_getcurline()
3263{
3264 return ml_get_buf(syn_buf, current_lnum, FALSE);
3265}
3266
3267/*
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003268 * Call vim_regexec() to find a match with "rmp" in "syn_buf".
Bram Moolenaar071d4272004-06-13 20:20:40 +00003269 * Returns TRUE when there is a match.
3270 */
3271 static int
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003272syn_regexec(rmp, lnum, col, st)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003273 regmmatch_T *rmp;
3274 linenr_T lnum;
3275 colnr_T col;
Bram Moolenaar4e312962013-06-06 21:19:51 +02003276 syn_time_T *st UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003277{
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003278 int r;
Bram Moolenaarf7512552013-06-06 14:55:19 +02003279#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003280 proftime_T pt;
3281
3282 if (syn_time_on)
3283 profile_start(&pt);
3284#endif
3285
Bram Moolenaar3b56eb32005-07-11 22:40:32 +00003286 rmp->rmm_maxcol = syn_buf->b_p_smc;
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003287 r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL);
3288
Bram Moolenaarf7512552013-06-06 14:55:19 +02003289#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003290 if (syn_time_on)
3291 {
3292 profile_end(&pt);
3293 profile_add(&st->total, &pt);
3294 if (profile_cmp(&pt, &st->slowest) < 0)
3295 st->slowest = pt;
3296 ++st->count;
3297 if (r > 0)
3298 ++st->match;
3299 }
3300#endif
3301
3302 if (r > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003303 {
3304 rmp->startpos[0].lnum += lnum;
3305 rmp->endpos[0].lnum += lnum;
3306 return TRUE;
3307 }
3308 return FALSE;
3309}
3310
3311/*
3312 * Check one position in a line for a matching keyword.
3313 * The caller must check if a keyword can start at startcol.
3314 * Return it's ID if found, 0 otherwise.
3315 */
3316 static int
Bram Moolenaar860cae12010-06-05 23:22:07 +02003317check_keyword_id(line, startcol, endcolp, flagsp, next_listp, cur_si, ccharp)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003318 char_u *line;
3319 int startcol; /* position in line to check for keyword */
3320 int *endcolp; /* return: character after found keyword */
3321 long *flagsp; /* return: flags of matching keyword */
3322 short **next_listp; /* return: next_list of matching keyword */
3323 stateitem_T *cur_si; /* item at the top of the stack */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003324 int *ccharp UNUSED; /* conceal substitution char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003325{
Bram Moolenaardad6b692005-01-25 22:14:34 +00003326 keyentry_T *kp;
3327 char_u *kwp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003328 int round;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003329 int kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003330 char_u keyword[MAXKEYWLEN + 1]; /* assume max. keyword len is 80 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003331 hashtab_T *ht;
3332 hashitem_T *hi;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003333
3334 /* Find first character after the keyword. First character was already
3335 * checked. */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003336 kwp = line + startcol;
3337 kwlen = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003338 do
3339 {
3340#ifdef FEAT_MBYTE
3341 if (has_mbyte)
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00003342 kwlen += (*mb_ptr2len)(kwp + kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003343 else
3344#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003345 ++kwlen;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003346 }
Bram Moolenaar9d182dd2013-01-23 15:53:15 +01003347 while (vim_iswordp_buf(kwp + kwlen, syn_buf));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003348
Bram Moolenaardad6b692005-01-25 22:14:34 +00003349 if (kwlen > MAXKEYWLEN)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003350 return 0;
3351
3352 /*
3353 * Must make a copy of the keyword, so we can add a NUL and make it
3354 * lowercase.
3355 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003356 vim_strncpy(keyword, kwp, kwlen);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003357
3358 /*
3359 * Try twice:
3360 * 1. matching case
3361 * 2. ignoring case
3362 */
3363 for (round = 1; round <= 2; ++round)
3364 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003365 ht = round == 1 ? &syn_block->b_keywtab : &syn_block->b_keywtab_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003366 if (ht->ht_used == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003367 continue;
Bram Moolenaardad6b692005-01-25 22:14:34 +00003368 if (round == 2) /* ignore case */
3369 (void)str_foldcase(kwp, kwlen, keyword, MAXKEYWLEN + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003370
3371 /*
Bram Moolenaardad6b692005-01-25 22:14:34 +00003372 * Find keywords that match. There can be several with different
3373 * attributes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00003374 * When current_next_list is non-zero accept only that group, otherwise:
3375 * Accept a not-contained keyword at toplevel.
3376 * Accept a keyword at other levels only if it is in the contains list.
3377 */
Bram Moolenaardad6b692005-01-25 22:14:34 +00003378 hi = hash_find(ht, keyword);
3379 if (!HASHITEM_EMPTY(hi))
3380 for (kp = HI2KE(hi); kp != NULL; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003381 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00003382 if (current_next_list != 0
3383 ? in_id_list(NULL, current_next_list, &kp->k_syn, 0)
3384 : (cur_si == NULL
3385 ? !(kp->flags & HL_CONTAINED)
3386 : in_id_list(cur_si, cur_si->si_cont_list,
3387 &kp->k_syn, kp->flags & HL_CONTAINED)))
3388 {
3389 *endcolp = startcol + kwlen;
3390 *flagsp = kp->flags;
3391 *next_listp = kp->next_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003392#ifdef FEAT_CONCEAL
3393 *ccharp = kp->k_char;
3394#endif
Bram Moolenaardad6b692005-01-25 22:14:34 +00003395 return kp->k_syn.id;
3396 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00003397 }
3398 }
3399 return 0;
3400}
3401
3402/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02003403 * Handle ":syntax conceal" command.
3404 */
3405 static void
3406syn_cmd_conceal(eap, syncing)
3407 exarg_T *eap UNUSED;
3408 int syncing UNUSED;
3409{
3410#ifdef FEAT_CONCEAL
3411 char_u *arg = eap->arg;
3412 char_u *next;
3413
3414 eap->nextcmd = find_nextcmd(arg);
3415 if (eap->skip)
3416 return;
3417
3418 next = skiptowhite(arg);
3419 if (STRNICMP(arg, "on", 2) == 0 && next - arg == 2)
3420 curwin->w_s->b_syn_conceal = TRUE;
3421 else if (STRNICMP(arg, "off", 3) == 0 && next - arg == 3)
3422 curwin->w_s->b_syn_conceal = FALSE;
3423 else
3424 EMSG2(_("E390: Illegal argument: %s"), arg);
3425#endif
3426}
3427
3428/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003429 * Handle ":syntax case" command.
3430 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003431 static void
3432syn_cmd_case(eap, syncing)
3433 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003434 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003435{
3436 char_u *arg = eap->arg;
3437 char_u *next;
3438
3439 eap->nextcmd = find_nextcmd(arg);
3440 if (eap->skip)
3441 return;
3442
3443 next = skiptowhite(arg);
3444 if (STRNICMP(arg, "match", 5) == 0 && next - arg == 5)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003445 curwin->w_s->b_syn_ic = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003446 else if (STRNICMP(arg, "ignore", 6) == 0 && next - arg == 6)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003447 curwin->w_s->b_syn_ic = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003448 else
3449 EMSG2(_("E390: Illegal argument: %s"), arg);
3450}
3451
3452/*
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003453 * Handle ":syntax spell" command.
3454 */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003455 static void
3456syn_cmd_spell(eap, syncing)
3457 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003458 int syncing UNUSED;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003459{
3460 char_u *arg = eap->arg;
3461 char_u *next;
3462
3463 eap->nextcmd = find_nextcmd(arg);
3464 if (eap->skip)
3465 return;
3466
3467 next = skiptowhite(arg);
3468 if (STRNICMP(arg, "toplevel", 8) == 0 && next - arg == 8)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003469 curwin->w_s->b_syn_spell = SYNSPL_TOP;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003470 else if (STRNICMP(arg, "notoplevel", 10) == 0 && next - arg == 10)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003471 curwin->w_s->b_syn_spell = SYNSPL_NOTOP;
Bram Moolenaar86ea7642007-02-04 01:48:10 +00003472 else if (STRNICMP(arg, "default", 7) == 0 && next - arg == 7)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003473 curwin->w_s->b_syn_spell = SYNSPL_DEFAULT;
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003474 else
Bram Moolenaar5081d202015-06-25 18:36:26 +02003475 {
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003476 EMSG2(_("E390: Illegal argument: %s"), arg);
Bram Moolenaar5081d202015-06-25 18:36:26 +02003477 return;
3478 }
3479
3480 /* assume spell checking changed, force a redraw */
3481 redraw_win_later(curwin, NOT_VALID);
Bram Moolenaarce0842a2005-07-18 21:58:11 +00003482}
3483
3484/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003485 * Clear all syntax info for one buffer.
3486 */
3487 void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003488syntax_clear(block)
3489 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003490{
3491 int i;
3492
Bram Moolenaar860cae12010-06-05 23:22:07 +02003493 block->b_syn_error = FALSE; /* clear previous error */
3494 block->b_syn_ic = FALSE; /* Use case, by default */
3495 block->b_syn_spell = SYNSPL_DEFAULT; /* default spell checking */
3496 block->b_syn_containedin = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003497
3498 /* free the keywords */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003499 clear_keywtab(&block->b_keywtab);
3500 clear_keywtab(&block->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003501
3502 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003503 for (i = block->b_syn_patterns.ga_len; --i >= 0; )
3504 syn_clear_pattern(block, i);
3505 ga_clear(&block->b_syn_patterns);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003506
3507 /* free the syntax clusters */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003508 for (i = block->b_syn_clusters.ga_len; --i >= 0; )
3509 syn_clear_cluster(block, i);
3510 ga_clear(&block->b_syn_clusters);
3511 block->b_spell_cluster_id = 0;
3512 block->b_nospell_cluster_id = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003513
Bram Moolenaar860cae12010-06-05 23:22:07 +02003514 block->b_syn_sync_flags = 0;
3515 block->b_syn_sync_minlines = 0;
3516 block->b_syn_sync_maxlines = 0;
3517 block->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003518
Bram Moolenaar473de612013-06-08 18:19:48 +02003519 vim_regfree(block->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003520 block->b_syn_linecont_prog = NULL;
3521 vim_free(block->b_syn_linecont_pat);
3522 block->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003523#ifdef FEAT_FOLDING
Bram Moolenaar860cae12010-06-05 23:22:07 +02003524 block->b_syn_folditems = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003525#endif
3526
3527 /* free the stored states */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003528 syn_stack_free_all(block);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003529 invalidate_current_state();
Bram Moolenaar42431a72011-04-01 14:44:59 +02003530
3531 /* Reset the counter for ":syn include" */
3532 running_syn_inc_tag = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003533}
3534
3535/*
Bram Moolenaarfd29f462010-06-06 16:11:09 +02003536 * Get rid of ownsyntax for window "wp".
3537 */
3538 void
3539reset_synblock(wp)
3540 win_T *wp;
3541{
3542 if (wp->w_s != &wp->w_buffer->b_s)
3543 {
3544 syntax_clear(wp->w_s);
3545 vim_free(wp->w_s);
3546 wp->w_s = &wp->w_buffer->b_s;
3547 }
3548}
3549
3550/*
Bram Moolenaar071d4272004-06-13 20:20:40 +00003551 * Clear syncing info for one buffer.
3552 */
3553 static void
3554syntax_sync_clear()
3555{
3556 int i;
3557
3558 /* free the syntax patterns */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003559 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
3560 if (SYN_ITEMS(curwin->w_s)[i].sp_syncing)
3561 syn_remove_pattern(curwin->w_s, i);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003562
Bram Moolenaar860cae12010-06-05 23:22:07 +02003563 curwin->w_s->b_syn_sync_flags = 0;
3564 curwin->w_s->b_syn_sync_minlines = 0;
3565 curwin->w_s->b_syn_sync_maxlines = 0;
3566 curwin->w_s->b_syn_sync_linebreaks = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003567
Bram Moolenaar473de612013-06-08 18:19:48 +02003568 vim_regfree(curwin->w_s->b_syn_linecont_prog);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003569 curwin->w_s->b_syn_linecont_prog = NULL;
3570 vim_free(curwin->w_s->b_syn_linecont_pat);
3571 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003572
Bram Moolenaar860cae12010-06-05 23:22:07 +02003573 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003574}
3575
3576/*
3577 * Remove one pattern from the buffer's pattern list.
3578 */
3579 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003580syn_remove_pattern(block, idx)
3581 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003582 int idx;
3583{
3584 synpat_T *spp;
3585
Bram Moolenaar860cae12010-06-05 23:22:07 +02003586 spp = &(SYN_ITEMS(block)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003587#ifdef FEAT_FOLDING
3588 if (spp->sp_flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02003589 --block->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003590#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02003591 syn_clear_pattern(block, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003592 mch_memmove(spp, spp + 1,
Bram Moolenaar860cae12010-06-05 23:22:07 +02003593 sizeof(synpat_T) * (block->b_syn_patterns.ga_len - idx - 1));
3594 --block->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003595}
3596
3597/*
3598 * Clear and free one syntax pattern. When clearing all, must be called from
3599 * last to first!
3600 */
3601 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003602syn_clear_pattern(block, i)
3603 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003604 int i;
3605{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003606 vim_free(SYN_ITEMS(block)[i].sp_pattern);
Bram Moolenaar473de612013-06-08 18:19:48 +02003607 vim_regfree(SYN_ITEMS(block)[i].sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003608 /* Only free sp_cont_list and sp_next_list of first start pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003609 if (i == 0 || SYN_ITEMS(block)[i - 1].sp_type != SPTYPE_START)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003610 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003611 vim_free(SYN_ITEMS(block)[i].sp_cont_list);
3612 vim_free(SYN_ITEMS(block)[i].sp_next_list);
3613 vim_free(SYN_ITEMS(block)[i].sp_syn.cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003614 }
3615}
3616
3617/*
3618 * Clear and free one syntax cluster.
3619 */
3620 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02003621syn_clear_cluster(block, i)
3622 synblock_T *block;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003623 int i;
3624{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003625 vim_free(SYN_CLSTR(block)[i].scl_name);
3626 vim_free(SYN_CLSTR(block)[i].scl_name_u);
3627 vim_free(SYN_CLSTR(block)[i].scl_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003628}
3629
3630/*
3631 * Handle ":syntax clear" command.
3632 */
3633 static void
3634syn_cmd_clear(eap, syncing)
3635 exarg_T *eap;
3636 int syncing;
3637{
3638 char_u *arg = eap->arg;
3639 char_u *arg_end;
3640 int id;
3641
3642 eap->nextcmd = find_nextcmd(arg);
3643 if (eap->skip)
3644 return;
3645
3646 /*
3647 * We have to disable this within ":syn include @group filename",
3648 * because otherwise @group would get deleted.
3649 * Only required for Vim 5.x syntax files, 6.0 ones don't contain ":syn
3650 * clear".
3651 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003652 if (curwin->w_s->b_syn_topgrp != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003653 return;
3654
3655 if (ends_excmd(*arg))
3656 {
3657 /*
3658 * No argument: Clear all syntax items.
3659 */
3660 if (syncing)
3661 syntax_sync_clear();
3662 else
3663 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003664 syntax_clear(curwin->w_s);
3665 if (curwin->w_s == &curwin->w_buffer->b_s)
3666 do_unlet((char_u *)"b:current_syntax", TRUE);
Bram Moolenaar1950c352010-06-06 15:21:10 +02003667 do_unlet((char_u *)"w:current_syntax", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003668 }
3669 }
3670 else
3671 {
3672 /*
3673 * Clear the group IDs that are in the argument.
3674 */
3675 while (!ends_excmd(*arg))
3676 {
3677 arg_end = skiptowhite(arg);
3678 if (*arg == '@')
3679 {
3680 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3681 if (id == 0)
3682 {
3683 EMSG2(_("E391: No such syntax cluster: %s"), arg);
3684 break;
3685 }
3686 else
3687 {
3688 /*
3689 * We can't physically delete a cluster without changing
3690 * the IDs of other clusters, so we do the next best thing
3691 * and make it empty.
3692 */
3693 short scl_id = id - SYNID_CLUSTER;
3694
Bram Moolenaar860cae12010-06-05 23:22:07 +02003695 vim_free(SYN_CLSTR(curwin->w_s)[scl_id].scl_list);
3696 SYN_CLSTR(curwin->w_s)[scl_id].scl_list = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003697 }
3698 }
3699 else
3700 {
3701 id = syn_namen2id(arg, (int)(arg_end - arg));
3702 if (id == 0)
3703 {
3704 EMSG2(_(e_nogroup), arg);
3705 break;
3706 }
3707 else
3708 syn_clear_one(id, syncing);
3709 }
3710 arg = skipwhite(arg_end);
3711 }
3712 }
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00003713 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003714 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003715}
3716
3717/*
3718 * Clear one syntax group for the current buffer.
3719 */
3720 static void
3721syn_clear_one(id, syncing)
3722 int id;
3723 int syncing;
3724{
3725 synpat_T *spp;
3726 int idx;
3727
3728 /* Clear keywords only when not ":syn sync clear group-name" */
3729 if (!syncing)
3730 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003731 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab);
3732 (void)syn_clear_keyword(id, &curwin->w_s->b_keywtab_ic);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003733 }
3734
3735 /* clear the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02003736 for (idx = curwin->w_s->b_syn_patterns.ga_len; --idx >= 0; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00003737 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003738 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003739 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
3740 continue;
Bram Moolenaar860cae12010-06-05 23:22:07 +02003741 syn_remove_pattern(curwin->w_s, idx);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003742 }
3743}
3744
3745/*
3746 * Handle ":syntax on" command.
3747 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003748 static void
3749syn_cmd_on(eap, syncing)
3750 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003751 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003752{
3753 syn_cmd_onoff(eap, "syntax");
3754}
3755
3756/*
3757 * Handle ":syntax enable" command.
3758 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003759 static void
3760syn_cmd_enable(eap, syncing)
3761 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003762 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003763{
3764 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable");
3765 syn_cmd_onoff(eap, "syntax");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003766 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003767}
3768
3769/*
3770 * Handle ":syntax reset" command.
3771 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003772 static void
3773syn_cmd_reset(eap, syncing)
3774 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003775 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003776{
3777 eap->nextcmd = check_nextcmd(eap->arg);
3778 if (!eap->skip)
3779 {
3780 set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset");
3781 do_cmdline_cmd((char_u *)"runtime! syntax/syncolor.vim");
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00003782 do_unlet((char_u *)"g:syntax_cmd", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003783 }
3784}
3785
3786/*
3787 * Handle ":syntax manual" command.
3788 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003789 static void
3790syn_cmd_manual(eap, syncing)
3791 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003792 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003793{
3794 syn_cmd_onoff(eap, "manual");
3795}
3796
3797/*
3798 * Handle ":syntax off" command.
3799 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00003800 static void
3801syn_cmd_off(eap, syncing)
3802 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00003803 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00003804{
3805 syn_cmd_onoff(eap, "nosyntax");
3806}
3807
3808 static void
3809syn_cmd_onoff(eap, name)
3810 exarg_T *eap;
3811 char *name;
3812{
3813 char_u buf[100];
3814
3815 eap->nextcmd = check_nextcmd(eap->arg);
3816 if (!eap->skip)
3817 {
3818 STRCPY(buf, "so ");
Bram Moolenaar555b2802005-05-19 21:08:39 +00003819 vim_snprintf((char *)buf + 3, sizeof(buf) - 3, SYNTAX_FNAME, name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003820 do_cmdline_cmd(buf);
3821 }
3822}
3823
3824/*
3825 * Handle ":syntax [list]" command: list current syntax words.
3826 */
3827 static void
3828syn_cmd_list(eap, syncing)
3829 exarg_T *eap;
3830 int syncing; /* when TRUE: list syncing items */
3831{
3832 char_u *arg = eap->arg;
3833 int id;
3834 char_u *arg_end;
3835
3836 eap->nextcmd = find_nextcmd(arg);
3837 if (eap->skip)
3838 return;
3839
Bram Moolenaar860cae12010-06-05 23:22:07 +02003840 if (!syntax_present(curwin))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003841 {
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02003842 MSG(_(msg_no_items));
Bram Moolenaar071d4272004-06-13 20:20:40 +00003843 return;
3844 }
3845
3846 if (syncing)
3847 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003848 if (curwin->w_s->b_syn_sync_flags & SF_CCOMMENT)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003849 {
3850 MSG_PUTS(_("syncing on C-style comments"));
3851 syn_lines_msg();
3852 syn_match_msg();
3853 return;
3854 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003855 else if (!(curwin->w_s->b_syn_sync_flags & SF_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00003856 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02003857 if (curwin->w_s->b_syn_sync_minlines == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003858 MSG_PUTS(_("no syncing"));
3859 else
3860 {
3861 MSG_PUTS(_("syncing starts "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003862 msg_outnum(curwin->w_s->b_syn_sync_minlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003863 MSG_PUTS(_(" lines before top line"));
3864 syn_match_msg();
3865 }
3866 return;
3867 }
3868 MSG_PUTS_TITLE(_("\n--- Syntax sync items ---"));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003869 if (curwin->w_s->b_syn_sync_minlines > 0
3870 || curwin->w_s->b_syn_sync_maxlines > 0
3871 || curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003872 {
3873 MSG_PUTS(_("\nsyncing on items"));
3874 syn_lines_msg();
3875 syn_match_msg();
3876 }
3877 }
3878 else
3879 MSG_PUTS_TITLE(_("\n--- Syntax items ---"));
3880 if (ends_excmd(*arg))
3881 {
3882 /*
3883 * No argument: List all group IDs and all syntax clusters.
3884 */
3885 for (id = 1; id <= highlight_ga.ga_len && !got_int; ++id)
3886 syn_list_one(id, syncing, FALSE);
Bram Moolenaar860cae12010-06-05 23:22:07 +02003887 for (id = 0; id < curwin->w_s->b_syn_clusters.ga_len && !got_int; ++id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003888 syn_list_cluster(id);
3889 }
3890 else
3891 {
3892 /*
3893 * List the group IDs and syntax clusters that are in the argument.
3894 */
3895 while (!ends_excmd(*arg) && !got_int)
3896 {
3897 arg_end = skiptowhite(arg);
3898 if (*arg == '@')
3899 {
3900 id = syn_scl_namen2id(arg + 1, (int)(arg_end - arg - 1));
3901 if (id == 0)
3902 EMSG2(_("E392: No such syntax cluster: %s"), arg);
3903 else
3904 syn_list_cluster(id - SYNID_CLUSTER);
3905 }
3906 else
3907 {
3908 id = syn_namen2id(arg, (int)(arg_end - arg));
3909 if (id == 0)
3910 EMSG2(_(e_nogroup), arg);
3911 else
3912 syn_list_one(id, syncing, TRUE);
3913 }
3914 arg = skipwhite(arg_end);
3915 }
3916 }
3917 eap->nextcmd = check_nextcmd(arg);
3918}
3919
3920 static void
3921syn_lines_msg()
3922{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003923 if (curwin->w_s->b_syn_sync_maxlines > 0
3924 || curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003925 {
3926 MSG_PUTS("; ");
Bram Moolenaar860cae12010-06-05 23:22:07 +02003927 if (curwin->w_s->b_syn_sync_minlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003928 {
3929 MSG_PUTS(_("minimal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003930 msg_outnum(curwin->w_s->b_syn_sync_minlines);
3931 if (curwin->w_s->b_syn_sync_maxlines)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003932 MSG_PUTS(", ");
3933 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02003934 if (curwin->w_s->b_syn_sync_maxlines > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003935 {
3936 MSG_PUTS(_("maximal "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003937 msg_outnum(curwin->w_s->b_syn_sync_maxlines);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003938 }
3939 MSG_PUTS(_(" lines before top line"));
3940 }
3941}
3942
3943 static void
3944syn_match_msg()
3945{
Bram Moolenaar860cae12010-06-05 23:22:07 +02003946 if (curwin->w_s->b_syn_sync_linebreaks > 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00003947 {
3948 MSG_PUTS(_("; match "));
Bram Moolenaar860cae12010-06-05 23:22:07 +02003949 msg_outnum(curwin->w_s->b_syn_sync_linebreaks);
Bram Moolenaar071d4272004-06-13 20:20:40 +00003950 MSG_PUTS(_(" line breaks"));
3951 }
3952}
3953
3954static int last_matchgroup;
3955
3956struct name_list
3957{
3958 int flag;
3959 char *name;
3960};
3961
3962static void syn_list_flags __ARGS((struct name_list *nl, int flags, int attr));
3963
3964/*
3965 * List one syntax item, for ":syntax" or "syntax list syntax_name".
3966 */
3967 static void
3968syn_list_one(id, syncing, link_only)
3969 int id;
3970 int syncing; /* when TRUE: list syncing items */
3971 int link_only; /* when TRUE; list link-only too */
3972{
3973 int attr;
3974 int idx;
3975 int did_header = FALSE;
3976 synpat_T *spp;
3977 static struct name_list namelist1[] =
3978 {
3979 {HL_DISPLAY, "display"},
3980 {HL_CONTAINED, "contained"},
3981 {HL_ONELINE, "oneline"},
3982 {HL_KEEPEND, "keepend"},
3983 {HL_EXTEND, "extend"},
3984 {HL_EXCLUDENL, "excludenl"},
3985 {HL_TRANSP, "transparent"},
3986 {HL_FOLD, "fold"},
Bram Moolenaar860cae12010-06-05 23:22:07 +02003987#ifdef FEAT_CONCEAL
3988 {HL_CONCEAL, "conceal"},
3989 {HL_CONCEALENDS, "concealends"},
3990#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00003991 {0, NULL}
3992 };
3993 static struct name_list namelist2[] =
3994 {
3995 {HL_SKIPWHITE, "skipwhite"},
3996 {HL_SKIPNL, "skipnl"},
3997 {HL_SKIPEMPTY, "skipempty"},
3998 {0, NULL}
3999 };
4000
4001 attr = hl_attr(HLF_D); /* highlight like directories */
4002
4003 /* list the keywords for "id" */
4004 if (!syncing)
4005 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004006 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab, FALSE, attr);
4007 did_header = syn_list_keywords(id, &curwin->w_s->b_keywtab_ic,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004008 did_header, attr);
4009 }
4010
4011 /* list the patterns for "id" */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004012 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len && !got_int; ++idx)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004013 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004014 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004015 if (spp->sp_syn.id != id || spp->sp_syncing != syncing)
4016 continue;
4017
4018 (void)syn_list_header(did_header, 999, id);
4019 did_header = TRUE;
4020 last_matchgroup = 0;
4021 if (spp->sp_type == SPTYPE_MATCH)
4022 {
4023 put_pattern("match", ' ', spp, attr);
4024 msg_putchar(' ');
4025 }
4026 else if (spp->sp_type == SPTYPE_START)
4027 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004028 while (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_START)
4029 put_pattern("start", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4030 if (SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_SKIP)
4031 put_pattern("skip", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
4032 while (idx < curwin->w_s->b_syn_patterns.ga_len
4033 && SYN_ITEMS(curwin->w_s)[idx].sp_type == SPTYPE_END)
4034 put_pattern("end", '=', &SYN_ITEMS(curwin->w_s)[idx++], attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004035 --idx;
4036 msg_putchar(' ');
4037 }
4038 syn_list_flags(namelist1, spp->sp_flags, attr);
4039
4040 if (spp->sp_cont_list != NULL)
4041 put_id_list((char_u *)"contains", spp->sp_cont_list, attr);
4042
4043 if (spp->sp_syn.cont_in_list != NULL)
4044 put_id_list((char_u *)"containedin",
4045 spp->sp_syn.cont_in_list, attr);
4046
4047 if (spp->sp_next_list != NULL)
4048 {
4049 put_id_list((char_u *)"nextgroup", spp->sp_next_list, attr);
4050 syn_list_flags(namelist2, spp->sp_flags, attr);
4051 }
4052 if (spp->sp_flags & (HL_SYNC_HERE|HL_SYNC_THERE))
4053 {
4054 if (spp->sp_flags & HL_SYNC_HERE)
4055 msg_puts_attr((char_u *)"grouphere", attr);
4056 else
4057 msg_puts_attr((char_u *)"groupthere", attr);
4058 msg_putchar(' ');
4059 if (spp->sp_sync_idx >= 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004060 msg_outtrans(HL_TABLE()[SYN_ITEMS(curwin->w_s)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004061 [spp->sp_sync_idx].sp_syn.id - 1].sg_name);
4062 else
4063 MSG_PUTS("NONE");
4064 msg_putchar(' ');
4065 }
4066 }
4067
4068 /* list the link, if there is one */
4069 if (HL_TABLE()[id - 1].sg_link && (did_header || link_only) && !got_int)
4070 {
4071 (void)syn_list_header(did_header, 999, id);
4072 msg_puts_attr((char_u *)"links to", attr);
4073 msg_putchar(' ');
4074 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
4075 }
4076}
4077
4078 static void
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004079syn_list_flags(nlist, flags, attr)
4080 struct name_list *nlist;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004081 int flags;
4082 int attr;
4083{
4084 int i;
4085
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004086 for (i = 0; nlist[i].flag != 0; ++i)
4087 if (flags & nlist[i].flag)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004088 {
Bram Moolenaar70b2a562012-01-10 22:26:17 +01004089 msg_puts_attr((char_u *)nlist[i].name, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004090 msg_putchar(' ');
4091 }
4092}
4093
4094/*
4095 * List one syntax cluster, for ":syntax" or "syntax list syntax_name".
4096 */
4097 static void
4098syn_list_cluster(id)
4099 int id;
4100{
4101 int endcol = 15;
4102
4103 /* slight hack: roughly duplicate the guts of syn_list_header() */
4104 msg_putchar('\n');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004105 msg_outtrans(SYN_CLSTR(curwin->w_s)[id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004106
4107 if (msg_col >= endcol) /* output at least one space */
4108 endcol = msg_col + 1;
4109 if (Columns <= endcol) /* avoid hang for tiny window */
4110 endcol = Columns - 1;
4111
4112 msg_advance(endcol);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004113 if (SYN_CLSTR(curwin->w_s)[id].scl_list != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004114 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004115 put_id_list((char_u *)"cluster", SYN_CLSTR(curwin->w_s)[id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004116 hl_attr(HLF_D));
4117 }
4118 else
4119 {
4120 msg_puts_attr((char_u *)"cluster", hl_attr(HLF_D));
4121 msg_puts((char_u *)"=NONE");
4122 }
4123}
4124
4125 static void
4126put_id_list(name, list, attr)
4127 char_u *name;
4128 short *list;
4129 int attr;
4130{
4131 short *p;
4132
4133 msg_puts_attr(name, attr);
4134 msg_putchar('=');
4135 for (p = list; *p; ++p)
4136 {
4137 if (*p >= SYNID_ALLBUT && *p < SYNID_TOP)
4138 {
4139 if (p[1])
4140 MSG_PUTS("ALLBUT");
4141 else
4142 MSG_PUTS("ALL");
4143 }
4144 else if (*p >= SYNID_TOP && *p < SYNID_CONTAINED)
4145 {
4146 MSG_PUTS("TOP");
4147 }
4148 else if (*p >= SYNID_CONTAINED && *p < SYNID_CLUSTER)
4149 {
4150 MSG_PUTS("CONTAINED");
4151 }
4152 else if (*p >= SYNID_CLUSTER)
4153 {
4154 short scl_id = *p - SYNID_CLUSTER;
4155
4156 msg_putchar('@');
Bram Moolenaar860cae12010-06-05 23:22:07 +02004157 msg_outtrans(SYN_CLSTR(curwin->w_s)[scl_id].scl_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004158 }
4159 else
4160 msg_outtrans(HL_TABLE()[*p - 1].sg_name);
4161 if (p[1])
4162 msg_putchar(',');
4163 }
4164 msg_putchar(' ');
4165}
4166
4167 static void
4168put_pattern(s, c, spp, attr)
4169 char *s;
4170 int c;
4171 synpat_T *spp;
4172 int attr;
4173{
4174 long n;
4175 int mask;
4176 int first;
4177 static char *sepchars = "/+=-#@\"|'^&";
4178 int i;
4179
4180 /* May have to write "matchgroup=group" */
4181 if (last_matchgroup != spp->sp_syn_match_id)
4182 {
4183 last_matchgroup = spp->sp_syn_match_id;
4184 msg_puts_attr((char_u *)"matchgroup", attr);
4185 msg_putchar('=');
4186 if (last_matchgroup == 0)
4187 msg_outtrans((char_u *)"NONE");
4188 else
4189 msg_outtrans(HL_TABLE()[last_matchgroup - 1].sg_name);
4190 msg_putchar(' ');
4191 }
4192
4193 /* output the name of the pattern and an '=' or ' ' */
4194 msg_puts_attr((char_u *)s, attr);
4195 msg_putchar(c);
4196
4197 /* output the pattern, in between a char that is not in the pattern */
4198 for (i = 0; vim_strchr(spp->sp_pattern, sepchars[i]) != NULL; )
4199 if (sepchars[++i] == NUL)
4200 {
4201 i = 0; /* no good char found, just use the first one */
4202 break;
4203 }
4204 msg_putchar(sepchars[i]);
4205 msg_outtrans(spp->sp_pattern);
4206 msg_putchar(sepchars[i]);
4207
4208 /* output any pattern options */
4209 first = TRUE;
4210 for (i = 0; i < SPO_COUNT; ++i)
4211 {
4212 mask = (1 << i);
4213 if (spp->sp_off_flags & (mask + (mask << SPO_COUNT)))
4214 {
4215 if (!first)
4216 msg_putchar(','); /* separate with commas */
4217 msg_puts((char_u *)spo_name_tab[i]);
4218 n = spp->sp_offsets[i];
4219 if (i != SPO_LC_OFF)
4220 {
4221 if (spp->sp_off_flags & mask)
4222 msg_putchar('s');
4223 else
4224 msg_putchar('e');
4225 if (n > 0)
4226 msg_putchar('+');
4227 }
4228 if (n || i == SPO_LC_OFF)
4229 msg_outnum(n);
4230 first = FALSE;
4231 }
4232 }
4233 msg_putchar(' ');
4234}
4235
4236/*
4237 * List or clear the keywords for one syntax group.
4238 * Return TRUE if the header has been printed.
4239 */
4240 static int
Bram Moolenaardad6b692005-01-25 22:14:34 +00004241syn_list_keywords(id, ht, did_header, attr)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004242 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004243 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004244 int did_header; /* header has already been printed */
4245 int attr;
4246{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004247 int outlen;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004248 hashitem_T *hi;
4249 keyentry_T *kp;
4250 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004251 int prev_contained = 0;
4252 short *prev_next_list = NULL;
4253 short *prev_cont_in_list = NULL;
4254 int prev_skipnl = 0;
4255 int prev_skipwhite = 0;
4256 int prev_skipempty = 0;
4257
Bram Moolenaar071d4272004-06-13 20:20:40 +00004258 /*
4259 * Unfortunately, this list of keywords is not sorted on alphabet but on
4260 * hash value...
4261 */
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004262 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004263 for (hi = ht->ht_array; todo > 0 && !got_int; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004264 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004265 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004266 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004267 --todo;
4268 for (kp = HI2KE(hi); kp != NULL && !got_int; kp = kp->ke_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004269 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004270 if (kp->k_syn.id == id)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004271 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004272 if (prev_contained != (kp->flags & HL_CONTAINED)
4273 || prev_skipnl != (kp->flags & HL_SKIPNL)
4274 || prev_skipwhite != (kp->flags & HL_SKIPWHITE)
4275 || prev_skipempty != (kp->flags & HL_SKIPEMPTY)
4276 || prev_cont_in_list != kp->k_syn.cont_in_list
4277 || prev_next_list != kp->next_list)
4278 outlen = 9999;
4279 else
4280 outlen = (int)STRLEN(kp->keyword);
4281 /* output "contained" and "nextgroup" on each line */
4282 if (syn_list_header(did_header, outlen, id))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004283 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004284 prev_contained = 0;
4285 prev_next_list = NULL;
4286 prev_cont_in_list = NULL;
4287 prev_skipnl = 0;
4288 prev_skipwhite = 0;
4289 prev_skipempty = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004290 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004291 did_header = TRUE;
4292 if (prev_contained != (kp->flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004293 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004294 msg_puts_attr((char_u *)"contained", attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004295 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004296 prev_contained = (kp->flags & HL_CONTAINED);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004297 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004298 if (kp->k_syn.cont_in_list != prev_cont_in_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004299 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004300 put_id_list((char_u *)"containedin",
4301 kp->k_syn.cont_in_list, attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004302 msg_putchar(' ');
Bram Moolenaardad6b692005-01-25 22:14:34 +00004303 prev_cont_in_list = kp->k_syn.cont_in_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004304 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004305 if (kp->next_list != prev_next_list)
4306 {
4307 put_id_list((char_u *)"nextgroup", kp->next_list, attr);
4308 msg_putchar(' ');
4309 prev_next_list = kp->next_list;
4310 if (kp->flags & HL_SKIPNL)
4311 {
4312 msg_puts_attr((char_u *)"skipnl", attr);
4313 msg_putchar(' ');
4314 prev_skipnl = (kp->flags & HL_SKIPNL);
4315 }
4316 if (kp->flags & HL_SKIPWHITE)
4317 {
4318 msg_puts_attr((char_u *)"skipwhite", attr);
4319 msg_putchar(' ');
4320 prev_skipwhite = (kp->flags & HL_SKIPWHITE);
4321 }
4322 if (kp->flags & HL_SKIPEMPTY)
4323 {
4324 msg_puts_attr((char_u *)"skipempty", attr);
4325 msg_putchar(' ');
4326 prev_skipempty = (kp->flags & HL_SKIPEMPTY);
4327 }
4328 }
4329 msg_outtrans(kp->keyword);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004330 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004331 }
4332 }
4333 }
4334
4335 return did_header;
4336}
4337
4338 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004339syn_clear_keyword(id, ht)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004340 int id;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004341 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004342{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004343 hashitem_T *hi;
4344 keyentry_T *kp;
4345 keyentry_T *kp_prev;
4346 keyentry_T *kp_next;
4347 int todo;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004348
Bram Moolenaardad6b692005-01-25 22:14:34 +00004349 hash_lock(ht);
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004350 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004351 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004352 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004353 if (!HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004354 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004355 --todo;
4356 kp_prev = NULL;
4357 for (kp = HI2KE(hi); kp != NULL; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00004358 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004359 if (kp->k_syn.id == id)
4360 {
4361 kp_next = kp->ke_next;
4362 if (kp_prev == NULL)
4363 {
4364 if (kp_next == NULL)
4365 hash_remove(ht, hi);
4366 else
4367 hi->hi_key = KE2HIKEY(kp_next);
4368 }
4369 else
4370 kp_prev->ke_next = kp_next;
4371 vim_free(kp->next_list);
4372 vim_free(kp->k_syn.cont_in_list);
4373 vim_free(kp);
4374 kp = kp_next;
4375 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004376 else
Bram Moolenaardad6b692005-01-25 22:14:34 +00004377 {
4378 kp_prev = kp;
4379 kp = kp->ke_next;
4380 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004381 }
4382 }
4383 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004384 hash_unlock(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004385}
4386
4387/*
Bram Moolenaardad6b692005-01-25 22:14:34 +00004388 * Clear a whole keyword table.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004389 */
4390 static void
Bram Moolenaardad6b692005-01-25 22:14:34 +00004391clear_keywtab(ht)
4392 hashtab_T *ht;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004393{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004394 hashitem_T *hi;
4395 int todo;
4396 keyentry_T *kp;
4397 keyentry_T *kp_next;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004398
Bram Moolenaara93fa7e2006-04-17 22:14:47 +00004399 todo = (int)ht->ht_used;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004400 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004401 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004402 if (!HASHITEM_EMPTY(hi))
4403 {
4404 --todo;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004405 for (kp = HI2KE(hi); kp != NULL; kp = kp_next)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004406 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004407 kp_next = kp->ke_next;
4408 vim_free(kp->next_list);
4409 vim_free(kp->k_syn.cont_in_list);
4410 vim_free(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004411 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004412 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004413 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004414 hash_clear(ht);
4415 hash_init(ht);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004416}
4417
4418/*
4419 * Add a keyword to the list of keywords.
4420 */
4421 static void
Bram Moolenaar860cae12010-06-05 23:22:07 +02004422add_keyword(name, id, flags, cont_in_list, next_list, conceal_char)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004423 char_u *name; /* name of keyword */
4424 int id; /* group ID for this keyword */
4425 int flags; /* flags for this keyword */
4426 short *cont_in_list; /* containedin for this keyword */
4427 short *next_list; /* nextgroup for this keyword */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004428 int conceal_char;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004429{
Bram Moolenaardad6b692005-01-25 22:14:34 +00004430 keyentry_T *kp;
4431 hashtab_T *ht;
4432 hashitem_T *hi;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004433 char_u *name_ic;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004434 long_u hash;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004435 char_u name_folded[MAXKEYWLEN + 1];
Bram Moolenaar071d4272004-06-13 20:20:40 +00004436
Bram Moolenaar860cae12010-06-05 23:22:07 +02004437 if (curwin->w_s->b_syn_ic)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004438 name_ic = str_foldcase(name, (int)STRLEN(name),
4439 name_folded, MAXKEYWLEN + 1);
4440 else
4441 name_ic = name;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004442 kp = (keyentry_T *)alloc((int)(sizeof(keyentry_T) + STRLEN(name_ic)));
4443 if (kp == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004444 return;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004445 STRCPY(kp->keyword, name_ic);
Bram Moolenaardad6b692005-01-25 22:14:34 +00004446 kp->k_syn.id = id;
4447 kp->k_syn.inc_tag = current_syn_inc_tag;
4448 kp->flags = flags;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004449 kp->k_char = conceal_char;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004450 kp->k_syn.cont_in_list = copy_id_list(cont_in_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004451 if (cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004452 curwin->w_s->b_syn_containedin = TRUE;
Bram Moolenaardad6b692005-01-25 22:14:34 +00004453 kp->next_list = copy_id_list(next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004454
Bram Moolenaar860cae12010-06-05 23:22:07 +02004455 if (curwin->w_s->b_syn_ic)
4456 ht = &curwin->w_s->b_keywtab_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004457 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02004458 ht = &curwin->w_s->b_keywtab;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004459
Bram Moolenaardad6b692005-01-25 22:14:34 +00004460 hash = hash_hash(kp->keyword);
4461 hi = hash_lookup(ht, kp->keyword, hash);
4462 if (HASHITEM_EMPTY(hi))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004463 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004464 /* new keyword, add to hashtable */
4465 kp->ke_next = NULL;
4466 hash_add_item(ht, hi, kp->keyword, hash);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004467 }
Bram Moolenaardad6b692005-01-25 22:14:34 +00004468 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004469 {
Bram Moolenaardad6b692005-01-25 22:14:34 +00004470 /* keyword already exists, prepend to list */
4471 kp->ke_next = HI2KE(hi);
4472 hi->hi_key = KE2HIKEY(kp);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004473 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004474}
4475
4476/*
4477 * Get the start and end of the group name argument.
4478 * Return a pointer to the first argument.
4479 * Return NULL if the end of the command was found instead of further args.
4480 */
4481 static char_u *
4482get_group_name(arg, name_end)
4483 char_u *arg; /* start of the argument */
4484 char_u **name_end; /* pointer to end of the name */
4485{
4486 char_u *rest;
4487
4488 *name_end = skiptowhite(arg);
4489 rest = skipwhite(*name_end);
4490
4491 /*
4492 * Check if there are enough arguments. The first argument may be a
4493 * pattern, where '|' is allowed, so only check for NUL.
4494 */
4495 if (ends_excmd(*arg) || *rest == NUL)
4496 return NULL;
4497 return rest;
4498}
4499
4500/*
4501 * Check for syntax command option arguments.
4502 * This can be called at any place in the list of arguments, and just picks
4503 * out the arguments that are known. Can be called several times in a row to
4504 * collect all options in between other arguments.
4505 * Return a pointer to the next argument (which isn't an option).
4506 * Return NULL for any error;
4507 */
4508 static char_u *
Bram Moolenaar860cae12010-06-05 23:22:07 +02004509get_syn_options(arg, opt, conceal_char)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004510 char_u *arg; /* next argument to be checked */
4511 syn_opt_arg_T *opt; /* various things */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004512 int *conceal_char UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004513{
Bram Moolenaar071d4272004-06-13 20:20:40 +00004514 char_u *gname_start, *gname;
4515 int syn_id;
4516 int len;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004517 char *p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004518 int i;
4519 int fidx;
4520 static struct flag
4521 {
4522 char *name;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004523 int argtype;
4524 int flags;
4525 } flagtab[] = { {"cCoOnNtTaAiInNeEdD", 0, HL_CONTAINED},
4526 {"oOnNeElLiInNeE", 0, HL_ONELINE},
4527 {"kKeEeEpPeEnNdD", 0, HL_KEEPEND},
4528 {"eExXtTeEnNdD", 0, HL_EXTEND},
4529 {"eExXcClLuUdDeEnNlL", 0, HL_EXCLUDENL},
4530 {"tTrRaAnNsSpPaArReEnNtT", 0, HL_TRANSP},
4531 {"sSkKiIpPnNlL", 0, HL_SKIPNL},
4532 {"sSkKiIpPwWhHiItTeE", 0, HL_SKIPWHITE},
4533 {"sSkKiIpPeEmMpPtTyY", 0, HL_SKIPEMPTY},
4534 {"gGrRoOuUpPhHeErReE", 0, HL_SYNC_HERE},
4535 {"gGrRoOuUpPtThHeErReE", 0, HL_SYNC_THERE},
4536 {"dDiIsSpPlLaAyY", 0, HL_DISPLAY},
4537 {"fFoOlLdD", 0, HL_FOLD},
Bram Moolenaar860cae12010-06-05 23:22:07 +02004538 {"cCoOnNcCeEaAlL", 0, HL_CONCEAL},
4539 {"cCoOnNcCeEaAlLeEnNdDsS", 0, HL_CONCEALENDS},
4540 {"cCcChHaArR", 11, 0},
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004541 {"cCoOnNtTaAiInNsS", 1, 0},
4542 {"cCoOnNtTaAiInNeEdDiInN", 2, 0},
4543 {"nNeExXtTgGrRoOuUpP", 3, 0},
Bram Moolenaar071d4272004-06-13 20:20:40 +00004544 };
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004545 static char *first_letters = "cCoOkKeEtTsSgGdDfFnN";
Bram Moolenaar071d4272004-06-13 20:20:40 +00004546
4547 if (arg == NULL) /* already detected error */
4548 return NULL;
4549
Bram Moolenaar860cae12010-06-05 23:22:07 +02004550#ifdef FEAT_CONCEAL
4551 if (curwin->w_s->b_syn_conceal)
4552 opt->flags |= HL_CONCEAL;
4553#endif
4554
Bram Moolenaar071d4272004-06-13 20:20:40 +00004555 for (;;)
4556 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004557 /*
4558 * This is used very often when a large number of keywords is defined.
4559 * Need to skip quickly when no option name is found.
4560 * Also avoid tolower(), it's slow.
4561 */
4562 if (strchr(first_letters, *arg) == NULL)
4563 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004564
4565 for (fidx = sizeof(flagtab) / sizeof(struct flag); --fidx >= 0; )
4566 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004567 p = flagtab[fidx].name;
4568 for (i = 0, len = 0; p[i] != NUL; i += 2, ++len)
4569 if (arg[len] != p[i] && arg[len] != p[i + 1])
4570 break;
4571 if (p[i] == NUL && (vim_iswhite(arg[len])
4572 || (flagtab[fidx].argtype > 0
4573 ? arg[len] == '='
4574 : ends_excmd(arg[len]))))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004575 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004576 if (opt->keyword
4577 && (flagtab[fidx].flags == HL_DISPLAY
4578 || flagtab[fidx].flags == HL_FOLD
4579 || flagtab[fidx].flags == HL_EXTEND))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004580 /* treat "display", "fold" and "extend" as a keyword */
4581 fidx = -1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004582 break;
4583 }
4584 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004585 if (fidx < 0) /* no match found */
4586 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004587
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004588 if (flagtab[fidx].argtype == 1)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004589 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004590 if (!opt->has_cont_list)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004591 {
4592 EMSG(_("E395: contains argument not accepted here"));
4593 return NULL;
4594 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004595 if (get_id_list(&arg, 8, &opt->cont_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004596 return NULL;
4597 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004598 else if (flagtab[fidx].argtype == 2)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004599 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004600 if (get_id_list(&arg, 11, &opt->cont_in_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004601 return NULL;
4602 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004603 else if (flagtab[fidx].argtype == 3)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004604 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004605 if (get_id_list(&arg, 9, &opt->next_list) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004606 return NULL;
4607 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02004608 else if (flagtab[fidx].argtype == 11 && arg[5] == '=')
4609 {
4610#ifdef FEAT_MBYTE
4611 /* cchar=? */
4612 if (has_mbyte)
4613 {
4614# ifdef FEAT_CONCEAL
4615 *conceal_char = mb_ptr2char(arg + 6);
4616# endif
4617 arg += mb_ptr2len(arg + 6) - 1;
4618 }
4619 else
4620#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004621 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004622#ifdef FEAT_CONCEAL
4623 *conceal_char = arg[6];
4624#else
4625 ;
4626#endif
Bram Moolenaar56be9502010-06-06 14:20:26 +02004627 }
Bram Moolenaar4124e722011-01-22 00:58:20 +01004628#ifdef FEAT_CONCEAL
4629 if (!vim_isprintc_strict(*conceal_char))
4630 {
4631 EMSG(_("E844: invalid cchar value"));
4632 return NULL;
4633 }
4634#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02004635 arg = skipwhite(arg + 7);
4636 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004637 else
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004638 {
4639 opt->flags |= flagtab[fidx].flags;
4640 arg = skipwhite(arg + len);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004641
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004642 if (flagtab[fidx].flags == HL_SYNC_HERE
4643 || flagtab[fidx].flags == HL_SYNC_THERE)
4644 {
4645 if (opt->sync_idx == NULL)
4646 {
4647 EMSG(_("E393: group[t]here not accepted here"));
4648 return NULL;
4649 }
4650 gname_start = arg;
4651 arg = skiptowhite(arg);
4652 if (gname_start == arg)
4653 return NULL;
4654 gname = vim_strnsave(gname_start, (int)(arg - gname_start));
4655 if (gname == NULL)
4656 return NULL;
4657 if (STRCMP(gname, "NONE") == 0)
4658 *opt->sync_idx = NONE_IDX;
4659 else
4660 {
4661 syn_id = syn_name2id(gname);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004662 for (i = curwin->w_s->b_syn_patterns.ga_len; --i >= 0; )
4663 if (SYN_ITEMS(curwin->w_s)[i].sp_syn.id == syn_id
4664 && SYN_ITEMS(curwin->w_s)[i].sp_type == SPTYPE_START)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004665 {
4666 *opt->sync_idx = i;
4667 break;
4668 }
4669 if (i < 0)
4670 {
4671 EMSG2(_("E394: Didn't find region item for %s"), gname);
4672 vim_free(gname);
4673 return NULL;
4674 }
4675 }
4676
4677 vim_free(gname);
4678 arg = skipwhite(arg);
4679 }
4680#ifdef FEAT_FOLDING
4681 else if (flagtab[fidx].flags == HL_FOLD
4682 && foldmethodIsSyntax(curwin))
4683 /* Need to update folds later. */
4684 foldUpdateAll(curwin);
4685#endif
4686 }
4687 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004688
4689 return arg;
4690}
4691
4692/*
4693 * Adjustments to syntax item when declared in a ":syn include"'d file.
4694 * Set the contained flag, and if the item is not already contained, add it
4695 * to the specified top-level group, if any.
4696 */
4697 static void
4698syn_incl_toplevel(id, flagsp)
4699 int id;
4700 int *flagsp;
4701{
Bram Moolenaar860cae12010-06-05 23:22:07 +02004702 if ((*flagsp & HL_CONTAINED) || curwin->w_s->b_syn_topgrp == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004703 return;
4704 *flagsp |= HL_CONTAINED;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004705 if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004706 {
4707 /* We have to alloc this, because syn_combine_list() will free it. */
4708 short *grp_list = (short *)alloc((unsigned)(2 * sizeof(short)));
Bram Moolenaar860cae12010-06-05 23:22:07 +02004709 int tlg_id = curwin->w_s->b_syn_topgrp - SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004710
4711 if (grp_list != NULL)
4712 {
4713 grp_list[0] = id;
4714 grp_list[1] = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004715 syn_combine_list(&SYN_CLSTR(curwin->w_s)[tlg_id].scl_list, &grp_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00004716 CLUSTER_ADD);
4717 }
4718 }
4719}
4720
4721/*
4722 * Handle ":syntax include [@{group-name}] filename" command.
4723 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004724 static void
4725syn_cmd_include(eap, syncing)
4726 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004727 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004728{
4729 char_u *arg = eap->arg;
4730 int sgl_id = 1;
4731 char_u *group_name_end;
4732 char_u *rest;
4733 char_u *errormsg = NULL;
4734 int prev_toplvl_grp;
4735 int prev_syn_inc_tag;
4736 int source = FALSE;
4737
4738 eap->nextcmd = find_nextcmd(arg);
4739 if (eap->skip)
4740 return;
4741
4742 if (arg[0] == '@')
4743 {
4744 ++arg;
4745 rest = get_group_name(arg, &group_name_end);
4746 if (rest == NULL)
4747 {
4748 EMSG((char_u *)_("E397: Filename required"));
4749 return;
4750 }
4751 sgl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004752 if (sgl_id == 0)
4753 return;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004754 /* separate_nextcmd() and expand_filename() depend on this */
4755 eap->arg = rest;
4756 }
4757
4758 /*
4759 * Everything that's left, up to the next command, should be the
4760 * filename to include.
4761 */
4762 eap->argt |= (XFILE | NOSPC);
4763 separate_nextcmd(eap);
4764 if (*eap->arg == '<' || *eap->arg == '$' || mch_isFullName(eap->arg))
4765 {
4766 /* For an absolute path, "$VIM/..." or "<sfile>.." we ":source" the
4767 * file. Need to expand the file name first. In other cases
4768 * ":runtime!" is used. */
4769 source = TRUE;
4770 if (expand_filename(eap, syn_cmdlinep, &errormsg) == FAIL)
4771 {
4772 if (errormsg != NULL)
4773 EMSG(errormsg);
4774 return;
4775 }
4776 }
4777
4778 /*
4779 * Save and restore the existing top-level grouplist id and ":syn
4780 * include" tag around the actual inclusion.
4781 */
Bram Moolenaar42431a72011-04-01 14:44:59 +02004782 if (running_syn_inc_tag >= MAX_SYN_INC_TAG)
4783 {
4784 EMSG((char_u *)_("E847: Too many syntax includes"));
4785 return;
4786 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004787 prev_syn_inc_tag = current_syn_inc_tag;
4788 current_syn_inc_tag = ++running_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004789 prev_toplvl_grp = curwin->w_s->b_syn_topgrp;
4790 curwin->w_s->b_syn_topgrp = sgl_id;
Bram Moolenaar786b1dc2007-06-28 10:03:45 +00004791 if (source ? do_source(eap->arg, FALSE, DOSO_NONE) == FAIL
4792 : source_runtime(eap->arg, TRUE) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00004793 EMSG2(_(e_notopen), eap->arg);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004794 curwin->w_s->b_syn_topgrp = prev_toplvl_grp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004795 current_syn_inc_tag = prev_syn_inc_tag;
4796}
4797
4798/*
4799 * Handle ":syntax keyword {group-name} [{option}] keyword .." command.
4800 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004801 static void
4802syn_cmd_keyword(eap, syncing)
4803 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00004804 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004805{
4806 char_u *arg = eap->arg;
4807 char_u *group_name_end;
4808 int syn_id;
4809 char_u *rest;
Bram Moolenaar42431a72011-04-01 14:44:59 +02004810 char_u *keyword_copy = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004811 char_u *p;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004812 char_u *kw;
4813 syn_opt_arg_T syn_opt_arg;
4814 int cnt;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004815 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004816
4817 rest = get_group_name(arg, &group_name_end);
4818
4819 if (rest != NULL)
4820 {
4821 syn_id = syn_check_group(arg, (int)(group_name_end - arg));
Bram Moolenaar42431a72011-04-01 14:44:59 +02004822 if (syn_id != 0)
4823 /* allocate a buffer, for removing backslashes in the keyword */
4824 keyword_copy = alloc((unsigned)STRLEN(rest) + 1);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004825 if (keyword_copy != NULL)
4826 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004827 syn_opt_arg.flags = 0;
4828 syn_opt_arg.keyword = TRUE;
4829 syn_opt_arg.sync_idx = NULL;
4830 syn_opt_arg.has_cont_list = FALSE;
4831 syn_opt_arg.cont_in_list = NULL;
4832 syn_opt_arg.next_list = NULL;
4833
Bram Moolenaar071d4272004-06-13 20:20:40 +00004834 /*
4835 * The options given apply to ALL keywords, so all options must be
4836 * found before keywords can be created.
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004837 * 1: collect the options and copy the keywords to keyword_copy.
Bram Moolenaar071d4272004-06-13 20:20:40 +00004838 */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004839 cnt = 0;
4840 p = keyword_copy;
4841 for ( ; rest != NULL && !ends_excmd(*rest); rest = skipwhite(rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004842 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02004843 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004844 if (rest == NULL || ends_excmd(*rest))
4845 break;
4846 /* Copy the keyword, removing backslashes, and add a NUL. */
4847 while (*rest != NUL && !vim_iswhite(*rest))
Bram Moolenaar071d4272004-06-13 20:20:40 +00004848 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004849 if (*rest == '\\' && rest[1] != NUL)
4850 ++rest;
4851 *p++ = *rest++;
4852 }
4853 *p++ = NUL;
4854 ++cnt;
4855 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004856
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004857 if (!eap->skip)
4858 {
4859 /* Adjust flags for use of ":syn include". */
4860 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
4861
4862 /*
4863 * 2: Add an entry for each keyword.
4864 */
4865 for (kw = keyword_copy; --cnt >= 0; kw += STRLEN(kw) + 1)
4866 {
4867 for (p = vim_strchr(kw, '['); ; )
4868 {
4869 if (p != NULL)
4870 *p = NUL;
4871 add_keyword(kw, syn_id, syn_opt_arg.flags,
Bram Moolenaar860cae12010-06-05 23:22:07 +02004872 syn_opt_arg.cont_in_list,
4873 syn_opt_arg.next_list, conceal_char);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004874 if (p == NULL)
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004875 break;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004876 if (p[1] == NUL)
4877 {
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00004878 EMSG2(_("E789: Missing ']': %s"), kw);
Bram Moolenaar1560d072015-08-13 22:53:29 +02004879 goto error;
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004880 }
4881 if (p[1] == ']')
4882 {
Bram Moolenaar1560d072015-08-13 22:53:29 +02004883 if (p[2] != NUL)
4884 {
4885 EMSG3(_("E890: trailing char after ']': %s]%s"),
4886 kw, &p[2]);
4887 goto error;
4888 }
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004889 kw = p + 1; /* skip over the "]" */
4890 break;
4891 }
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004892#ifdef FEAT_MBYTE
4893 if (has_mbyte)
4894 {
Bram Moolenaar0fa313a2005-08-10 21:07:57 +00004895 int l = (*mb_ptr2len)(p + 1);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004896
4897 mch_memmove(p, p + 1, l);
4898 p += l;
4899 }
4900 else
Bram Moolenaar071d4272004-06-13 20:20:40 +00004901#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004902 {
4903 p[0] = p[1];
4904 ++p;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004905 }
4906 }
4907 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00004908 }
Bram Moolenaar1560d072015-08-13 22:53:29 +02004909error:
Bram Moolenaar071d4272004-06-13 20:20:40 +00004910 vim_free(keyword_copy);
Bram Moolenaar26a60b42005-02-22 08:49:11 +00004911 vim_free(syn_opt_arg.cont_in_list);
4912 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004913 }
4914 }
4915
4916 if (rest != NULL)
4917 eap->nextcmd = check_nextcmd(rest);
4918 else
4919 EMSG2(_(e_invarg2), arg);
4920
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00004921 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02004922 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00004923}
4924
4925/*
4926 * Handle ":syntax match {name} [{options}] {pattern} [{options}]".
4927 *
4928 * Also ":syntax sync match {name} [[grouphere | groupthere] {group-name}] .."
4929 */
4930 static void
4931syn_cmd_match(eap, syncing)
4932 exarg_T *eap;
4933 int syncing; /* TRUE for ":syntax sync match .. " */
4934{
4935 char_u *arg = eap->arg;
4936 char_u *group_name_end;
4937 char_u *rest;
4938 synpat_T item; /* the item found in the line */
4939 int syn_id;
4940 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004941 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004942 int sync_idx = 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004943 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004944
4945 /* Isolate the group name, check for validity */
4946 rest = get_group_name(arg, &group_name_end);
4947
4948 /* Get options before the pattern */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004949 syn_opt_arg.flags = 0;
4950 syn_opt_arg.keyword = FALSE;
4951 syn_opt_arg.sync_idx = syncing ? &sync_idx : NULL;
4952 syn_opt_arg.has_cont_list = TRUE;
4953 syn_opt_arg.cont_list = NULL;
4954 syn_opt_arg.cont_in_list = NULL;
4955 syn_opt_arg.next_list = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004956 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004957
4958 /* get the pattern. */
4959 init_syn_patterns();
4960 vim_memset(&item, 0, sizeof(item));
4961 rest = get_syn_pattern(rest, &item);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004962 if (vim_regcomp_had_eol() && !(syn_opt_arg.flags & HL_EXCLUDENL))
4963 syn_opt_arg.flags |= HL_HAS_EOL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00004964
4965 /* Get options after the pattern */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004966 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004967
4968 if (rest != NULL) /* all arguments are valid */
4969 {
4970 /*
4971 * Check for trailing command and illegal trailing arguments.
4972 */
4973 eap->nextcmd = check_nextcmd(rest);
4974 if (!ends_excmd(*rest) || eap->skip)
4975 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004976 else if (ga_grow(&curwin->w_s->b_syn_patterns, 1) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00004977 && (syn_id = syn_check_group(arg,
4978 (int)(group_name_end - arg))) != 0)
4979 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004980 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00004981 /*
4982 * Store the pattern in the syn_items list
4983 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02004984 idx = curwin->w_s->b_syn_patterns.ga_len;
4985 SYN_ITEMS(curwin->w_s)[idx] = item;
4986 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
4987 SYN_ITEMS(curwin->w_s)[idx].sp_type = SPTYPE_MATCH;
4988 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
4989 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag = current_syn_inc_tag;
4990 SYN_ITEMS(curwin->w_s)[idx].sp_flags = syn_opt_arg.flags;
4991 SYN_ITEMS(curwin->w_s)[idx].sp_sync_idx = sync_idx;
4992 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list = syn_opt_arg.cont_list;
4993 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004994 syn_opt_arg.cont_in_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004995#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02004996 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02004997#endif
Bram Moolenaar6ac54292005-02-02 23:07:25 +00004998 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02004999 curwin->w_s->b_syn_containedin = TRUE;
5000 SYN_ITEMS(curwin->w_s)[idx].sp_next_list = syn_opt_arg.next_list;
5001 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005002
5003 /* remember that we found a match for syncing on */
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005004 if (syn_opt_arg.flags & (HL_SYNC_HERE|HL_SYNC_THERE))
Bram Moolenaar860cae12010-06-05 23:22:07 +02005005 curwin->w_s->b_syn_sync_flags |= SF_MATCH;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005006#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005007 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005008 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005009#endif
5010
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005011 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005012 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005013 return; /* don't free the progs and patterns now */
5014 }
5015 }
5016
5017 /*
5018 * Something failed, free the allocated memory.
5019 */
Bram Moolenaar473de612013-06-08 18:19:48 +02005020 vim_regfree(item.sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005021 vim_free(item.sp_pattern);
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005022 vim_free(syn_opt_arg.cont_list);
5023 vim_free(syn_opt_arg.cont_in_list);
5024 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005025
5026 if (rest == NULL)
5027 EMSG2(_(e_invarg2), arg);
5028}
5029
5030/*
5031 * Handle ":syntax region {group-name} [matchgroup={group-name}]
5032 * start {start} .. [skip {skip}] end {end} .. [{options}]".
5033 */
5034 static void
5035syn_cmd_region(eap, syncing)
5036 exarg_T *eap;
5037 int syncing; /* TRUE for ":syntax sync region .." */
5038{
5039 char_u *arg = eap->arg;
5040 char_u *group_name_end;
5041 char_u *rest; /* next arg, NULL on error */
5042 char_u *key_end;
5043 char_u *key = NULL;
5044 char_u *p;
5045 int item;
5046#define ITEM_START 0
5047#define ITEM_SKIP 1
5048#define ITEM_END 2
5049#define ITEM_MATCHGROUP 3
5050 struct pat_ptr
5051 {
5052 synpat_T *pp_synp; /* pointer to syn_pattern */
5053 int pp_matchgroup_id; /* matchgroup ID */
5054 struct pat_ptr *pp_next; /* pointer to next pat_ptr */
5055 } *(pat_ptrs[3]);
5056 /* patterns found in the line */
5057 struct pat_ptr *ppp;
5058 struct pat_ptr *ppp_next;
5059 int pat_count = 0; /* nr of syn_patterns found */
5060 int syn_id;
5061 int matchgroup_id = 0;
5062 int not_enough = FALSE; /* not enough arguments */
5063 int illegal = FALSE; /* illegal arguments */
5064 int success = FALSE;
5065 int idx;
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005066 syn_opt_arg_T syn_opt_arg;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005067 int conceal_char = NUL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005068
5069 /* Isolate the group name, check for validity */
5070 rest = get_group_name(arg, &group_name_end);
5071
5072 pat_ptrs[0] = NULL;
5073 pat_ptrs[1] = NULL;
5074 pat_ptrs[2] = NULL;
5075
5076 init_syn_patterns();
5077
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005078 syn_opt_arg.flags = 0;
5079 syn_opt_arg.keyword = FALSE;
5080 syn_opt_arg.sync_idx = NULL;
5081 syn_opt_arg.has_cont_list = TRUE;
5082 syn_opt_arg.cont_list = NULL;
5083 syn_opt_arg.cont_in_list = NULL;
5084 syn_opt_arg.next_list = NULL;
5085
Bram Moolenaar071d4272004-06-13 20:20:40 +00005086 /*
5087 * get the options, patterns and matchgroup.
5088 */
5089 while (rest != NULL && !ends_excmd(*rest))
5090 {
5091 /* Check for option arguments */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005092 rest = get_syn_options(rest, &syn_opt_arg, &conceal_char);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005093 if (rest == NULL || ends_excmd(*rest))
5094 break;
5095
5096 /* must be a pattern or matchgroup then */
5097 key_end = rest;
5098 while (*key_end && !vim_iswhite(*key_end) && *key_end != '=')
5099 ++key_end;
5100 vim_free(key);
5101 key = vim_strnsave_up(rest, (int)(key_end - rest));
5102 if (key == NULL) /* out of memory */
5103 {
5104 rest = NULL;
5105 break;
5106 }
5107 if (STRCMP(key, "MATCHGROUP") == 0)
5108 item = ITEM_MATCHGROUP;
5109 else if (STRCMP(key, "START") == 0)
5110 item = ITEM_START;
5111 else if (STRCMP(key, "END") == 0)
5112 item = ITEM_END;
5113 else if (STRCMP(key, "SKIP") == 0)
5114 {
5115 if (pat_ptrs[ITEM_SKIP] != NULL) /* one skip pattern allowed */
5116 {
5117 illegal = TRUE;
5118 break;
5119 }
5120 item = ITEM_SKIP;
5121 }
5122 else
5123 break;
5124 rest = skipwhite(key_end);
5125 if (*rest != '=')
5126 {
5127 rest = NULL;
5128 EMSG2(_("E398: Missing '=': %s"), arg);
5129 break;
5130 }
5131 rest = skipwhite(rest + 1);
5132 if (*rest == NUL)
5133 {
5134 not_enough = TRUE;
5135 break;
5136 }
5137
5138 if (item == ITEM_MATCHGROUP)
5139 {
5140 p = skiptowhite(rest);
5141 if ((p - rest == 4 && STRNCMP(rest, "NONE", 4) == 0) || eap->skip)
5142 matchgroup_id = 0;
5143 else
5144 {
5145 matchgroup_id = syn_check_group(rest, (int)(p - rest));
5146 if (matchgroup_id == 0)
5147 {
5148 illegal = TRUE;
5149 break;
5150 }
5151 }
5152 rest = skipwhite(p);
5153 }
5154 else
5155 {
5156 /*
5157 * Allocate room for a syn_pattern, and link it in the list of
5158 * syn_patterns for this item, at the start (because the list is
5159 * used from end to start).
5160 */
5161 ppp = (struct pat_ptr *)alloc((unsigned)sizeof(struct pat_ptr));
5162 if (ppp == NULL)
5163 {
5164 rest = NULL;
5165 break;
5166 }
5167 ppp->pp_next = pat_ptrs[item];
5168 pat_ptrs[item] = ppp;
5169 ppp->pp_synp = (synpat_T *)alloc_clear((unsigned)sizeof(synpat_T));
5170 if (ppp->pp_synp == NULL)
5171 {
5172 rest = NULL;
5173 break;
5174 }
5175
5176 /*
5177 * Get the syntax pattern and the following offset(s).
5178 */
5179 /* Enable the appropriate \z specials. */
5180 if (item == ITEM_START)
5181 reg_do_extmatch = REX_SET;
5182 else if (item == ITEM_SKIP || item == ITEM_END)
5183 reg_do_extmatch = REX_USE;
5184 rest = get_syn_pattern(rest, ppp->pp_synp);
5185 reg_do_extmatch = 0;
5186 if (item == ITEM_END && vim_regcomp_had_eol()
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005187 && !(syn_opt_arg.flags & HL_EXCLUDENL))
Bram Moolenaar071d4272004-06-13 20:20:40 +00005188 ppp->pp_synp->sp_flags |= HL_HAS_EOL;
5189 ppp->pp_matchgroup_id = matchgroup_id;
5190 ++pat_count;
5191 }
5192 }
5193 vim_free(key);
5194 if (illegal || not_enough)
5195 rest = NULL;
5196
5197 /*
5198 * Must have a "start" and "end" pattern.
5199 */
5200 if (rest != NULL && (pat_ptrs[ITEM_START] == NULL ||
5201 pat_ptrs[ITEM_END] == NULL))
5202 {
5203 not_enough = TRUE;
5204 rest = NULL;
5205 }
5206
5207 if (rest != NULL)
5208 {
5209 /*
5210 * Check for trailing garbage or command.
5211 * If OK, add the item.
5212 */
5213 eap->nextcmd = check_nextcmd(rest);
5214 if (!ends_excmd(*rest) || eap->skip)
5215 rest = NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005216 else if (ga_grow(&(curwin->w_s->b_syn_patterns), pat_count) != FAIL
Bram Moolenaar071d4272004-06-13 20:20:40 +00005217 && (syn_id = syn_check_group(arg,
5218 (int)(group_name_end - arg))) != 0)
5219 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005220 syn_incl_toplevel(syn_id, &syn_opt_arg.flags);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005221 /*
5222 * Store the start/skip/end in the syn_items list
5223 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005224 idx = curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005225 for (item = ITEM_START; item <= ITEM_END; ++item)
5226 {
5227 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp->pp_next)
5228 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005229 SYN_ITEMS(curwin->w_s)[idx] = *(ppp->pp_synp);
5230 SYN_ITEMS(curwin->w_s)[idx].sp_syncing = syncing;
5231 SYN_ITEMS(curwin->w_s)[idx].sp_type =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005232 (item == ITEM_START) ? SPTYPE_START :
5233 (item == ITEM_SKIP) ? SPTYPE_SKIP : SPTYPE_END;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005234 SYN_ITEMS(curwin->w_s)[idx].sp_flags |= syn_opt_arg.flags;
5235 SYN_ITEMS(curwin->w_s)[idx].sp_syn.id = syn_id;
Bram Moolenaar42431a72011-04-01 14:44:59 +02005236 SYN_ITEMS(curwin->w_s)[idx].sp_syn.inc_tag =
5237 current_syn_inc_tag;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005238 SYN_ITEMS(curwin->w_s)[idx].sp_syn_match_id =
Bram Moolenaar071d4272004-06-13 20:20:40 +00005239 ppp->pp_matchgroup_id;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005240#ifdef FEAT_CONCEAL
Bram Moolenaar6e202e52010-07-28 18:14:45 +02005241 SYN_ITEMS(curwin->w_s)[idx].sp_cchar = conceal_char;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005242#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005243 if (item == ITEM_START)
5244 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005245 SYN_ITEMS(curwin->w_s)[idx].sp_cont_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005246 syn_opt_arg.cont_list;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005247 SYN_ITEMS(curwin->w_s)[idx].sp_syn.cont_in_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005248 syn_opt_arg.cont_in_list;
5249 if (syn_opt_arg.cont_in_list != NULL)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005250 curwin->w_s->b_syn_containedin = TRUE;
5251 SYN_ITEMS(curwin->w_s)[idx].sp_next_list =
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005252 syn_opt_arg.next_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005253 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005254 ++curwin->w_s->b_syn_patterns.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005255 ++idx;
5256#ifdef FEAT_FOLDING
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005257 if (syn_opt_arg.flags & HL_FOLD)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005258 ++curwin->w_s->b_syn_folditems;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005259#endif
5260 }
5261 }
5262
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005263 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005264 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005265 success = TRUE; /* don't free the progs and patterns now */
5266 }
5267 }
5268
5269 /*
5270 * Free the allocated memory.
5271 */
5272 for (item = ITEM_START; item <= ITEM_END; ++item)
5273 for (ppp = pat_ptrs[item]; ppp != NULL; ppp = ppp_next)
5274 {
5275 if (!success)
5276 {
Bram Moolenaar473de612013-06-08 18:19:48 +02005277 vim_regfree(ppp->pp_synp->sp_prog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005278 vim_free(ppp->pp_synp->sp_pattern);
5279 }
5280 vim_free(ppp->pp_synp);
5281 ppp_next = ppp->pp_next;
5282 vim_free(ppp);
5283 }
5284
5285 if (!success)
5286 {
Bram Moolenaar6ac54292005-02-02 23:07:25 +00005287 vim_free(syn_opt_arg.cont_list);
5288 vim_free(syn_opt_arg.cont_in_list);
5289 vim_free(syn_opt_arg.next_list);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005290 if (not_enough)
5291 EMSG2(_("E399: Not enough arguments: syntax region %s"), arg);
5292 else if (illegal || rest == NULL)
5293 EMSG2(_(e_invarg2), arg);
5294 }
5295}
5296
5297/*
5298 * A simple syntax group ID comparison function suitable for use in qsort()
5299 */
5300 static int
5301#ifdef __BORLANDC__
5302_RTLENTRYF
5303#endif
5304syn_compare_stub(v1, v2)
5305 const void *v1;
5306 const void *v2;
5307{
5308 const short *s1 = v1;
5309 const short *s2 = v2;
5310
5311 return (*s1 > *s2 ? 1 : *s1 < *s2 ? -1 : 0);
5312}
5313
5314/*
5315 * Combines lists of syntax clusters.
5316 * *clstr1 and *clstr2 must both be allocated memory; they will be consumed.
5317 */
5318 static void
5319syn_combine_list(clstr1, clstr2, list_op)
5320 short **clstr1;
5321 short **clstr2;
5322 int list_op;
5323{
5324 int count1 = 0;
5325 int count2 = 0;
5326 short *g1;
5327 short *g2;
5328 short *clstr = NULL;
5329 int count;
5330 int round;
5331
5332 /*
5333 * Handle degenerate cases.
5334 */
5335 if (*clstr2 == NULL)
5336 return;
5337 if (*clstr1 == NULL || list_op == CLUSTER_REPLACE)
5338 {
5339 if (list_op == CLUSTER_REPLACE)
5340 vim_free(*clstr1);
5341 if (list_op == CLUSTER_REPLACE || list_op == CLUSTER_ADD)
5342 *clstr1 = *clstr2;
5343 else
5344 vim_free(*clstr2);
5345 return;
5346 }
5347
5348 for (g1 = *clstr1; *g1; g1++)
5349 ++count1;
5350 for (g2 = *clstr2; *g2; g2++)
5351 ++count2;
5352
5353 /*
5354 * For speed purposes, sort both lists.
5355 */
5356 qsort(*clstr1, (size_t)count1, sizeof(short), syn_compare_stub);
5357 qsort(*clstr2, (size_t)count2, sizeof(short), syn_compare_stub);
5358
5359 /*
5360 * We proceed in two passes; in round 1, we count the elements to place
5361 * in the new list, and in round 2, we allocate and populate the new
5362 * list. For speed, we use a mergesort-like method, adding the smaller
5363 * of the current elements in each list to the new list.
5364 */
5365 for (round = 1; round <= 2; round++)
5366 {
5367 g1 = *clstr1;
5368 g2 = *clstr2;
5369 count = 0;
5370
5371 /*
5372 * First, loop through the lists until one of them is empty.
5373 */
5374 while (*g1 && *g2)
5375 {
5376 /*
5377 * We always want to add from the first list.
5378 */
5379 if (*g1 < *g2)
5380 {
5381 if (round == 2)
5382 clstr[count] = *g1;
5383 count++;
5384 g1++;
5385 continue;
5386 }
5387 /*
5388 * We only want to add from the second list if we're adding the
5389 * lists.
5390 */
5391 if (list_op == CLUSTER_ADD)
5392 {
5393 if (round == 2)
5394 clstr[count] = *g2;
5395 count++;
5396 }
5397 if (*g1 == *g2)
5398 g1++;
5399 g2++;
5400 }
5401
5402 /*
5403 * Now add the leftovers from whichever list didn't get finished
5404 * first. As before, we only want to add from the second list if
5405 * we're adding the lists.
5406 */
5407 for (; *g1; g1++, count++)
5408 if (round == 2)
5409 clstr[count] = *g1;
5410 if (list_op == CLUSTER_ADD)
5411 for (; *g2; g2++, count++)
5412 if (round == 2)
5413 clstr[count] = *g2;
5414
5415 if (round == 1)
5416 {
5417 /*
5418 * If the group ended up empty, we don't need to allocate any
5419 * space for it.
5420 */
5421 if (count == 0)
5422 {
5423 clstr = NULL;
5424 break;
5425 }
5426 clstr = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
5427 if (clstr == NULL)
5428 break;
5429 clstr[count] = 0;
5430 }
5431 }
5432
5433 /*
5434 * Finally, put the new list in place.
5435 */
5436 vim_free(*clstr1);
5437 vim_free(*clstr2);
5438 *clstr1 = clstr;
5439}
5440
5441/*
5442 * Lookup a syntax cluster name and return it's ID.
5443 * If it is not found, 0 is returned.
5444 */
5445 static int
5446syn_scl_name2id(name)
5447 char_u *name;
5448{
5449 int i;
5450 char_u *name_u;
5451
5452 /* Avoid using stricmp() too much, it's slow on some systems */
5453 name_u = vim_strsave_up(name);
5454 if (name_u == NULL)
5455 return 0;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005456 for (i = curwin->w_s->b_syn_clusters.ga_len; --i >= 0; )
5457 if (SYN_CLSTR(curwin->w_s)[i].scl_name_u != NULL
5458 && STRCMP(name_u, SYN_CLSTR(curwin->w_s)[i].scl_name_u) == 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005459 break;
5460 vim_free(name_u);
5461 return (i < 0 ? 0 : i + SYNID_CLUSTER);
5462}
5463
5464/*
5465 * Like syn_scl_name2id(), but take a pointer + length argument.
5466 */
5467 static int
5468syn_scl_namen2id(linep, len)
5469 char_u *linep;
5470 int len;
5471{
5472 char_u *name;
5473 int id = 0;
5474
5475 name = vim_strnsave(linep, len);
5476 if (name != NULL)
5477 {
5478 id = syn_scl_name2id(name);
5479 vim_free(name);
5480 }
5481 return id;
5482}
5483
5484/*
5485 * Find syntax cluster name in the table and return it's ID.
5486 * The argument is a pointer to the name and the length of the name.
5487 * If it doesn't exist yet, a new entry is created.
5488 * Return 0 for failure.
5489 */
5490 static int
5491syn_check_cluster(pp, len)
5492 char_u *pp;
5493 int len;
5494{
5495 int id;
5496 char_u *name;
5497
5498 name = vim_strnsave(pp, len);
5499 if (name == NULL)
5500 return 0;
5501
5502 id = syn_scl_name2id(name);
5503 if (id == 0) /* doesn't exist yet */
5504 id = syn_add_cluster(name);
5505 else
5506 vim_free(name);
5507 return id;
5508}
5509
5510/*
5511 * Add new syntax cluster and return it's ID.
5512 * "name" must be an allocated string, it will be consumed.
5513 * Return 0 for failure.
5514 */
5515 static int
5516syn_add_cluster(name)
Bram Moolenaar217ad922005-03-20 22:37:15 +00005517 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005518{
Bram Moolenaar217ad922005-03-20 22:37:15 +00005519 int len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005520
5521 /*
5522 * First call for this growarray: init growing array.
5523 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005524 if (curwin->w_s->b_syn_clusters.ga_data == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005525 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005526 curwin->w_s->b_syn_clusters.ga_itemsize = sizeof(syn_cluster_T);
5527 curwin->w_s->b_syn_clusters.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005528 }
5529
Bram Moolenaar42431a72011-04-01 14:44:59 +02005530 len = curwin->w_s->b_syn_clusters.ga_len;
5531 if (len >= MAX_CLUSTER_ID)
5532 {
5533 EMSG((char_u *)_("E848: Too many syntax clusters"));
5534 vim_free(name);
5535 return 0;
5536 }
5537
Bram Moolenaar071d4272004-06-13 20:20:40 +00005538 /*
5539 * Make room for at least one other cluster entry.
5540 */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005541 if (ga_grow(&curwin->w_s->b_syn_clusters, 1) == FAIL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005542 {
5543 vim_free(name);
5544 return 0;
5545 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00005546
Bram Moolenaar860cae12010-06-05 23:22:07 +02005547 vim_memset(&(SYN_CLSTR(curwin->w_s)[len]), 0, sizeof(syn_cluster_T));
5548 SYN_CLSTR(curwin->w_s)[len].scl_name = name;
5549 SYN_CLSTR(curwin->w_s)[len].scl_name_u = vim_strsave_up(name);
5550 SYN_CLSTR(curwin->w_s)[len].scl_list = NULL;
5551 ++curwin->w_s->b_syn_clusters.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005552
Bram Moolenaar217ad922005-03-20 22:37:15 +00005553 if (STRICMP(name, "Spell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005554 curwin->w_s->b_spell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar6bb68362005-03-22 23:03:44 +00005555 if (STRICMP(name, "NoSpell") == 0)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005556 curwin->w_s->b_nospell_cluster_id = len + SYNID_CLUSTER;
Bram Moolenaar217ad922005-03-20 22:37:15 +00005557
Bram Moolenaar071d4272004-06-13 20:20:40 +00005558 return len + SYNID_CLUSTER;
5559}
5560
5561/*
5562 * Handle ":syntax cluster {cluster-name} [contains={groupname},..]
5563 * [add={groupname},..] [remove={groupname},..]".
5564 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005565 static void
5566syn_cmd_cluster(eap, syncing)
5567 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005568 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005569{
5570 char_u *arg = eap->arg;
5571 char_u *group_name_end;
5572 char_u *rest;
5573 int scl_id;
5574 short *clstr_list;
5575 int got_clstr = FALSE;
5576 int opt_len;
5577 int list_op;
5578
5579 eap->nextcmd = find_nextcmd(arg);
5580 if (eap->skip)
5581 return;
5582
5583 rest = get_group_name(arg, &group_name_end);
5584
5585 if (rest != NULL)
5586 {
Bram Moolenaar42431a72011-04-01 14:44:59 +02005587 scl_id = syn_check_cluster(arg, (int)(group_name_end - arg));
5588 if (scl_id == 0)
5589 return;
5590 scl_id -= SYNID_CLUSTER;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005591
5592 for (;;)
5593 {
5594 if (STRNICMP(rest, "add", 3) == 0
5595 && (vim_iswhite(rest[3]) || rest[3] == '='))
5596 {
5597 opt_len = 3;
5598 list_op = CLUSTER_ADD;
5599 }
5600 else if (STRNICMP(rest, "remove", 6) == 0
5601 && (vim_iswhite(rest[6]) || rest[6] == '='))
5602 {
5603 opt_len = 6;
5604 list_op = CLUSTER_SUBTRACT;
5605 }
5606 else if (STRNICMP(rest, "contains", 8) == 0
5607 && (vim_iswhite(rest[8]) || rest[8] == '='))
5608 {
5609 opt_len = 8;
5610 list_op = CLUSTER_REPLACE;
5611 }
5612 else
5613 break;
5614
5615 clstr_list = NULL;
5616 if (get_id_list(&rest, opt_len, &clstr_list) == FAIL)
5617 {
5618 EMSG2(_(e_invarg2), rest);
5619 break;
5620 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005621 syn_combine_list(&SYN_CLSTR(curwin->w_s)[scl_id].scl_list,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005622 &clstr_list, list_op);
5623 got_clstr = TRUE;
5624 }
5625
5626 if (got_clstr)
5627 {
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005628 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar42431a72011-04-01 14:44:59 +02005629 syn_stack_free_all(curwin->w_s); /* Need to recompute all. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005630 }
5631 }
5632
5633 if (!got_clstr)
5634 EMSG(_("E400: No cluster specified"));
5635 if (rest == NULL || !ends_excmd(*rest))
5636 EMSG2(_(e_invarg2), arg);
5637}
5638
5639/*
5640 * On first call for current buffer: Init growing array.
5641 */
5642 static void
5643init_syn_patterns()
5644{
Bram Moolenaar860cae12010-06-05 23:22:07 +02005645 curwin->w_s->b_syn_patterns.ga_itemsize = sizeof(synpat_T);
5646 curwin->w_s->b_syn_patterns.ga_growsize = 10;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005647}
5648
5649/*
5650 * Get one pattern for a ":syntax match" or ":syntax region" command.
5651 * Stores the pattern and program in a synpat_T.
5652 * Returns a pointer to the next argument, or NULL in case of an error.
5653 */
5654 static char_u *
5655get_syn_pattern(arg, ci)
5656 char_u *arg;
5657 synpat_T *ci;
5658{
5659 char_u *end;
5660 int *p;
5661 int idx;
5662 char_u *cpo_save;
5663
5664 /* need at least three chars */
Bram Moolenaar38219782015-08-11 15:27:13 +02005665 if (arg == NULL || arg[0] == NUL || arg[1] == NUL || arg[2] == NUL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005666 return NULL;
5667
5668 end = skip_regexp(arg + 1, *arg, TRUE, NULL);
5669 if (*end != *arg) /* end delimiter not found */
5670 {
5671 EMSG2(_("E401: Pattern delimiter not found: %s"), arg);
5672 return NULL;
5673 }
5674 /* store the pattern and compiled regexp program */
5675 if ((ci->sp_pattern = vim_strnsave(arg + 1, (int)(end - arg - 1))) == NULL)
5676 return NULL;
5677
5678 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5679 cpo_save = p_cpo;
5680 p_cpo = (char_u *)"";
5681 ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
5682 p_cpo = cpo_save;
5683
5684 if (ci->sp_prog == NULL)
5685 return NULL;
Bram Moolenaar860cae12010-06-05 23:22:07 +02005686 ci->sp_ic = curwin->w_s->b_syn_ic;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005687#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005688 syn_clear_time(&ci->sp_time);
5689#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005690
5691 /*
5692 * Check for a match, highlight or region offset.
5693 */
5694 ++end;
5695 do
5696 {
5697 for (idx = SPO_COUNT; --idx >= 0; )
5698 if (STRNCMP(end, spo_name_tab[idx], 3) == 0)
5699 break;
5700 if (idx >= 0)
5701 {
5702 p = &(ci->sp_offsets[idx]);
5703 if (idx != SPO_LC_OFF)
5704 switch (end[3])
5705 {
5706 case 's': break;
5707 case 'b': break;
5708 case 'e': idx += SPO_COUNT; break;
5709 default: idx = -1; break;
5710 }
5711 if (idx >= 0)
5712 {
5713 ci->sp_off_flags |= (1 << idx);
5714 if (idx == SPO_LC_OFF) /* lc=99 */
5715 {
5716 end += 3;
5717 *p = getdigits(&end);
5718
5719 /* "lc=" offset automatically sets "ms=" offset */
5720 if (!(ci->sp_off_flags & (1 << SPO_MS_OFF)))
5721 {
5722 ci->sp_off_flags |= (1 << SPO_MS_OFF);
5723 ci->sp_offsets[SPO_MS_OFF] = *p;
5724 }
5725 }
5726 else /* yy=x+99 */
5727 {
5728 end += 4;
5729 if (*end == '+')
5730 {
5731 ++end;
5732 *p = getdigits(&end); /* positive offset */
5733 }
5734 else if (*end == '-')
5735 {
5736 ++end;
5737 *p = -getdigits(&end); /* negative offset */
5738 }
5739 }
5740 if (*end != ',')
5741 break;
5742 ++end;
5743 }
5744 }
5745 } while (idx >= 0);
5746
5747 if (!ends_excmd(*end) && !vim_iswhite(*end))
5748 {
5749 EMSG2(_("E402: Garbage after pattern: %s"), arg);
5750 return NULL;
5751 }
5752 return skipwhite(end);
5753}
5754
5755/*
5756 * Handle ":syntax sync .." command.
5757 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005758 static void
5759syn_cmd_sync(eap, syncing)
5760 exarg_T *eap;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00005761 int syncing UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005762{
5763 char_u *arg_start = eap->arg;
5764 char_u *arg_end;
5765 char_u *key = NULL;
5766 char_u *next_arg;
5767 int illegal = FALSE;
5768 int finished = FALSE;
5769 long n;
5770 char_u *cpo_save;
5771
5772 if (ends_excmd(*arg_start))
5773 {
5774 syn_cmd_list(eap, TRUE);
5775 return;
5776 }
5777
5778 while (!ends_excmd(*arg_start))
5779 {
5780 arg_end = skiptowhite(arg_start);
5781 next_arg = skipwhite(arg_end);
5782 vim_free(key);
5783 key = vim_strnsave_up(arg_start, (int)(arg_end - arg_start));
5784 if (STRCMP(key, "CCOMMENT") == 0)
5785 {
5786 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005787 curwin->w_s->b_syn_sync_flags |= SF_CCOMMENT;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005788 if (!ends_excmd(*next_arg))
5789 {
5790 arg_end = skiptowhite(next_arg);
5791 if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005792 curwin->w_s->b_syn_sync_id = syn_check_group(next_arg,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005793 (int)(arg_end - next_arg));
5794 next_arg = skipwhite(arg_end);
5795 }
5796 else if (!eap->skip)
Bram Moolenaar860cae12010-06-05 23:22:07 +02005797 curwin->w_s->b_syn_sync_id = syn_name2id((char_u *)"Comment");
Bram Moolenaar071d4272004-06-13 20:20:40 +00005798 }
5799 else if ( STRNCMP(key, "LINES", 5) == 0
5800 || STRNCMP(key, "MINLINES", 8) == 0
5801 || STRNCMP(key, "MAXLINES", 8) == 0
5802 || STRNCMP(key, "LINEBREAKS", 10) == 0)
5803 {
5804 if (key[4] == 'S')
5805 arg_end = key + 6;
5806 else if (key[0] == 'L')
5807 arg_end = key + 11;
5808 else
5809 arg_end = key + 9;
5810 if (arg_end[-1] != '=' || !VIM_ISDIGIT(*arg_end))
5811 {
5812 illegal = TRUE;
5813 break;
5814 }
5815 n = getdigits(&arg_end);
5816 if (!eap->skip)
5817 {
5818 if (key[4] == 'B')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005819 curwin->w_s->b_syn_sync_linebreaks = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005820 else if (key[1] == 'A')
Bram Moolenaar860cae12010-06-05 23:22:07 +02005821 curwin->w_s->b_syn_sync_maxlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005822 else
Bram Moolenaar860cae12010-06-05 23:22:07 +02005823 curwin->w_s->b_syn_sync_minlines = n;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005824 }
5825 }
5826 else if (STRCMP(key, "FROMSTART") == 0)
5827 {
5828 if (!eap->skip)
5829 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005830 curwin->w_s->b_syn_sync_minlines = MAXLNUM;
5831 curwin->w_s->b_syn_sync_maxlines = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005832 }
5833 }
5834 else if (STRCMP(key, "LINECONT") == 0)
5835 {
Bram Moolenaar2795e212016-01-05 22:04:49 +01005836 if (*next_arg == NUL) /* missing pattern */
5837 {
5838 illegal = TRUE;
5839 break;
5840 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005841 if (curwin->w_s->b_syn_linecont_pat != NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005842 {
5843 EMSG(_("E403: syntax sync: line continuations pattern specified twice"));
5844 finished = TRUE;
5845 break;
5846 }
5847 arg_end = skip_regexp(next_arg + 1, *next_arg, TRUE, NULL);
5848 if (*arg_end != *next_arg) /* end delimiter not found */
5849 {
5850 illegal = TRUE;
5851 break;
5852 }
5853
5854 if (!eap->skip)
5855 {
5856 /* store the pattern and compiled regexp program */
Bram Moolenaar860cae12010-06-05 23:22:07 +02005857 if ((curwin->w_s->b_syn_linecont_pat = vim_strnsave(next_arg + 1,
Bram Moolenaar071d4272004-06-13 20:20:40 +00005858 (int)(arg_end - next_arg - 1))) == NULL)
5859 {
5860 finished = TRUE;
5861 break;
5862 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02005863 curwin->w_s->b_syn_linecont_ic = curwin->w_s->b_syn_ic;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005864
5865 /* Make 'cpoptions' empty, to avoid the 'l' flag */
5866 cpo_save = p_cpo;
5867 p_cpo = (char_u *)"";
Bram Moolenaar860cae12010-06-05 23:22:07 +02005868 curwin->w_s->b_syn_linecont_prog =
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005869 vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005870 p_cpo = cpo_save;
Bram Moolenaarf7512552013-06-06 14:55:19 +02005871#ifdef FEAT_PROFILE
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02005872 syn_clear_time(&curwin->w_s->b_syn_linecont_time);
5873#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00005874
Bram Moolenaar860cae12010-06-05 23:22:07 +02005875 if (curwin->w_s->b_syn_linecont_prog == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00005876 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02005877 vim_free(curwin->w_s->b_syn_linecont_pat);
5878 curwin->w_s->b_syn_linecont_pat = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00005879 finished = TRUE;
5880 break;
5881 }
5882 }
5883 next_arg = skipwhite(arg_end + 1);
5884 }
5885 else
5886 {
5887 eap->arg = next_arg;
5888 if (STRCMP(key, "MATCH") == 0)
5889 syn_cmd_match(eap, TRUE);
5890 else if (STRCMP(key, "REGION") == 0)
5891 syn_cmd_region(eap, TRUE);
5892 else if (STRCMP(key, "CLEAR") == 0)
5893 syn_cmd_clear(eap, TRUE);
5894 else
5895 illegal = TRUE;
5896 finished = TRUE;
5897 break;
5898 }
5899 arg_start = next_arg;
5900 }
5901 vim_free(key);
5902 if (illegal)
5903 EMSG2(_("E404: Illegal arguments: %s"), arg_start);
5904 else if (!finished)
5905 {
5906 eap->nextcmd = check_nextcmd(arg_start);
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00005907 redraw_curbuf_later(SOME_VALID);
Bram Moolenaar860cae12010-06-05 23:22:07 +02005908 syn_stack_free_all(curwin->w_s); /* Need to recompute all syntax. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00005909 }
5910}
5911
5912/*
5913 * Convert a line of highlight group names into a list of group ID numbers.
5914 * "arg" should point to the "contains" or "nextgroup" keyword.
5915 * "arg" is advanced to after the last group name.
5916 * Careful: the argument is modified (NULs added).
5917 * returns FAIL for some error, OK for success.
5918 */
5919 static int
5920get_id_list(arg, keylen, list)
5921 char_u **arg;
5922 int keylen; /* length of keyword */
5923 short **list; /* where to store the resulting list, if not
5924 NULL, the list is silently skipped! */
5925{
5926 char_u *p = NULL;
5927 char_u *end;
5928 int round;
5929 int count;
5930 int total_count = 0;
5931 short *retval = NULL;
5932 char_u *name;
5933 regmatch_T regmatch;
5934 int id;
5935 int i;
5936 int failed = FALSE;
5937
5938 /*
5939 * We parse the list twice:
5940 * round == 1: count the number of items, allocate the array.
5941 * round == 2: fill the array with the items.
5942 * In round 1 new groups may be added, causing the number of items to
5943 * grow when a regexp is used. In that case round 1 is done once again.
5944 */
5945 for (round = 1; round <= 2; ++round)
5946 {
5947 /*
5948 * skip "contains"
5949 */
5950 p = skipwhite(*arg + keylen);
5951 if (*p != '=')
5952 {
5953 EMSG2(_("E405: Missing equal sign: %s"), *arg);
5954 break;
5955 }
5956 p = skipwhite(p + 1);
5957 if (ends_excmd(*p))
5958 {
5959 EMSG2(_("E406: Empty argument: %s"), *arg);
5960 break;
5961 }
5962
5963 /*
5964 * parse the arguments after "contains"
5965 */
5966 count = 0;
5967 while (!ends_excmd(*p))
5968 {
5969 for (end = p; *end && !vim_iswhite(*end) && *end != ','; ++end)
5970 ;
5971 name = alloc((int)(end - p + 3)); /* leave room for "^$" */
5972 if (name == NULL)
5973 {
5974 failed = TRUE;
5975 break;
5976 }
Bram Moolenaarce0842a2005-07-18 21:58:11 +00005977 vim_strncpy(name + 1, p, end - p);
Bram Moolenaar071d4272004-06-13 20:20:40 +00005978 if ( STRCMP(name + 1, "ALLBUT") == 0
5979 || STRCMP(name + 1, "ALL") == 0
5980 || STRCMP(name + 1, "TOP") == 0
5981 || STRCMP(name + 1, "CONTAINED") == 0)
5982 {
5983 if (TOUPPER_ASC(**arg) != 'C')
5984 {
5985 EMSG2(_("E407: %s not allowed here"), name + 1);
5986 failed = TRUE;
5987 vim_free(name);
5988 break;
5989 }
5990 if (count != 0)
5991 {
5992 EMSG2(_("E408: %s must be first in contains list"), name + 1);
5993 failed = TRUE;
5994 vim_free(name);
5995 break;
5996 }
5997 if (name[1] == 'A')
5998 id = SYNID_ALLBUT;
5999 else if (name[1] == 'T')
6000 id = SYNID_TOP;
6001 else
6002 id = SYNID_CONTAINED;
6003 id += current_syn_inc_tag;
6004 }
6005 else if (name[1] == '@')
6006 {
6007 id = syn_check_cluster(name + 2, (int)(end - p - 1));
6008 }
6009 else
6010 {
6011 /*
6012 * Handle full group name.
6013 */
6014 if (vim_strpbrk(name + 1, (char_u *)"\\.*^$~[") == NULL)
6015 id = syn_check_group(name + 1, (int)(end - p));
6016 else
6017 {
6018 /*
6019 * Handle match of regexp with group names.
6020 */
6021 *name = '^';
6022 STRCAT(name, "$");
6023 regmatch.regprog = vim_regcomp(name, RE_MAGIC);
6024 if (regmatch.regprog == NULL)
6025 {
6026 failed = TRUE;
6027 vim_free(name);
6028 break;
6029 }
6030
6031 regmatch.rm_ic = TRUE;
6032 id = 0;
6033 for (i = highlight_ga.ga_len; --i >= 0; )
6034 {
6035 if (vim_regexec(&regmatch, HL_TABLE()[i].sg_name,
6036 (colnr_T)0))
6037 {
6038 if (round == 2)
6039 {
6040 /* Got more items than expected; can happen
6041 * when adding items that match:
6042 * "contains=a.*b,axb".
6043 * Go back to first round */
6044 if (count >= total_count)
6045 {
6046 vim_free(retval);
6047 round = 1;
6048 }
6049 else
6050 retval[count] = i + 1;
6051 }
6052 ++count;
6053 id = -1; /* remember that we found one */
6054 }
6055 }
Bram Moolenaar473de612013-06-08 18:19:48 +02006056 vim_regfree(regmatch.regprog);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006057 }
6058 }
6059 vim_free(name);
6060 if (id == 0)
6061 {
6062 EMSG2(_("E409: Unknown group name: %s"), p);
6063 failed = TRUE;
6064 break;
6065 }
6066 if (id > 0)
6067 {
6068 if (round == 2)
6069 {
6070 /* Got more items than expected, go back to first round */
6071 if (count >= total_count)
6072 {
6073 vim_free(retval);
6074 round = 1;
6075 }
6076 else
6077 retval[count] = id;
6078 }
6079 ++count;
6080 }
6081 p = skipwhite(end);
6082 if (*p != ',')
6083 break;
6084 p = skipwhite(p + 1); /* skip comma in between arguments */
6085 }
6086 if (failed)
6087 break;
6088 if (round == 1)
6089 {
6090 retval = (short *)alloc((unsigned)((count + 1) * sizeof(short)));
6091 if (retval == NULL)
6092 break;
6093 retval[count] = 0; /* zero means end of the list */
6094 total_count = count;
6095 }
6096 }
6097
6098 *arg = p;
6099 if (failed || retval == NULL)
6100 {
6101 vim_free(retval);
6102 return FAIL;
6103 }
6104
6105 if (*list == NULL)
6106 *list = retval;
6107 else
6108 vim_free(retval); /* list already found, don't overwrite it */
6109
6110 return OK;
6111}
6112
6113/*
6114 * Make a copy of an ID list.
6115 */
6116 static short *
6117copy_id_list(list)
6118 short *list;
6119{
6120 int len;
6121 int count;
6122 short *retval;
6123
6124 if (list == NULL)
6125 return NULL;
6126
6127 for (count = 0; list[count]; ++count)
6128 ;
6129 len = (count + 1) * sizeof(short);
6130 retval = (short *)alloc((unsigned)len);
6131 if (retval != NULL)
6132 mch_memmove(retval, list, (size_t)len);
6133
6134 return retval;
6135}
6136
6137/*
6138 * Check if syntax group "ssp" is in the ID list "list" of "cur_si".
6139 * "cur_si" can be NULL if not checking the "containedin" list.
6140 * Used to check if a syntax item is in the "contains" or "nextgroup" list of
6141 * the current item.
6142 * This function is called very often, keep it fast!!
6143 */
6144 static int
6145in_id_list(cur_si, list, ssp, contained)
6146 stateitem_T *cur_si; /* current item or NULL */
6147 short *list; /* id list */
6148 struct sp_syn *ssp; /* group id and ":syn include" tag of group */
6149 int contained; /* group id is contained */
6150{
6151 int retval;
6152 short *scl_list;
6153 short item;
6154 short id = ssp->id;
6155 static int depth = 0;
6156 int r;
6157
Bram Moolenaar84a05ac2013-05-06 04:24:17 +02006158 /* If ssp has a "containedin" list and "cur_si" is in it, return TRUE. */
Bram Moolenaar293ee4d2004-12-09 21:34:53 +00006159 if (cur_si != NULL && ssp->cont_in_list != NULL
6160 && !(cur_si->si_flags & HL_MATCH))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006161 {
6162 /* Ignore transparent items without a contains argument. Double check
6163 * that we don't go back past the first one. */
6164 while ((cur_si->si_flags & HL_TRANS_CONT)
6165 && cur_si > (stateitem_T *)(current_state.ga_data))
6166 --cur_si;
6167 /* cur_si->si_idx is -1 for keywords, these never contain anything. */
6168 if (cur_si->si_idx >= 0 && in_id_list(NULL, ssp->cont_in_list,
Bram Moolenaar860cae12010-06-05 23:22:07 +02006169 &(SYN_ITEMS(syn_block)[cur_si->si_idx].sp_syn),
6170 SYN_ITEMS(syn_block)[cur_si->si_idx].sp_flags & HL_CONTAINED))
Bram Moolenaar071d4272004-06-13 20:20:40 +00006171 return TRUE;
6172 }
6173
6174 if (list == NULL)
6175 return FALSE;
6176
6177 /*
6178 * If list is ID_LIST_ALL, we are in a transparent item that isn't
6179 * inside anything. Only allow not-contained groups.
6180 */
6181 if (list == ID_LIST_ALL)
6182 return !contained;
6183
6184 /*
6185 * If the first item is "ALLBUT", return TRUE if "id" is NOT in the
6186 * contains list. We also require that "id" is at the same ":syn include"
6187 * level as the list.
6188 */
6189 item = *list;
6190 if (item >= SYNID_ALLBUT && item < SYNID_CLUSTER)
6191 {
6192 if (item < SYNID_TOP)
6193 {
6194 /* ALL or ALLBUT: accept all groups in the same file */
6195 if (item - SYNID_ALLBUT != ssp->inc_tag)
6196 return FALSE;
6197 }
6198 else if (item < SYNID_CONTAINED)
6199 {
6200 /* TOP: accept all not-contained groups in the same file */
6201 if (item - SYNID_TOP != ssp->inc_tag || contained)
6202 return FALSE;
6203 }
6204 else
6205 {
6206 /* CONTAINED: accept all contained groups in the same file */
6207 if (item - SYNID_CONTAINED != ssp->inc_tag || !contained)
6208 return FALSE;
6209 }
6210 item = *++list;
6211 retval = FALSE;
6212 }
6213 else
6214 retval = TRUE;
6215
6216 /*
6217 * Return "retval" if id is in the contains list.
6218 */
6219 while (item != 0)
6220 {
6221 if (item == id)
6222 return retval;
6223 if (item >= SYNID_CLUSTER)
6224 {
Bram Moolenaar860cae12010-06-05 23:22:07 +02006225 scl_list = SYN_CLSTR(syn_block)[item - SYNID_CLUSTER].scl_list;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006226 /* restrict recursiveness to 30 to avoid an endless loop for a
6227 * cluster that includes itself (indirectly) */
6228 if (scl_list != NULL && depth < 30)
6229 {
6230 ++depth;
6231 r = in_id_list(NULL, scl_list, ssp, contained);
6232 --depth;
6233 if (r)
6234 return retval;
6235 }
6236 }
6237 item = *++list;
6238 }
6239 return !retval;
6240}
6241
6242struct subcommand
6243{
6244 char *name; /* subcommand name */
6245 void (*func)__ARGS((exarg_T *, int)); /* function to call */
6246};
6247
6248static struct subcommand subcommands[] =
6249{
6250 {"case", syn_cmd_case},
6251 {"clear", syn_cmd_clear},
6252 {"cluster", syn_cmd_cluster},
Bram Moolenaar860cae12010-06-05 23:22:07 +02006253 {"conceal", syn_cmd_conceal},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006254 {"enable", syn_cmd_enable},
6255 {"include", syn_cmd_include},
6256 {"keyword", syn_cmd_keyword},
6257 {"list", syn_cmd_list},
6258 {"manual", syn_cmd_manual},
6259 {"match", syn_cmd_match},
6260 {"on", syn_cmd_on},
6261 {"off", syn_cmd_off},
6262 {"region", syn_cmd_region},
6263 {"reset", syn_cmd_reset},
Bram Moolenaarce0842a2005-07-18 21:58:11 +00006264 {"spell", syn_cmd_spell},
Bram Moolenaar071d4272004-06-13 20:20:40 +00006265 {"sync", syn_cmd_sync},
6266 {"", syn_cmd_list},
6267 {NULL, NULL}
6268};
6269
6270/*
6271 * ":syntax".
6272 * This searches the subcommands[] table for the subcommand name, and calls a
6273 * syntax_subcommand() function to do the rest.
6274 */
6275 void
6276ex_syntax(eap)
6277 exarg_T *eap;
6278{
6279 char_u *arg = eap->arg;
6280 char_u *subcmd_end;
6281 char_u *subcmd_name;
6282 int i;
6283
6284 syn_cmdlinep = eap->cmdlinep;
6285
6286 /* isolate subcommand name */
6287 for (subcmd_end = arg; ASCII_ISALPHA(*subcmd_end); ++subcmd_end)
6288 ;
6289 subcmd_name = vim_strnsave(arg, (int)(subcmd_end - arg));
6290 if (subcmd_name != NULL)
6291 {
6292 if (eap->skip) /* skip error messages for all subcommands */
6293 ++emsg_skip;
6294 for (i = 0; ; ++i)
6295 {
6296 if (subcommands[i].name == NULL)
6297 {
6298 EMSG2(_("E410: Invalid :syntax subcommand: %s"), subcmd_name);
6299 break;
6300 }
6301 if (STRCMP(subcmd_name, (char_u *)subcommands[i].name) == 0)
6302 {
6303 eap->arg = skipwhite(subcmd_end);
6304 (subcommands[i].func)(eap, FALSE);
6305 break;
6306 }
6307 }
6308 vim_free(subcmd_name);
6309 if (eap->skip)
6310 --emsg_skip;
6311 }
6312}
6313
Bram Moolenaar860cae12010-06-05 23:22:07 +02006314 void
6315ex_ownsyntax(eap)
6316 exarg_T *eap;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006317{
Bram Moolenaar1950c352010-06-06 15:21:10 +02006318 char_u *old_value;
6319 char_u *new_value;
6320
Bram Moolenaar860cae12010-06-05 23:22:07 +02006321 if (curwin->w_s == &curwin->w_buffer->b_s)
6322 {
6323 curwin->w_s = (synblock_T *)alloc(sizeof(synblock_T));
6324 memset(curwin->w_s, 0, sizeof(synblock_T));
Bram Moolenaar670acbc2015-08-25 11:58:36 +02006325 hash_init(&curwin->w_s->b_keywtab);
6326 hash_init(&curwin->w_s->b_keywtab_ic);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006327#ifdef FEAT_SPELL
Bram Moolenaar2683c8e2014-11-19 19:33:16 +01006328 /* TODO: keep the spell checking as it was. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006329 curwin->w_p_spell = FALSE; /* No spell checking */
6330 clear_string_option(&curwin->w_s->b_p_spc);
6331 clear_string_option(&curwin->w_s->b_p_spf);
Bram Moolenaar860cae12010-06-05 23:22:07 +02006332 clear_string_option(&curwin->w_s->b_p_spl);
6333#endif
6334 }
Bram Moolenaar1950c352010-06-06 15:21:10 +02006335
6336 /* save value of b:current_syntax */
6337 old_value = get_var_value((char_u *)"b:current_syntax");
6338 if (old_value != NULL)
6339 old_value = vim_strsave(old_value);
6340
6341 /* Apply the "syntax" autocommand event, this finds and loads the syntax
6342 * file. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006343 apply_autocmds(EVENT_SYNTAX, eap->arg, curbuf->b_fname, TRUE, curbuf);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006344
6345 /* move value of b:current_syntax to w:current_syntax */
6346 new_value = get_var_value((char_u *)"b:current_syntax");
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006347 if (new_value != NULL)
6348 set_internal_string_var((char_u *)"w:current_syntax", new_value);
Bram Moolenaar1950c352010-06-06 15:21:10 +02006349
6350 /* restore value of b:current_syntax */
Bram Moolenaare0c6a652010-06-06 23:10:19 +02006351 if (old_value == NULL)
6352 do_unlet((char_u *)"b:current_syntax", TRUE);
6353 else
Bram Moolenaar1950c352010-06-06 15:21:10 +02006354 {
6355 set_internal_string_var((char_u *)"b:current_syntax", old_value);
6356 vim_free(old_value);
6357 }
Bram Moolenaar860cae12010-06-05 23:22:07 +02006358}
6359
6360 int
6361syntax_present(win)
6362 win_T *win;
6363{
6364 return (win->w_s->b_syn_patterns.ga_len != 0
6365 || win->w_s->b_syn_clusters.ga_len != 0
6366 || win->w_s->b_keywtab.ht_used > 0
6367 || win->w_s->b_keywtab_ic.ht_used > 0);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006368}
6369
6370#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6371
6372static enum
6373{
6374 EXP_SUBCMD, /* expand ":syn" sub-commands */
6375 EXP_CASE /* expand ":syn case" arguments */
6376} expand_what;
6377
Bram Moolenaar4f688582007-07-24 12:34:30 +00006378/*
6379 * Reset include_link, include_default, include_none to 0.
6380 * Called when we are done expanding.
6381 */
6382 void
6383reset_expand_highlight()
6384{
6385 include_link = include_default = include_none = 0;
6386}
6387
6388/*
6389 * Handle command line completion for :match and :echohl command: Add "None"
6390 * as highlight group.
6391 */
6392 void
6393set_context_in_echohl_cmd(xp, arg)
6394 expand_T *xp;
6395 char_u *arg;
6396{
6397 xp->xp_context = EXPAND_HIGHLIGHT;
6398 xp->xp_pattern = arg;
6399 include_none = 1;
6400}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006401
6402/*
6403 * Handle command line completion for :syntax command.
6404 */
6405 void
6406set_context_in_syntax_cmd(xp, arg)
6407 expand_T *xp;
6408 char_u *arg;
6409{
6410 char_u *p;
6411
6412 /* Default: expand subcommands */
6413 xp->xp_context = EXPAND_SYNTAX;
6414 expand_what = EXP_SUBCMD;
6415 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00006416 include_link = 0;
6417 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006418
6419 /* (part of) subcommand already typed */
6420 if (*arg != NUL)
6421 {
6422 p = skiptowhite(arg);
6423 if (*p != NUL) /* past first word */
6424 {
6425 xp->xp_pattern = skipwhite(p);
6426 if (*skiptowhite(xp->xp_pattern) != NUL)
6427 xp->xp_context = EXPAND_NOTHING;
6428 else if (STRNICMP(arg, "case", p - arg) == 0)
6429 expand_what = EXP_CASE;
6430 else if ( STRNICMP(arg, "keyword", p - arg) == 0
6431 || STRNICMP(arg, "region", p - arg) == 0
6432 || STRNICMP(arg, "match", p - arg) == 0
6433 || STRNICMP(arg, "list", p - arg) == 0)
6434 xp->xp_context = EXPAND_HIGHLIGHT;
6435 else
6436 xp->xp_context = EXPAND_NOTHING;
6437 }
6438 }
6439}
6440
6441static char *(case_args[]) = {"match", "ignore", NULL};
6442
6443/*
6444 * Function given to ExpandGeneric() to obtain the list syntax names for
6445 * expansion.
6446 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006447 char_u *
6448get_syntax_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00006449 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006450 int idx;
6451{
6452 if (expand_what == EXP_SUBCMD)
6453 return (char_u *)subcommands[idx].name;
6454 return (char_u *)case_args[idx];
6455}
6456
6457#endif /* FEAT_CMDL_COMPL */
6458
Bram Moolenaar071d4272004-06-13 20:20:40 +00006459/*
6460 * Function called for expression evaluation: get syntax ID at file position.
6461 */
6462 int
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006463syn_get_id(wp, lnum, col, trans, spellp, keep_state)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006464 win_T *wp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00006465 long lnum;
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006466 colnr_T col;
Bram Moolenaarf5b63862009-12-16 17:13:44 +00006467 int trans; /* remove transparency */
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006468 int *spellp; /* return: can do spell checking */
6469 int keep_state; /* keep state of char at "col" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006470{
6471 /* When the position is not after the current position and in the same
6472 * line of the same buffer, need to restart parsing. */
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006473 if (wp->w_buffer != syn_buf
Bram Moolenaar071d4272004-06-13 20:20:40 +00006474 || lnum != current_lnum
Bram Moolenaar9d0ec2e2005-04-20 19:45:58 +00006475 || col < current_col)
Bram Moolenaar81f1ecb2005-08-25 21:27:31 +00006476 syntax_start(wp, lnum);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006477
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006478 (void)get_syntax_attr(col, spellp, keep_state);
Bram Moolenaar071d4272004-06-13 20:20:40 +00006479
6480 return (trans ? current_trans_id : current_id);
6481}
Bram Moolenaar071d4272004-06-13 20:20:40 +00006482
Bram Moolenaar860cae12010-06-05 23:22:07 +02006483#if defined(FEAT_CONCEAL) || defined(PROTO)
6484/*
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006485 * Get extra information about the syntax item. Must be called right after
6486 * get_syntax_attr().
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006487 * Stores the current item sequence nr in "*seqnrp".
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006488 * Returns the current flags.
6489 */
6490 int
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006491get_syntax_info(seqnrp)
6492 int *seqnrp;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006493{
Bram Moolenaarffbbcb52010-07-24 17:29:03 +02006494 *seqnrp = current_seqnr;
Bram Moolenaar27c735b2010-07-22 22:16:29 +02006495 return current_flags;
6496}
6497
6498/*
Bram Moolenaar860cae12010-06-05 23:22:07 +02006499 * Return conceal substitution character
6500 */
6501 int
6502syn_get_sub_char()
6503{
6504 return current_sub_char;
6505}
6506#endif
6507
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006508#if defined(FEAT_EVAL) || defined(PROTO)
6509/*
6510 * Return the syntax ID at position "i" in the current stack.
6511 * The caller must have called syn_get_id() before to fill the stack.
6512 * Returns -1 when "i" is out of range.
6513 */
6514 int
6515syn_get_stack_item(i)
6516 int i;
6517{
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006518 if (i >= current_state.ga_len)
6519 {
6520 /* Need to invalidate the state, because we didn't properly finish it
6521 * for the last character, "keep_state" was TRUE. */
6522 invalidate_current_state();
6523 current_col = MAXCOL;
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006524 return -1;
Bram Moolenaar56cefaf2008-01-12 15:47:10 +00006525 }
Bram Moolenaar9d188ab2008-01-10 21:24:39 +00006526 return CUR_STATE(i).si_id;
6527}
6528#endif
6529
Bram Moolenaar071d4272004-06-13 20:20:40 +00006530#if defined(FEAT_FOLDING) || defined(PROTO)
6531/*
6532 * Function called to get folding level for line "lnum" in window "wp".
6533 */
6534 int
6535syn_get_foldlevel(wp, lnum)
6536 win_T *wp;
6537 long lnum;
6538{
6539 int level = 0;
6540 int i;
6541
6542 /* Return quickly when there are no fold items at all. */
Bram Moolenaar860cae12010-06-05 23:22:07 +02006543 if (wp->w_s->b_syn_folditems != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00006544 {
6545 syntax_start(wp, lnum);
6546
6547 for (i = 0; i < current_state.ga_len; ++i)
6548 if (CUR_STATE(i).si_flags & HL_FOLD)
6549 ++level;
6550 }
6551 if (level > wp->w_p_fdn)
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006552 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00006553 level = wp->w_p_fdn;
Bram Moolenaar74c596b2006-11-01 11:44:31 +00006554 if (level < 0)
6555 level = 0;
6556 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00006557 return level;
6558}
6559#endif
6560
Bram Moolenaar01615492015-02-03 13:00:38 +01006561#if defined(FEAT_PROFILE) || defined(PROTO)
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006562/*
6563 * ":syntime".
6564 */
6565 void
6566ex_syntime(eap)
6567 exarg_T *eap;
6568{
6569 if (STRCMP(eap->arg, "on") == 0)
6570 syn_time_on = TRUE;
6571 else if (STRCMP(eap->arg, "off") == 0)
6572 syn_time_on = FALSE;
6573 else if (STRCMP(eap->arg, "clear") == 0)
6574 syntime_clear();
6575 else if (STRCMP(eap->arg, "report") == 0)
6576 syntime_report();
6577 else
6578 EMSG2(_(e_invarg2), eap->arg);
6579}
6580
6581 static void
6582syn_clear_time(st)
6583 syn_time_T *st;
6584{
6585 profile_zero(&st->total);
6586 profile_zero(&st->slowest);
6587 st->count = 0;
6588 st->match = 0;
6589}
6590
6591/*
6592 * Clear the syntax timing for the current buffer.
6593 */
6594 static void
6595syntime_clear()
6596{
6597 int idx;
6598 synpat_T *spp;
6599
6600 if (!syntax_present(curwin))
6601 {
6602 MSG(_(msg_no_items));
6603 return;
6604 }
6605 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6606 {
6607 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6608 syn_clear_time(&spp->sp_time);
6609 }
6610}
6611
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006612#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
6613/*
6614 * Function given to ExpandGeneric() to obtain the possible arguments of the
6615 * ":syntime {on,off,clear,report}" command.
6616 */
6617 char_u *
6618get_syntime_arg(xp, idx)
6619 expand_T *xp UNUSED;
6620 int idx;
6621{
6622 switch (idx)
6623 {
6624 case 0: return (char_u *)"on";
6625 case 1: return (char_u *)"off";
6626 case 2: return (char_u *)"clear";
6627 case 3: return (char_u *)"report";
6628 }
6629 return NULL;
6630}
6631#endif
6632
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006633typedef struct
6634{
6635 proftime_T total;
6636 int count;
6637 int match;
6638 proftime_T slowest;
6639 proftime_T average;
6640 int id;
6641 char_u *pattern;
6642} time_entry_T;
6643
6644 static int
6645#ifdef __BORLANDC__
6646_RTLENTRYF
6647#endif
6648syn_compare_syntime(v1, v2)
6649 const void *v1;
6650 const void *v2;
6651{
6652 const time_entry_T *s1 = v1;
6653 const time_entry_T *s2 = v2;
6654
6655 return profile_cmp(&s1->total, &s2->total);
6656}
6657
6658/*
6659 * Clear the syntax timing for the current buffer.
6660 */
6661 static void
6662syntime_report()
6663{
6664 int idx;
6665 synpat_T *spp;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006666# ifdef FEAT_FLOAT
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006667 proftime_T tm;
Bram Moolenaarcd9c4622013-06-08 15:24:48 +02006668# endif
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006669 int len;
6670 proftime_T total_total;
6671 int total_count = 0;
6672 garray_T ga;
6673 time_entry_T *p;
6674
6675 if (!syntax_present(curwin))
6676 {
6677 MSG(_(msg_no_items));
6678 return;
6679 }
6680
6681 ga_init2(&ga, sizeof(time_entry_T), 50);
6682 profile_zero(&total_total);
6683 for (idx = 0; idx < curwin->w_s->b_syn_patterns.ga_len; ++idx)
6684 {
6685 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6686 if (spp->sp_time.count > 0)
6687 {
Bram Moolenaarcde88542015-08-11 19:14:00 +02006688 (void)ga_grow(&ga, 1);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006689 p = ((time_entry_T *)ga.ga_data) + ga.ga_len;
6690 p->total = spp->sp_time.total;
6691 profile_add(&total_total, &spp->sp_time.total);
6692 p->count = spp->sp_time.count;
6693 p->match = spp->sp_time.match;
6694 total_count += spp->sp_time.count;
6695 p->slowest = spp->sp_time.slowest;
6696# ifdef FEAT_FLOAT
6697 profile_divide(&spp->sp_time.total, spp->sp_time.count, &tm);
6698 p->average = tm;
6699# endif
6700 p->id = spp->sp_syn.id;
6701 p->pattern = spp->sp_pattern;
6702 ++ga.ga_len;
6703 }
6704 }
6705
6706 /* sort on total time */
Bram Moolenaar4e312962013-06-06 21:19:51 +02006707 qsort(ga.ga_data, (size_t)ga.ga_len, sizeof(time_entry_T),
6708 syn_compare_syntime);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006709
6710 MSG_PUTS_TITLE(_(" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN"));
6711 MSG_PUTS("\n");
6712 for (idx = 0; idx < ga.ga_len && !got_int; ++idx)
6713 {
6714 spp = &(SYN_ITEMS(curwin->w_s)[idx]);
6715 p = ((time_entry_T *)ga.ga_data) + idx;
6716
6717 MSG_PUTS(profile_msg(&p->total));
6718 MSG_PUTS(" "); /* make sure there is always a separating space */
6719 msg_advance(13);
6720 msg_outnum(p->count);
6721 MSG_PUTS(" ");
6722 msg_advance(20);
6723 msg_outnum(p->match);
6724 MSG_PUTS(" ");
6725 msg_advance(26);
6726 MSG_PUTS(profile_msg(&p->slowest));
6727 MSG_PUTS(" ");
6728 msg_advance(38);
6729# ifdef FEAT_FLOAT
6730 MSG_PUTS(profile_msg(&p->average));
6731 MSG_PUTS(" ");
6732# endif
6733 msg_advance(50);
6734 msg_outtrans(HL_TABLE()[p->id - 1].sg_name);
6735 MSG_PUTS(" ");
6736
6737 msg_advance(69);
6738 if (Columns < 80)
6739 len = 20; /* will wrap anyway */
6740 else
6741 len = Columns - 70;
6742 if (len > (int)STRLEN(p->pattern))
6743 len = (int)STRLEN(p->pattern);
6744 msg_outtrans_len(p->pattern, len);
6745 MSG_PUTS("\n");
6746 }
Bram Moolenaar45fc5392013-06-07 19:48:39 +02006747 ga_clear(&ga);
Bram Moolenaar8a7f5a22013-06-06 14:01:46 +02006748 if (!got_int)
6749 {
6750 MSG_PUTS("\n");
6751 MSG_PUTS(profile_msg(&total_total));
6752 msg_advance(13);
6753 msg_outnum(total_count);
6754 MSG_PUTS("\n");
6755 }
6756}
6757#endif
6758
Bram Moolenaar071d4272004-06-13 20:20:40 +00006759#endif /* FEAT_SYN_HL */
6760
Bram Moolenaar071d4272004-06-13 20:20:40 +00006761/**************************************
6762 * Highlighting stuff *
6763 **************************************/
6764
6765/*
6766 * The default highlight groups. These are compiled-in for fast startup and
6767 * they still work when the runtime files can't be found.
6768 * When making changes here, also change runtime/colors/default.vim!
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006769 * The #ifdefs are needed to reduce the amount of static data. Helps to make
6770 * the 16 bit DOS (museum) version compile.
Bram Moolenaar071d4272004-06-13 20:20:40 +00006771 */
Bram Moolenaar61623362010-07-14 22:04:22 +02006772#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006773# define CENT(a, b) b
6774#else
6775# define CENT(a, b) a
6776#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006777static char *(highlight_init_both[]) =
6778 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006779 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
6780 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
6781 CENT("IncSearch term=reverse cterm=reverse",
6782 "IncSearch term=reverse cterm=reverse gui=reverse"),
6783 CENT("ModeMsg term=bold cterm=bold",
6784 "ModeMsg term=bold cterm=bold gui=bold"),
6785 CENT("NonText term=bold ctermfg=Blue",
6786 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
6787 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
6788 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
6789 CENT("StatusLineNC term=reverse cterm=reverse",
6790 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006791#ifdef FEAT_VERTSPLIT
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006792 CENT("VertSplit term=reverse cterm=reverse",
6793 "VertSplit term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006794#endif
6795#ifdef FEAT_CLIPBOARD
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006796 CENT("VisualNOS term=underline,bold cterm=underline,bold",
6797 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006798#endif
6799#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006800 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
6801 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006802#endif
6803#ifdef FEAT_INS_EXPAND
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006804 CENT("PmenuSbar ctermbg=Grey",
6805 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006806#endif
6807#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006808 CENT("TabLineSel term=bold cterm=bold",
6809 "TabLineSel term=bold cterm=bold gui=bold"),
6810 CENT("TabLineFill term=reverse cterm=reverse",
6811 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006812#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006813#ifdef FEAT_GUI
6814 "Cursor guibg=fg guifg=bg",
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006815 "lCursor guibg=fg guifg=bg", /* should be different, but what? */
Bram Moolenaar071d4272004-06-13 20:20:40 +00006816#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006817 NULL
6818 };
6819
6820static char *(highlight_init_light[]) =
6821 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006822 CENT("Directory term=bold ctermfg=DarkBlue",
6823 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
6824 CENT("LineNr term=underline ctermfg=Brown",
6825 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006826 CENT("CursorLineNr term=bold ctermfg=Brown",
6827 "CursorLineNr term=bold ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006828 CENT("MoreMsg term=bold ctermfg=DarkGreen",
6829 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6830 CENT("Question term=standout ctermfg=DarkGreen",
6831 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
6832 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
6833 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006834#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006835 CENT("SpellBad term=reverse ctermbg=LightRed",
6836 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
6837 CENT("SpellCap term=reverse ctermbg=LightBlue",
6838 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
6839 CENT("SpellRare term=reverse ctermbg=LightMagenta",
6840 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
6841 CENT("SpellLocal term=underline ctermbg=Cyan",
6842 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006843#endif
6844#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006845 CENT("PmenuThumb ctermbg=Black",
6846 "PmenuThumb ctermbg=Black guibg=Black"),
6847 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
6848 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
6849 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
6850 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006851#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006852 CENT("SpecialKey term=bold ctermfg=DarkBlue",
6853 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
6854 CENT("Title term=bold ctermfg=DarkMagenta",
6855 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
6856 CENT("WarningMsg term=standout ctermfg=DarkRed",
6857 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006858#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006859 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6860 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006861#endif
6862#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006863 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
6864 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
6865 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6866 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006867#endif
6868#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006869 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
6870 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006871#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006872 CENT("Visual term=reverse",
6873 "Visual term=reverse guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006874#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006875 CENT("DiffAdd term=bold ctermbg=LightBlue",
6876 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
6877 CENT("DiffChange term=bold ctermbg=LightMagenta",
6878 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
6879 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
6880 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006881#endif
6882#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006883 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
6884 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006885#endif
6886#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006887 CENT("CursorColumn term=reverse ctermbg=LightGrey",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006888 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006889 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaare2f98b92006-03-29 21:18:24 +00006890 "CursorLine term=underline cterm=underline guibg=Grey90"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006891 CENT("ColorColumn term=reverse ctermbg=LightRed",
6892 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006893#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006894#ifdef FEAT_CONCEAL
6895 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6896 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6897#endif
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006898#ifdef FEAT_AUTOCMD
6899 CENT("MatchParen term=reverse ctermbg=Cyan",
6900 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
6901#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006902#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006903 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006904#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006905 NULL
6906 };
6907
6908static char *(highlight_init_dark[]) =
6909 {
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006910 CENT("Directory term=bold ctermfg=LightCyan",
6911 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
6912 CENT("LineNr term=underline ctermfg=Yellow",
6913 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar06ca5132012-03-23 16:25:17 +01006914 CENT("CursorLineNr term=bold ctermfg=Yellow",
6915 "CursorLineNr term=bold ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006916 CENT("MoreMsg term=bold ctermfg=LightGreen",
6917 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
6918 CENT("Question term=standout ctermfg=LightGreen",
6919 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
6920 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
6921 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
6922 CENT("SpecialKey term=bold ctermfg=LightBlue",
6923 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006924#ifdef FEAT_SPELL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006925 CENT("SpellBad term=reverse ctermbg=Red",
6926 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
6927 CENT("SpellCap term=reverse ctermbg=Blue",
6928 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
6929 CENT("SpellRare term=reverse ctermbg=Magenta",
6930 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
6931 CENT("SpellLocal term=underline ctermbg=Cyan",
6932 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006933#endif
6934#ifdef FEAT_INS_EXPAND
Bram Moolenaar5d3a8032012-03-16 20:16:46 +01006935 CENT("PmenuThumb ctermbg=White",
6936 "PmenuThumb ctermbg=White guibg=White"),
6937 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
6938 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
Bram Moolenaar049d8e72012-07-19 17:39:07 +02006939 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
6940 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006941#endif
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006942 CENT("Title term=bold ctermfg=LightMagenta",
6943 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
6944 CENT("WarningMsg term=standout ctermfg=LightRed",
6945 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006946#ifdef FEAT_WILDMENU
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006947 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
6948 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006949#endif
6950#ifdef FEAT_FOLDING
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006951 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
6952 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
6953 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6954 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006955#endif
6956#ifdef FEAT_SIGNS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006957 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
6958 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006959#endif
Bram Moolenaarc1e37902006-04-18 21:55:01 +00006960 CENT("Visual term=reverse",
6961 "Visual term=reverse guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006962#ifdef FEAT_DIFF
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006963 CENT("DiffAdd term=bold ctermbg=DarkBlue",
6964 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
6965 CENT("DiffChange term=bold ctermbg=DarkMagenta",
6966 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
6967 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
6968 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006969#endif
6970#ifdef FEAT_WINDOWS
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006971 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
6972 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006973#endif
6974#ifdef FEAT_SYN_HL
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006975 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006976 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
Bram Moolenaar899dddf2006-03-26 21:06:50 +00006977 CENT("CursorLine term=underline cterm=underline",
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006978 "CursorLine term=underline cterm=underline guibg=Grey40"),
Bram Moolenaar1a384422010-07-14 19:53:30 +02006979 CENT("ColorColumn term=reverse ctermbg=DarkRed",
6980 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
Bram Moolenaarb21e5842006-04-16 18:30:08 +00006981#endif
6982#ifdef FEAT_AUTOCMD
6983 CENT("MatchParen term=reverse ctermbg=DarkCyan",
6984 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006985#endif
Bram Moolenaar860cae12010-06-05 23:22:07 +02006986#ifdef FEAT_CONCEAL
6987 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
6988 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
6989#endif
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006990#ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00006991 "Normal gui=NONE",
Bram Moolenaar42bbef42006-03-25 22:02:07 +00006992#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00006993 NULL
6994 };
6995
6996 void
6997init_highlight(both, reset)
6998 int both; /* include groups where 'bg' doesn't matter */
6999 int reset; /* clear group first */
7000{
7001 int i;
7002 char **pp;
7003 static int had_both = FALSE;
7004#ifdef FEAT_EVAL
7005 char_u *p;
7006
7007 /*
7008 * Try finding the color scheme file. Used when a color file was loaded
7009 * and 'background' or 't_Co' is changed.
7010 */
7011 p = get_var_value((char_u *)"g:colors_name");
Bram Moolenaarc7dc1f42015-03-13 12:53:37 +01007012 if (p != NULL)
7013 {
7014 /* The value of g:colors_name could be freed when sourcing the script,
7015 * making "p" invalid, so copy it. */
7016 char_u *copy_p = vim_strsave(p);
7017 int r;
7018
7019 if (copy_p != NULL)
7020 {
7021 r = load_colors(copy_p);
7022 vim_free(copy_p);
7023 if (r == OK)
7024 return;
7025 }
7026 }
7027
Bram Moolenaar071d4272004-06-13 20:20:40 +00007028#endif
7029
7030 /*
7031 * Didn't use a color file, use the compiled-in colors.
7032 */
7033 if (both)
7034 {
7035 had_both = TRUE;
7036 pp = highlight_init_both;
7037 for (i = 0; pp[i] != NULL; ++i)
7038 do_highlight((char_u *)pp[i], reset, TRUE);
7039 }
7040 else if (!had_both)
7041 /* Don't do anything before the call with both == TRUE from main().
7042 * Not everything has been setup then, and that call will overrule
7043 * everything anyway. */
7044 return;
7045
7046 if (*p_bg == 'l')
7047 pp = highlight_init_light;
7048 else
7049 pp = highlight_init_dark;
7050 for (i = 0; pp[i] != NULL; ++i)
7051 do_highlight((char_u *)pp[i], reset, TRUE);
7052
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007053 /* Reverse looks ugly, but grey may not work for 8 colors. Thus let it
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007054 * depend on the number of colors available.
7055 * With 8 colors brown is equal to yellow, need to use black for Search fg
Bram Moolenaarda40c852008-08-06 13:28:57 +00007056 * to avoid Statement highlighted text disappears.
7057 * Clear the attributes, needed when changing the t_Co value. */
Bram Moolenaarab194812005-09-14 21:40:12 +00007058 if (t_colors > 8)
Bram Moolenaarda40c852008-08-06 13:28:57 +00007059 do_highlight((char_u *)(*p_bg == 'l'
7060 ? "Visual cterm=NONE ctermbg=LightGrey"
7061 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
Bram Moolenaarc1e37902006-04-18 21:55:01 +00007062 else
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007063 {
Bram Moolenaarda40c852008-08-06 13:28:57 +00007064 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
7065 FALSE, TRUE);
Bram Moolenaarf193fff2006-04-27 00:02:13 +00007066 if (*p_bg == 'l')
7067 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
7068 }
Bram Moolenaarab194812005-09-14 21:40:12 +00007069
Bram Moolenaar071d4272004-06-13 20:20:40 +00007070#ifdef FEAT_SYN_HL
7071 /*
7072 * If syntax highlighting is enabled load the highlighting for it.
7073 */
7074 if (get_var_value((char_u *)"g:syntax_on") != NULL)
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007075 {
7076 static int recursive = 0;
7077
7078 if (recursive >= 5)
7079 EMSG(_("E679: recursive loop loading syncolor.vim"));
7080 else
7081 {
7082 ++recursive;
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007083 (void)source_runtime((char_u *)"syntax/syncolor.vim", TRUE);
Bram Moolenaarc0197e22004-09-13 20:26:32 +00007084 --recursive;
7085 }
7086 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007087#endif
7088}
7089
7090/*
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007091 * Load color file "name".
Bram Moolenaar071d4272004-06-13 20:20:40 +00007092 * Return OK for success, FAIL for failure.
7093 */
7094 int
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007095load_colors(name)
7096 char_u *name;
Bram Moolenaar071d4272004-06-13 20:20:40 +00007097{
7098 char_u *buf;
7099 int retval = FAIL;
7100 static int recursive = FALSE;
7101
7102 /* When being called recursively, this is probably because setting
7103 * 'background' caused the highlighting to be reloaded. This means it is
7104 * working, thus we should return OK. */
7105 if (recursive)
7106 return OK;
7107
7108 recursive = TRUE;
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007109 buf = alloc((unsigned)(STRLEN(name) + 12));
Bram Moolenaar071d4272004-06-13 20:20:40 +00007110 if (buf != NULL)
7111 {
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007112 sprintf((char *)buf, "colors/%s.vim", name);
Bram Moolenaar90cfdbe2005-08-12 19:59:19 +00007113 retval = source_runtime(buf, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007114 vim_free(buf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007115#ifdef FEAT_AUTOCMD
Bram Moolenaarb95186f2013-11-28 18:53:52 +01007116 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
Bram Moolenaarcfbc5ee2004-07-02 15:38:35 +00007117#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007118 }
7119 recursive = FALSE;
7120
7121 return retval;
7122}
7123
7124/*
7125 * Handle the ":highlight .." command.
7126 * When using ":hi clear" this is called recursively for each group with
7127 * "forceit" and "init" both TRUE.
7128 */
7129 void
7130do_highlight(line, forceit, init)
7131 char_u *line;
7132 int forceit;
7133 int init; /* TRUE when called for initializing */
7134{
7135 char_u *name_end;
7136 char_u *p;
7137 char_u *linep;
7138 char_u *key_start;
7139 char_u *arg_start;
7140 char_u *key = NULL, *arg = NULL;
7141 long i;
7142 int off;
7143 int len;
7144 int attr;
7145 int id;
7146 int idx;
7147 int dodefault = FALSE;
7148 int doclear = FALSE;
7149 int dolink = FALSE;
7150 int error = FALSE;
7151 int color;
7152 int is_normal_group = FALSE; /* "Normal" group */
7153#ifdef FEAT_GUI_X11
7154 int is_menu_group = FALSE; /* "Menu" group */
7155 int is_scrollbar_group = FALSE; /* "Scrollbar" group */
7156 int is_tooltip_group = FALSE; /* "Tooltip" group */
7157 int do_colors = FALSE; /* need to update colors? */
7158#else
7159# define is_menu_group 0
7160# define is_tooltip_group 0
7161#endif
7162
7163 /*
7164 * If no argument, list current highlighting.
7165 */
7166 if (ends_excmd(*line))
7167 {
7168 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
7169 /* TODO: only call when the group has attributes set */
7170 highlight_list_one((int)i);
7171 return;
7172 }
7173
7174 /*
7175 * Isolate the name.
7176 */
7177 name_end = skiptowhite(line);
7178 linep = skipwhite(name_end);
7179
7180 /*
7181 * Check for "default" argument.
7182 */
7183 if (STRNCMP(line, "default", name_end - line) == 0)
7184 {
7185 dodefault = TRUE;
7186 line = linep;
7187 name_end = skiptowhite(line);
7188 linep = skipwhite(name_end);
7189 }
7190
7191 /*
7192 * Check for "clear" or "link" argument.
7193 */
7194 if (STRNCMP(line, "clear", name_end - line) == 0)
7195 doclear = TRUE;
7196 if (STRNCMP(line, "link", name_end - line) == 0)
7197 dolink = TRUE;
7198
7199 /*
7200 * ":highlight {group-name}": list highlighting for one group.
7201 */
7202 if (!doclear && !dolink && ends_excmd(*linep))
7203 {
7204 id = syn_namen2id(line, (int)(name_end - line));
7205 if (id == 0)
7206 EMSG2(_("E411: highlight group not found: %s"), line);
7207 else
7208 highlight_list_one(id);
7209 return;
7210 }
7211
7212 /*
7213 * Handle ":highlight link {from} {to}" command.
7214 */
7215 if (dolink)
7216 {
7217 char_u *from_start = linep;
7218 char_u *from_end;
7219 char_u *to_start;
7220 char_u *to_end;
7221 int from_id;
7222 int to_id;
7223
7224 from_end = skiptowhite(from_start);
7225 to_start = skipwhite(from_end);
7226 to_end = skiptowhite(to_start);
7227
7228 if (ends_excmd(*from_start) || ends_excmd(*to_start))
7229 {
7230 EMSG2(_("E412: Not enough arguments: \":highlight link %s\""),
7231 from_start);
7232 return;
7233 }
7234
7235 if (!ends_excmd(*skipwhite(to_end)))
7236 {
7237 EMSG2(_("E413: Too many arguments: \":highlight link %s\""), from_start);
7238 return;
7239 }
7240
7241 from_id = syn_check_group(from_start, (int)(from_end - from_start));
7242 if (STRNCMP(to_start, "NONE", 4) == 0)
7243 to_id = 0;
7244 else
7245 to_id = syn_check_group(to_start, (int)(to_end - to_start));
7246
7247 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
7248 {
7249 /*
7250 * Don't allow a link when there already is some highlighting
7251 * for the group, unless '!' is used
7252 */
7253 if (to_id > 0 && !forceit && !init
7254 && hl_has_settings(from_id - 1, dodefault))
7255 {
7256 if (sourcing_name == NULL && !dodefault)
7257 EMSG(_("E414: group has settings, highlight link ignored"));
7258 }
7259 else
7260 {
7261 if (!init)
7262 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
7263 HL_TABLE()[from_id - 1].sg_link = to_id;
Bram Moolenaar661b1822005-07-28 22:36:45 +00007264#ifdef FEAT_EVAL
7265 HL_TABLE()[from_id - 1].sg_scriptID = current_SID;
7266#endif
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00007267 redraw_all_later(SOME_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007268 }
7269 }
7270
7271 /* Only call highlight_changed() once, after sourcing a syntax file */
7272 need_highlight_changed = TRUE;
7273
7274 return;
7275 }
7276
7277 if (doclear)
7278 {
7279 /*
7280 * ":highlight clear [group]" command.
7281 */
7282 line = linep;
7283 if (ends_excmd(*line))
7284 {
7285#ifdef FEAT_GUI
7286 /* First, we do not destroy the old values, but allocate the new
7287 * ones and update the display. THEN we destroy the old values.
7288 * If we destroy the old values first, then the old values
7289 * (such as GuiFont's or GuiFontset's) will still be displayed but
7290 * invalid because they were free'd.
7291 */
7292 if (gui.in_use)
7293 {
7294# ifdef FEAT_BEVAL_TIP
7295 gui_init_tooltip_font();
7296# endif
7297# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
7298 gui_init_menu_font();
7299# endif
7300 }
7301# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
7302 gui_mch_def_colors();
7303# endif
7304# ifdef FEAT_GUI_X11
7305# ifdef FEAT_MENU
7306
7307 /* This only needs to be done when there is no Menu highlight
7308 * group defined by default, which IS currently the case.
7309 */
7310 gui_mch_new_menu_colors();
7311# endif
7312 if (gui.in_use)
7313 {
7314 gui_new_scrollbar_colors();
7315# ifdef FEAT_BEVAL
7316 gui_mch_new_tooltip_colors();
7317# endif
7318# ifdef FEAT_MENU
7319 gui_mch_new_menu_font();
7320# endif
7321 }
7322# endif
7323
7324 /* Ok, we're done allocating the new default graphics items.
7325 * The screen should already be refreshed at this point.
7326 * It is now Ok to clear out the old data.
7327 */
7328#endif
7329#ifdef FEAT_EVAL
Bram Moolenaar2ce06f62005-01-31 19:19:04 +00007330 do_unlet((char_u *)"colors_name", TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007331#endif
7332 restore_cterm_colors();
7333
7334 /*
7335 * Clear all default highlight groups and load the defaults.
7336 */
7337 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
7338 highlight_clear(idx);
7339 init_highlight(TRUE, TRUE);
7340#ifdef FEAT_GUI
7341 if (gui.in_use)
7342 highlight_gui_started();
7343#endif
7344 highlight_changed();
7345 redraw_later_clear();
7346 return;
7347 }
7348 name_end = skiptowhite(line);
7349 linep = skipwhite(name_end);
7350 }
7351
7352 /*
7353 * Find the group name in the table. If it does not exist yet, add it.
7354 */
7355 id = syn_check_group(line, (int)(name_end - line));
7356 if (id == 0) /* failed (out of memory) */
7357 return;
7358 idx = id - 1; /* index is ID minus one */
7359
7360 /* Return if "default" was used and the group already has settings. */
7361 if (dodefault && hl_has_settings(idx, TRUE))
7362 return;
7363
7364 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
7365 is_normal_group = TRUE;
7366#ifdef FEAT_GUI_X11
7367 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
7368 is_menu_group = TRUE;
7369 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
7370 is_scrollbar_group = TRUE;
7371 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
7372 is_tooltip_group = TRUE;
7373#endif
7374
7375 /* Clear the highlighting for ":hi clear {group}" and ":hi clear". */
7376 if (doclear || (forceit && init))
7377 {
7378 highlight_clear(idx);
7379 if (!doclear)
7380 HL_TABLE()[idx].sg_set = 0;
7381 }
7382
7383 if (!doclear)
7384 while (!ends_excmd(*linep))
7385 {
7386 key_start = linep;
7387 if (*linep == '=')
7388 {
7389 EMSG2(_("E415: unexpected equal sign: %s"), key_start);
7390 error = TRUE;
7391 break;
7392 }
7393
7394 /*
7395 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
7396 * "guibg").
7397 */
7398 while (*linep && !vim_iswhite(*linep) && *linep != '=')
7399 ++linep;
7400 vim_free(key);
7401 key = vim_strnsave_up(key_start, (int)(linep - key_start));
7402 if (key == NULL)
7403 {
7404 error = TRUE;
7405 break;
7406 }
7407 linep = skipwhite(linep);
7408
7409 if (STRCMP(key, "NONE") == 0)
7410 {
7411 if (!init || HL_TABLE()[idx].sg_set == 0)
7412 {
7413 if (!init)
7414 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
7415 highlight_clear(idx);
7416 }
7417 continue;
7418 }
7419
7420 /*
7421 * Check for the equal sign.
7422 */
7423 if (*linep != '=')
7424 {
7425 EMSG2(_("E416: missing equal sign: %s"), key_start);
7426 error = TRUE;
7427 break;
7428 }
7429 ++linep;
7430
7431 /*
7432 * Isolate the argument.
7433 */
7434 linep = skipwhite(linep);
7435 if (*linep == '\'') /* guifg='color name' */
7436 {
7437 arg_start = ++linep;
7438 linep = vim_strchr(linep, '\'');
7439 if (linep == NULL)
7440 {
7441 EMSG2(_(e_invarg2), key_start);
7442 error = TRUE;
7443 break;
7444 }
7445 }
7446 else
7447 {
7448 arg_start = linep;
7449 linep = skiptowhite(linep);
7450 }
7451 if (linep == arg_start)
7452 {
7453 EMSG2(_("E417: missing argument: %s"), key_start);
7454 error = TRUE;
7455 break;
7456 }
7457 vim_free(arg);
7458 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
7459 if (arg == NULL)
7460 {
7461 error = TRUE;
7462 break;
7463 }
7464 if (*linep == '\'')
7465 ++linep;
7466
7467 /*
7468 * Store the argument.
7469 */
7470 if ( STRCMP(key, "TERM") == 0
7471 || STRCMP(key, "CTERM") == 0
7472 || STRCMP(key, "GUI") == 0)
7473 {
7474 attr = 0;
7475 off = 0;
7476 while (arg[off] != NUL)
7477 {
7478 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
7479 {
7480 len = (int)STRLEN(hl_name_table[i]);
7481 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
7482 {
7483 attr |= hl_attr_table[i];
7484 off += len;
7485 break;
7486 }
7487 }
7488 if (i < 0)
7489 {
7490 EMSG2(_("E418: Illegal value: %s"), arg);
7491 error = TRUE;
7492 break;
7493 }
7494 if (arg[off] == ',') /* another one follows */
7495 ++off;
7496 }
7497 if (error)
7498 break;
7499 if (*key == 'T')
7500 {
7501 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
7502 {
7503 if (!init)
7504 HL_TABLE()[idx].sg_set |= SG_TERM;
7505 HL_TABLE()[idx].sg_term = attr;
7506 }
7507 }
7508 else if (*key == 'C')
7509 {
7510 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7511 {
7512 if (!init)
7513 HL_TABLE()[idx].sg_set |= SG_CTERM;
7514 HL_TABLE()[idx].sg_cterm = attr;
7515 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7516 }
7517 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007518#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007519 else
7520 {
7521 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7522 {
7523 if (!init)
7524 HL_TABLE()[idx].sg_set |= SG_GUI;
7525 HL_TABLE()[idx].sg_gui = attr;
7526 }
7527 }
7528#endif
7529 }
7530 else if (STRCMP(key, "FONT") == 0)
7531 {
7532 /* in non-GUI fonts are simply ignored */
7533#ifdef FEAT_GUI
7534 if (!gui.shell_created)
7535 {
7536 /* GUI not started yet, always accept the name. */
7537 vim_free(HL_TABLE()[idx].sg_font_name);
7538 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7539 }
7540 else
7541 {
7542 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
7543# ifdef FEAT_XFONTSET
7544 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
7545# endif
7546 /* First, save the current font/fontset.
7547 * Then try to allocate the font/fontset.
7548 * If the allocation fails, HL_TABLE()[idx].sg_font OR
7549 * sg_fontset will be set to NOFONT or NOFONTSET respectively.
7550 */
7551
7552 HL_TABLE()[idx].sg_font = NOFONT;
7553# ifdef FEAT_XFONTSET
7554 HL_TABLE()[idx].sg_fontset = NOFONTSET;
7555# endif
7556 hl_do_font(idx, arg, is_normal_group, is_menu_group,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007557 is_tooltip_group, FALSE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00007558
7559# ifdef FEAT_XFONTSET
7560 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
7561 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007562 /* New fontset was accepted. Free the old one, if there
7563 * was one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007564 gui_mch_free_fontset(temp_sg_fontset);
7565 vim_free(HL_TABLE()[idx].sg_font_name);
7566 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7567 }
7568 else
7569 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
7570# endif
7571 if (HL_TABLE()[idx].sg_font != NOFONT)
7572 {
7573 /* New font was accepted. Free the old one, if there was
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02007574 * one. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007575 gui_mch_free_font(temp_sg_font);
7576 vim_free(HL_TABLE()[idx].sg_font_name);
7577 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
7578 }
7579 else
7580 HL_TABLE()[idx].sg_font = temp_sg_font;
7581 }
7582#endif
7583 }
7584 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
7585 {
7586 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
7587 {
7588 if (!init)
7589 HL_TABLE()[idx].sg_set |= SG_CTERM;
7590
7591 /* When setting the foreground color, and previously the "bold"
7592 * flag was set for a light color, reset it now */
7593 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
7594 {
7595 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7596 HL_TABLE()[idx].sg_cterm_bold = FALSE;
7597 }
7598
7599 if (VIM_ISDIGIT(*arg))
7600 color = atoi((char *)arg);
7601 else if (STRICMP(arg, "fg") == 0)
7602 {
7603 if (cterm_normal_fg_color)
7604 color = cterm_normal_fg_color - 1;
7605 else
7606 {
7607 EMSG(_("E419: FG color unknown"));
7608 error = TRUE;
7609 break;
7610 }
7611 }
7612 else if (STRICMP(arg, "bg") == 0)
7613 {
7614 if (cterm_normal_bg_color > 0)
7615 color = cterm_normal_bg_color - 1;
7616 else
7617 {
7618 EMSG(_("E420: BG color unknown"));
7619 error = TRUE;
7620 break;
7621 }
7622 }
7623 else
7624 {
7625 static char *(color_names[28]) = {
7626 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
7627 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
7628 "Gray", "Grey",
7629 "LightGray", "LightGrey", "DarkGray", "DarkGrey",
7630 "Blue", "LightBlue", "Green", "LightGreen",
7631 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
7632 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
7633 static int color_numbers_16[28] = {0, 1, 2, 3,
7634 4, 5, 6, 6,
7635 7, 7,
7636 7, 7, 8, 8,
7637 9, 9, 10, 10,
7638 11, 11, 12, 12, 13,
7639 13, 14, 14, 15, -1};
7640 /* for xterm with 88 colors... */
7641 static int color_numbers_88[28] = {0, 4, 2, 6,
7642 1, 5, 32, 72,
7643 84, 84,
7644 7, 7, 82, 82,
7645 12, 43, 10, 61,
7646 14, 63, 9, 74, 13,
7647 75, 11, 78, 15, -1};
7648 /* for xterm with 256 colors... */
7649 static int color_numbers_256[28] = {0, 4, 2, 6,
7650 1, 5, 130, 130,
7651 248, 248,
7652 7, 7, 242, 242,
7653 12, 81, 10, 121,
7654 14, 159, 9, 224, 13,
7655 225, 11, 229, 15, -1};
7656 /* for terminals with less than 16 colors... */
7657 static int color_numbers_8[28] = {0, 4, 2, 6,
7658 1, 5, 3, 3,
7659 7, 7,
7660 7, 7, 0+8, 0+8,
7661 4+8, 4+8, 2+8, 2+8,
7662 6+8, 6+8, 1+8, 1+8, 5+8,
7663 5+8, 3+8, 3+8, 7+8, -1};
7664#if defined(__QNXNTO__)
7665 static int *color_numbers_8_qansi = color_numbers_8;
7666 /* On qnx, the 8 & 16 color arrays are the same */
7667 if (STRNCMP(T_NAME, "qansi", 5) == 0)
7668 color_numbers_8_qansi = color_numbers_16;
7669#endif
7670
7671 /* reduce calls to STRICMP a bit, it can be slow */
7672 off = TOUPPER_ASC(*arg);
7673 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
7674 if (off == color_names[i][0]
7675 && STRICMP(arg + 1, color_names[i] + 1) == 0)
7676 break;
7677 if (i < 0)
7678 {
7679 EMSG2(_("E421: Color name or number not recognized: %s"), key_start);
7680 error = TRUE;
7681 break;
7682 }
7683
7684 /* Use the _16 table to check if its a valid color name. */
7685 color = color_numbers_16[i];
7686 if (color >= 0)
7687 {
7688 if (t_colors == 8)
7689 {
7690 /* t_Co is 8: use the 8 colors table */
7691#if defined(__QNXNTO__)
7692 color = color_numbers_8_qansi[i];
7693#else
7694 color = color_numbers_8[i];
7695#endif
7696 if (key[5] == 'F')
7697 {
7698 /* set/reset bold attribute to get light foreground
7699 * colors (on some terminals, e.g. "linux") */
7700 if (color & 8)
7701 {
7702 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
7703 HL_TABLE()[idx].sg_cterm_bold = TRUE;
7704 }
7705 else
7706 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
7707 }
7708 color &= 7; /* truncate to 8 colors */
7709 }
7710 else if (t_colors == 16 || t_colors == 88
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007711 || t_colors >= 256)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007712 {
7713 /*
7714 * Guess: if the termcap entry ends in 'm', it is
7715 * probably an xterm-like terminal. Use the changed
7716 * order for colors.
7717 */
7718 if (*T_CAF != NUL)
7719 p = T_CAF;
7720 else
7721 p = T_CSF;
Bram Moolenaarfa03fd62016-01-02 22:03:00 +01007722 if (*p != NUL && (t_colors > 256
7723 || *(p + STRLEN(p) - 1) == 'm'))
7724 {
7725 if (t_colors == 88)
7726 color = color_numbers_88[i];
7727 else if (t_colors >= 256)
7728 color = color_numbers_256[i];
7729 else
7730 color = color_numbers_8[i];
7731 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007732 }
7733 }
7734 }
Bram Moolenaarccbab932010-05-13 15:40:30 +02007735 /* Add one to the argument, to avoid zero. Zero is used for
7736 * "NONE", then "color" is -1. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007737 if (key[5] == 'F')
7738 {
7739 HL_TABLE()[idx].sg_cterm_fg = color + 1;
7740 if (is_normal_group)
7741 {
7742 cterm_normal_fg_color = color + 1;
7743 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
7744#ifdef FEAT_GUI
7745 /* Don't do this if the GUI is used. */
7746 if (!gui.in_use && !gui.starting)
7747#endif
7748 {
7749 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007750 if (termcap_active && color >= 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00007751 term_fg_color(color);
7752 }
7753 }
7754 }
7755 else
7756 {
7757 HL_TABLE()[idx].sg_cterm_bg = color + 1;
7758 if (is_normal_group)
7759 {
7760 cterm_normal_bg_color = color + 1;
7761#ifdef FEAT_GUI
7762 /* Don't mess with 'background' if the GUI is used. */
7763 if (!gui.in_use && !gui.starting)
7764#endif
7765 {
7766 must_redraw = CLEAR;
Bram Moolenaarccbab932010-05-13 15:40:30 +02007767 if (color >= 0)
7768 {
7769 if (termcap_active)
7770 term_bg_color(color);
7771 if (t_colors < 16)
7772 i = (color == 0 || color == 4);
7773 else
7774 i = (color < 7 || color == 8);
7775 /* Set the 'background' option if the value is
7776 * wrong. */
7777 if (i != (*p_bg == 'd'))
7778 set_option_value((char_u *)"bg", 0L,
7779 i ? (char_u *)"dark"
7780 : (char_u *)"light", 0);
7781 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007782 }
7783 }
7784 }
7785 }
7786 }
7787 else if (STRCMP(key, "GUIFG") == 0)
7788 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007789#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007790 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007791 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007792 if (!init)
7793 HL_TABLE()[idx].sg_set |= SG_GUI;
7794
Bram Moolenaar61623362010-07-14 22:04:22 +02007795# ifdef FEAT_GUI
7796 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007797 i = color_name2handle(arg);
7798 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7799 {
7800 HL_TABLE()[idx].sg_gui_fg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007801# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007802 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
7803 if (STRCMP(arg, "NONE"))
7804 HL_TABLE()[idx].sg_gui_fg_name = vim_strsave(arg);
7805 else
7806 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007807# ifdef FEAT_GUI
7808# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007809 if (is_menu_group)
7810 gui.menu_fg_pixel = i;
7811 if (is_scrollbar_group)
7812 gui.scroll_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007813# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007814 if (is_tooltip_group)
7815 gui.tooltip_fg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007816# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007817 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007818# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007819 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007820# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007821 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007822#endif
7823 }
7824 else if (STRCMP(key, "GUIBG") == 0)
7825 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007826#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007827 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
Bram Moolenaar071d4272004-06-13 20:20:40 +00007828 {
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007829 if (!init)
7830 HL_TABLE()[idx].sg_set |= SG_GUI;
7831
Bram Moolenaar61623362010-07-14 22:04:22 +02007832# ifdef FEAT_GUI
7833 /* In GUI guifg colors are only used when recognized */
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007834 i = color_name2handle(arg);
7835 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7836 {
7837 HL_TABLE()[idx].sg_gui_bg = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007838# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007839 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
7840 if (STRCMP(arg, "NONE") != 0)
7841 HL_TABLE()[idx].sg_gui_bg_name = vim_strsave(arg);
7842 else
7843 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007844# ifdef FEAT_GUI
7845# ifdef FEAT_GUI_X11
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007846 if (is_menu_group)
7847 gui.menu_bg_pixel = i;
7848 if (is_scrollbar_group)
7849 gui.scroll_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007850# ifdef FEAT_BEVAL
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007851 if (is_tooltip_group)
7852 gui.tooltip_bg_pixel = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007853# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007854 do_colors = TRUE;
Bram Moolenaar61623362010-07-14 22:04:22 +02007855# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007856 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007857# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00007858 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007859#endif
7860 }
7861 else if (STRCMP(key, "GUISP") == 0)
7862 {
Bram Moolenaar61623362010-07-14 22:04:22 +02007863#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007864 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
7865 {
7866 if (!init)
7867 HL_TABLE()[idx].sg_set |= SG_GUI;
7868
Bram Moolenaar61623362010-07-14 22:04:22 +02007869# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007870 i = color_name2handle(arg);
7871 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
7872 {
7873 HL_TABLE()[idx].sg_gui_sp = i;
Bram Moolenaar61623362010-07-14 22:04:22 +02007874# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007875 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
7876 if (STRCMP(arg, "NONE") != 0)
7877 HL_TABLE()[idx].sg_gui_sp_name = vim_strsave(arg);
7878 else
7879 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02007880# ifdef FEAT_GUI
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007881 }
Bram Moolenaar61623362010-07-14 22:04:22 +02007882# endif
Bram Moolenaare2cc9702005-03-15 22:43:58 +00007883 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00007884#endif
7885 }
7886 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
7887 {
7888 char_u buf[100];
7889 char_u *tname;
7890
7891 if (!init)
7892 HL_TABLE()[idx].sg_set |= SG_TERM;
7893
7894 /*
7895 * The "start" and "stop" arguments can be a literal escape
Bram Moolenaarb8017e72007-05-10 18:59:07 +00007896 * sequence, or a comma separated list of terminal codes.
Bram Moolenaar071d4272004-06-13 20:20:40 +00007897 */
7898 if (STRNCMP(arg, "t_", 2) == 0)
7899 {
7900 off = 0;
7901 buf[0] = 0;
7902 while (arg[off] != NUL)
7903 {
7904 /* Isolate one termcap name */
7905 for (len = 0; arg[off + len] &&
7906 arg[off + len] != ','; ++len)
7907 ;
7908 tname = vim_strnsave(arg + off, len);
7909 if (tname == NULL) /* out of memory */
7910 {
7911 error = TRUE;
7912 break;
7913 }
7914 /* lookup the escape sequence for the item */
7915 p = get_term_code(tname);
7916 vim_free(tname);
7917 if (p == NULL) /* ignore non-existing things */
7918 p = (char_u *)"";
7919
7920 /* Append it to the already found stuff */
7921 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
7922 {
7923 EMSG2(_("E422: terminal code too long: %s"), arg);
7924 error = TRUE;
7925 break;
7926 }
7927 STRCAT(buf, p);
7928
7929 /* Advance to the next item */
7930 off += len;
7931 if (arg[off] == ',') /* another one follows */
7932 ++off;
7933 }
7934 }
7935 else
7936 {
7937 /*
7938 * Copy characters from arg[] to buf[], translating <> codes.
7939 */
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007940 for (p = arg, off = 0; off < 100 - 6 && *p; )
Bram Moolenaar071d4272004-06-13 20:20:40 +00007941 {
7942 len = trans_special(&p, buf + off, FALSE);
Bram Moolenaara8fc7982010-09-29 18:32:52 +02007943 if (len > 0) /* recognized special char */
Bram Moolenaar071d4272004-06-13 20:20:40 +00007944 off += len;
7945 else /* copy as normal char */
7946 buf[off++] = *p++;
7947 }
7948 buf[off] = NUL;
7949 }
7950 if (error)
7951 break;
7952
7953 if (STRCMP(buf, "NONE") == 0) /* resetting the value */
7954 p = NULL;
7955 else
7956 p = vim_strsave(buf);
7957 if (key[2] == 'A')
7958 {
7959 vim_free(HL_TABLE()[idx].sg_start);
7960 HL_TABLE()[idx].sg_start = p;
7961 }
7962 else
7963 {
7964 vim_free(HL_TABLE()[idx].sg_stop);
7965 HL_TABLE()[idx].sg_stop = p;
7966 }
7967 }
7968 else
7969 {
7970 EMSG2(_("E423: Illegal argument: %s"), key_start);
7971 error = TRUE;
7972 break;
7973 }
7974
7975 /*
7976 * When highlighting has been given for a group, don't link it.
7977 */
7978 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
7979 HL_TABLE()[idx].sg_link = 0;
7980
7981 /*
7982 * Continue with next argument.
7983 */
7984 linep = skipwhite(linep);
7985 }
7986
7987 /*
7988 * If there is an error, and it's a new entry, remove it from the table.
7989 */
7990 if (error && idx == highlight_ga.ga_len)
7991 syn_unadd_group();
7992 else
7993 {
7994 if (is_normal_group)
7995 {
7996 HL_TABLE()[idx].sg_term_attr = 0;
7997 HL_TABLE()[idx].sg_cterm_attr = 0;
7998#ifdef FEAT_GUI
7999 HL_TABLE()[idx].sg_gui_attr = 0;
8000 /*
8001 * Need to update all groups, because they might be using "bg"
8002 * and/or "fg", which have been changed now.
8003 */
8004 if (gui.in_use)
8005 highlight_gui_started();
8006#endif
8007 }
8008#ifdef FEAT_GUI_X11
8009# ifdef FEAT_MENU
8010 else if (is_menu_group)
8011 {
8012 if (gui.in_use && do_colors)
8013 gui_mch_new_menu_colors();
8014 }
8015# endif
8016 else if (is_scrollbar_group)
8017 {
8018 if (gui.in_use && do_colors)
8019 gui_new_scrollbar_colors();
8020 }
8021# ifdef FEAT_BEVAL
8022 else if (is_tooltip_group)
8023 {
8024 if (gui.in_use && do_colors)
8025 gui_mch_new_tooltip_colors();
8026 }
8027# endif
8028#endif
8029 else
8030 set_hl_attr(idx);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008031#ifdef FEAT_EVAL
8032 HL_TABLE()[idx].sg_scriptID = current_SID;
8033#endif
Bram Moolenaar910f66f2006-04-05 20:41:53 +00008034 redraw_all_later(NOT_VALID);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008035 }
8036 vim_free(key);
8037 vim_free(arg);
8038
8039 /* Only call highlight_changed() once, after sourcing a syntax file */
8040 need_highlight_changed = TRUE;
8041}
8042
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008043#if defined(EXITFREE) || defined(PROTO)
8044 void
8045free_highlight()
8046{
8047 int i;
8048
8049 for (i = 0; i < highlight_ga.ga_len; ++i)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008050 {
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008051 highlight_clear(i);
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008052 vim_free(HL_TABLE()[i].sg_name);
8053 vim_free(HL_TABLE()[i].sg_name_u);
8054 }
Bram Moolenaar1ec484f2005-06-24 23:07:47 +00008055 ga_clear(&highlight_ga);
8056}
8057#endif
8058
Bram Moolenaar071d4272004-06-13 20:20:40 +00008059/*
8060 * Reset the cterm colors to what they were before Vim was started, if
8061 * possible. Otherwise reset them to zero.
8062 */
8063 void
8064restore_cterm_colors()
8065{
8066#if defined(MSDOS) || (defined(WIN3264) && !defined(FEAT_GUI_W32))
8067 /* Since t_me has been set, this probably means that the user
8068 * wants to use this as default colors. Need to reset default
8069 * background/foreground colors. */
8070 mch_set_normal_colors();
8071#else
8072 cterm_normal_fg_color = 0;
8073 cterm_normal_fg_bold = 0;
8074 cterm_normal_bg_color = 0;
8075#endif
8076}
8077
8078/*
8079 * Return TRUE if highlight group "idx" has any settings.
8080 * When "check_link" is TRUE also check for an existing link.
8081 */
8082 static int
8083hl_has_settings(idx, check_link)
8084 int idx;
8085 int check_link;
8086{
8087 return ( HL_TABLE()[idx].sg_term_attr != 0
8088 || HL_TABLE()[idx].sg_cterm_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008089 || HL_TABLE()[idx].sg_cterm_fg != 0
8090 || HL_TABLE()[idx].sg_cterm_bg != 0
Bram Moolenaar071d4272004-06-13 20:20:40 +00008091#ifdef FEAT_GUI
8092 || HL_TABLE()[idx].sg_gui_attr != 0
Bram Moolenaare3a8bab2014-06-12 12:20:54 +02008093 || HL_TABLE()[idx].sg_gui_fg_name != NULL
8094 || HL_TABLE()[idx].sg_gui_bg_name != NULL
8095 || HL_TABLE()[idx].sg_gui_sp_name != NULL
8096 || HL_TABLE()[idx].sg_font_name != NUL
Bram Moolenaar071d4272004-06-13 20:20:40 +00008097#endif
8098 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
8099}
8100
8101/*
8102 * Clear highlighting for one group.
8103 */
8104 static void
8105highlight_clear(idx)
8106 int idx;
8107{
8108 HL_TABLE()[idx].sg_term = 0;
8109 vim_free(HL_TABLE()[idx].sg_start);
8110 HL_TABLE()[idx].sg_start = NULL;
8111 vim_free(HL_TABLE()[idx].sg_stop);
8112 HL_TABLE()[idx].sg_stop = NULL;
8113 HL_TABLE()[idx].sg_term_attr = 0;
8114 HL_TABLE()[idx].sg_cterm = 0;
8115 HL_TABLE()[idx].sg_cterm_bold = FALSE;
8116 HL_TABLE()[idx].sg_cterm_fg = 0;
8117 HL_TABLE()[idx].sg_cterm_bg = 0;
8118 HL_TABLE()[idx].sg_cterm_attr = 0;
Bram Moolenaar61623362010-07-14 22:04:22 +02008119#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008120 HL_TABLE()[idx].sg_gui = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008121 vim_free(HL_TABLE()[idx].sg_gui_fg_name);
8122 HL_TABLE()[idx].sg_gui_fg_name = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008123 vim_free(HL_TABLE()[idx].sg_gui_bg_name);
8124 HL_TABLE()[idx].sg_gui_bg_name = NULL;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008125 vim_free(HL_TABLE()[idx].sg_gui_sp_name);
8126 HL_TABLE()[idx].sg_gui_sp_name = NULL;
Bram Moolenaar61623362010-07-14 22:04:22 +02008127#endif
8128#ifdef FEAT_GUI
8129 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
8130 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
8131 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008132 gui_mch_free_font(HL_TABLE()[idx].sg_font);
8133 HL_TABLE()[idx].sg_font = NOFONT;
8134# ifdef FEAT_XFONTSET
8135 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
8136 HL_TABLE()[idx].sg_fontset = NOFONTSET;
8137# endif
8138 vim_free(HL_TABLE()[idx].sg_font_name);
8139 HL_TABLE()[idx].sg_font_name = NULL;
8140 HL_TABLE()[idx].sg_gui_attr = 0;
8141#endif
Bram Moolenaar661b1822005-07-28 22:36:45 +00008142#ifdef FEAT_EVAL
8143 /* Clear the script ID only when there is no link, since that is not
8144 * cleared. */
8145 if (HL_TABLE()[idx].sg_link == 0)
8146 HL_TABLE()[idx].sg_scriptID = 0;
8147#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008148}
8149
8150#if defined(FEAT_GUI) || defined(PROTO)
8151/*
8152 * Set the normal foreground and background colors according to the "Normal"
Bram Moolenaarf5b63862009-12-16 17:13:44 +00008153 * highlighting group. For X11 also set "Menu", "Scrollbar", and
Bram Moolenaar071d4272004-06-13 20:20:40 +00008154 * "Tooltip" colors.
8155 */
8156 void
8157set_normal_colors()
8158{
8159 if (set_group_colors((char_u *)"Normal",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008160 &gui.norm_pixel, &gui.back_pixel,
8161 FALSE, TRUE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008162 {
8163 gui_mch_new_colors();
8164 must_redraw = CLEAR;
8165 }
8166#ifdef FEAT_GUI_X11
8167 if (set_group_colors((char_u *)"Menu",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008168 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
8169 TRUE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008170 {
8171# ifdef FEAT_MENU
8172 gui_mch_new_menu_colors();
8173# endif
8174 must_redraw = CLEAR;
8175 }
8176# ifdef FEAT_BEVAL
8177 if (set_group_colors((char_u *)"Tooltip",
8178 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
8179 FALSE, FALSE, TRUE))
8180 {
8181# ifdef FEAT_TOOLBAR
8182 gui_mch_new_tooltip_colors();
8183# endif
8184 must_redraw = CLEAR;
8185 }
8186#endif
8187 if (set_group_colors((char_u *)"Scrollbar",
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008188 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
8189 FALSE, FALSE, FALSE))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008190 {
8191 gui_new_scrollbar_colors();
8192 must_redraw = CLEAR;
8193 }
8194#endif
8195}
8196
8197/*
8198 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
8199 */
8200 static int
8201set_group_colors(name, fgp, bgp, do_menu, use_norm, do_tooltip)
8202 char_u *name;
8203 guicolor_T *fgp;
8204 guicolor_T *bgp;
8205 int do_menu;
8206 int use_norm;
8207 int do_tooltip;
8208{
8209 int idx;
8210
8211 idx = syn_name2id(name) - 1;
8212 if (idx >= 0)
8213 {
8214 gui_do_one_color(idx, do_menu, do_tooltip);
8215
8216 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
8217 *fgp = HL_TABLE()[idx].sg_gui_fg;
8218 else if (use_norm)
8219 *fgp = gui.def_norm_pixel;
8220 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
8221 *bgp = HL_TABLE()[idx].sg_gui_bg;
8222 else if (use_norm)
8223 *bgp = gui.def_back_pixel;
8224 return TRUE;
8225 }
8226 return FALSE;
8227}
8228
8229/*
8230 * Get the font of the "Normal" group.
8231 * Returns "" when it's not found or not set.
8232 */
8233 char_u *
8234hl_get_font_name()
8235{
8236 int id;
8237 char_u *s;
8238
8239 id = syn_name2id((char_u *)"Normal");
8240 if (id > 0)
8241 {
8242 s = HL_TABLE()[id - 1].sg_font_name;
8243 if (s != NULL)
8244 return s;
8245 }
8246 return (char_u *)"";
8247}
8248
8249/*
8250 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
8251 * actually chosen to be used.
8252 */
8253 void
8254hl_set_font_name(font_name)
8255 char_u *font_name;
8256{
8257 int id;
8258
8259 id = syn_name2id((char_u *)"Normal");
8260 if (id > 0)
8261 {
8262 vim_free(HL_TABLE()[id - 1].sg_font_name);
8263 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
8264 }
8265}
8266
8267/*
8268 * Set background color for "Normal" group. Called by gui_set_bg_color()
8269 * when the color is known.
8270 */
8271 void
8272hl_set_bg_color_name(name)
8273 char_u *name; /* must have been allocated */
8274{
8275 int id;
8276
8277 if (name != NULL)
8278 {
8279 id = syn_name2id((char_u *)"Normal");
8280 if (id > 0)
8281 {
8282 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
8283 HL_TABLE()[id - 1].sg_gui_bg_name = name;
8284 }
8285 }
8286}
8287
8288/*
8289 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
8290 * when the color is known.
8291 */
8292 void
8293hl_set_fg_color_name(name)
8294 char_u *name; /* must have been allocated */
8295{
8296 int id;
8297
8298 if (name != NULL)
8299 {
8300 id = syn_name2id((char_u *)"Normal");
8301 if (id > 0)
8302 {
8303 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
8304 HL_TABLE()[id - 1].sg_gui_fg_name = name;
8305 }
8306 }
8307}
8308
8309/*
8310 * Return the handle for a color name.
8311 * Returns INVALCOLOR when failed.
8312 */
8313 static guicolor_T
8314color_name2handle(name)
8315 char_u *name;
8316{
8317 if (STRCMP(name, "NONE") == 0)
8318 return INVALCOLOR;
8319
8320 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
8321 return gui.norm_pixel;
8322 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
8323 return gui.back_pixel;
8324
8325 return gui_get_color(name);
8326}
8327
8328/*
8329 * Return the handle for a font name.
8330 * Returns NOFONT when failed.
8331 */
8332 static GuiFont
8333font_name2handle(name)
8334 char_u *name;
8335{
8336 if (STRCMP(name, "NONE") == 0)
8337 return NOFONT;
8338
8339 return gui_mch_get_font(name, TRUE);
8340}
8341
8342# ifdef FEAT_XFONTSET
8343/*
8344 * Return the handle for a fontset name.
8345 * Returns NOFONTSET when failed.
8346 */
8347 static GuiFontset
8348fontset_name2handle(name, fixed_width)
8349 char_u *name;
8350 int fixed_width;
8351{
8352 if (STRCMP(name, "NONE") == 0)
8353 return NOFONTSET;
8354
8355 return gui_mch_get_fontset(name, TRUE, fixed_width);
8356}
8357# endif
8358
8359/*
8360 * Get the font or fontset for one highlight group.
8361 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008362 static void
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008363hl_do_font(idx, arg, do_normal, do_menu, do_tooltip, free_font)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008364 int idx;
8365 char_u *arg;
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00008366 int do_normal; /* set normal font */
8367 int do_menu UNUSED; /* set menu font */
8368 int do_tooltip UNUSED; /* set tooltip font */
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008369 int free_font; /* free current font/fontset */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008370{
8371# ifdef FEAT_XFONTSET
8372 /* If 'guifontset' is not empty, first try using the name as a
8373 * fontset. If that doesn't work, use it as a font name. */
8374 if (*p_guifontset != NUL
8375# ifdef FONTSET_ALWAYS
8376 || do_menu
8377# endif
8378# ifdef FEAT_BEVAL_TIP
8379 /* In Athena & Motif, the Tooltip highlight group is always a fontset */
8380 || do_tooltip
8381# endif
8382 )
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008383 {
Bram Moolenaara9a2d8f2012-10-21 21:25:22 +02008384 if (free_font)
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008385 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008386 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
8387# ifdef FONTSET_ALWAYS
8388 || do_menu
8389# endif
8390# ifdef FEAT_BEVAL_TIP
8391 || do_tooltip
8392# endif
8393 );
Bram Moolenaar29f49ee2013-05-04 03:42:34 +02008394 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008395 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
8396 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008397 /* If it worked and it's the Normal group, use it as the normal
8398 * fontset. Same for the Menu group. */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008399 if (do_normal)
8400 gui_init_font(arg, TRUE);
8401# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8402 if (do_menu)
8403 {
8404# ifdef FONTSET_ALWAYS
8405 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
8406# else
8407 /* YIKES! This is a bug waiting to crash the program */
8408 gui.menu_font = HL_TABLE()[idx].sg_fontset;
8409# endif
8410 gui_mch_new_menu_font();
8411 }
8412# ifdef FEAT_BEVAL
8413 if (do_tooltip)
8414 {
8415 /* The Athena widget set cannot currently handle switching between
8416 * displaying a single font and a fontset.
8417 * If the XtNinternational resource is set to True at widget
Bram Moolenaarb8017e72007-05-10 18:59:07 +00008418 * creation, then a fontset is always used, otherwise an
Bram Moolenaar071d4272004-06-13 20:20:40 +00008419 * XFontStruct is used.
8420 */
8421 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
8422 gui_mch_new_tooltip_font();
8423 }
8424# endif
8425# endif
8426 }
8427 else
8428# endif
8429 {
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02008430 if (free_font)
8431 gui_mch_free_font(HL_TABLE()[idx].sg_font);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008432 HL_TABLE()[idx].sg_font = font_name2handle(arg);
8433 /* If it worked and it's the Normal group, use it as the
8434 * normal font. Same for the Menu group. */
8435 if (HL_TABLE()[idx].sg_font != NOFONT)
8436 {
8437 if (do_normal)
8438 gui_init_font(arg, FALSE);
8439#ifndef FONTSET_ALWAYS
8440# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
8441 if (do_menu)
8442 {
8443 gui.menu_font = HL_TABLE()[idx].sg_font;
8444 gui_mch_new_menu_font();
8445 }
8446# endif
8447#endif
8448 }
8449 }
8450}
8451
8452#endif /* FEAT_GUI */
8453
8454/*
8455 * Table with the specifications for an attribute number.
8456 * Note that this table is used by ALL buffers. This is required because the
8457 * GUI can redraw at any time for any buffer.
8458 */
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008459static garray_T term_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008460
8461#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
8462
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008463static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008464
8465#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
8466
8467#ifdef FEAT_GUI
Bram Moolenaar6c0b44b2005-06-01 21:56:33 +00008468static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
Bram Moolenaar071d4272004-06-13 20:20:40 +00008469
8470#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
8471#endif
8472
8473/*
8474 * Return the attr number for a set of colors and font.
8475 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
8476 * if the combination is new.
8477 * Return 0 for error (no more room).
8478 */
8479 static int
8480get_attr_entry(table, aep)
8481 garray_T *table;
8482 attrentry_T *aep;
8483{
8484 int i;
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008485 attrentry_T *taep;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008486 static int recursive = FALSE;
8487
8488 /*
8489 * Init the table, in case it wasn't done yet.
8490 */
8491 table->ga_itemsize = sizeof(attrentry_T);
8492 table->ga_growsize = 7;
8493
8494 /*
8495 * Try to find an entry with the same specifications.
8496 */
8497 for (i = 0; i < table->ga_len; ++i)
8498 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008499 taep = &(((attrentry_T *)table->ga_data)[i]);
8500 if ( aep->ae_attr == taep->ae_attr
Bram Moolenaar071d4272004-06-13 20:20:40 +00008501 && (
8502#ifdef FEAT_GUI
8503 (table == &gui_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008504 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
8505 && aep->ae_u.gui.bg_color
8506 == taep->ae_u.gui.bg_color
8507 && aep->ae_u.gui.sp_color
8508 == taep->ae_u.gui.sp_color
8509 && aep->ae_u.gui.font == taep->ae_u.gui.font
Bram Moolenaar071d4272004-06-13 20:20:40 +00008510# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008511 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
Bram Moolenaar071d4272004-06-13 20:20:40 +00008512# endif
8513 ))
8514 ||
8515#endif
8516 (table == &term_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008517 && (aep->ae_u.term.start == NULL)
8518 == (taep->ae_u.term.start == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008519 && (aep->ae_u.term.start == NULL
8520 || STRCMP(aep->ae_u.term.start,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008521 taep->ae_u.term.start) == 0)
8522 && (aep->ae_u.term.stop == NULL)
8523 == (taep->ae_u.term.stop == NULL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008524 && (aep->ae_u.term.stop == NULL
8525 || STRCMP(aep->ae_u.term.stop,
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008526 taep->ae_u.term.stop) == 0))
Bram Moolenaar071d4272004-06-13 20:20:40 +00008527 || (table == &cterm_attr_table
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008528 && aep->ae_u.cterm.fg_color
8529 == taep->ae_u.cterm.fg_color
8530 && aep->ae_u.cterm.bg_color
8531 == taep->ae_u.cterm.bg_color)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008532 ))
8533
8534 return i + ATTR_OFF;
8535 }
8536
Bram Moolenaar34cdc3e2005-05-18 22:24:46 +00008537 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008538 {
8539 /*
8540 * Running out of attribute entries! remove all attributes, and
8541 * compute new ones for all groups.
8542 * When called recursively, we are really out of numbers.
8543 */
8544 if (recursive)
8545 {
8546 EMSG(_("E424: Too many different highlighting attributes in use"));
8547 return 0;
8548 }
8549 recursive = TRUE;
8550
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008551 clear_hl_tables();
8552
Bram Moolenaar071d4272004-06-13 20:20:40 +00008553 must_redraw = CLEAR;
8554
8555 for (i = 0; i < highlight_ga.ga_len; ++i)
8556 set_hl_attr(i);
8557
8558 recursive = FALSE;
8559 }
8560
8561 /*
8562 * This is a new combination of colors and font, add an entry.
8563 */
8564 if (ga_grow(table, 1) == FAIL)
8565 return 0;
8566
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008567 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
8568 vim_memset(taep, 0, sizeof(attrentry_T));
8569 taep->ae_attr = aep->ae_attr;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008570#ifdef FEAT_GUI
8571 if (table == &gui_attr_table)
8572 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008573 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
8574 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
8575 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
8576 taep->ae_u.gui.font = aep->ae_u.gui.font;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008577# ifdef FEAT_XFONTSET
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008578 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008579# endif
8580 }
8581#endif
8582 if (table == &term_attr_table)
8583 {
8584 if (aep->ae_u.term.start == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008585 taep->ae_u.term.start = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008586 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008587 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008588 if (aep->ae_u.term.stop == NULL)
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008589 taep->ae_u.term.stop = NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008590 else
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008591 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008592 }
8593 else if (table == &cterm_attr_table)
8594 {
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008595 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
8596 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008597 }
8598 ++table->ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008599 return (table->ga_len - 1 + ATTR_OFF);
8600}
8601
Bram Moolenaarf461c8e2005-06-25 23:04:51 +00008602/*
8603 * Clear all highlight tables.
8604 */
8605 void
8606clear_hl_tables()
8607{
8608 int i;
8609 attrentry_T *taep;
8610
8611#ifdef FEAT_GUI
8612 ga_clear(&gui_attr_table);
8613#endif
8614 for (i = 0; i < term_attr_table.ga_len; ++i)
8615 {
8616 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
8617 vim_free(taep->ae_u.term.start);
8618 vim_free(taep->ae_u.term.stop);
8619 }
8620 ga_clear(&term_attr_table);
8621 ga_clear(&cterm_attr_table);
8622}
8623
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008624#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008625/*
Bram Moolenaar30abd282005-06-22 22:35:10 +00008626 * Combine special attributes (e.g., for spelling) with other attributes
8627 * (e.g., for syntax highlighting).
8628 * "prim_attr" overrules "char_attr".
Bram Moolenaar217ad922005-03-20 22:37:15 +00008629 * This creates a new group when required.
8630 * Since we expect there to be few spelling mistakes we don't cache the
8631 * result.
8632 * Return the resulting attributes.
8633 */
8634 int
Bram Moolenaar30abd282005-06-22 22:35:10 +00008635hl_combine_attr(char_attr, prim_attr)
Bram Moolenaar217ad922005-03-20 22:37:15 +00008636 int char_attr;
Bram Moolenaar30abd282005-06-22 22:35:10 +00008637 int prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008638{
8639 attrentry_T *char_aep = NULL;
8640 attrentry_T *spell_aep;
8641 attrentry_T new_en;
8642
8643 if (char_attr == 0)
Bram Moolenaar30abd282005-06-22 22:35:10 +00008644 return prim_attr;
8645 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
8646 return char_attr | prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008647#ifdef FEAT_GUI
8648 if (gui.in_use)
8649 {
8650 if (char_attr > HL_ALL)
8651 char_aep = syn_gui_attr2entry(char_attr);
8652 if (char_aep != NULL)
8653 new_en = *char_aep;
8654 else
8655 {
8656 vim_memset(&new_en, 0, sizeof(new_en));
Bram Moolenaard5cdbeb2005-10-10 20:59:28 +00008657 new_en.ae_u.gui.fg_color = INVALCOLOR;
8658 new_en.ae_u.gui.bg_color = INVALCOLOR;
8659 new_en.ae_u.gui.sp_color = INVALCOLOR;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008660 if (char_attr <= HL_ALL)
8661 new_en.ae_attr = char_attr;
8662 }
8663
Bram Moolenaar30abd282005-06-22 22:35:10 +00008664 if (prim_attr <= HL_ALL)
8665 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008666 else
8667 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008668 spell_aep = syn_gui_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008669 if (spell_aep != NULL)
8670 {
8671 new_en.ae_attr |= spell_aep->ae_attr;
8672 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
8673 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
8674 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
8675 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
8676 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
8677 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
8678 if (spell_aep->ae_u.gui.font != NOFONT)
8679 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
8680# ifdef FEAT_XFONTSET
8681 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
8682 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
8683# endif
8684 }
8685 }
8686 return get_attr_entry(&gui_attr_table, &new_en);
8687 }
8688#endif
8689
8690 if (t_colors > 1)
8691 {
8692 if (char_attr > HL_ALL)
8693 char_aep = syn_cterm_attr2entry(char_attr);
8694 if (char_aep != NULL)
8695 new_en = *char_aep;
8696 else
8697 {
8698 vim_memset(&new_en, 0, sizeof(new_en));
8699 if (char_attr <= HL_ALL)
8700 new_en.ae_attr = char_attr;
8701 }
8702
Bram Moolenaar30abd282005-06-22 22:35:10 +00008703 if (prim_attr <= HL_ALL)
8704 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008705 else
8706 {
Bram Moolenaar30abd282005-06-22 22:35:10 +00008707 spell_aep = syn_cterm_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008708 if (spell_aep != NULL)
8709 {
8710 new_en.ae_attr |= spell_aep->ae_attr;
8711 if (spell_aep->ae_u.cterm.fg_color > 0)
8712 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
8713 if (spell_aep->ae_u.cterm.bg_color > 0)
8714 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
8715 }
8716 }
8717 return get_attr_entry(&cterm_attr_table, &new_en);
8718 }
8719
8720 if (char_attr > HL_ALL)
8721 char_aep = syn_term_attr2entry(char_attr);
8722 if (char_aep != NULL)
8723 new_en = *char_aep;
8724 else
8725 {
8726 vim_memset(&new_en, 0, sizeof(new_en));
8727 if (char_attr <= HL_ALL)
8728 new_en.ae_attr = char_attr;
8729 }
8730
Bram Moolenaar30abd282005-06-22 22:35:10 +00008731 if (prim_attr <= HL_ALL)
8732 new_en.ae_attr |= prim_attr;
Bram Moolenaar217ad922005-03-20 22:37:15 +00008733 else
8734 {
Bram Moolenaar3b181812005-12-17 22:10:02 +00008735 spell_aep = syn_term_attr2entry(prim_attr);
Bram Moolenaar217ad922005-03-20 22:37:15 +00008736 if (spell_aep != NULL)
8737 {
8738 new_en.ae_attr |= spell_aep->ae_attr;
8739 if (spell_aep->ae_u.term.start != NULL)
8740 {
8741 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
8742 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
8743 }
8744 }
8745 }
8746 return get_attr_entry(&term_attr_table, &new_en);
8747}
8748#endif
8749
Bram Moolenaar071d4272004-06-13 20:20:40 +00008750#ifdef FEAT_GUI
8751
8752 attrentry_T *
8753syn_gui_attr2entry(attr)
8754 int attr;
8755{
8756 attr -= ATTR_OFF;
8757 if (attr >= gui_attr_table.ga_len) /* did ":syntax clear" */
8758 return NULL;
8759 return &(GUI_ATTR_ENTRY(attr));
8760}
Bram Moolenaar071d4272004-06-13 20:20:40 +00008761#endif /* FEAT_GUI */
8762
Bram Moolenaar1c8f93f2006-03-12 22:10:07 +00008763/*
8764 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
8765 * Only to be used when "attr" > HL_ALL.
8766 */
8767 int
8768syn_attr2attr(attr)
8769 int attr;
8770{
8771 attrentry_T *aep;
8772
8773#ifdef FEAT_GUI
8774 if (gui.in_use)
8775 aep = syn_gui_attr2entry(attr);
8776 else
8777#endif
8778 if (t_colors > 1)
8779 aep = syn_cterm_attr2entry(attr);
8780 else
8781 aep = syn_term_attr2entry(attr);
8782
8783 if (aep == NULL) /* highlighting not set */
8784 return 0;
8785 return aep->ae_attr;
8786}
8787
8788
Bram Moolenaar071d4272004-06-13 20:20:40 +00008789 attrentry_T *
8790syn_term_attr2entry(attr)
8791 int attr;
8792{
8793 attr -= ATTR_OFF;
8794 if (attr >= term_attr_table.ga_len) /* did ":syntax clear" */
8795 return NULL;
8796 return &(TERM_ATTR_ENTRY(attr));
8797}
8798
8799 attrentry_T *
8800syn_cterm_attr2entry(attr)
8801 int attr;
8802{
8803 attr -= ATTR_OFF;
8804 if (attr >= cterm_attr_table.ga_len) /* did ":syntax clear" */
8805 return NULL;
8806 return &(CTERM_ATTR_ENTRY(attr));
8807}
8808
8809#define LIST_ATTR 1
8810#define LIST_STRING 2
8811#define LIST_INT 3
8812
8813 static void
8814highlight_list_one(id)
8815 int id;
8816{
8817 struct hl_group *sgp;
8818 int didh = FALSE;
8819
8820 sgp = &HL_TABLE()[id - 1]; /* index is ID minus one */
8821
8822 didh = highlight_list_arg(id, didh, LIST_ATTR,
8823 sgp->sg_term, NULL, "term");
8824 didh = highlight_list_arg(id, didh, LIST_STRING,
8825 0, sgp->sg_start, "start");
8826 didh = highlight_list_arg(id, didh, LIST_STRING,
8827 0, sgp->sg_stop, "stop");
8828
8829 didh = highlight_list_arg(id, didh, LIST_ATTR,
8830 sgp->sg_cterm, NULL, "cterm");
8831 didh = highlight_list_arg(id, didh, LIST_INT,
8832 sgp->sg_cterm_fg, NULL, "ctermfg");
8833 didh = highlight_list_arg(id, didh, LIST_INT,
8834 sgp->sg_cterm_bg, NULL, "ctermbg");
8835
Bram Moolenaar61623362010-07-14 22:04:22 +02008836#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008837 didh = highlight_list_arg(id, didh, LIST_ATTR,
8838 sgp->sg_gui, NULL, "gui");
8839 didh = highlight_list_arg(id, didh, LIST_STRING,
8840 0, sgp->sg_gui_fg_name, "guifg");
8841 didh = highlight_list_arg(id, didh, LIST_STRING,
8842 0, sgp->sg_gui_bg_name, "guibg");
8843 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008844 0, sgp->sg_gui_sp_name, "guisp");
Bram Moolenaar61623362010-07-14 22:04:22 +02008845#endif
8846#ifdef FEAT_GUI
Bram Moolenaar75c50c42005-06-04 22:06:24 +00008847 didh = highlight_list_arg(id, didh, LIST_STRING,
Bram Moolenaar071d4272004-06-13 20:20:40 +00008848 0, sgp->sg_font_name, "font");
8849#endif
8850
Bram Moolenaar661b1822005-07-28 22:36:45 +00008851 if (sgp->sg_link && !got_int)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008852 {
8853 (void)syn_list_header(didh, 9999, id);
Bram Moolenaar661b1822005-07-28 22:36:45 +00008854 didh = TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008855 msg_puts_attr((char_u *)"links to", hl_attr(HLF_D));
8856 msg_putchar(' ');
8857 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
8858 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008859
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008860 if (!didh)
8861 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
Bram Moolenaar661b1822005-07-28 22:36:45 +00008862#ifdef FEAT_EVAL
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008863 if (p_verbose > 0)
Bram Moolenaar661b1822005-07-28 22:36:45 +00008864 last_set_msg(sgp->sg_scriptID);
8865#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00008866}
8867
8868 static int
8869highlight_list_arg(id, didh, type, iarg, sarg, name)
8870 int id;
8871 int didh;
8872 int type;
8873 int iarg;
8874 char_u *sarg;
8875 char *name;
8876{
8877 char_u buf[100];
8878 char_u *ts;
8879 int i;
8880
Bram Moolenaar661b1822005-07-28 22:36:45 +00008881 if (got_int)
8882 return FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008883 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
8884 {
8885 ts = buf;
8886 if (type == LIST_INT)
8887 sprintf((char *)buf, "%d", iarg - 1);
8888 else if (type == LIST_STRING)
8889 ts = sarg;
8890 else /* type == LIST_ATTR */
8891 {
8892 buf[0] = NUL;
8893 for (i = 0; hl_attr_table[i] != 0; ++i)
8894 {
8895 if (iarg & hl_attr_table[i])
8896 {
8897 if (buf[0] != NUL)
Bram Moolenaaref9d6aa2011-04-11 16:56:35 +02008898 vim_strcat(buf, (char_u *)",", 100);
8899 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
Bram Moolenaar071d4272004-06-13 20:20:40 +00008900 iarg &= ~hl_attr_table[i]; /* don't want "inverse" */
8901 }
8902 }
8903 }
8904
8905 (void)syn_list_header(didh,
8906 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
8907 didh = TRUE;
Bram Moolenaar661b1822005-07-28 22:36:45 +00008908 if (!got_int)
8909 {
Bram Moolenaar5e3cb7e2006-02-27 23:58:35 +00008910 if (*name != NUL)
8911 {
8912 MSG_PUTS_ATTR(name, hl_attr(HLF_D));
8913 MSG_PUTS_ATTR("=", hl_attr(HLF_D));
8914 }
Bram Moolenaar661b1822005-07-28 22:36:45 +00008915 msg_outtrans(ts);
8916 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00008917 }
8918 return didh;
8919}
8920
8921#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
8922/*
8923 * Return "1" if highlight group "id" has attribute "flag".
8924 * Return NULL otherwise.
8925 */
8926 char_u *
8927highlight_has_attr(id, flag, modec)
8928 int id;
8929 int flag;
8930 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8931{
8932 int attr;
8933
8934 if (id <= 0 || id > highlight_ga.ga_len)
8935 return NULL;
8936
Bram Moolenaar61623362010-07-14 22:04:22 +02008937#if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00008938 if (modec == 'g')
8939 attr = HL_TABLE()[id - 1].sg_gui;
8940 else
8941#endif
8942 if (modec == 'c')
8943 attr = HL_TABLE()[id - 1].sg_cterm;
8944 else
8945 attr = HL_TABLE()[id - 1].sg_term;
8946
8947 if (attr & flag)
8948 return (char_u *)"1";
8949 return NULL;
8950}
8951#endif
8952
8953#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
8954/*
8955 * Return color name of highlight group "id".
8956 */
8957 char_u *
8958highlight_color(id, what, modec)
8959 int id;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008960 char_u *what; /* "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#" */
Bram Moolenaar071d4272004-06-13 20:20:40 +00008961 int modec; /* 'g' for GUI, 'c' for cterm, 't' for term */
8962{
8963 static char_u name[20];
8964 int n;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008965 int fg = FALSE;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008966 int sp = FALSE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008967 int font = FALSE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008968
8969 if (id <= 0 || id > highlight_ga.ga_len)
8970 return NULL;
8971
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008972 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008973 fg = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008974 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
Bram Moolenaarcc448b32010-07-14 16:52:17 +02008975 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008976 font = TRUE;
8977 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008978 sp = TRUE;
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008979 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
8980 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008981 if (modec == 'g')
8982 {
Bram Moolenaar61623362010-07-14 22:04:22 +02008983# ifdef FEAT_GUI
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008984 /* return font name */
8985 if (font)
8986 return HL_TABLE()[id - 1].sg_font_name;
8987
Bram Moolenaar071d4272004-06-13 20:20:40 +00008988 /* return #RRGGBB form (only possible when GUI is running) */
Bram Moolenaar12682fd2010-03-10 13:43:49 +01008989 if (gui.in_use && what[2] == '#')
Bram Moolenaar071d4272004-06-13 20:20:40 +00008990 {
8991 guicolor_T color;
8992 long_u rgb;
8993 static char_u buf[10];
8994
8995 if (fg)
8996 color = HL_TABLE()[id - 1].sg_gui_fg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00008997 else if (sp)
8998 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00008999 else
9000 color = HL_TABLE()[id - 1].sg_gui_bg;
9001 if (color == INVALCOLOR)
9002 return NULL;
9003 rgb = gui_mch_get_rgb(color);
9004 sprintf((char *)buf, "#%02x%02x%02x",
9005 (unsigned)(rgb >> 16),
9006 (unsigned)(rgb >> 8) & 255,
9007 (unsigned)rgb & 255);
9008 return buf;
9009 }
Bram Moolenaar61623362010-07-14 22:04:22 +02009010#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009011 if (fg)
9012 return (HL_TABLE()[id - 1].sg_gui_fg_name);
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009013 if (sp)
9014 return (HL_TABLE()[id - 1].sg_gui_sp_name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009015 return (HL_TABLE()[id - 1].sg_gui_bg_name);
9016 }
Bram Moolenaar12682fd2010-03-10 13:43:49 +01009017 if (font || sp)
9018 return NULL;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009019 if (modec == 'c')
9020 {
9021 if (fg)
9022 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
9023 else
9024 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
9025 sprintf((char *)name, "%d", n);
9026 return name;
9027 }
9028 /* term doesn't have color */
9029 return NULL;
9030}
9031#endif
9032
9033#if (defined(FEAT_SYN_HL) && defined(FEAT_GUI) && defined(FEAT_PRINTER)) \
9034 || defined(PROTO)
9035/*
9036 * Return color name of highlight group "id" as RGB value.
9037 */
9038 long_u
9039highlight_gui_color_rgb(id, fg)
9040 int id;
9041 int fg; /* TRUE = fg, FALSE = bg */
9042{
9043 guicolor_T color;
9044
9045 if (id <= 0 || id > highlight_ga.ga_len)
9046 return 0L;
9047
9048 if (fg)
9049 color = HL_TABLE()[id - 1].sg_gui_fg;
9050 else
9051 color = HL_TABLE()[id - 1].sg_gui_bg;
9052
9053 if (color == INVALCOLOR)
9054 return 0L;
9055
9056 return gui_mch_get_rgb(color);
9057}
9058#endif
9059
9060/*
9061 * Output the syntax list header.
9062 * Return TRUE when started a new line.
9063 */
9064 static int
9065syn_list_header(did_header, outlen, id)
9066 int did_header; /* did header already */
9067 int outlen; /* length of string that comes */
9068 int id; /* highlight group id */
9069{
9070 int endcol = 19;
9071 int newline = TRUE;
9072
9073 if (!did_header)
9074 {
9075 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009076 if (got_int)
9077 return TRUE;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009078 msg_outtrans(HL_TABLE()[id - 1].sg_name);
9079 endcol = 15;
9080 }
9081 else if (msg_col + outlen + 1 >= Columns)
Bram Moolenaar661b1822005-07-28 22:36:45 +00009082 {
Bram Moolenaar071d4272004-06-13 20:20:40 +00009083 msg_putchar('\n');
Bram Moolenaar661b1822005-07-28 22:36:45 +00009084 if (got_int)
9085 return TRUE;
9086 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009087 else
9088 {
9089 if (msg_col >= endcol) /* wrap around is like starting a new line */
9090 newline = FALSE;
9091 }
9092
9093 if (msg_col >= endcol) /* output at least one space */
9094 endcol = msg_col + 1;
9095 if (Columns <= endcol) /* avoid hang for tiny window */
9096 endcol = Columns - 1;
9097
9098 msg_advance(endcol);
9099
9100 /* Show "xxx" with the attributes. */
9101 if (!did_header)
9102 {
9103 msg_puts_attr((char_u *)"xxx", syn_id2attr(id));
9104 msg_putchar(' ');
9105 }
9106
9107 return newline;
9108}
9109
9110/*
9111 * Set the attribute numbers for a highlight group.
9112 * Called after one of the attributes has changed.
9113 */
9114 static void
9115set_hl_attr(idx)
9116 int idx; /* index in array */
9117{
9118 attrentry_T at_en;
9119 struct hl_group *sgp = HL_TABLE() + idx;
9120
9121 /* The "Normal" group doesn't need an attribute number */
9122 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
9123 return;
9124
9125#ifdef FEAT_GUI
9126 /*
9127 * For the GUI mode: If there are other than "normal" highlighting
9128 * attributes, need to allocate an attr number.
9129 */
9130 if (sgp->sg_gui_fg == INVALCOLOR
9131 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009132 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaar071d4272004-06-13 20:20:40 +00009133 && sgp->sg_font == NOFONT
9134# ifdef FEAT_XFONTSET
9135 && sgp->sg_fontset == NOFONTSET
9136# endif
9137 )
9138 {
9139 sgp->sg_gui_attr = sgp->sg_gui;
9140 }
9141 else
9142 {
9143 at_en.ae_attr = sgp->sg_gui;
9144 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
9145 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009146 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009147 at_en.ae_u.gui.font = sgp->sg_font;
9148# ifdef FEAT_XFONTSET
9149 at_en.ae_u.gui.fontset = sgp->sg_fontset;
9150# endif
9151 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
9152 }
9153#endif
9154 /*
9155 * For the term mode: If there are other than "normal" highlighting
9156 * attributes, need to allocate an attr number.
9157 */
9158 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
9159 sgp->sg_term_attr = sgp->sg_term;
9160 else
9161 {
9162 at_en.ae_attr = sgp->sg_term;
9163 at_en.ae_u.term.start = sgp->sg_start;
9164 at_en.ae_u.term.stop = sgp->sg_stop;
9165 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
9166 }
9167
9168 /*
9169 * For the color term mode: If there are other than "normal"
9170 * highlighting attributes, need to allocate an attr number.
9171 */
9172 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0)
9173 sgp->sg_cterm_attr = sgp->sg_cterm;
9174 else
9175 {
9176 at_en.ae_attr = sgp->sg_cterm;
9177 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
9178 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
9179 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
9180 }
9181}
9182
9183/*
9184 * Lookup a highlight group name and return it's ID.
9185 * If it is not found, 0 is returned.
9186 */
9187 int
9188syn_name2id(name)
9189 char_u *name;
9190{
9191 int i;
9192 char_u name_u[200];
9193
9194 /* Avoid using stricmp() too much, it's slow on some systems */
9195 /* Avoid alloc()/free(), these are slow too. ID names over 200 chars
9196 * don't deserve to be found! */
Bram Moolenaarce0842a2005-07-18 21:58:11 +00009197 vim_strncpy(name_u, name, 199);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009198 vim_strup(name_u);
9199 for (i = highlight_ga.ga_len; --i >= 0; )
9200 if (HL_TABLE()[i].sg_name_u != NULL
9201 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
9202 break;
9203 return i + 1;
9204}
9205
9206#if defined(FEAT_EVAL) || defined(PROTO)
9207/*
9208 * Return TRUE if highlight group "name" exists.
9209 */
9210 int
9211highlight_exists(name)
9212 char_u *name;
9213{
9214 return (syn_name2id(name) > 0);
9215}
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009216
9217# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
9218/*
9219 * Return the name of highlight group "id".
9220 * When not a valid ID return an empty string.
9221 */
9222 char_u *
9223syn_id2name(id)
9224 int id;
9225{
Bram Moolenaar6ee10162007-07-26 20:58:42 +00009226 if (id <= 0 || id > highlight_ga.ga_len)
Bram Moolenaar910f66f2006-04-05 20:41:53 +00009227 return (char_u *)"";
9228 return HL_TABLE()[id - 1].sg_name;
9229}
9230# endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009231#endif
9232
9233/*
9234 * Like syn_name2id(), but take a pointer + length argument.
9235 */
9236 int
9237syn_namen2id(linep, len)
9238 char_u *linep;
9239 int len;
9240{
9241 char_u *name;
9242 int id = 0;
9243
9244 name = vim_strnsave(linep, len);
9245 if (name != NULL)
9246 {
9247 id = syn_name2id(name);
9248 vim_free(name);
9249 }
9250 return id;
9251}
9252
9253/*
9254 * Find highlight group name in the table and return it's ID.
9255 * The argument is a pointer to the name and the length of the name.
9256 * If it doesn't exist yet, a new entry is created.
9257 * Return 0 for failure.
9258 */
9259 int
9260syn_check_group(pp, len)
9261 char_u *pp;
9262 int len;
9263{
9264 int id;
9265 char_u *name;
9266
9267 name = vim_strnsave(pp, len);
9268 if (name == NULL)
9269 return 0;
9270
9271 id = syn_name2id(name);
9272 if (id == 0) /* doesn't exist yet */
9273 id = syn_add_group(name);
9274 else
9275 vim_free(name);
9276 return id;
9277}
9278
9279/*
9280 * Add new highlight group and return it's ID.
9281 * "name" must be an allocated string, it will be consumed.
9282 * Return 0 for failure.
9283 */
9284 static int
9285syn_add_group(name)
9286 char_u *name;
9287{
9288 char_u *p;
9289
9290 /* Check that the name is ASCII letters, digits and underscore. */
9291 for (p = name; *p != NUL; ++p)
9292 {
9293 if (!vim_isprintc(*p))
9294 {
9295 EMSG(_("E669: Unprintable character in group name"));
Bram Moolenaarf5b63862009-12-16 17:13:44 +00009296 vim_free(name);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009297 return 0;
9298 }
9299 else if (!ASCII_ISALNUM(*p) && *p != '_')
9300 {
9301 /* This is an error, but since there previously was no check only
9302 * give a warning. */
Bram Moolenaar2df6dcc2004-07-12 15:53:54 +00009303 msg_source(hl_attr(HLF_W));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009304 MSG(_("W18: Invalid character in group name"));
9305 break;
9306 }
9307 }
9308
9309 /*
9310 * First call for this growarray: init growing array.
9311 */
9312 if (highlight_ga.ga_data == NULL)
9313 {
9314 highlight_ga.ga_itemsize = sizeof(struct hl_group);
9315 highlight_ga.ga_growsize = 10;
9316 }
9317
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009318 if (highlight_ga.ga_len >= MAX_HL_ID)
Bram Moolenaar42431a72011-04-01 14:44:59 +02009319 {
Bram Moolenaar2dfb3862011-04-02 15:12:50 +02009320 EMSG(_("E849: Too many highlight and syntax groups"));
Bram Moolenaar42431a72011-04-01 14:44:59 +02009321 vim_free(name);
9322 return 0;
9323 }
9324
Bram Moolenaar071d4272004-06-13 20:20:40 +00009325 /*
9326 * Make room for at least one other syntax_highlight entry.
9327 */
9328 if (ga_grow(&highlight_ga, 1) == FAIL)
9329 {
9330 vim_free(name);
9331 return 0;
9332 }
9333
9334 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(struct hl_group));
9335 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
9336 HL_TABLE()[highlight_ga.ga_len].sg_name_u = vim_strsave_up(name);
9337#ifdef FEAT_GUI
9338 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
9339 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009340 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009341#endif
9342 ++highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009343
9344 return highlight_ga.ga_len; /* ID is index plus one */
9345}
9346
9347/*
9348 * When, just after calling syn_add_group(), an error is discovered, this
9349 * function deletes the new name.
9350 */
9351 static void
9352syn_unadd_group()
9353{
9354 --highlight_ga.ga_len;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009355 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
9356 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
9357}
9358
9359/*
9360 * Translate a group ID to highlight attributes.
9361 */
9362 int
9363syn_id2attr(hl_id)
9364 int hl_id;
9365{
9366 int attr;
9367 struct hl_group *sgp;
9368
9369 hl_id = syn_get_final_id(hl_id);
9370 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9371
9372#ifdef FEAT_GUI
9373 /*
9374 * Only use GUI attr when the GUI is being used.
9375 */
9376 if (gui.in_use)
9377 attr = sgp->sg_gui_attr;
9378 else
9379#endif
9380 if (t_colors > 1)
9381 attr = sgp->sg_cterm_attr;
9382 else
9383 attr = sgp->sg_term_attr;
9384
9385 return attr;
9386}
9387
9388#ifdef FEAT_GUI
9389/*
9390 * Get the GUI colors and attributes for a group ID.
9391 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
9392 */
9393 int
9394syn_id2colors(hl_id, fgp, bgp)
9395 int hl_id;
9396 guicolor_T *fgp;
9397 guicolor_T *bgp;
9398{
9399 struct hl_group *sgp;
9400
9401 hl_id = syn_get_final_id(hl_id);
9402 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9403
9404 *fgp = sgp->sg_gui_fg;
9405 *bgp = sgp->sg_gui_bg;
9406 return sgp->sg_gui;
9407}
9408#endif
9409
9410/*
9411 * Translate a group ID to the final group ID (following links).
9412 */
9413 int
9414syn_get_final_id(hl_id)
9415 int hl_id;
9416{
9417 int count;
9418 struct hl_group *sgp;
9419
9420 if (hl_id > highlight_ga.ga_len || hl_id < 1)
9421 return 0; /* Can be called from eval!! */
9422
9423 /*
9424 * Follow links until there is no more.
9425 * Look out for loops! Break after 100 links.
9426 */
9427 for (count = 100; --count >= 0; )
9428 {
9429 sgp = &HL_TABLE()[hl_id - 1]; /* index is ID minus one */
9430 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
9431 break;
9432 hl_id = sgp->sg_link;
9433 }
9434
9435 return hl_id;
9436}
9437
9438#ifdef FEAT_GUI
9439/*
9440 * Call this function just after the GUI has started.
9441 * It finds the font and color handles for the highlighting groups.
9442 */
9443 void
9444highlight_gui_started()
9445{
9446 int idx;
9447
9448 /* First get the colors from the "Normal" and "Menu" group, if set */
9449 set_normal_colors();
9450
9451 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9452 gui_do_one_color(idx, FALSE, FALSE);
9453
9454 highlight_changed();
9455}
9456
9457 static void
9458gui_do_one_color(idx, do_menu, do_tooltip)
9459 int idx;
9460 int do_menu; /* TRUE: might set the menu font */
9461 int do_tooltip; /* TRUE: might set the tooltip font */
9462{
9463 int didit = FALSE;
9464
9465 if (HL_TABLE()[idx].sg_font_name != NULL)
9466 {
9467 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
Bram Moolenaarc4b98fb2012-10-21 01:40:30 +02009468 do_tooltip, TRUE);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009469 didit = TRUE;
9470 }
9471 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
9472 {
9473 HL_TABLE()[idx].sg_gui_fg =
9474 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
9475 didit = TRUE;
9476 }
9477 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
9478 {
9479 HL_TABLE()[idx].sg_gui_bg =
9480 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
9481 didit = TRUE;
9482 }
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009483 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
9484 {
9485 HL_TABLE()[idx].sg_gui_sp =
9486 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
9487 didit = TRUE;
9488 }
Bram Moolenaar071d4272004-06-13 20:20:40 +00009489 if (didit) /* need to get a new attr number */
9490 set_hl_attr(idx);
9491}
9492
9493#endif
9494
9495/*
9496 * Translate the 'highlight' option into attributes in highlight_attr[] and
9497 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
9498 * corresponding highlights to use on top of HLF_SNC is computed.
9499 * Called only when the 'highlight' option has been changed and upon first
9500 * screen redraw after any :highlight command.
9501 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
9502 */
9503 int
9504highlight_changed()
9505{
9506 int hlf;
9507 int i;
9508 char_u *p;
9509 int attr;
9510 char_u *end;
9511 int id;
9512#ifdef USER_HIGHLIGHT
9513 char_u userhl[10];
9514# ifdef FEAT_STL_OPT
9515 int id_SNC = -1;
9516 int id_S = -1;
9517 int hlcnt;
9518# endif
9519#endif
9520 static int hl_flags[HLF_COUNT] = HL_FLAGS;
9521
9522 need_highlight_changed = FALSE;
9523
9524 /*
9525 * Clear all attributes.
9526 */
9527 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9528 highlight_attr[hlf] = 0;
9529
9530 /*
9531 * First set all attributes to their default value.
9532 * Then use the attributes from the 'highlight' option.
9533 */
9534 for (i = 0; i < 2; ++i)
9535 {
9536 if (i)
9537 p = p_hl;
9538 else
9539 p = get_highlight_default();
9540 if (p == NULL) /* just in case */
9541 continue;
9542
9543 while (*p)
9544 {
9545 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
9546 if (hl_flags[hlf] == *p)
9547 break;
9548 ++p;
9549 if (hlf == (int)HLF_COUNT || *p == NUL)
9550 return FAIL;
9551
9552 /*
9553 * Allow several hl_flags to be combined, like "bu" for
9554 * bold-underlined.
9555 */
9556 attr = 0;
9557 for ( ; *p && *p != ','; ++p) /* parse upto comma */
9558 {
9559 if (vim_iswhite(*p)) /* ignore white space */
9560 continue;
9561
9562 if (attr > HL_ALL) /* Combination with ':' is not allowed. */
9563 return FAIL;
9564
9565 switch (*p)
9566 {
9567 case 'b': attr |= HL_BOLD;
9568 break;
9569 case 'i': attr |= HL_ITALIC;
9570 break;
9571 case '-':
9572 case 'n': /* no highlighting */
9573 break;
9574 case 'r': attr |= HL_INVERSE;
9575 break;
9576 case 's': attr |= HL_STANDOUT;
9577 break;
9578 case 'u': attr |= HL_UNDERLINE;
9579 break;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009580 case 'c': attr |= HL_UNDERCURL;
9581 break;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009582 case ':': ++p; /* highlight group name */
9583 if (attr || *p == NUL) /* no combinations */
9584 return FAIL;
9585 end = vim_strchr(p, ',');
9586 if (end == NULL)
9587 end = p + STRLEN(p);
9588 id = syn_check_group(p, (int)(end - p));
9589 if (id == 0)
9590 return FAIL;
9591 attr = syn_id2attr(id);
9592 p = end - 1;
9593#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
9594 if (hlf == (int)HLF_SNC)
9595 id_SNC = syn_get_final_id(id);
9596 else if (hlf == (int)HLF_S)
9597 id_S = syn_get_final_id(id);
9598#endif
9599 break;
9600 default: return FAIL;
9601 }
9602 }
9603 highlight_attr[hlf] = attr;
9604
9605 p = skip_to_option_part(p); /* skip comma and spaces */
9606 }
9607 }
9608
9609#ifdef USER_HIGHLIGHT
9610 /* Setup the user highlights
9611 *
9612 * Temporarily utilize 10 more hl entries. Have to be in there
9613 * simultaneously in case of table overflows in get_attr_entry()
9614 */
9615# ifdef FEAT_STL_OPT
9616 if (ga_grow(&highlight_ga, 10) == FAIL)
9617 return FAIL;
9618 hlcnt = highlight_ga.ga_len;
9619 if (id_S == 0)
9620 { /* Make sure id_S is always valid to simplify code below */
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009621 vim_memset(&HL_TABLE()[hlcnt + 9], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009622 HL_TABLE()[hlcnt + 9].sg_term = highlight_attr[HLF_S];
9623 id_S = hlcnt + 10;
9624 }
9625# endif
9626 for (i = 0; i < 9; i++)
9627 {
9628 sprintf((char *)userhl, "User%d", i + 1);
9629 id = syn_name2id(userhl);
9630 if (id == 0)
9631 {
9632 highlight_user[i] = 0;
9633# ifdef FEAT_STL_OPT
9634 highlight_stlnc[i] = 0;
9635# endif
9636 }
9637 else
9638 {
9639# ifdef FEAT_STL_OPT
9640 struct hl_group *hlt = HL_TABLE();
9641# endif
9642
9643 highlight_user[i] = syn_id2attr(id);
9644# ifdef FEAT_STL_OPT
9645 if (id_SNC == 0)
9646 {
Bram Moolenaar7db5fc82010-05-24 11:59:29 +02009647 vim_memset(&hlt[hlcnt + i], 0, sizeof(struct hl_group));
Bram Moolenaar071d4272004-06-13 20:20:40 +00009648 hlt[hlcnt + i].sg_term = highlight_attr[HLF_SNC];
9649 hlt[hlcnt + i].sg_cterm = highlight_attr[HLF_SNC];
Bram Moolenaar61623362010-07-14 22:04:22 +02009650# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009651 hlt[hlcnt + i].sg_gui = highlight_attr[HLF_SNC];
9652# endif
9653 }
9654 else
9655 mch_memmove(&hlt[hlcnt + i],
9656 &hlt[id_SNC - 1],
9657 sizeof(struct hl_group));
9658 hlt[hlcnt + i].sg_link = 0;
9659
9660 /* Apply difference between UserX and HLF_S to HLF_SNC */
9661 hlt[hlcnt + i].sg_term ^=
9662 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
9663 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
9664 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
9665 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
9666 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
9667 hlt[hlcnt + i].sg_cterm ^=
9668 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
9669 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
9670 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
9671 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
9672 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
Bram Moolenaar61623362010-07-14 22:04:22 +02009673# if defined(FEAT_GUI) || defined(FEAT_EVAL)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009674 hlt[hlcnt + i].sg_gui ^=
9675 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
Bram Moolenaar61623362010-07-14 22:04:22 +02009676# endif
9677# ifdef FEAT_GUI
Bram Moolenaar071d4272004-06-13 20:20:40 +00009678 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
9679 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
9680 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
9681 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
Bram Moolenaare2cc9702005-03-15 22:43:58 +00009682 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
9683 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009684 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
9685 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
9686# ifdef FEAT_XFONTSET
9687 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
9688 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
9689# endif
9690# endif
9691 highlight_ga.ga_len = hlcnt + i + 1;
9692 set_hl_attr(hlcnt + i); /* At long last we can apply */
9693 highlight_stlnc[i] = syn_id2attr(hlcnt + i + 1);
9694# endif
9695 }
9696 }
9697# ifdef FEAT_STL_OPT
9698 highlight_ga.ga_len = hlcnt;
9699# endif
9700
9701#endif /* USER_HIGHLIGHT */
9702
9703 return OK;
9704}
9705
Bram Moolenaar4f688582007-07-24 12:34:30 +00009706#if defined(FEAT_CMDL_COMPL) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009707
9708static void highlight_list __ARGS((void));
9709static void highlight_list_two __ARGS((int cnt, int attr));
9710
9711/*
9712 * Handle command line completion for :highlight command.
9713 */
9714 void
9715set_context_in_highlight_cmd(xp, arg)
9716 expand_T *xp;
9717 char_u *arg;
9718{
9719 char_u *p;
9720
9721 /* Default: expand group names */
9722 xp->xp_context = EXPAND_HIGHLIGHT;
9723 xp->xp_pattern = arg;
Bram Moolenaar4f688582007-07-24 12:34:30 +00009724 include_link = 2;
9725 include_default = 1;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009726
9727 /* (part of) subcommand already typed */
9728 if (*arg != NUL)
9729 {
9730 p = skiptowhite(arg);
9731 if (*p != NUL) /* past "default" or group name */
9732 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009733 include_default = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009734 if (STRNCMP("default", arg, p - arg) == 0)
9735 {
9736 arg = skipwhite(p);
9737 xp->xp_pattern = arg;
9738 p = skiptowhite(arg);
9739 }
9740 if (*p != NUL) /* past group name */
9741 {
Bram Moolenaar4f688582007-07-24 12:34:30 +00009742 include_link = 0;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009743 if (arg[1] == 'i' && arg[0] == 'N')
9744 highlight_list();
9745 if (STRNCMP("link", arg, p - arg) == 0
9746 || STRNCMP("clear", arg, p - arg) == 0)
9747 {
9748 xp->xp_pattern = skipwhite(p);
9749 p = skiptowhite(xp->xp_pattern);
9750 if (*p != NUL) /* past first group name */
9751 {
9752 xp->xp_pattern = skipwhite(p);
9753 p = skiptowhite(xp->xp_pattern);
9754 }
9755 }
9756 if (*p != NUL) /* past group name(s) */
9757 xp->xp_context = EXPAND_NOTHING;
9758 }
9759 }
9760 }
9761}
9762
9763/*
9764 * List highlighting matches in a nice way.
9765 */
9766 static void
9767highlight_list()
9768{
9769 int i;
9770
9771 for (i = 10; --i >= 0; )
9772 highlight_list_two(i, hl_attr(HLF_D));
9773 for (i = 40; --i >= 0; )
9774 highlight_list_two(99, 0);
9775}
9776
9777 static void
9778highlight_list_two(cnt, attr)
9779 int cnt;
9780 int attr;
9781{
Bram Moolenaar112f3182012-06-01 13:18:53 +02009782 msg_puts_attr((char_u *)&("N \bI \b! \b"[cnt / 11]), attr);
Bram Moolenaar071d4272004-06-13 20:20:40 +00009783 msg_clr_eos();
9784 out_flush();
9785 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
9786}
9787
9788#endif /* FEAT_CMDL_COMPL */
9789
9790#if defined(FEAT_CMDL_COMPL) || (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) \
9791 || defined(FEAT_SIGNS) || defined(PROTO)
9792/*
9793 * Function given to ExpandGeneric() to obtain the list of group names.
9794 * Also used for synIDattr() function.
9795 */
Bram Moolenaar071d4272004-06-13 20:20:40 +00009796 char_u *
9797get_highlight_name(xp, idx)
Bram Moolenaar2c4278f2009-05-17 11:33:22 +00009798 expand_T *xp UNUSED;
Bram Moolenaar071d4272004-06-13 20:20:40 +00009799 int idx;
9800{
Bram Moolenaar071d4272004-06-13 20:20:40 +00009801#ifdef FEAT_CMDL_COMPL
Bram Moolenaar4f688582007-07-24 12:34:30 +00009802 if (idx == highlight_ga.ga_len && include_none != 0)
9803 return (char_u *)"none";
9804 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009805 return (char_u *)"default";
Bram Moolenaar4f688582007-07-24 12:34:30 +00009806 if (idx == highlight_ga.ga_len + include_none + include_default
9807 && include_link != 0)
9808 return (char_u *)"link";
9809 if (idx == highlight_ga.ga_len + include_none + include_default + 1
9810 && include_link != 0)
9811 return (char_u *)"clear";
9812#endif
Bram Moolenaar071d4272004-06-13 20:20:40 +00009813 if (idx < 0 || idx >= highlight_ga.ga_len)
9814 return NULL;
9815 return HL_TABLE()[idx].sg_name;
9816}
9817#endif
9818
Bram Moolenaar4f688582007-07-24 12:34:30 +00009819#if defined(FEAT_GUI) || defined(PROTO)
Bram Moolenaar071d4272004-06-13 20:20:40 +00009820/*
9821 * Free all the highlight group fonts.
9822 * Used when quitting for systems which need it.
9823 */
9824 void
9825free_highlight_fonts()
9826{
9827 int idx;
9828
9829 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
9830 {
9831 gui_mch_free_font(HL_TABLE()[idx].sg_font);
9832 HL_TABLE()[idx].sg_font = NOFONT;
9833# ifdef FEAT_XFONTSET
9834 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
9835 HL_TABLE()[idx].sg_fontset = NOFONTSET;
9836# endif
9837 }
9838
9839 gui_mch_free_font(gui.norm_font);
9840# ifdef FEAT_XFONTSET
9841 gui_mch_free_fontset(gui.fontset);
9842# endif
Bram Moolenaar0eda7ac2010-06-26 05:38:18 +02009843# ifndef FEAT_GUI_GTK
Bram Moolenaar071d4272004-06-13 20:20:40 +00009844 gui_mch_free_font(gui.bold_font);
9845 gui_mch_free_font(gui.ital_font);
9846 gui_mch_free_font(gui.boldital_font);
9847# endif
9848}
9849#endif
9850
9851/**************************************
9852 * End of Highlighting stuff *
9853 **************************************/