blob: 9da87f21528385534d834ecf2b3abb353fedafe8 [file] [log] [blame]
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001/* vi:set ts=8 sts=4 sw=4 noet:
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/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +020011 * Highlighting stuff.
12 * Includes highlighting matches.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020013 */
14
15#include "vim.h"
16
17#define SG_TERM 1 // term has been set
18#define SG_CTERM 2 // cterm has been set
19#define SG_GUI 4 // gui has been set
20#define SG_LINK 8 // link has been set
21
22/*
23 * The "term", "cterm" and "gui" arguments can be any combination of the
24 * following names, separated by commas (but no spaces!).
25 */
26static char *(hl_name_table[]) =
27 {"bold", "standout", "underline", "undercurl",
28 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
29static int hl_attr_table[] =
30 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
31#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
32
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020033/*
34 * Structure that stores information about a highlight group.
35 * The ID of a highlight group is also called group ID. It is the index in
36 * the highlight_ga array PLUS ONE.
37 */
38typedef struct
39{
40 char_u *sg_name; // highlight group name
41 char_u *sg_name_u; // uppercase of sg_name
42 int sg_cleared; // "hi clear" was used
43// for normal terminals
44 int sg_term; // "term=" highlighting attributes
45 char_u *sg_start; // terminal string for start highl
46 char_u *sg_stop; // terminal string for stop highl
47 int sg_term_attr; // Screen attr for term mode
48// for color terminals
49 int sg_cterm; // "cterm=" highlighting attr
50 int sg_cterm_bold; // bold attr was set for light color
51 int sg_cterm_fg; // terminal fg color number + 1
52 int sg_cterm_bg; // terminal bg color number + 1
53 int sg_cterm_attr; // Screen attr for color term mode
54// for when using the GUI
55#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
56 guicolor_T sg_gui_fg; // GUI foreground color handle
57 guicolor_T sg_gui_bg; // GUI background color handle
58#endif
59#ifdef FEAT_GUI
60 guicolor_T sg_gui_sp; // GUI special color handle
61 GuiFont sg_font; // GUI font handle
62#ifdef FEAT_XFONTSET
63 GuiFontset sg_fontset; // GUI fontset handle
64#endif
65 char_u *sg_font_name; // GUI font or fontset name
66 int sg_gui_attr; // Screen attr for GUI mode
67#endif
68#if defined(FEAT_GUI) || defined(FEAT_EVAL)
69// Store the sp color name for the GUI or synIDattr()
70 int sg_gui; // "gui=" highlighting attributes
71 char_u *sg_gui_fg_name;// GUI foreground color name
72 char_u *sg_gui_bg_name;// GUI background color name
73 char_u *sg_gui_sp_name;// GUI special color name
74#endif
75 int sg_link; // link to this highlight group ID
76 int sg_set; // combination of SG_* flags
77#ifdef FEAT_EVAL
78 sctx_T sg_script_ctx; // script in which the group was last set
79#endif
80} hl_group_T;
81
82// highlight groups for 'highlight' option
83static garray_T highlight_ga;
84#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
85
86/*
87 * An attribute number is the index in attr_table plus ATTR_OFF.
88 */
89#define ATTR_OFF (HL_ALL + 1)
90
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020091static void syn_unadd_group(void);
92static void set_hl_attr(int idx);
93static void highlight_list_one(int id);
94static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
95static int syn_add_group(char_u *name);
96static int hl_has_settings(int idx, int check_link);
97static void highlight_clear(int idx);
98
99#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
100static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
101#endif
102#ifdef FEAT_GUI
103static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
104static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
105#endif
106
107/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200108 * The default highlight groups. These are compiled-in for fast startup and
109 * they still work when the runtime files can't be found.
110 * When making changes here, also change runtime/colors/default.vim!
111 * The #ifdefs are needed to reduce the amount of static data. Helps to make
112 * the 16 bit DOS (museum) version compile.
113 */
114#if defined(FEAT_GUI) || defined(FEAT_EVAL)
115# define CENT(a, b) b
116#else
117# define CENT(a, b) a
118#endif
119static char *(highlight_init_both[]) = {
120 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
121 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
122 CENT("IncSearch term=reverse cterm=reverse",
123 "IncSearch term=reverse cterm=reverse gui=reverse"),
124 CENT("ModeMsg term=bold cterm=bold",
125 "ModeMsg term=bold cterm=bold gui=bold"),
126 CENT("NonText term=bold ctermfg=Blue",
127 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
128 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
129 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
130 CENT("StatusLineNC term=reverse cterm=reverse",
131 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
132 "default link EndOfBuffer NonText",
133 CENT("VertSplit term=reverse cterm=reverse",
134 "VertSplit term=reverse cterm=reverse gui=reverse"),
135#ifdef FEAT_CLIPBOARD
136 CENT("VisualNOS term=underline,bold cterm=underline,bold",
137 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
138#endif
139#ifdef FEAT_DIFF
140 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
141 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
142#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200143 CENT("PmenuSbar ctermbg=Grey",
144 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200145 CENT("TabLineSel term=bold cterm=bold",
146 "TabLineSel term=bold cterm=bold gui=bold"),
147 CENT("TabLineFill term=reverse cterm=reverse",
148 "TabLineFill term=reverse cterm=reverse gui=reverse"),
149#ifdef FEAT_GUI
150 "Cursor guibg=fg guifg=bg",
151 "lCursor guibg=fg guifg=bg", // should be different, but what?
152#endif
153 "default link QuickFixLine Search",
154 CENT("Normal cterm=NONE", "Normal gui=NONE"),
155 NULL
156};
157
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200158// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200159static char *(highlight_init_light[]) = {
160 CENT("Directory term=bold ctermfg=DarkBlue",
161 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
162 CENT("LineNr term=underline ctermfg=Brown",
163 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200164 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
165 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200166 CENT("MoreMsg term=bold ctermfg=DarkGreen",
167 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
168 CENT("Question term=standout ctermfg=DarkGreen",
169 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
170 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
171 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
172#ifdef FEAT_SPELL
173 CENT("SpellBad term=reverse ctermbg=LightRed",
174 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
175 CENT("SpellCap term=reverse ctermbg=LightBlue",
176 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
177 CENT("SpellRare term=reverse ctermbg=LightMagenta",
178 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
179 CENT("SpellLocal term=underline ctermbg=Cyan",
180 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
181#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200182 CENT("PmenuThumb ctermbg=Black",
183 "PmenuThumb ctermbg=Black guibg=Black"),
184 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
185 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
186 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
187 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200188 CENT("SpecialKey term=bold ctermfg=DarkBlue",
189 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
190 CENT("Title term=bold ctermfg=DarkMagenta",
191 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
192 CENT("WarningMsg term=standout ctermfg=DarkRed",
193 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
194#ifdef FEAT_WILDMENU
195 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
196 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
197#endif
198#ifdef FEAT_FOLDING
199 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
200 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
201 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
202 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
203#endif
204#ifdef FEAT_SIGNS
205 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
206 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
207#endif
208 CENT("Visual term=reverse",
209 "Visual term=reverse guibg=LightGrey"),
210#ifdef FEAT_DIFF
211 CENT("DiffAdd term=bold ctermbg=LightBlue",
212 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
213 CENT("DiffChange term=bold ctermbg=LightMagenta",
214 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
215 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
216 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
217#endif
218 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
219 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
220#ifdef FEAT_SYN_HL
221 CENT("CursorColumn term=reverse ctermbg=LightGrey",
222 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
223 CENT("CursorLine term=underline cterm=underline",
224 "CursorLine term=underline cterm=underline guibg=Grey90"),
225 CENT("ColorColumn term=reverse ctermbg=LightRed",
226 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
227#endif
228#ifdef FEAT_CONCEAL
229 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
230 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
231#endif
232 CENT("MatchParen term=reverse ctermbg=Cyan",
233 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
234#ifdef FEAT_TERMINAL
235 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
236 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
237 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
238 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
239#endif
240#ifdef FEAT_MENU
241 CENT("ToolbarLine term=underline ctermbg=LightGrey",
242 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
243 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
244 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
245#endif
246 NULL
247};
248
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200249// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200250static char *(highlight_init_dark[]) = {
251 CENT("Directory term=bold ctermfg=LightCyan",
252 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
253 CENT("LineNr term=underline ctermfg=Yellow",
254 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200255 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
256 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200257 CENT("MoreMsg term=bold ctermfg=LightGreen",
258 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
259 CENT("Question term=standout ctermfg=LightGreen",
260 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
261 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
262 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
263 CENT("SpecialKey term=bold ctermfg=LightBlue",
264 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
265#ifdef FEAT_SPELL
266 CENT("SpellBad term=reverse ctermbg=Red",
267 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
268 CENT("SpellCap term=reverse ctermbg=Blue",
269 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
270 CENT("SpellRare term=reverse ctermbg=Magenta",
271 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
272 CENT("SpellLocal term=underline ctermbg=Cyan",
273 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
274#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200275 CENT("PmenuThumb ctermbg=White",
276 "PmenuThumb ctermbg=White guibg=White"),
277 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
278 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
279 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
280 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200281 CENT("Title term=bold ctermfg=LightMagenta",
282 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
283 CENT("WarningMsg term=standout ctermfg=LightRed",
284 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
285#ifdef FEAT_WILDMENU
286 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
287 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
288#endif
289#ifdef FEAT_FOLDING
290 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
291 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
292 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
293 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
294#endif
295#ifdef FEAT_SIGNS
296 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
297 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
298#endif
299 CENT("Visual term=reverse",
300 "Visual term=reverse guibg=DarkGrey"),
301#ifdef FEAT_DIFF
302 CENT("DiffAdd term=bold ctermbg=DarkBlue",
303 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
304 CENT("DiffChange term=bold ctermbg=DarkMagenta",
305 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
306 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
307 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
308#endif
309 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
310 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
311#ifdef FEAT_SYN_HL
312 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
313 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
314 CENT("CursorLine term=underline cterm=underline",
315 "CursorLine term=underline cterm=underline guibg=Grey40"),
316 CENT("ColorColumn term=reverse ctermbg=DarkRed",
317 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
318#endif
319 CENT("MatchParen term=reverse ctermbg=DarkCyan",
320 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
321#ifdef FEAT_CONCEAL
322 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
323 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
324#endif
325#ifdef FEAT_TERMINAL
326 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
327 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
328 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
329 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
330#endif
331#ifdef FEAT_MENU
332 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
333 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
334 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
335 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
336#endif
337 NULL
338};
339
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200340/*
341 * Returns the number of highlight groups.
342 */
343 int
344highlight_num_groups(void)
345{
346 return highlight_ga.ga_len;
347}
348
349/*
350 * Returns the name of a highlight group.
351 */
352 char_u *
353highlight_group_name(int id)
354{
355 return HL_TABLE()[id].sg_name;
356}
357
358/*
359 * Returns the ID of the link to a highlight group.
360 */
361 int
362highlight_link_id(int id)
363{
364 return HL_TABLE()[id].sg_link;
365}
366
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200367 void
368init_highlight(
369 int both, // include groups where 'bg' doesn't matter
370 int reset) // clear group first
371{
372 int i;
373 char **pp;
374 static int had_both = FALSE;
375#ifdef FEAT_EVAL
376 char_u *p;
377
378 /*
379 * Try finding the color scheme file. Used when a color file was loaded
380 * and 'background' or 't_Co' is changed.
381 */
382 p = get_var_value((char_u *)"g:colors_name");
383 if (p != NULL)
384 {
385 // The value of g:colors_name could be freed when sourcing the script,
386 // making "p" invalid, so copy it.
387 char_u *copy_p = vim_strsave(p);
388 int r;
389
390 if (copy_p != NULL)
391 {
392 r = load_colors(copy_p);
393 vim_free(copy_p);
394 if (r == OK)
395 return;
396 }
397 }
398
399#endif
400
401 /*
402 * Didn't use a color file, use the compiled-in colors.
403 */
404 if (both)
405 {
406 had_both = TRUE;
407 pp = highlight_init_both;
408 for (i = 0; pp[i] != NULL; ++i)
409 do_highlight((char_u *)pp[i], reset, TRUE);
410 }
411 else if (!had_both)
412 // Don't do anything before the call with both == TRUE from main().
413 // Not everything has been setup then, and that call will overrule
414 // everything anyway.
415 return;
416
417 if (*p_bg == 'l')
418 pp = highlight_init_light;
419 else
420 pp = highlight_init_dark;
421 for (i = 0; pp[i] != NULL; ++i)
422 do_highlight((char_u *)pp[i], reset, TRUE);
423
424 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
425 // depend on the number of colors available.
426 // With 8 colors brown is equal to yellow, need to use black for Search fg
427 // to avoid Statement highlighted text disappears.
428 // Clear the attributes, needed when changing the t_Co value.
429 if (t_colors > 8)
430 do_highlight((char_u *)(*p_bg == 'l'
431 ? "Visual cterm=NONE ctermbg=LightGrey"
432 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
433 else
434 {
435 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
436 FALSE, TRUE);
437 if (*p_bg == 'l')
438 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
439 }
440
441#ifdef FEAT_SYN_HL
442 /*
443 * If syntax highlighting is enabled load the highlighting for it.
444 */
445 if (get_var_value((char_u *)"g:syntax_on") != NULL)
446 {
447 static int recursive = 0;
448
449 if (recursive >= 5)
450 emsg(_("E679: recursive loop loading syncolor.vim"));
451 else
452 {
453 ++recursive;
454 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
455 --recursive;
456 }
457 }
458#endif
459}
460
461/*
462 * Load color file "name".
463 * Return OK for success, FAIL for failure.
464 */
465 int
466load_colors(char_u *name)
467{
468 char_u *buf;
469 int retval = FAIL;
470 static int recursive = FALSE;
471
472 // When being called recursively, this is probably because setting
473 // 'background' caused the highlighting to be reloaded. This means it is
474 // working, thus we should return OK.
475 if (recursive)
476 return OK;
477
478 recursive = TRUE;
479 buf = alloc(STRLEN(name) + 12);
480 if (buf != NULL)
481 {
482 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
483 curbuf->b_fname, FALSE, curbuf);
484 sprintf((char *)buf, "colors/%s.vim", name);
485 retval = source_runtime(buf, DIP_START + DIP_OPT);
486 vim_free(buf);
487 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
488 }
489 recursive = FALSE;
490
491 return retval;
492}
493
494static char *(color_names[28]) = {
495 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
496 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
497 "Gray", "Grey", "LightGray", "LightGrey",
498 "DarkGray", "DarkGrey",
499 "Blue", "LightBlue", "Green", "LightGreen",
500 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
501 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
502 // indices:
503 // 0, 1, 2, 3,
504 // 4, 5, 6, 7,
505 // 8, 9, 10, 11,
506 // 12, 13,
507 // 14, 15, 16, 17,
508 // 18, 19, 20, 21, 22,
509 // 23, 24, 25, 26, 27
510static int color_numbers_16[28] = {0, 1, 2, 3,
511 4, 5, 6, 6,
512 7, 7, 7, 7,
513 8, 8,
514 9, 9, 10, 10,
515 11, 11, 12, 12, 13,
516 13, 14, 14, 15, -1};
517// for xterm with 88 colors...
518static int color_numbers_88[28] = {0, 4, 2, 6,
519 1, 5, 32, 72,
520 84, 84, 7, 7,
521 82, 82,
522 12, 43, 10, 61,
523 14, 63, 9, 74, 13,
524 75, 11, 78, 15, -1};
525// for xterm with 256 colors...
526static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200527 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200528 248, 248, 7, 7,
529 242, 242,
530 12, 81, 10, 121,
531 14, 159, 9, 224, 13,
532 225, 11, 229, 15, -1};
533// for terminals with less than 16 colors...
534static int color_numbers_8[28] = {0, 4, 2, 6,
535 1, 5, 3, 3,
536 7, 7, 7, 7,
537 0+8, 0+8,
538 4+8, 4+8, 2+8, 2+8,
539 6+8, 6+8, 1+8, 1+8, 5+8,
540 5+8, 3+8, 3+8, 7+8, -1};
541
542/*
543 * Lookup the "cterm" value to be used for color with index "idx" in
544 * color_names[].
545 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
546 * colors, otherwise it will be unchanged.
547 */
548 int
549lookup_color(int idx, int foreground, int *boldp)
550{
551 int color = color_numbers_16[idx];
552 char_u *p;
553
554 // Use the _16 table to check if it's a valid color name.
555 if (color < 0)
556 return -1;
557
558 if (t_colors == 8)
559 {
560 // t_Co is 8: use the 8 colors table
561#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100562 // On qnx, the 8 & 16 color arrays are the same
563 if (STRNCMP(T_NAME, "qansi", 5) == 0)
564 color = color_numbers_16[idx];
565 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200566#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100567 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200568 if (foreground)
569 {
570 // set/reset bold attribute to get light foreground
571 // colors (on some terminals, e.g. "linux")
572 if (color & 8)
573 *boldp = TRUE;
574 else
575 *boldp = FALSE;
576 }
577 color &= 7; // truncate to 8 colors
578 }
579 else if (t_colors == 16 || t_colors == 88
580 || t_colors >= 256)
581 {
582 /*
583 * Guess: if the termcap entry ends in 'm', it is
584 * probably an xterm-like terminal. Use the changed
585 * order for colors.
586 */
587 if (*T_CAF != NUL)
588 p = T_CAF;
589 else
590 p = T_CSF;
591 if (*p != NUL && (t_colors > 256
592 || *(p + STRLEN(p) - 1) == 'm'))
593 {
594 if (t_colors == 88)
595 color = color_numbers_88[idx];
596 else if (t_colors >= 256)
597 color = color_numbers_256[idx];
598 else
599 color = color_numbers_8[idx];
600 }
601#ifdef FEAT_TERMRESPONSE
602 if (t_colors >= 256 && color == 15 && is_mac_terminal)
603 // Terminal.app has a bug: 15 is light grey. Use white
604 // from the color cube instead.
605 color = 231;
606#endif
607 }
608 return color;
609}
610
611/*
612 * Handle the ":highlight .." command.
613 * When using ":hi clear" this is called recursively for each group with
614 * "forceit" and "init" both TRUE.
615 */
616 void
617do_highlight(
618 char_u *line,
619 int forceit,
620 int init) // TRUE when called for initializing
621{
622 char_u *name_end;
623 char_u *p;
624 char_u *linep;
625 char_u *key_start;
626 char_u *arg_start;
627 char_u *key = NULL, *arg = NULL;
628 long i;
629 int off;
630 int len;
631 int attr;
632 int id;
633 int idx;
634 hl_group_T item_before;
635 int did_change = FALSE;
636 int dodefault = FALSE;
637 int doclear = FALSE;
638 int dolink = FALSE;
639 int error = FALSE;
640 int color;
641 int is_normal_group = FALSE; // "Normal" group
642#ifdef FEAT_TERMINAL
643 int is_terminal_group = FALSE; // "Terminal" group
644#endif
645#ifdef FEAT_GUI_X11
646 int is_menu_group = FALSE; // "Menu" group
647 int is_scrollbar_group = FALSE; // "Scrollbar" group
648 int is_tooltip_group = FALSE; // "Tooltip" group
649 int do_colors = FALSE; // need to update colors?
650#else
651# define is_menu_group 0
652# define is_tooltip_group 0
653#endif
654#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
655 int did_highlight_changed = FALSE;
656#endif
657
658 /*
659 * If no argument, list current highlighting.
660 */
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +0200661 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200662 {
663 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
664 // TODO: only call when the group has attributes set
665 highlight_list_one((int)i);
666 return;
667 }
668
669 /*
670 * Isolate the name.
671 */
672 name_end = skiptowhite(line);
673 linep = skipwhite(name_end);
674
675 /*
676 * Check for "default" argument.
677 */
678 if (STRNCMP(line, "default", name_end - line) == 0)
679 {
680 dodefault = TRUE;
681 line = linep;
682 name_end = skiptowhite(line);
683 linep = skipwhite(name_end);
684 }
685
686 /*
687 * Check for "clear" or "link" argument.
688 */
689 if (STRNCMP(line, "clear", name_end - line) == 0)
690 doclear = TRUE;
691 if (STRNCMP(line, "link", name_end - line) == 0)
692 dolink = TRUE;
693
694 /*
695 * ":highlight {group-name}": list highlighting for one group.
696 */
Bram Moolenaar1966c242020-04-20 22:42:32 +0200697 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200698 {
699 id = syn_namen2id(line, (int)(name_end - line));
700 if (id == 0)
701 semsg(_("E411: highlight group not found: %s"), line);
702 else
703 highlight_list_one(id);
704 return;
705 }
706
707 /*
708 * Handle ":highlight link {from} {to}" command.
709 */
710 if (dolink)
711 {
712 char_u *from_start = linep;
713 char_u *from_end;
714 char_u *to_start;
715 char_u *to_end;
716 int from_id;
717 int to_id;
718
719 from_end = skiptowhite(from_start);
720 to_start = skipwhite(from_end);
721 to_end = skiptowhite(to_start);
722
Bram Moolenaar1966c242020-04-20 22:42:32 +0200723 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200724 {
725 semsg(_("E412: Not enough arguments: \":highlight link %s\""),
726 from_start);
727 return;
728 }
729
Bram Moolenaar1966c242020-04-20 22:42:32 +0200730 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200731 {
732 semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
733 return;
734 }
735
736 from_id = syn_check_group(from_start, (int)(from_end - from_start));
737 if (STRNCMP(to_start, "NONE", 4) == 0)
738 to_id = 0;
739 else
740 to_id = syn_check_group(to_start, (int)(to_end - to_start));
741
742 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
743 {
744 /*
745 * Don't allow a link when there already is some highlighting
746 * for the group, unless '!' is used
747 */
748 if (to_id > 0 && !forceit && !init
749 && hl_has_settings(from_id - 1, dodefault))
750 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100751 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200752 emsg(_("E414: group has settings, highlight link ignored"));
753 }
754 else if (HL_TABLE()[from_id - 1].sg_link != to_id
755#ifdef FEAT_EVAL
756 || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
757 != current_sctx.sc_sid
758#endif
759 || HL_TABLE()[from_id - 1].sg_cleared)
760 {
761 if (!init)
762 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
763 HL_TABLE()[from_id - 1].sg_link = to_id;
764#ifdef FEAT_EVAL
765 HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100766 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200767#endif
768 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
769 redraw_all_later(SOME_VALID);
770
771 // Only call highlight_changed() once after multiple changes.
772 need_highlight_changed = TRUE;
773 }
774 }
775
776 return;
777 }
778
779 if (doclear)
780 {
781 /*
782 * ":highlight clear [group]" command.
783 */
Bram Moolenaar1966c242020-04-20 22:42:32 +0200784 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200785 {
786#ifdef FEAT_GUI
787 // First, we do not destroy the old values, but allocate the new
788 // ones and update the display. THEN we destroy the old values.
789 // If we destroy the old values first, then the old values
790 // (such as GuiFont's or GuiFontset's) will still be displayed but
791 // invalid because they were free'd.
792 if (gui.in_use)
793 {
794# ifdef FEAT_BEVAL_TIP
795 gui_init_tooltip_font();
796# endif
797# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
798 gui_init_menu_font();
799# endif
800 }
801# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
802 gui_mch_def_colors();
803# endif
804# ifdef FEAT_GUI_X11
805# ifdef FEAT_MENU
806
807 // This only needs to be done when there is no Menu highlight
808 // group defined by default, which IS currently the case.
809 gui_mch_new_menu_colors();
810# endif
811 if (gui.in_use)
812 {
813 gui_new_scrollbar_colors();
814# ifdef FEAT_BEVAL_GUI
815 gui_mch_new_tooltip_colors();
816# endif
817# ifdef FEAT_MENU
818 gui_mch_new_menu_font();
819# endif
820 }
821# endif
822
823 // Ok, we're done allocating the new default graphics items.
824 // The screen should already be refreshed at this point.
825 // It is now Ok to clear out the old data.
826#endif
827#ifdef FEAT_EVAL
Bram Moolenaar1966c242020-04-20 22:42:32 +0200828 do_unlet((char_u *)"g:colors_name", TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200829#endif
830 restore_cterm_colors();
831
832 /*
833 * Clear all default highlight groups and load the defaults.
834 */
835 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
836 highlight_clear(idx);
837 init_highlight(TRUE, TRUE);
838#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
839 if (USE_24BIT)
840 highlight_gui_started();
841 else
842#endif
843 highlight_changed();
844 redraw_later_clear();
845 return;
846 }
Bram Moolenaar1966c242020-04-20 22:42:32 +0200847 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200848 name_end = skiptowhite(line);
849 linep = skipwhite(name_end);
850 }
851
852 /*
853 * Find the group name in the table. If it does not exist yet, add it.
854 */
855 id = syn_check_group(line, (int)(name_end - line));
856 if (id == 0) // failed (out of memory)
857 return;
858 idx = id - 1; // index is ID minus one
859
860 // Return if "default" was used and the group already has settings.
861 if (dodefault && hl_has_settings(idx, TRUE))
862 return;
863
864 // Make a copy so we can check if any attribute actually changed.
865 item_before = HL_TABLE()[idx];
866
867 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
868 is_normal_group = TRUE;
869#ifdef FEAT_TERMINAL
870 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
871 is_terminal_group = TRUE;
872#endif
873#ifdef FEAT_GUI_X11
874 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
875 is_menu_group = TRUE;
876 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
877 is_scrollbar_group = TRUE;
878 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
879 is_tooltip_group = TRUE;
880#endif
881
882 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
883 if (doclear || (forceit && init))
884 {
885 highlight_clear(idx);
886 if (!doclear)
887 HL_TABLE()[idx].sg_set = 0;
888 }
889
890 if (!doclear)
Bram Moolenaar1966c242020-04-20 22:42:32 +0200891 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200892 {
893 key_start = linep;
894 if (*linep == '=')
895 {
896 semsg(_("E415: unexpected equal sign: %s"), key_start);
897 error = TRUE;
898 break;
899 }
900
901 /*
902 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
903 * "guibg").
904 */
905 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
906 ++linep;
907 vim_free(key);
908 key = vim_strnsave_up(key_start, (int)(linep - key_start));
909 if (key == NULL)
910 {
911 error = TRUE;
912 break;
913 }
914 linep = skipwhite(linep);
915
916 if (STRCMP(key, "NONE") == 0)
917 {
918 if (!init || HL_TABLE()[idx].sg_set == 0)
919 {
920 if (!init)
921 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
922 highlight_clear(idx);
923 }
924 continue;
925 }
926
927 /*
928 * Check for the equal sign.
929 */
930 if (*linep != '=')
931 {
932 semsg(_("E416: missing equal sign: %s"), key_start);
933 error = TRUE;
934 break;
935 }
936 ++linep;
937
938 /*
939 * Isolate the argument.
940 */
941 linep = skipwhite(linep);
942 if (*linep == '\'') // guifg='color name'
943 {
944 arg_start = ++linep;
945 linep = vim_strchr(linep, '\'');
946 if (linep == NULL)
947 {
948 semsg(_(e_invarg2), key_start);
949 error = TRUE;
950 break;
951 }
952 }
953 else
954 {
955 arg_start = linep;
956 linep = skiptowhite(linep);
957 }
958 if (linep == arg_start)
959 {
960 semsg(_("E417: missing argument: %s"), key_start);
961 error = TRUE;
962 break;
963 }
964 vim_free(arg);
965 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
966 if (arg == NULL)
967 {
968 error = TRUE;
969 break;
970 }
971 if (*linep == '\'')
972 ++linep;
973
974 /*
975 * Store the argument.
976 */
977 if ( STRCMP(key, "TERM") == 0
978 || STRCMP(key, "CTERM") == 0
979 || STRCMP(key, "GUI") == 0)
980 {
981 attr = 0;
982 off = 0;
983 while (arg[off] != NUL)
984 {
985 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
986 {
987 len = (int)STRLEN(hl_name_table[i]);
988 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
989 {
990 attr |= hl_attr_table[i];
991 off += len;
992 break;
993 }
994 }
995 if (i < 0)
996 {
997 semsg(_("E418: Illegal value: %s"), arg);
998 error = TRUE;
999 break;
1000 }
1001 if (arg[off] == ',') // another one follows
1002 ++off;
1003 }
1004 if (error)
1005 break;
1006 if (*key == 'T')
1007 {
1008 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
1009 {
1010 if (!init)
1011 HL_TABLE()[idx].sg_set |= SG_TERM;
1012 HL_TABLE()[idx].sg_term = attr;
1013 }
1014 }
1015 else if (*key == 'C')
1016 {
1017 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1018 {
1019 if (!init)
1020 HL_TABLE()[idx].sg_set |= SG_CTERM;
1021 HL_TABLE()[idx].sg_cterm = attr;
1022 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1023 }
1024 }
1025#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1026 else
1027 {
1028 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1029 {
1030 if (!init)
1031 HL_TABLE()[idx].sg_set |= SG_GUI;
1032 HL_TABLE()[idx].sg_gui = attr;
1033 }
1034 }
1035#endif
1036 }
1037 else if (STRCMP(key, "FONT") == 0)
1038 {
1039 // in non-GUI fonts are simply ignored
1040#ifdef FEAT_GUI
1041 if (HL_TABLE()[idx].sg_font_name != NULL
1042 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
1043 {
1044 // Font name didn't change, ignore.
1045 }
1046 else if (!gui.shell_created)
1047 {
1048 // GUI not started yet, always accept the name.
1049 vim_free(HL_TABLE()[idx].sg_font_name);
1050 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1051 did_change = TRUE;
1052 }
1053 else
1054 {
1055 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
1056# ifdef FEAT_XFONTSET
1057 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
1058# endif
1059 // First, save the current font/fontset.
1060 // Then try to allocate the font/fontset.
1061 // If the allocation fails, HL_TABLE()[idx].sg_font OR
1062 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
1063
1064 HL_TABLE()[idx].sg_font = NOFONT;
1065# ifdef FEAT_XFONTSET
1066 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1067# endif
1068 hl_do_font(idx, arg, is_normal_group, is_menu_group,
1069 is_tooltip_group, FALSE);
1070
1071# ifdef FEAT_XFONTSET
1072 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1073 {
1074 // New fontset was accepted. Free the old one, if there
1075 // was one.
1076 gui_mch_free_fontset(temp_sg_fontset);
1077 vim_free(HL_TABLE()[idx].sg_font_name);
1078 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1079 did_change = TRUE;
1080 }
1081 else
1082 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
1083# endif
1084 if (HL_TABLE()[idx].sg_font != NOFONT)
1085 {
1086 // New font was accepted. Free the old one, if there was
1087 // one.
1088 gui_mch_free_font(temp_sg_font);
1089 vim_free(HL_TABLE()[idx].sg_font_name);
1090 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1091 did_change = TRUE;
1092 }
1093 else
1094 HL_TABLE()[idx].sg_font = temp_sg_font;
1095 }
1096#endif
1097 }
1098 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0)
1099 {
1100 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1101 {
1102 if (!init)
1103 HL_TABLE()[idx].sg_set |= SG_CTERM;
1104
1105 // When setting the foreground color, and previously the "bold"
1106 // flag was set for a light color, reset it now
1107 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1108 {
1109 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1110 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1111 }
1112
1113 if (VIM_ISDIGIT(*arg))
1114 color = atoi((char *)arg);
1115 else if (STRICMP(arg, "fg") == 0)
1116 {
1117 if (cterm_normal_fg_color)
1118 color = cterm_normal_fg_color - 1;
1119 else
1120 {
1121 emsg(_("E419: FG color unknown"));
1122 error = TRUE;
1123 break;
1124 }
1125 }
1126 else if (STRICMP(arg, "bg") == 0)
1127 {
1128 if (cterm_normal_bg_color > 0)
1129 color = cterm_normal_bg_color - 1;
1130 else
1131 {
1132 emsg(_("E420: BG color unknown"));
1133 error = TRUE;
1134 break;
1135 }
1136 }
1137 else
1138 {
1139 int bold = MAYBE;
1140
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001141 // reduce calls to STRICMP a bit, it can be slow
1142 off = TOUPPER_ASC(*arg);
1143 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
1144 if (off == color_names[i][0]
1145 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1146 break;
1147 if (i < 0)
1148 {
1149 semsg(_("E421: Color name or number not recognized: %s"), key_start);
1150 error = TRUE;
1151 break;
1152 }
1153
1154 color = lookup_color(i, key[5] == 'F', &bold);
1155
1156 // set/reset bold attribute to get light foreground
1157 // colors (on some terminals, e.g. "linux")
1158 if (bold == TRUE)
1159 {
1160 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1161 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1162 }
1163 else if (bold == FALSE)
1164 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1165 }
1166
1167 // Add one to the argument, to avoid zero. Zero is used for
1168 // "NONE", then "color" is -1.
1169 if (key[5] == 'F')
1170 {
1171 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1172 if (is_normal_group)
1173 {
1174 cterm_normal_fg_color = color + 1;
1175 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
1176#ifdef FEAT_GUI
1177 // Don't do this if the GUI is used.
1178 if (!gui.in_use && !gui.starting)
1179#endif
1180 {
1181 must_redraw = CLEAR;
1182 if (termcap_active && color >= 0)
1183 term_fg_color(color);
1184 }
1185 }
1186 }
1187 else
1188 {
1189 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1190 if (is_normal_group)
1191 {
1192 cterm_normal_bg_color = color + 1;
1193#ifdef FEAT_GUI
1194 // Don't mess with 'background' if the GUI is used.
1195 if (!gui.in_use && !gui.starting)
1196#endif
1197 {
1198 must_redraw = CLEAR;
1199 if (color >= 0)
1200 {
1201 int dark = -1;
1202
1203 if (termcap_active)
1204 term_bg_color(color);
1205 if (t_colors < 16)
1206 dark = (color == 0 || color == 4);
1207 // Limit the heuristic to the standard 16 colors
1208 else if (color < 16)
1209 dark = (color < 7 || color == 8);
1210 // Set the 'background' option if the value is
1211 // wrong.
1212 if (dark != -1
1213 && dark != (*p_bg == 'd')
1214 && !option_was_set((char_u *)"bg"))
1215 {
1216 set_option_value((char_u *)"bg", 0L,
1217 (char_u *)(dark ? "dark" : "light"), 0);
1218 reset_option_was_set((char_u *)"bg");
1219 }
1220 }
1221 }
1222 }
1223 }
1224 }
1225 }
1226 else if (STRCMP(key, "GUIFG") == 0)
1227 {
1228#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1229 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
1230
1231 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1232 {
1233 if (!init)
1234 HL_TABLE()[idx].sg_set |= SG_GUI;
1235
1236# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1237 // In GUI guifg colors are only used when recognized
1238 i = color_name2handle(arg);
1239 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1240 {
1241 HL_TABLE()[idx].sg_gui_fg = i;
1242# endif
1243 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1244 {
1245 vim_free(*namep);
1246 if (STRCMP(arg, "NONE") != 0)
1247 *namep = vim_strsave(arg);
1248 else
1249 *namep = NULL;
1250 did_change = TRUE;
1251 }
1252# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1253# ifdef FEAT_GUI_X11
1254 if (is_menu_group && gui.menu_fg_pixel != i)
1255 {
1256 gui.menu_fg_pixel = i;
1257 do_colors = TRUE;
1258 }
1259 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1260 {
1261 gui.scroll_fg_pixel = i;
1262 do_colors = TRUE;
1263 }
1264# ifdef FEAT_BEVAL_GUI
1265 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1266 {
1267 gui.tooltip_fg_pixel = i;
1268 do_colors = TRUE;
1269 }
1270# endif
1271# endif
1272 }
1273# endif
1274 }
1275#endif
1276 }
1277 else if (STRCMP(key, "GUIBG") == 0)
1278 {
1279#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1280 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
1281
1282 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1283 {
1284 if (!init)
1285 HL_TABLE()[idx].sg_set |= SG_GUI;
1286
1287# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1288 // In GUI guifg colors are only used when recognized
1289 i = color_name2handle(arg);
1290 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1291 {
1292 HL_TABLE()[idx].sg_gui_bg = i;
1293# endif
1294 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1295 {
1296 vim_free(*namep);
1297 if (STRCMP(arg, "NONE") != 0)
1298 *namep = vim_strsave(arg);
1299 else
1300 *namep = NULL;
1301 did_change = TRUE;
1302 }
1303# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1304# ifdef FEAT_GUI_X11
1305 if (is_menu_group && gui.menu_bg_pixel != i)
1306 {
1307 gui.menu_bg_pixel = i;
1308 do_colors = TRUE;
1309 }
1310 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1311 {
1312 gui.scroll_bg_pixel = i;
1313 do_colors = TRUE;
1314 }
1315# ifdef FEAT_BEVAL_GUI
1316 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1317 {
1318 gui.tooltip_bg_pixel = i;
1319 do_colors = TRUE;
1320 }
1321# endif
1322# endif
1323 }
1324# endif
1325 }
1326#endif
1327 }
1328 else if (STRCMP(key, "GUISP") == 0)
1329 {
1330#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1331 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
1332
1333 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1334 {
1335 if (!init)
1336 HL_TABLE()[idx].sg_set |= SG_GUI;
1337
1338# ifdef FEAT_GUI
1339 i = color_name2handle(arg);
1340 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !gui.in_use)
1341 {
1342 HL_TABLE()[idx].sg_gui_sp = i;
1343# endif
1344 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1345 {
1346 vim_free(*namep);
1347 if (STRCMP(arg, "NONE") != 0)
1348 *namep = vim_strsave(arg);
1349 else
1350 *namep = NULL;
1351 did_change = TRUE;
1352 }
1353# ifdef FEAT_GUI
1354 }
1355# endif
1356 }
1357#endif
1358 }
1359 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1360 {
1361 char_u buf[100];
1362 char_u *tname;
1363
1364 if (!init)
1365 HL_TABLE()[idx].sg_set |= SG_TERM;
1366
1367 /*
1368 * The "start" and "stop" arguments can be a literal escape
1369 * sequence, or a comma separated list of terminal codes.
1370 */
1371 if (STRNCMP(arg, "t_", 2) == 0)
1372 {
1373 off = 0;
1374 buf[0] = 0;
1375 while (arg[off] != NUL)
1376 {
1377 // Isolate one termcap name
1378 for (len = 0; arg[off + len] &&
1379 arg[off + len] != ','; ++len)
1380 ;
1381 tname = vim_strnsave(arg + off, len);
1382 if (tname == NULL) // out of memory
1383 {
1384 error = TRUE;
1385 break;
1386 }
1387 // lookup the escape sequence for the item
1388 p = get_term_code(tname);
1389 vim_free(tname);
1390 if (p == NULL) // ignore non-existing things
1391 p = (char_u *)"";
1392
1393 // Append it to the already found stuff
1394 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1395 {
1396 semsg(_("E422: terminal code too long: %s"), arg);
1397 error = TRUE;
1398 break;
1399 }
1400 STRCAT(buf, p);
1401
1402 // Advance to the next item
1403 off += len;
1404 if (arg[off] == ',') // another one follows
1405 ++off;
1406 }
1407 }
1408 else
1409 {
1410 /*
1411 * Copy characters from arg[] to buf[], translating <> codes.
1412 */
1413 for (p = arg, off = 0; off < 100 - 6 && *p; )
1414 {
Bram Moolenaarebe9d342020-05-30 21:52:54 +02001415 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001416 if (len > 0) // recognized special char
1417 off += len;
1418 else // copy as normal char
1419 buf[off++] = *p++;
1420 }
1421 buf[off] = NUL;
1422 }
1423 if (error)
1424 break;
1425
1426 if (STRCMP(buf, "NONE") == 0) // resetting the value
1427 p = NULL;
1428 else
1429 p = vim_strsave(buf);
1430 if (key[2] == 'A')
1431 {
1432 vim_free(HL_TABLE()[idx].sg_start);
1433 HL_TABLE()[idx].sg_start = p;
1434 }
1435 else
1436 {
1437 vim_free(HL_TABLE()[idx].sg_stop);
1438 HL_TABLE()[idx].sg_stop = p;
1439 }
1440 }
1441 else
1442 {
1443 semsg(_("E423: Illegal argument: %s"), key_start);
1444 error = TRUE;
1445 break;
1446 }
1447 HL_TABLE()[idx].sg_cleared = FALSE;
1448
1449 /*
1450 * When highlighting has been given for a group, don't link it.
1451 */
1452 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1453 HL_TABLE()[idx].sg_link = 0;
1454
1455 /*
1456 * Continue with next argument.
1457 */
1458 linep = skipwhite(linep);
1459 }
1460
1461 /*
1462 * If there is an error, and it's a new entry, remove it from the table.
1463 */
1464 if (error && idx == highlight_ga.ga_len)
1465 syn_unadd_group();
1466 else
1467 {
1468 if (is_normal_group)
1469 {
1470 HL_TABLE()[idx].sg_term_attr = 0;
1471 HL_TABLE()[idx].sg_cterm_attr = 0;
1472#ifdef FEAT_GUI
1473 HL_TABLE()[idx].sg_gui_attr = 0;
1474 /*
1475 * Need to update all groups, because they might be using "bg"
1476 * and/or "fg", which have been changed now.
1477 */
1478#endif
1479#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1480 if (USE_24BIT)
1481 {
1482 highlight_gui_started();
1483 did_highlight_changed = TRUE;
1484 redraw_all_later(NOT_VALID);
1485 }
1486#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001487#ifdef FEAT_VTP
1488 control_console_color_rgb();
1489#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001490 }
1491#ifdef FEAT_TERMINAL
1492 else if (is_terminal_group)
1493 set_terminal_default_colors(
1494 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
1495#endif
1496#ifdef FEAT_GUI_X11
1497# ifdef FEAT_MENU
1498 else if (is_menu_group)
1499 {
1500 if (gui.in_use && do_colors)
1501 gui_mch_new_menu_colors();
1502 }
1503# endif
1504 else if (is_scrollbar_group)
1505 {
1506 if (gui.in_use && do_colors)
1507 gui_new_scrollbar_colors();
1508 else
1509 set_hl_attr(idx);
1510 }
1511# ifdef FEAT_BEVAL_GUI
1512 else if (is_tooltip_group)
1513 {
1514 if (gui.in_use && do_colors)
1515 gui_mch_new_tooltip_colors();
1516 }
1517# endif
1518#endif
1519 else
1520 set_hl_attr(idx);
1521#ifdef FEAT_EVAL
1522 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001523 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001524#endif
1525 }
1526
1527 vim_free(key);
1528 vim_free(arg);
1529
1530 // Only call highlight_changed() once, after a sequence of highlight
1531 // commands, and only if an attribute actually changed.
1532 if ((did_change
1533 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1534#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1535 && !did_highlight_changed
1536#endif
1537 )
1538 {
1539 // Do not trigger a redraw when highlighting is changed while
1540 // redrawing. This may happen when evaluating 'statusline' changes the
1541 // StatusLine group.
1542 if (!updating_screen)
1543 redraw_all_later(NOT_VALID);
1544 need_highlight_changed = TRUE;
1545 }
1546}
1547
1548#if defined(EXITFREE) || defined(PROTO)
1549 void
1550free_highlight(void)
1551{
1552 int i;
1553
1554 for (i = 0; i < highlight_ga.ga_len; ++i)
1555 {
1556 highlight_clear(i);
1557 vim_free(HL_TABLE()[i].sg_name);
1558 vim_free(HL_TABLE()[i].sg_name_u);
1559 }
1560 ga_clear(&highlight_ga);
1561}
1562#endif
1563
1564/*
1565 * Reset the cterm colors to what they were before Vim was started, if
1566 * possible. Otherwise reset them to zero.
1567 */
1568 void
1569restore_cterm_colors(void)
1570{
1571#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1572 // Since t_me has been set, this probably means that the user
1573 // wants to use this as default colors. Need to reset default
1574 // background/foreground colors.
1575 mch_set_normal_colors();
1576#else
1577# ifdef VIMDLL
1578 if (!gui.in_use)
1579 {
1580 mch_set_normal_colors();
1581 return;
1582 }
1583# endif
1584 cterm_normal_fg_color = 0;
1585 cterm_normal_fg_bold = 0;
1586 cterm_normal_bg_color = 0;
1587# ifdef FEAT_TERMGUICOLORS
1588 cterm_normal_fg_gui_color = INVALCOLOR;
1589 cterm_normal_bg_gui_color = INVALCOLOR;
1590# endif
1591#endif
1592}
1593
1594/*
1595 * Return TRUE if highlight group "idx" has any settings.
1596 * When "check_link" is TRUE also check for an existing link.
1597 */
1598 static int
1599hl_has_settings(int idx, int check_link)
1600{
1601 return ( HL_TABLE()[idx].sg_term_attr != 0
1602 || HL_TABLE()[idx].sg_cterm_attr != 0
1603 || HL_TABLE()[idx].sg_cterm_fg != 0
1604 || HL_TABLE()[idx].sg_cterm_bg != 0
1605#ifdef FEAT_GUI
1606 || HL_TABLE()[idx].sg_gui_attr != 0
1607 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1608 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1609 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1610 || HL_TABLE()[idx].sg_font_name != NULL
1611#endif
1612 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1613}
1614
1615/*
1616 * Clear highlighting for one group.
1617 */
1618 static void
1619highlight_clear(int idx)
1620{
1621 HL_TABLE()[idx].sg_cleared = TRUE;
1622
1623 HL_TABLE()[idx].sg_term = 0;
1624 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1625 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1626 HL_TABLE()[idx].sg_term_attr = 0;
1627 HL_TABLE()[idx].sg_cterm = 0;
1628 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1629 HL_TABLE()[idx].sg_cterm_fg = 0;
1630 HL_TABLE()[idx].sg_cterm_bg = 0;
1631 HL_TABLE()[idx].sg_cterm_attr = 0;
1632#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1633 HL_TABLE()[idx].sg_gui = 0;
1634 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1635 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1636 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1637#endif
1638#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1639 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1640 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
1641#endif
1642#ifdef FEAT_GUI
1643 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
1644 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1645 HL_TABLE()[idx].sg_font = NOFONT;
1646# ifdef FEAT_XFONTSET
1647 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1648 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1649# endif
1650 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1651 HL_TABLE()[idx].sg_gui_attr = 0;
1652#endif
1653#ifdef FEAT_EVAL
1654 // Clear the script ID only when there is no link, since that is not
1655 // cleared.
1656 if (HL_TABLE()[idx].sg_link == 0)
1657 {
1658 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
1659 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
1660 }
1661#endif
1662}
1663
1664#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1665/*
1666 * Set the normal foreground and background colors according to the "Normal"
1667 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1668 * "Tooltip" colors.
1669 */
1670 void
1671set_normal_colors(void)
1672{
1673# ifdef FEAT_GUI
1674# ifdef FEAT_TERMGUICOLORS
1675 if (gui.in_use)
1676# endif
1677 {
1678 if (set_group_colors((char_u *)"Normal",
1679 &gui.norm_pixel, &gui.back_pixel,
1680 FALSE, TRUE, FALSE))
1681 {
1682 gui_mch_new_colors();
1683 must_redraw = CLEAR;
1684 }
1685# ifdef FEAT_GUI_X11
1686 if (set_group_colors((char_u *)"Menu",
1687 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1688 TRUE, FALSE, FALSE))
1689 {
1690# ifdef FEAT_MENU
1691 gui_mch_new_menu_colors();
1692# endif
1693 must_redraw = CLEAR;
1694 }
1695# ifdef FEAT_BEVAL_GUI
1696 if (set_group_colors((char_u *)"Tooltip",
1697 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1698 FALSE, FALSE, TRUE))
1699 {
1700# ifdef FEAT_TOOLBAR
1701 gui_mch_new_tooltip_colors();
1702# endif
1703 must_redraw = CLEAR;
1704 }
1705# endif
1706 if (set_group_colors((char_u *)"Scrollbar",
1707 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1708 FALSE, FALSE, FALSE))
1709 {
1710 gui_new_scrollbar_colors();
1711 must_redraw = CLEAR;
1712 }
1713# endif
1714 }
1715# endif
1716# ifdef FEAT_TERMGUICOLORS
1717# ifdef FEAT_GUI
1718 else
1719# endif
1720 {
1721 int idx;
1722
1723 idx = syn_name2id((char_u *)"Normal") - 1;
1724 if (idx >= 0)
1725 {
1726 gui_do_one_color(idx, FALSE, FALSE);
1727
1728 // If the normal fg or bg color changed a complete redraw is
1729 // required.
1730 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1731 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1732 {
1733 // if the GUI color is INVALCOLOR then we use the default cterm
1734 // color
1735 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1736 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1737 must_redraw = CLEAR;
1738 }
1739 }
1740 }
1741# endif
1742}
1743#endif
1744
1745#if defined(FEAT_GUI) || defined(PROTO)
1746/*
1747 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1748 */
1749 static int
1750set_group_colors(
1751 char_u *name,
1752 guicolor_T *fgp,
1753 guicolor_T *bgp,
1754 int do_menu,
1755 int use_norm,
1756 int do_tooltip)
1757{
1758 int idx;
1759
1760 idx = syn_name2id(name) - 1;
1761 if (idx >= 0)
1762 {
1763 gui_do_one_color(idx, do_menu, do_tooltip);
1764
1765 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1766 *fgp = HL_TABLE()[idx].sg_gui_fg;
1767 else if (use_norm)
1768 *fgp = gui.def_norm_pixel;
1769 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1770 *bgp = HL_TABLE()[idx].sg_gui_bg;
1771 else if (use_norm)
1772 *bgp = gui.def_back_pixel;
1773 return TRUE;
1774 }
1775 return FALSE;
1776}
1777
1778/*
1779 * Get the font of the "Normal" group.
1780 * Returns "" when it's not found or not set.
1781 */
1782 char_u *
1783hl_get_font_name(void)
1784{
1785 int id;
1786 char_u *s;
1787
1788 id = syn_name2id((char_u *)"Normal");
1789 if (id > 0)
1790 {
1791 s = HL_TABLE()[id - 1].sg_font_name;
1792 if (s != NULL)
1793 return s;
1794 }
1795 return (char_u *)"";
1796}
1797
1798/*
1799 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
1800 * actually chosen to be used.
1801 */
1802 void
1803hl_set_font_name(char_u *font_name)
1804{
1805 int id;
1806
1807 id = syn_name2id((char_u *)"Normal");
1808 if (id > 0)
1809 {
1810 vim_free(HL_TABLE()[id - 1].sg_font_name);
1811 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
1812 }
1813}
1814
1815/*
1816 * Set background color for "Normal" group. Called by gui_set_bg_color()
1817 * when the color is known.
1818 */
1819 void
1820hl_set_bg_color_name(
1821 char_u *name) // must have been allocated
1822{
1823 int id;
1824
1825 if (name != NULL)
1826 {
1827 id = syn_name2id((char_u *)"Normal");
1828 if (id > 0)
1829 {
1830 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
1831 HL_TABLE()[id - 1].sg_gui_bg_name = name;
1832 }
1833 }
1834}
1835
1836/*
1837 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
1838 * when the color is known.
1839 */
1840 void
1841hl_set_fg_color_name(
1842 char_u *name) // must have been allocated
1843{
1844 int id;
1845
1846 if (name != NULL)
1847 {
1848 id = syn_name2id((char_u *)"Normal");
1849 if (id > 0)
1850 {
1851 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
1852 HL_TABLE()[id - 1].sg_gui_fg_name = name;
1853 }
1854 }
1855}
1856
1857/*
1858 * Return the handle for a font name.
1859 * Returns NOFONT when failed.
1860 */
1861 static GuiFont
1862font_name2handle(char_u *name)
1863{
1864 if (STRCMP(name, "NONE") == 0)
1865 return NOFONT;
1866
1867 return gui_mch_get_font(name, TRUE);
1868}
1869
1870# ifdef FEAT_XFONTSET
1871/*
1872 * Return the handle for a fontset name.
1873 * Returns NOFONTSET when failed.
1874 */
1875 static GuiFontset
1876fontset_name2handle(char_u *name, int fixed_width)
1877{
1878 if (STRCMP(name, "NONE") == 0)
1879 return NOFONTSET;
1880
1881 return gui_mch_get_fontset(name, TRUE, fixed_width);
1882}
1883# endif
1884
1885/*
1886 * Get the font or fontset for one highlight group.
1887 */
1888 static void
1889hl_do_font(
1890 int idx,
1891 char_u *arg,
1892 int do_normal, // set normal font
1893 int do_menu UNUSED, // set menu font
1894 int do_tooltip UNUSED, // set tooltip font
1895 int free_font) // free current font/fontset
1896{
1897# ifdef FEAT_XFONTSET
1898 // If 'guifontset' is not empty, first try using the name as a
1899 // fontset. If that doesn't work, use it as a font name.
1900 if (*p_guifontset != NUL
1901# ifdef FONTSET_ALWAYS
1902 || do_menu
1903# endif
1904# ifdef FEAT_BEVAL_TIP
1905 // In Athena & Motif, the Tooltip highlight group is always a fontset
1906 || do_tooltip
1907# endif
1908 )
1909 {
1910 if (free_font)
1911 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1912 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
1913# ifdef FONTSET_ALWAYS
1914 || do_menu
1915# endif
1916# ifdef FEAT_BEVAL_TIP
1917 || do_tooltip
1918# endif
1919 );
1920 }
1921 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1922 {
1923 // If it worked and it's the Normal group, use it as the normal
1924 // fontset. Same for the Menu group.
1925 if (do_normal)
1926 gui_init_font(arg, TRUE);
1927# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1928 if (do_menu)
1929 {
1930# ifdef FONTSET_ALWAYS
1931 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
1932# else
1933 // YIKES! This is a bug waiting to crash the program
1934 gui.menu_font = HL_TABLE()[idx].sg_fontset;
1935# endif
1936 gui_mch_new_menu_font();
1937 }
1938# ifdef FEAT_BEVAL_GUI
1939 if (do_tooltip)
1940 {
1941 // The Athena widget set cannot currently handle switching between
1942 // displaying a single font and a fontset.
1943 // If the XtNinternational resource is set to True at widget
1944 // creation, then a fontset is always used, otherwise an
1945 // XFontStruct is used.
1946 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
1947 gui_mch_new_tooltip_font();
1948 }
1949# endif
1950# endif
1951 }
1952 else
1953# endif
1954 {
1955 if (free_font)
1956 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1957 HL_TABLE()[idx].sg_font = font_name2handle(arg);
1958 // If it worked and it's the Normal group, use it as the
1959 // normal font. Same for the Menu group.
1960 if (HL_TABLE()[idx].sg_font != NOFONT)
1961 {
1962 if (do_normal)
1963 gui_init_font(arg, FALSE);
1964#ifndef FONTSET_ALWAYS
1965# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1966 if (do_menu)
1967 {
1968 gui.menu_font = HL_TABLE()[idx].sg_font;
1969 gui_mch_new_menu_font();
1970 }
1971# endif
1972#endif
1973 }
1974 }
1975}
1976
1977#endif // FEAT_GUI
1978
1979#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1980/*
1981 * Return the handle for a color name.
1982 * Returns INVALCOLOR when failed.
1983 */
1984 guicolor_T
1985color_name2handle(char_u *name)
1986{
1987 if (STRCMP(name, "NONE") == 0)
1988 return INVALCOLOR;
1989
1990 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
1991 {
1992#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
1993 if (gui.in_use)
1994#endif
1995#ifdef FEAT_GUI
1996 return gui.norm_pixel;
1997#endif
1998#ifdef FEAT_TERMGUICOLORS
1999 if (cterm_normal_fg_gui_color != INVALCOLOR)
2000 return cterm_normal_fg_gui_color;
2001 // Guess that the foreground is black or white.
2002 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2003#endif
2004 }
2005 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2006 {
2007#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2008 if (gui.in_use)
2009#endif
2010#ifdef FEAT_GUI
2011 return gui.back_pixel;
2012#endif
2013#ifdef FEAT_TERMGUICOLORS
2014 if (cterm_normal_bg_gui_color != INVALCOLOR)
2015 return cterm_normal_bg_gui_color;
2016 // Guess that the background is white or black.
2017 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2018#endif
2019 }
2020
2021 return GUI_GET_COLOR(name);
2022}
2023#endif
2024
2025/*
2026 * Table with the specifications for an attribute number.
2027 * Note that this table is used by ALL buffers. This is required because the
2028 * GUI can redraw at any time for any buffer.
2029 */
2030static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2031
2032#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2033
2034static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2035
2036#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2037
2038#ifdef FEAT_GUI
2039static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2040
2041#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2042#endif
2043
2044/*
2045 * Return the attr number for a set of colors and font.
2046 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2047 * if the combination is new.
2048 * Return 0 for error (no more room).
2049 */
2050 static int
2051get_attr_entry(garray_T *table, attrentry_T *aep)
2052{
2053 int i;
2054 attrentry_T *taep;
2055 static int recursive = FALSE;
2056
2057 /*
2058 * Init the table, in case it wasn't done yet.
2059 */
2060 table->ga_itemsize = sizeof(attrentry_T);
2061 table->ga_growsize = 7;
2062
2063 /*
2064 * Try to find an entry with the same specifications.
2065 */
2066 for (i = 0; i < table->ga_len; ++i)
2067 {
2068 taep = &(((attrentry_T *)table->ga_data)[i]);
2069 if ( aep->ae_attr == taep->ae_attr
2070 && (
2071#ifdef FEAT_GUI
2072 (table == &gui_attr_table
2073 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2074 && aep->ae_u.gui.bg_color
2075 == taep->ae_u.gui.bg_color
2076 && aep->ae_u.gui.sp_color
2077 == taep->ae_u.gui.sp_color
2078 && aep->ae_u.gui.font == taep->ae_u.gui.font
2079# ifdef FEAT_XFONTSET
2080 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2081# endif
2082 ))
2083 ||
2084#endif
2085 (table == &term_attr_table
2086 && (aep->ae_u.term.start == NULL)
2087 == (taep->ae_u.term.start == NULL)
2088 && (aep->ae_u.term.start == NULL
2089 || STRCMP(aep->ae_u.term.start,
2090 taep->ae_u.term.start) == 0)
2091 && (aep->ae_u.term.stop == NULL)
2092 == (taep->ae_u.term.stop == NULL)
2093 && (aep->ae_u.term.stop == NULL
2094 || STRCMP(aep->ae_u.term.stop,
2095 taep->ae_u.term.stop) == 0))
2096 || (table == &cterm_attr_table
2097 && aep->ae_u.cterm.fg_color
2098 == taep->ae_u.cterm.fg_color
2099 && aep->ae_u.cterm.bg_color
2100 == taep->ae_u.cterm.bg_color
2101#ifdef FEAT_TERMGUICOLORS
2102 && aep->ae_u.cterm.fg_rgb
2103 == taep->ae_u.cterm.fg_rgb
2104 && aep->ae_u.cterm.bg_rgb
2105 == taep->ae_u.cterm.bg_rgb
2106#endif
2107 )))
2108
2109 return i + ATTR_OFF;
2110 }
2111
2112 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2113 {
2114 /*
2115 * Running out of attribute entries! remove all attributes, and
2116 * compute new ones for all groups.
2117 * When called recursively, we are really out of numbers.
2118 */
2119 if (recursive)
2120 {
2121 emsg(_("E424: Too many different highlighting attributes in use"));
2122 return 0;
2123 }
2124 recursive = TRUE;
2125
2126 clear_hl_tables();
2127
2128 must_redraw = CLEAR;
2129
2130 for (i = 0; i < highlight_ga.ga_len; ++i)
2131 set_hl_attr(i);
2132
2133 recursive = FALSE;
2134 }
2135
2136 /*
2137 * This is a new combination of colors and font, add an entry.
2138 */
2139 if (ga_grow(table, 1) == FAIL)
2140 return 0;
2141
2142 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002143 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002144 taep->ae_attr = aep->ae_attr;
2145#ifdef FEAT_GUI
2146 if (table == &gui_attr_table)
2147 {
2148 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2149 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2150 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2151 taep->ae_u.gui.font = aep->ae_u.gui.font;
2152# ifdef FEAT_XFONTSET
2153 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2154# endif
2155 }
2156#endif
2157 if (table == &term_attr_table)
2158 {
2159 if (aep->ae_u.term.start == NULL)
2160 taep->ae_u.term.start = NULL;
2161 else
2162 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2163 if (aep->ae_u.term.stop == NULL)
2164 taep->ae_u.term.stop = NULL;
2165 else
2166 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2167 }
2168 else if (table == &cterm_attr_table)
2169 {
2170 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2171 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
2172#ifdef FEAT_TERMGUICOLORS
2173 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2174 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
2175#endif
2176 }
2177 ++table->ga_len;
2178 return (table->ga_len - 1 + ATTR_OFF);
2179}
2180
2181#if defined(FEAT_TERMINAL) || defined(PROTO)
2182/*
2183 * Get an attribute index for a cterm entry.
2184 * Uses an existing entry when possible or adds one when needed.
2185 */
2186 int
2187get_cterm_attr_idx(int attr, int fg, int bg)
2188{
2189 attrentry_T at_en;
2190
Bram Moolenaara80faa82020-04-12 19:37:17 +02002191 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002192#ifdef FEAT_TERMGUICOLORS
2193 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2194 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2195#endif
2196 at_en.ae_attr = attr;
2197 at_en.ae_u.cterm.fg_color = fg;
2198 at_en.ae_u.cterm.bg_color = bg;
2199 return get_attr_entry(&cterm_attr_table, &at_en);
2200}
2201#endif
2202
2203#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2204/*
2205 * Get an attribute index for a 'termguicolors' entry.
2206 * Uses an existing entry when possible or adds one when needed.
2207 */
2208 int
2209get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2210{
2211 attrentry_T at_en;
2212
Bram Moolenaara80faa82020-04-12 19:37:17 +02002213 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002214 at_en.ae_attr = attr;
2215 if (fg == INVALCOLOR && bg == INVALCOLOR)
2216 {
2217 // If both GUI colors are not set fall back to the cterm colors. Helps
2218 // if the GUI only has an attribute, such as undercurl.
2219 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2220 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2221 }
2222 else
2223 {
2224 at_en.ae_u.cterm.fg_rgb = fg;
2225 at_en.ae_u.cterm.bg_rgb = bg;
2226 }
2227 return get_attr_entry(&cterm_attr_table, &at_en);
2228}
2229#endif
2230
2231#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2232/*
2233 * Get an attribute index for a cterm entry.
2234 * Uses an existing entry when possible or adds one when needed.
2235 */
2236 int
2237get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2238{
2239 attrentry_T at_en;
2240
Bram Moolenaara80faa82020-04-12 19:37:17 +02002241 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002242 at_en.ae_attr = attr;
2243 at_en.ae_u.gui.fg_color = fg;
2244 at_en.ae_u.gui.bg_color = bg;
2245 return get_attr_entry(&gui_attr_table, &at_en);
2246}
2247#endif
2248
2249/*
2250 * Clear all highlight tables.
2251 */
2252 void
2253clear_hl_tables(void)
2254{
2255 int i;
2256 attrentry_T *taep;
2257
2258#ifdef FEAT_GUI
2259 ga_clear(&gui_attr_table);
2260#endif
2261 for (i = 0; i < term_attr_table.ga_len; ++i)
2262 {
2263 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2264 vim_free(taep->ae_u.term.start);
2265 vim_free(taep->ae_u.term.stop);
2266 }
2267 ga_clear(&term_attr_table);
2268 ga_clear(&cterm_attr_table);
2269}
2270
2271/*
2272 * Combine special attributes (e.g., for spelling) with other attributes
2273 * (e.g., for syntax highlighting).
2274 * "prim_attr" overrules "char_attr".
2275 * This creates a new group when required.
2276 * Since we expect there to be few spelling mistakes we don't cache the
2277 * result.
2278 * Return the resulting attributes.
2279 */
2280 int
2281hl_combine_attr(int char_attr, int prim_attr)
2282{
2283 attrentry_T *char_aep = NULL;
2284 attrentry_T *spell_aep;
2285 attrentry_T new_en;
2286
2287 if (char_attr == 0)
2288 return prim_attr;
2289 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2290 return ATTR_COMBINE(char_attr, prim_attr);
2291#ifdef FEAT_GUI
2292 if (gui.in_use)
2293 {
2294 if (char_attr > HL_ALL)
2295 char_aep = syn_gui_attr2entry(char_attr);
2296 if (char_aep != NULL)
2297 new_en = *char_aep;
2298 else
2299 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002300 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002301 new_en.ae_u.gui.fg_color = INVALCOLOR;
2302 new_en.ae_u.gui.bg_color = INVALCOLOR;
2303 new_en.ae_u.gui.sp_color = INVALCOLOR;
2304 if (char_attr <= HL_ALL)
2305 new_en.ae_attr = char_attr;
2306 }
2307
2308 if (prim_attr <= HL_ALL)
2309 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2310 else
2311 {
2312 spell_aep = syn_gui_attr2entry(prim_attr);
2313 if (spell_aep != NULL)
2314 {
2315 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2316 spell_aep->ae_attr);
2317 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2318 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2319 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2320 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2321 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2322 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2323 if (spell_aep->ae_u.gui.font != NOFONT)
2324 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2325# ifdef FEAT_XFONTSET
2326 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2327 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2328# endif
2329 }
2330 }
2331 return get_attr_entry(&gui_attr_table, &new_en);
2332 }
2333#endif
2334
2335 if (IS_CTERM)
2336 {
2337 if (char_attr > HL_ALL)
2338 char_aep = syn_cterm_attr2entry(char_attr);
2339 if (char_aep != NULL)
2340 new_en = *char_aep;
2341 else
2342 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002343 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002344#ifdef FEAT_TERMGUICOLORS
2345 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2346 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2347#endif
2348 if (char_attr <= HL_ALL)
2349 new_en.ae_attr = char_attr;
2350 }
2351
2352 if (prim_attr <= HL_ALL)
2353 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2354 else
2355 {
2356 spell_aep = syn_cterm_attr2entry(prim_attr);
2357 if (spell_aep != NULL)
2358 {
2359 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2360 spell_aep->ae_attr);
2361 if (spell_aep->ae_u.cterm.fg_color > 0)
2362 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2363 if (spell_aep->ae_u.cterm.bg_color > 0)
2364 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
2365#ifdef FEAT_TERMGUICOLORS
2366 // If both fg and bg are not set fall back to cterm colors.
2367 // Helps for SpellBad which uses undercurl in the GUI.
2368 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2369 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2370 {
2371 if (spell_aep->ae_u.cterm.fg_color > 0)
2372 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2373 if (spell_aep->ae_u.cterm.bg_color > 0)
2374 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2375 }
2376 else
2377 {
2378 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2379 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2380 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2381 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2382 }
2383#endif
2384 }
2385 }
2386 return get_attr_entry(&cterm_attr_table, &new_en);
2387 }
2388
2389 if (char_attr > HL_ALL)
2390 char_aep = syn_term_attr2entry(char_attr);
2391 if (char_aep != NULL)
2392 new_en = *char_aep;
2393 else
2394 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002395 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002396 if (char_attr <= HL_ALL)
2397 new_en.ae_attr = char_attr;
2398 }
2399
2400 if (prim_attr <= HL_ALL)
2401 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2402 else
2403 {
2404 spell_aep = syn_term_attr2entry(prim_attr);
2405 if (spell_aep != NULL)
2406 {
2407 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2408 if (spell_aep->ae_u.term.start != NULL)
2409 {
2410 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2411 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2412 }
2413 }
2414 }
2415 return get_attr_entry(&term_attr_table, &new_en);
2416}
2417
2418#ifdef FEAT_GUI
2419 attrentry_T *
2420syn_gui_attr2entry(int attr)
2421{
2422 attr -= ATTR_OFF;
2423 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2424 return NULL;
2425 return &(GUI_ATTR_ENTRY(attr));
2426}
2427#endif
2428
2429/*
2430 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2431 * Only to be used when "attr" > HL_ALL.
2432 */
2433 int
2434syn_attr2attr(int attr)
2435{
2436 attrentry_T *aep;
2437
2438#ifdef FEAT_GUI
2439 if (gui.in_use)
2440 aep = syn_gui_attr2entry(attr);
2441 else
2442#endif
2443 if (IS_CTERM)
2444 aep = syn_cterm_attr2entry(attr);
2445 else
2446 aep = syn_term_attr2entry(attr);
2447
2448 if (aep == NULL) // highlighting not set
2449 return 0;
2450 return aep->ae_attr;
2451}
2452
2453
2454 attrentry_T *
2455syn_term_attr2entry(int attr)
2456{
2457 attr -= ATTR_OFF;
2458 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2459 return NULL;
2460 return &(TERM_ATTR_ENTRY(attr));
2461}
2462
2463 attrentry_T *
2464syn_cterm_attr2entry(int attr)
2465{
2466 attr -= ATTR_OFF;
2467 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2468 return NULL;
2469 return &(CTERM_ATTR_ENTRY(attr));
2470}
2471
2472#define LIST_ATTR 1
2473#define LIST_STRING 2
2474#define LIST_INT 3
2475
2476 static void
2477highlight_list_one(int id)
2478{
2479 hl_group_T *sgp;
2480 int didh = FALSE;
2481
2482 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2483
2484 if (message_filtered(sgp->sg_name))
2485 return;
2486
2487 didh = highlight_list_arg(id, didh, LIST_ATTR,
2488 sgp->sg_term, NULL, "term");
2489 didh = highlight_list_arg(id, didh, LIST_STRING,
2490 0, sgp->sg_start, "start");
2491 didh = highlight_list_arg(id, didh, LIST_STRING,
2492 0, sgp->sg_stop, "stop");
2493
2494 didh = highlight_list_arg(id, didh, LIST_ATTR,
2495 sgp->sg_cterm, NULL, "cterm");
2496 didh = highlight_list_arg(id, didh, LIST_INT,
2497 sgp->sg_cterm_fg, NULL, "ctermfg");
2498 didh = highlight_list_arg(id, didh, LIST_INT,
2499 sgp->sg_cterm_bg, NULL, "ctermbg");
2500
2501#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2502 didh = highlight_list_arg(id, didh, LIST_ATTR,
2503 sgp->sg_gui, NULL, "gui");
2504 didh = highlight_list_arg(id, didh, LIST_STRING,
2505 0, sgp->sg_gui_fg_name, "guifg");
2506 didh = highlight_list_arg(id, didh, LIST_STRING,
2507 0, sgp->sg_gui_bg_name, "guibg");
2508 didh = highlight_list_arg(id, didh, LIST_STRING,
2509 0, sgp->sg_gui_sp_name, "guisp");
2510#endif
2511#ifdef FEAT_GUI
2512 didh = highlight_list_arg(id, didh, LIST_STRING,
2513 0, sgp->sg_font_name, "font");
2514#endif
2515
2516 if (sgp->sg_link && !got_int)
2517 {
2518 (void)syn_list_header(didh, 9999, id);
2519 didh = TRUE;
2520 msg_puts_attr("links to", HL_ATTR(HLF_D));
2521 msg_putchar(' ');
2522 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2523 }
2524
2525 if (!didh)
2526 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2527#ifdef FEAT_EVAL
2528 if (p_verbose > 0)
2529 last_set_msg(sgp->sg_script_ctx);
2530#endif
2531}
2532
2533 static int
2534highlight_list_arg(
2535 int id,
2536 int didh,
2537 int type,
2538 int iarg,
2539 char_u *sarg,
2540 char *name)
2541{
2542 char_u buf[100];
2543 char_u *ts;
2544 int i;
2545
2546 if (got_int)
2547 return FALSE;
2548 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2549 {
2550 ts = buf;
2551 if (type == LIST_INT)
2552 sprintf((char *)buf, "%d", iarg - 1);
2553 else if (type == LIST_STRING)
2554 ts = sarg;
2555 else // type == LIST_ATTR
2556 {
2557 buf[0] = NUL;
2558 for (i = 0; hl_attr_table[i] != 0; ++i)
2559 {
2560 if (iarg & hl_attr_table[i])
2561 {
2562 if (buf[0] != NUL)
2563 vim_strcat(buf, (char_u *)",", 100);
2564 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2565 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2566 }
2567 }
2568 }
2569
2570 (void)syn_list_header(didh,
2571 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2572 didh = TRUE;
2573 if (!got_int)
2574 {
2575 if (*name != NUL)
2576 {
2577 msg_puts_attr(name, HL_ATTR(HLF_D));
2578 msg_puts_attr("=", HL_ATTR(HLF_D));
2579 }
2580 msg_outtrans(ts);
2581 }
2582 }
2583 return didh;
2584}
2585
2586#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2587/*
2588 * Return "1" if highlight group "id" has attribute "flag".
2589 * Return NULL otherwise.
2590 */
2591 char_u *
2592highlight_has_attr(
2593 int id,
2594 int flag,
2595 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2596{
2597 int attr;
2598
2599 if (id <= 0 || id > highlight_ga.ga_len)
2600 return NULL;
2601
2602#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2603 if (modec == 'g')
2604 attr = HL_TABLE()[id - 1].sg_gui;
2605 else
2606#endif
2607 if (modec == 'c')
2608 attr = HL_TABLE()[id - 1].sg_cterm;
2609 else
2610 attr = HL_TABLE()[id - 1].sg_term;
2611
2612 if (attr & flag)
2613 return (char_u *)"1";
2614 return NULL;
2615}
2616#endif
2617
2618#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2619/*
2620 * Return color name of highlight group "id".
2621 */
2622 char_u *
2623highlight_color(
2624 int id,
2625 char_u *what, // "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#"
2626 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2627{
2628 static char_u name[20];
2629 int n;
2630 int fg = FALSE;
2631 int sp = FALSE;
2632 int font = FALSE;
2633
2634 if (id <= 0 || id > highlight_ga.ga_len)
2635 return NULL;
2636
2637 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2638 fg = TRUE;
2639 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2640 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2641 font = TRUE;
2642 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2643 sp = TRUE;
2644 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2645 return NULL;
2646 if (modec == 'g')
2647 {
2648# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2649# ifdef FEAT_GUI
2650 // return font name
2651 if (font)
2652 return HL_TABLE()[id - 1].sg_font_name;
2653# endif
2654
2655 // return #RRGGBB form (only possible when GUI is running)
2656 if ((USE_24BIT) && what[2] == '#')
2657 {
2658 guicolor_T color;
2659 long_u rgb;
2660 static char_u buf[10];
2661
2662 if (fg)
2663 color = HL_TABLE()[id - 1].sg_gui_fg;
2664 else if (sp)
2665# ifdef FEAT_GUI
2666 color = HL_TABLE()[id - 1].sg_gui_sp;
2667# else
2668 color = INVALCOLOR;
2669# endif
2670 else
2671 color = HL_TABLE()[id - 1].sg_gui_bg;
2672 if (color == INVALCOLOR)
2673 return NULL;
2674 rgb = (long_u)GUI_MCH_GET_RGB(color);
2675 sprintf((char *)buf, "#%02x%02x%02x",
2676 (unsigned)(rgb >> 16),
2677 (unsigned)(rgb >> 8) & 255,
2678 (unsigned)rgb & 255);
2679 return buf;
2680 }
2681# endif
2682 if (fg)
2683 return (HL_TABLE()[id - 1].sg_gui_fg_name);
2684 if (sp)
2685 return (HL_TABLE()[id - 1].sg_gui_sp_name);
2686 return (HL_TABLE()[id - 1].sg_gui_bg_name);
2687 }
2688 if (font || sp)
2689 return NULL;
2690 if (modec == 'c')
2691 {
2692 if (fg)
2693 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
2694 else
2695 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2696 if (n < 0)
2697 return NULL;
2698 sprintf((char *)name, "%d", n);
2699 return name;
2700 }
2701 // term doesn't have color
2702 return NULL;
2703}
2704#endif
2705
2706#if (defined(FEAT_SYN_HL) \
2707 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2708 && defined(FEAT_PRINTER)) || defined(PROTO)
2709/*
2710 * Return color name of highlight group "id" as RGB value.
2711 */
2712 long_u
2713highlight_gui_color_rgb(
2714 int id,
2715 int fg) // TRUE = fg, FALSE = bg
2716{
2717 guicolor_T color;
2718
2719 if (id <= 0 || id > highlight_ga.ga_len)
2720 return 0L;
2721
2722 if (fg)
2723 color = HL_TABLE()[id - 1].sg_gui_fg;
2724 else
2725 color = HL_TABLE()[id - 1].sg_gui_bg;
2726
2727 if (color == INVALCOLOR)
2728 return 0L;
2729
2730 return GUI_MCH_GET_RGB(color);
2731}
2732#endif
2733
2734/*
2735 * Output the syntax list header.
2736 * Return TRUE when started a new line.
2737 */
2738 int
2739syn_list_header(
2740 int did_header, // did header already
2741 int outlen, // length of string that comes
2742 int id) // highlight group id
2743{
2744 int endcol = 19;
2745 int newline = TRUE;
2746 int name_col = 0;
2747
2748 if (!did_header)
2749 {
2750 msg_putchar('\n');
2751 if (got_int)
2752 return TRUE;
2753 msg_outtrans(HL_TABLE()[id - 1].sg_name);
2754 name_col = msg_col;
2755 endcol = 15;
2756 }
2757 else if (msg_col + outlen + 1 >= Columns)
2758 {
2759 msg_putchar('\n');
2760 if (got_int)
2761 return TRUE;
2762 }
2763 else
2764 {
2765 if (msg_col >= endcol) // wrap around is like starting a new line
2766 newline = FALSE;
2767 }
2768
2769 if (msg_col >= endcol) // output at least one space
2770 endcol = msg_col + 1;
2771 if (Columns <= endcol) // avoid hang for tiny window
2772 endcol = Columns - 1;
2773
2774 msg_advance(endcol);
2775
2776 // Show "xxx" with the attributes.
2777 if (!did_header)
2778 {
2779 if (endcol == Columns - 1 && endcol <= name_col)
2780 msg_putchar(' ');
2781 msg_puts_attr("xxx", syn_id2attr(id));
2782 msg_putchar(' ');
2783 }
2784
2785 return newline;
2786}
2787
2788/*
2789 * Set the attribute numbers for a highlight group.
2790 * Called after one of the attributes has changed.
2791 */
2792 static void
2793set_hl_attr(
2794 int idx) // index in array
2795{
2796 attrentry_T at_en;
2797 hl_group_T *sgp = HL_TABLE() + idx;
2798
2799 // The "Normal" group doesn't need an attribute number
2800 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
2801 return;
2802
2803#ifdef FEAT_GUI
2804 /*
2805 * For the GUI mode: If there are other than "normal" highlighting
2806 * attributes, need to allocate an attr number.
2807 */
2808 if (sgp->sg_gui_fg == INVALCOLOR
2809 && sgp->sg_gui_bg == INVALCOLOR
2810 && sgp->sg_gui_sp == INVALCOLOR
2811 && sgp->sg_font == NOFONT
2812# ifdef FEAT_XFONTSET
2813 && sgp->sg_fontset == NOFONTSET
2814# endif
2815 )
2816 {
2817 sgp->sg_gui_attr = sgp->sg_gui;
2818 }
2819 else
2820 {
2821 at_en.ae_attr = sgp->sg_gui;
2822 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
2823 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
2824 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
2825 at_en.ae_u.gui.font = sgp->sg_font;
2826# ifdef FEAT_XFONTSET
2827 at_en.ae_u.gui.fontset = sgp->sg_fontset;
2828# endif
2829 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
2830 }
2831#endif
2832 /*
2833 * For the term mode: If there are other than "normal" highlighting
2834 * attributes, need to allocate an attr number.
2835 */
2836 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
2837 sgp->sg_term_attr = sgp->sg_term;
2838 else
2839 {
2840 at_en.ae_attr = sgp->sg_term;
2841 at_en.ae_u.term.start = sgp->sg_start;
2842 at_en.ae_u.term.stop = sgp->sg_stop;
2843 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
2844 }
2845
2846 /*
2847 * For the color term mode: If there are other than "normal"
2848 * highlighting attributes, need to allocate an attr number.
2849 */
2850 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
2851# ifdef FEAT_TERMGUICOLORS
2852 && sgp->sg_gui_fg == INVALCOLOR
2853 && sgp->sg_gui_bg == INVALCOLOR
2854# endif
2855 )
2856 sgp->sg_cterm_attr = sgp->sg_cterm;
2857 else
2858 {
2859 at_en.ae_attr = sgp->sg_cterm;
2860 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
2861 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
2862# ifdef FEAT_TERMGUICOLORS
2863# ifdef MSWIN
2864# ifdef VIMDLL
2865 // Only when not using the GUI.
2866 if (!gui.in_use && !gui.starting)
2867# endif
2868 {
2869 int id;
2870 guicolor_T fg, bg;
2871
2872 id = syn_name2id((char_u *)"Normal");
2873 if (id > 0)
2874 {
2875 syn_id2colors(id, &fg, &bg);
2876 if (sgp->sg_gui_fg == INVALCOLOR)
2877 sgp->sg_gui_fg = fg;
2878 if (sgp->sg_gui_bg == INVALCOLOR)
2879 sgp->sg_gui_bg = bg;
2880 }
2881
2882 }
2883# endif
2884 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
2885 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
2886 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2887 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2888 {
2889 // If both fg and bg are invalid fall back to the cterm colors.
2890 // Helps when the GUI only uses an attribute, e.g. undercurl.
2891 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2892 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2893 }
2894# endif
2895 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2896 }
2897}
2898
2899/*
2900 * Lookup a highlight group name and return its ID.
2901 * If it is not found, 0 is returned.
2902 */
2903 int
2904syn_name2id(char_u *name)
2905{
2906 int i;
2907 char_u name_u[200];
2908
2909 // Avoid using stricmp() too much, it's slow on some systems
2910 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
2911 // don't deserve to be found!
2912 vim_strncpy(name_u, name, 199);
2913 vim_strup(name_u);
2914 for (i = highlight_ga.ga_len; --i >= 0; )
2915 if (HL_TABLE()[i].sg_name_u != NULL
2916 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2917 break;
2918 return i + 1;
2919}
2920
2921/*
2922 * Lookup a highlight group name and return its attributes.
2923 * Return zero if not found.
2924 */
2925 int
2926syn_name2attr(char_u *name)
2927{
2928 int id = syn_name2id(name);
2929
2930 if (id != 0)
2931 return syn_id2attr(id);
2932 return 0;
2933}
2934
2935#if defined(FEAT_EVAL) || defined(PROTO)
2936/*
2937 * Return TRUE if highlight group "name" exists.
2938 */
2939 int
2940highlight_exists(char_u *name)
2941{
2942 return (syn_name2id(name) > 0);
2943}
2944
2945# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
2946/*
2947 * Return the name of highlight group "id".
2948 * When not a valid ID return an empty string.
2949 */
2950 char_u *
2951syn_id2name(int id)
2952{
2953 if (id <= 0 || id > highlight_ga.ga_len)
2954 return (char_u *)"";
2955 return HL_TABLE()[id - 1].sg_name;
2956}
2957# endif
2958#endif
2959
2960/*
2961 * Like syn_name2id(), but take a pointer + length argument.
2962 */
2963 int
2964syn_namen2id(char_u *linep, int len)
2965{
2966 char_u *name;
2967 int id = 0;
2968
2969 name = vim_strnsave(linep, len);
2970 if (name != NULL)
2971 {
2972 id = syn_name2id(name);
2973 vim_free(name);
2974 }
2975 return id;
2976}
2977
2978/*
2979 * Find highlight group name in the table and return its ID.
2980 * The argument is a pointer to the name and the length of the name.
2981 * If it doesn't exist yet, a new entry is created.
2982 * Return 0 for failure.
2983 */
2984 int
2985syn_check_group(char_u *pp, int len)
2986{
2987 int id;
2988 char_u *name;
2989
2990 name = vim_strnsave(pp, len);
2991 if (name == NULL)
2992 return 0;
2993
2994 id = syn_name2id(name);
2995 if (id == 0) // doesn't exist yet
2996 id = syn_add_group(name);
2997 else
2998 vim_free(name);
2999 return id;
3000}
3001
3002/*
3003 * Add new highlight group and return its ID.
3004 * "name" must be an allocated string, it will be consumed.
3005 * Return 0 for failure.
3006 */
3007 static int
3008syn_add_group(char_u *name)
3009{
3010 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003011 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003012
3013 // Check that the name is ASCII letters, digits and underscore.
3014 for (p = name; *p != NUL; ++p)
3015 {
3016 if (!vim_isprintc(*p))
3017 {
3018 emsg(_("E669: Unprintable character in group name"));
3019 vim_free(name);
3020 return 0;
3021 }
3022 else if (!ASCII_ISALNUM(*p) && *p != '_')
3023 {
3024 // This is an error, but since there previously was no check only
3025 // give a warning.
3026 msg_source(HL_ATTR(HLF_W));
3027 msg(_("W18: Invalid character in group name"));
3028 break;
3029 }
3030 }
3031
3032 /*
3033 * First call for this growarray: init growing array.
3034 */
3035 if (highlight_ga.ga_data == NULL)
3036 {
3037 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3038 highlight_ga.ga_growsize = 10;
3039 }
3040
3041 if (highlight_ga.ga_len >= MAX_HL_ID)
3042 {
3043 emsg(_("E849: Too many highlight and syntax groups"));
3044 vim_free(name);
3045 return 0;
3046 }
3047
3048 /*
3049 * Make room for at least one other syntax_highlight entry.
3050 */
3051 if (ga_grow(&highlight_ga, 1) == FAIL)
3052 {
3053 vim_free(name);
3054 return 0;
3055 }
3056
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003057 name_up = vim_strsave_up(name);
3058 if (name_up == NULL)
3059 {
3060 vim_free(name);
3061 return 0;
3062 }
3063
Bram Moolenaara80faa82020-04-12 19:37:17 +02003064 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003065 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003066 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003067#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3068 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3069 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
3070# ifdef FEAT_GUI
3071 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
3072# endif
3073#endif
3074 ++highlight_ga.ga_len;
3075
3076 return highlight_ga.ga_len; // ID is index plus one
3077}
3078
3079/*
3080 * When, just after calling syn_add_group(), an error is discovered, this
3081 * function deletes the new name.
3082 */
3083 static void
3084syn_unadd_group(void)
3085{
3086 --highlight_ga.ga_len;
3087 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3088 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3089}
3090
3091/*
3092 * Translate a group ID to highlight attributes.
3093 */
3094 int
3095syn_id2attr(int hl_id)
3096{
3097 int attr;
3098 hl_group_T *sgp;
3099
3100 hl_id = syn_get_final_id(hl_id);
3101 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3102
3103#ifdef FEAT_GUI
3104 /*
3105 * Only use GUI attr when the GUI is being used.
3106 */
3107 if (gui.in_use)
3108 attr = sgp->sg_gui_attr;
3109 else
3110#endif
3111 if (IS_CTERM)
3112 attr = sgp->sg_cterm_attr;
3113 else
3114 attr = sgp->sg_term_attr;
3115
3116 return attr;
3117}
3118
3119#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3120/*
3121 * Get the GUI colors and attributes for a group ID.
3122 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3123 */
3124 int
3125syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3126{
3127 hl_group_T *sgp;
3128
3129 hl_id = syn_get_final_id(hl_id);
3130 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3131
3132 *fgp = sgp->sg_gui_fg;
3133 *bgp = sgp->sg_gui_bg;
3134 return sgp->sg_gui;
3135}
3136#endif
3137
3138#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003139 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3140 && defined(FEAT_TERMGUICOLORS)) \
3141 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003142 void
3143syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3144{
3145 hl_group_T *sgp;
3146
3147 hl_id = syn_get_final_id(hl_id);
3148 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3149 *fgp = sgp->sg_cterm_fg - 1;
3150 *bgp = sgp->sg_cterm_bg - 1;
3151}
3152#endif
3153
3154/*
3155 * Translate a group ID to the final group ID (following links).
3156 */
3157 int
3158syn_get_final_id(int hl_id)
3159{
3160 int count;
3161 hl_group_T *sgp;
3162
3163 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3164 return 0; // Can be called from eval!!
3165
3166 /*
3167 * Follow links until there is no more.
3168 * Look out for loops! Break after 100 links.
3169 */
3170 for (count = 100; --count >= 0; )
3171 {
3172 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3173 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3174 break;
3175 hl_id = sgp->sg_link;
3176 }
3177
3178 return hl_id;
3179}
3180
3181#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3182/*
3183 * Call this function just after the GUI has started.
3184 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3185 * It finds the font and color handles for the highlighting groups.
3186 */
3187 void
3188highlight_gui_started(void)
3189{
3190 int idx;
3191
3192 // First get the colors from the "Normal" and "Menu" group, if set
3193 if (USE_24BIT)
3194 set_normal_colors();
3195
3196 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3197 gui_do_one_color(idx, FALSE, FALSE);
3198
3199 highlight_changed();
3200}
3201
3202 static void
3203gui_do_one_color(
3204 int idx,
3205 int do_menu UNUSED, // TRUE: might set the menu font
3206 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3207{
3208 int didit = FALSE;
3209
3210# ifdef FEAT_GUI
3211# ifdef FEAT_TERMGUICOLORS
3212 if (gui.in_use)
3213# endif
3214 if (HL_TABLE()[idx].sg_font_name != NULL)
3215 {
3216 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3217 do_tooltip, TRUE);
3218 didit = TRUE;
3219 }
3220# endif
3221 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3222 {
3223 HL_TABLE()[idx].sg_gui_fg =
3224 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3225 didit = TRUE;
3226 }
3227 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3228 {
3229 HL_TABLE()[idx].sg_gui_bg =
3230 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3231 didit = TRUE;
3232 }
3233# ifdef FEAT_GUI
3234 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3235 {
3236 HL_TABLE()[idx].sg_gui_sp =
3237 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3238 didit = TRUE;
3239 }
3240# endif
3241 if (didit) // need to get a new attr number
3242 set_hl_attr(idx);
3243}
3244#endif
3245
3246#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3247/*
3248 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3249 */
3250 static void
3251combine_stl_hlt(
3252 int id,
3253 int id_S,
3254 int id_alt,
3255 int hlcnt,
3256 int i,
3257 int hlf,
3258 int *table)
3259{
3260 hl_group_T *hlt = HL_TABLE();
3261
3262 if (id_alt == 0)
3263 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003264 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003265 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3266 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3267# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3268 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3269# endif
3270 }
3271 else
3272 mch_memmove(&hlt[hlcnt + i],
3273 &hlt[id_alt - 1],
3274 sizeof(hl_group_T));
3275 hlt[hlcnt + i].sg_link = 0;
3276
3277 hlt[hlcnt + i].sg_term ^=
3278 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3279 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3280 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3281 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3282 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3283 hlt[hlcnt + i].sg_cterm ^=
3284 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3285 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3286 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3287 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3288 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3289# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3290 hlt[hlcnt + i].sg_gui ^=
3291 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3292# endif
3293# ifdef FEAT_GUI
3294 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3295 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3296 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3297 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3298 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3299 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3300 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3301 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3302# ifdef FEAT_XFONTSET
3303 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3304 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3305# endif
3306# endif
3307 highlight_ga.ga_len = hlcnt + i + 1;
3308 set_hl_attr(hlcnt + i); // At long last we can apply
3309 table[i] = syn_id2attr(hlcnt + i + 1);
3310}
3311#endif
3312
3313/*
3314 * Translate the 'highlight' option into attributes in highlight_attr[] and
3315 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3316 * corresponding highlights to use on top of HLF_SNC is computed.
3317 * Called only when the 'highlight' option has been changed and upon first
3318 * screen redraw after any :highlight command.
3319 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3320 */
3321 int
3322highlight_changed(void)
3323{
3324 int hlf;
3325 int i;
3326 char_u *p;
3327 int attr;
3328 char_u *end;
3329 int id;
3330#ifdef USER_HIGHLIGHT
3331 char_u userhl[30]; // use 30 to avoid compiler warning
3332# ifdef FEAT_STL_OPT
3333 int id_S = -1;
3334 int id_SNC = 0;
3335# ifdef FEAT_TERMINAL
3336 int id_ST = 0;
3337 int id_STNC = 0;
3338# endif
3339 int hlcnt;
3340# endif
3341#endif
3342 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3343
3344 need_highlight_changed = FALSE;
3345
3346 /*
3347 * Clear all attributes.
3348 */
3349 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3350 highlight_attr[hlf] = 0;
3351
3352 /*
3353 * First set all attributes to their default value.
3354 * Then use the attributes from the 'highlight' option.
3355 */
3356 for (i = 0; i < 2; ++i)
3357 {
3358 if (i)
3359 p = p_hl;
3360 else
3361 p = get_highlight_default();
3362 if (p == NULL) // just in case
3363 continue;
3364
3365 while (*p)
3366 {
3367 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3368 if (hl_flags[hlf] == *p)
3369 break;
3370 ++p;
3371 if (hlf == (int)HLF_COUNT || *p == NUL)
3372 return FAIL;
3373
3374 /*
3375 * Allow several hl_flags to be combined, like "bu" for
3376 * bold-underlined.
3377 */
3378 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003379 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003380 {
3381 if (VIM_ISWHITE(*p)) // ignore white space
3382 continue;
3383
3384 if (attr > HL_ALL) // Combination with ':' is not allowed.
3385 return FAIL;
3386
3387 switch (*p)
3388 {
3389 case 'b': attr |= HL_BOLD;
3390 break;
3391 case 'i': attr |= HL_ITALIC;
3392 break;
3393 case '-':
3394 case 'n': // no highlighting
3395 break;
3396 case 'r': attr |= HL_INVERSE;
3397 break;
3398 case 's': attr |= HL_STANDOUT;
3399 break;
3400 case 'u': attr |= HL_UNDERLINE;
3401 break;
3402 case 'c': attr |= HL_UNDERCURL;
3403 break;
3404 case 't': attr |= HL_STRIKETHROUGH;
3405 break;
3406 case ':': ++p; // highlight group name
3407 if (attr || *p == NUL) // no combinations
3408 return FAIL;
3409 end = vim_strchr(p, ',');
3410 if (end == NULL)
3411 end = p + STRLEN(p);
3412 id = syn_check_group(p, (int)(end - p));
3413 if (id == 0)
3414 return FAIL;
3415 attr = syn_id2attr(id);
3416 p = end - 1;
3417#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3418 if (hlf == (int)HLF_SNC)
3419 id_SNC = syn_get_final_id(id);
3420# ifdef FEAT_TERMINAL
3421 else if (hlf == (int)HLF_ST)
3422 id_ST = syn_get_final_id(id);
3423 else if (hlf == (int)HLF_STNC)
3424 id_STNC = syn_get_final_id(id);
3425# endif
3426 else if (hlf == (int)HLF_S)
3427 id_S = syn_get_final_id(id);
3428#endif
3429 break;
3430 default: return FAIL;
3431 }
3432 }
3433 highlight_attr[hlf] = attr;
3434
3435 p = skip_to_option_part(p); // skip comma and spaces
3436 }
3437 }
3438
3439#ifdef USER_HIGHLIGHT
3440 /*
3441 * Setup the user highlights
3442 *
3443 * Temporarily utilize 28 more hl entries:
3444 * 9 for User1-User9 combined with StatusLineNC
3445 * 9 for User1-User9 combined with StatusLineTerm
3446 * 9 for User1-User9 combined with StatusLineTermNC
3447 * 1 for StatusLine default
3448 * Have to be in there simultaneously in case of table overflows in
3449 * get_attr_entry()
3450 */
3451# ifdef FEAT_STL_OPT
3452 if (ga_grow(&highlight_ga, 28) == FAIL)
3453 return FAIL;
3454 hlcnt = highlight_ga.ga_len;
3455 if (id_S == -1)
3456 {
3457 // Make sure id_S is always valid to simplify code below. Use the last
3458 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003459 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003460 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3461 id_S = hlcnt + 19;
3462 }
3463# endif
3464 for (i = 0; i < 9; i++)
3465 {
3466 sprintf((char *)userhl, "User%d", i + 1);
3467 id = syn_name2id(userhl);
3468 if (id == 0)
3469 {
3470 highlight_user[i] = 0;
3471# ifdef FEAT_STL_OPT
3472 highlight_stlnc[i] = 0;
3473# ifdef FEAT_TERMINAL
3474 highlight_stlterm[i] = 0;
3475 highlight_stltermnc[i] = 0;
3476# endif
3477# endif
3478 }
3479 else
3480 {
3481 highlight_user[i] = syn_id2attr(id);
3482# ifdef FEAT_STL_OPT
3483 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3484 HLF_SNC, highlight_stlnc);
3485# ifdef FEAT_TERMINAL
3486 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3487 HLF_ST, highlight_stlterm);
3488 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3489 HLF_STNC, highlight_stltermnc);
3490# endif
3491# endif
3492 }
3493 }
3494# ifdef FEAT_STL_OPT
3495 highlight_ga.ga_len = hlcnt;
3496# endif
3497
3498#endif // USER_HIGHLIGHT
3499
3500 return OK;
3501}
3502
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003503static void highlight_list(void);
3504static void highlight_list_two(int cnt, int attr);
3505
3506/*
3507 * Handle command line completion for :highlight command.
3508 */
3509 void
3510set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3511{
3512 char_u *p;
3513
3514 // Default: expand group names
3515 xp->xp_context = EXPAND_HIGHLIGHT;
3516 xp->xp_pattern = arg;
3517 include_link = 2;
3518 include_default = 1;
3519
3520 // (part of) subcommand already typed
3521 if (*arg != NUL)
3522 {
3523 p = skiptowhite(arg);
3524 if (*p != NUL) // past "default" or group name
3525 {
3526 include_default = 0;
3527 if (STRNCMP("default", arg, p - arg) == 0)
3528 {
3529 arg = skipwhite(p);
3530 xp->xp_pattern = arg;
3531 p = skiptowhite(arg);
3532 }
3533 if (*p != NUL) // past group name
3534 {
3535 include_link = 0;
3536 if (arg[1] == 'i' && arg[0] == 'N')
3537 highlight_list();
3538 if (STRNCMP("link", arg, p - arg) == 0
3539 || STRNCMP("clear", arg, p - arg) == 0)
3540 {
3541 xp->xp_pattern = skipwhite(p);
3542 p = skiptowhite(xp->xp_pattern);
3543 if (*p != NUL) // past first group name
3544 {
3545 xp->xp_pattern = skipwhite(p);
3546 p = skiptowhite(xp->xp_pattern);
3547 }
3548 }
3549 if (*p != NUL) // past group name(s)
3550 xp->xp_context = EXPAND_NOTHING;
3551 }
3552 }
3553 }
3554}
3555
3556/*
3557 * List highlighting matches in a nice way.
3558 */
3559 static void
3560highlight_list(void)
3561{
3562 int i;
3563
3564 for (i = 10; --i >= 0; )
3565 highlight_list_two(i, HL_ATTR(HLF_D));
3566 for (i = 40; --i >= 0; )
3567 highlight_list_two(99, 0);
3568}
3569
3570 static void
3571highlight_list_two(int cnt, int attr)
3572{
3573 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3574 msg_clr_eos();
3575 out_flush();
3576 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3577}
3578
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003579/*
3580 * Function given to ExpandGeneric() to obtain the list of group names.
3581 */
3582 char_u *
3583get_highlight_name(expand_T *xp UNUSED, int idx)
3584{
3585 return get_highlight_name_ext(xp, idx, TRUE);
3586}
3587
3588/*
3589 * Obtain a highlight group name.
3590 * When "skip_cleared" is TRUE don't return a cleared entry.
3591 */
3592 char_u *
3593get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3594{
3595 if (idx < 0)
3596 return NULL;
3597
3598 // Items are never removed from the table, skip the ones that were
3599 // cleared.
3600 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3601 return (char_u *)"";
3602
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003603 if (idx == highlight_ga.ga_len && include_none != 0)
3604 return (char_u *)"none";
3605 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3606 return (char_u *)"default";
3607 if (idx == highlight_ga.ga_len + include_none + include_default
3608 && include_link != 0)
3609 return (char_u *)"link";
3610 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3611 && include_link != 0)
3612 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003613 if (idx >= highlight_ga.ga_len)
3614 return NULL;
3615 return HL_TABLE()[idx].sg_name;
3616}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003617
3618#if defined(FEAT_GUI) || defined(PROTO)
3619/*
3620 * Free all the highlight group fonts.
3621 * Used when quitting for systems which need it.
3622 */
3623 void
3624free_highlight_fonts(void)
3625{
3626 int idx;
3627
3628 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3629 {
3630 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3631 HL_TABLE()[idx].sg_font = NOFONT;
3632# ifdef FEAT_XFONTSET
3633 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3634 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3635# endif
3636 }
3637
3638 gui_mch_free_font(gui.norm_font);
3639# ifdef FEAT_XFONTSET
3640 gui_mch_free_fontset(gui.fontset);
3641# endif
3642# ifndef FEAT_GUI_GTK
3643 gui_mch_free_font(gui.bold_font);
3644 gui_mch_free_font(gui.ital_font);
3645 gui_mch_free_font(gui.boldital_font);
3646# endif
3647}
3648#endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003649
3650
3651#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003652
3653# define SEARCH_HL_PRIORITY 0
3654
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003655/*
3656 * Add match to the match list of window 'wp'. The pattern 'pat' will be
3657 * highlighted with the group 'grp' with priority 'prio'.
3658 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3659 * If no particular ID is desired, -1 must be specified for 'id'.
3660 * Return ID of added match, -1 on failure.
3661 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003662 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003663match_add(
3664 win_T *wp,
3665 char_u *grp,
3666 char_u *pat,
3667 int prio,
3668 int id,
3669 list_T *pos_list,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003670 char_u *conceal_char UNUSED) // pointer to conceal replacement char
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003671{
3672 matchitem_T *cur;
3673 matchitem_T *prev;
3674 matchitem_T *m;
3675 int hlg_id;
3676 regprog_T *regprog = NULL;
3677 int rtype = SOME_VALID;
3678
3679 if (*grp == NUL || (pat != NULL && *pat == NUL))
3680 return -1;
3681 if (id < -1 || id == 0)
3682 {
Bram Moolenaar32aa1022019-11-02 22:54:41 +01003683 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"),
3684 id);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003685 return -1;
3686 }
3687 if (id != -1)
3688 {
3689 cur = wp->w_match_head;
3690 while (cur != NULL)
3691 {
3692 if (cur->id == id)
3693 {
3694 semsg(_("E801: ID already taken: %d"), id);
3695 return -1;
3696 }
3697 cur = cur->next;
3698 }
3699 }
3700 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3701 {
3702 semsg(_(e_nogroup), grp);
3703 return -1;
3704 }
3705 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3706 {
3707 semsg(_(e_invarg2), pat);
3708 return -1;
3709 }
3710
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003711 // Find available match ID.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003712 while (id == -1)
3713 {
3714 cur = wp->w_match_head;
3715 while (cur != NULL && cur->id != wp->w_next_match_id)
3716 cur = cur->next;
3717 if (cur == NULL)
3718 id = wp->w_next_match_id;
3719 wp->w_next_match_id++;
3720 }
3721
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003722 // Build new match.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003723 m = ALLOC_CLEAR_ONE(matchitem_T);
3724 m->id = id;
3725 m->priority = prio;
3726 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3727 m->hlg_id = hlg_id;
3728 m->match.regprog = regprog;
3729 m->match.rmm_ic = FALSE;
3730 m->match.rmm_maxcol = 0;
3731# if defined(FEAT_CONCEAL)
3732 m->conceal_char = 0;
3733 if (conceal_char != NULL)
3734 m->conceal_char = (*mb_ptr2char)(conceal_char);
3735# endif
3736
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003737 // Set up position matches
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003738 if (pos_list != NULL)
3739 {
3740 linenr_T toplnum = 0;
3741 linenr_T botlnum = 0;
3742 listitem_T *li;
3743 int i;
3744
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02003745 CHECK_LIST_MATERIALIZE(pos_list);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003746 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3747 i++, li = li->li_next)
3748 {
3749 linenr_T lnum = 0;
3750 colnr_T col = 0;
3751 int len = 1;
3752 list_T *subl;
3753 listitem_T *subli;
3754 int error = FALSE;
3755
3756 if (li->li_tv.v_type == VAR_LIST)
3757 {
3758 subl = li->li_tv.vval.v_list;
3759 if (subl == NULL)
3760 goto fail;
3761 subli = subl->lv_first;
3762 if (subli == NULL)
3763 goto fail;
3764 lnum = tv_get_number_chk(&subli->li_tv, &error);
3765 if (error == TRUE)
3766 goto fail;
3767 if (lnum == 0)
3768 {
3769 --i;
3770 continue;
3771 }
3772 m->pos.pos[i].lnum = lnum;
3773 subli = subli->li_next;
3774 if (subli != NULL)
3775 {
3776 col = tv_get_number_chk(&subli->li_tv, &error);
3777 if (error == TRUE)
3778 goto fail;
3779 subli = subli->li_next;
3780 if (subli != NULL)
3781 {
3782 len = tv_get_number_chk(&subli->li_tv, &error);
3783 if (error == TRUE)
3784 goto fail;
3785 }
3786 }
3787 m->pos.pos[i].col = col;
3788 m->pos.pos[i].len = len;
3789 }
3790 else if (li->li_tv.v_type == VAR_NUMBER)
3791 {
3792 if (li->li_tv.vval.v_number == 0)
3793 {
3794 --i;
3795 continue;
3796 }
3797 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3798 m->pos.pos[i].col = 0;
3799 m->pos.pos[i].len = 0;
3800 }
3801 else
3802 {
Bram Moolenaar1500a422019-12-24 15:38:21 +01003803 emsg(_("E290: List or number required"));
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003804 goto fail;
3805 }
3806 if (toplnum == 0 || lnum < toplnum)
3807 toplnum = lnum;
3808 if (botlnum == 0 || lnum >= botlnum)
3809 botlnum = lnum + 1;
3810 }
3811
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003812 // Calculate top and bottom lines for redrawing area
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003813 if (toplnum != 0)
3814 {
3815 if (wp->w_buffer->b_mod_set)
3816 {
3817 if (wp->w_buffer->b_mod_top > toplnum)
3818 wp->w_buffer->b_mod_top = toplnum;
3819 if (wp->w_buffer->b_mod_bot < botlnum)
3820 wp->w_buffer->b_mod_bot = botlnum;
3821 }
3822 else
3823 {
3824 wp->w_buffer->b_mod_set = TRUE;
3825 wp->w_buffer->b_mod_top = toplnum;
3826 wp->w_buffer->b_mod_bot = botlnum;
3827 wp->w_buffer->b_mod_xlines = 0;
3828 }
3829 m->pos.toplnum = toplnum;
3830 m->pos.botlnum = botlnum;
3831 rtype = VALID;
3832 }
3833 }
3834
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003835 // Insert new match. The match list is in ascending order with regard to
3836 // the match priorities.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003837 cur = wp->w_match_head;
3838 prev = cur;
3839 while (cur != NULL && prio >= cur->priority)
3840 {
3841 prev = cur;
3842 cur = cur->next;
3843 }
3844 if (cur == prev)
3845 wp->w_match_head = m;
3846 else
3847 prev->next = m;
3848 m->next = cur;
3849
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003850 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003851 return id;
3852
3853fail:
3854 vim_free(m);
3855 return -1;
3856}
3857
3858/*
3859 * Delete match with ID 'id' in the match list of window 'wp'.
3860 * Print error messages if 'perr' is TRUE.
3861 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003862 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003863match_delete(win_T *wp, int id, int perr)
3864{
3865 matchitem_T *cur = wp->w_match_head;
3866 matchitem_T *prev = cur;
3867 int rtype = SOME_VALID;
3868
3869 if (id < 1)
3870 {
3871 if (perr == TRUE)
3872 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3873 id);
3874 return -1;
3875 }
3876 while (cur != NULL && cur->id != id)
3877 {
3878 prev = cur;
3879 cur = cur->next;
3880 }
3881 if (cur == NULL)
3882 {
3883 if (perr == TRUE)
3884 semsg(_("E803: ID not found: %d"), id);
3885 return -1;
3886 }
3887 if (cur == prev)
3888 wp->w_match_head = cur->next;
3889 else
3890 prev->next = cur->next;
3891 vim_regfree(cur->match.regprog);
3892 vim_free(cur->pattern);
3893 if (cur->pos.toplnum != 0)
3894 {
3895 if (wp->w_buffer->b_mod_set)
3896 {
3897 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3898 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3899 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3900 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3901 }
3902 else
3903 {
3904 wp->w_buffer->b_mod_set = TRUE;
3905 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3906 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3907 wp->w_buffer->b_mod_xlines = 0;
3908 }
3909 rtype = VALID;
3910 }
3911 vim_free(cur);
Bram Moolenaar06029a82019-07-24 14:25:26 +02003912 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003913 return 0;
3914}
3915
3916/*
3917 * Delete all matches in the match list of window 'wp'.
3918 */
3919 void
3920clear_matches(win_T *wp)
3921{
3922 matchitem_T *m;
3923
3924 while (wp->w_match_head != NULL)
3925 {
3926 m = wp->w_match_head->next;
3927 vim_regfree(wp->w_match_head->match.regprog);
3928 vim_free(wp->w_match_head->pattern);
3929 vim_free(wp->w_match_head);
3930 wp->w_match_head = m;
3931 }
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003932 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003933}
3934
3935/*
3936 * Get match from ID 'id' in window 'wp'.
3937 * Return NULL if match not found.
3938 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003939 static matchitem_T *
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003940get_match(win_T *wp, int id)
3941{
3942 matchitem_T *cur = wp->w_match_head;
3943
3944 while (cur != NULL && cur->id != id)
3945 cur = cur->next;
3946 return cur;
3947}
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003948
3949/*
3950 * Init for calling prepare_search_hl().
3951 */
3952 void
3953init_search_hl(win_T *wp, match_T *search_hl)
3954{
3955 matchitem_T *cur;
3956
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003957 // Setup for match and 'hlsearch' highlighting. Disable any previous
3958 // match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003959 cur = wp->w_match_head;
3960 while (cur != NULL)
3961 {
3962 cur->hl.rm = cur->match;
3963 if (cur->hlg_id == 0)
3964 cur->hl.attr = 0;
3965 else
3966 cur->hl.attr = syn_id2attr(cur->hlg_id);
3967 cur->hl.buf = wp->w_buffer;
3968 cur->hl.lnum = 0;
3969 cur->hl.first_lnum = 0;
3970# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003971 // Set the time limit to 'redrawtime'.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003972 profile_setlimit(p_rdt, &(cur->hl.tm));
3973# endif
3974 cur = cur->next;
3975 }
3976 search_hl->buf = wp->w_buffer;
3977 search_hl->lnum = 0;
3978 search_hl->first_lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003979 // time limit is set at the toplevel, for all windows
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003980}
3981
3982/*
3983 * If there is a match fill "shl" and return one.
3984 * Return zero otherwise.
3985 */
3986 static int
3987next_search_hl_pos(
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003988 match_T *shl, // points to a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003989 linenr_T lnum,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003990 posmatch_T *posmatch, // match positions
3991 colnr_T mincol) // minimal column for a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003992{
3993 int i;
3994 int found = -1;
3995
3996 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
3997 {
3998 llpos_T *pos = &posmatch->pos[i];
3999
4000 if (pos->lnum == 0)
4001 break;
4002 if (pos->len == 0 && pos->col < mincol)
4003 continue;
4004 if (pos->lnum == lnum)
4005 {
4006 if (found >= 0)
4007 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004008 // if this match comes before the one at "found" then swap
4009 // them
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004010 if (pos->col < posmatch->pos[found].col)
4011 {
4012 llpos_T tmp = *pos;
4013
4014 *pos = posmatch->pos[found];
4015 posmatch->pos[found] = tmp;
4016 }
4017 }
4018 else
4019 found = i;
4020 }
4021 }
4022 posmatch->cur = 0;
4023 if (found >= 0)
4024 {
4025 colnr_T start = posmatch->pos[found].col == 0
4026 ? 0 : posmatch->pos[found].col - 1;
4027 colnr_T end = posmatch->pos[found].col == 0
4028 ? MAXCOL : start + posmatch->pos[found].len;
4029
4030 shl->lnum = lnum;
4031 shl->rm.startpos[0].lnum = 0;
4032 shl->rm.startpos[0].col = start;
4033 shl->rm.endpos[0].lnum = 0;
4034 shl->rm.endpos[0].col = end;
4035 shl->is_addpos = TRUE;
4036 posmatch->cur = found + 1;
4037 return 1;
4038 }
4039 return 0;
4040}
4041
4042/*
4043 * Search for a next 'hlsearch' or match.
4044 * Uses shl->buf.
4045 * Sets shl->lnum and shl->rm contents.
4046 * Note: Assumes a previous match is always before "lnum", unless
4047 * shl->lnum is zero.
4048 * Careful: Any pointers for buffer lines will become invalid.
4049 */
4050 static void
4051next_search_hl(
4052 win_T *win,
4053 match_T *search_hl,
4054 match_T *shl, // points to search_hl or a match
4055 linenr_T lnum,
4056 colnr_T mincol, // minimal column for a match
4057 matchitem_T *cur) // to retrieve match positions if any
4058{
4059 linenr_T l;
4060 colnr_T matchcol;
4061 long nmatched;
Bram Moolenaar53989552019-12-23 22:59:18 +01004062 int called_emsg_before = called_emsg;
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004063
4064 // for :{range}s/pat only highlight inside the range
4065 if (lnum < search_first_line || lnum > search_last_line)
4066 {
4067 shl->lnum = 0;
4068 return;
4069 }
4070
4071 if (shl->lnum != 0)
4072 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004073 // Check for three situations:
4074 // 1. If the "lnum" is below a previous match, start a new search.
4075 // 2. If the previous match includes "mincol", use it.
4076 // 3. Continue after the previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004077 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4078 if (lnum > l)
4079 shl->lnum = 0;
4080 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4081 return;
4082 }
4083
4084 /*
4085 * Repeat searching for a match until one is found that includes "mincol"
4086 * or none is found in this line.
4087 */
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004088 for (;;)
4089 {
4090# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004091 // Stop searching after passing the time limit.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004092 if (profile_passed_limit(&(shl->tm)))
4093 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004094 shl->lnum = 0; // no match found in time
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004095 break;
4096 }
4097# endif
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004098 // Three situations:
4099 // 1. No useful previous match: search from start of line.
4100 // 2. Not Vi compatible or empty match: continue at next character.
4101 // Break the loop if this is beyond the end of the line.
4102 // 3. Vi compatible searching: continue at end of previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004103 if (shl->lnum == 0)
4104 matchcol = 0;
4105 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4106 || (shl->rm.endpos[0].lnum == 0
4107 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4108 {
4109 char_u *ml;
4110
4111 matchcol = shl->rm.startpos[0].col;
4112 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4113 if (*ml == NUL)
4114 {
4115 ++matchcol;
4116 shl->lnum = 0;
4117 break;
4118 }
4119 if (has_mbyte)
4120 matchcol += mb_ptr2len(ml);
4121 else
4122 ++matchcol;
4123 }
4124 else
4125 matchcol = shl->rm.endpos[0].col;
4126
4127 shl->lnum = lnum;
4128 if (shl->rm.regprog != NULL)
4129 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004130 // Remember whether shl->rm is using a copy of the regprog in
4131 // cur->match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004132 int regprog_is_copy = (shl != search_hl && cur != NULL
4133 && shl == &cur->hl
4134 && cur->match.regprog == cur->hl.rm.regprog);
4135 int timed_out = FALSE;
4136
4137 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4138 matchcol,
4139#ifdef FEAT_RELTIME
4140 &(shl->tm), &timed_out
4141#else
4142 NULL, NULL
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004143#endif
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004144 );
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004145 // Copy the regprog, in case it got freed and recompiled.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004146 if (regprog_is_copy)
4147 cur->match.regprog = cur->hl.rm.regprog;
4148
Bram Moolenaar53989552019-12-23 22:59:18 +01004149 if (called_emsg > called_emsg_before || got_int || timed_out)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004150 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004151 // Error while handling regexp: stop using this regexp.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004152 if (shl == search_hl)
4153 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004154 // don't free regprog in the match list, it's a copy
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004155 vim_regfree(shl->rm.regprog);
4156 set_no_hlsearch(TRUE);
4157 }
4158 shl->rm.regprog = NULL;
4159 shl->lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004160 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004161 break;
4162 }
4163 }
4164 else if (cur != NULL)
4165 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4166 else
4167 nmatched = 0;
4168 if (nmatched == 0)
4169 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004170 shl->lnum = 0; // no match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004171 break;
4172 }
4173 if (shl->rm.startpos[0].lnum > 0
4174 || shl->rm.startpos[0].col >= mincol
4175 || nmatched > 1
4176 || shl->rm.endpos[0].col > mincol)
4177 {
4178 shl->lnum += shl->rm.startpos[0].lnum;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004179 break; // useful match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004180 }
4181 }
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004182}
4183
4184/*
4185 * Advance to the match in window "wp" line "lnum" or past it.
4186 */
4187 void
4188prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4189{
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004190 matchitem_T *cur; // points to the match list
4191 match_T *shl; // points to search_hl or a match
4192 int shl_flag; // flag to indicate whether search_hl
4193 // has been processed or not
4194 int pos_inprogress; // marks that position match search is
4195 // in progress
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004196 int n;
4197
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004198 // When using a multi-line pattern, start searching at the top
4199 // of the window or just after a closed fold.
4200 // Do this both for search_hl and the match list.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004201 cur = wp->w_match_head;
4202 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4203 while (cur != NULL || shl_flag == FALSE)
4204 {
4205 if (shl_flag == FALSE)
4206 {
4207 shl = search_hl;
4208 shl_flag = TRUE;
4209 }
4210 else
4211 shl = &cur->hl;
4212 if (shl->rm.regprog != NULL
4213 && shl->lnum == 0
4214 && re_multiline(shl->rm.regprog))
4215 {
4216 if (shl->first_lnum == 0)
4217 {
4218# ifdef FEAT_FOLDING
4219 for (shl->first_lnum = lnum;
4220 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4221 if (hasFoldingWin(wp, shl->first_lnum - 1,
4222 NULL, NULL, TRUE, NULL))
4223 break;
4224# else
4225 shl->first_lnum = wp->w_topline;
4226# endif
4227 }
4228 if (cur != NULL)
4229 cur->pos.cur = 0;
4230 pos_inprogress = TRUE;
4231 n = 0;
4232 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4233 || (cur != NULL && pos_inprogress)))
4234 {
4235 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4236 shl == search_hl ? NULL : cur);
4237 pos_inprogress = cur == NULL || cur->pos.cur == 0
4238 ? FALSE : TRUE;
4239 if (shl->lnum != 0)
4240 {
4241 shl->first_lnum = shl->lnum
4242 + shl->rm.endpos[0].lnum
4243 - shl->rm.startpos[0].lnum;
4244 n = shl->rm.endpos[0].col;
4245 }
4246 else
4247 {
4248 ++shl->first_lnum;
4249 n = 0;
4250 }
4251 }
4252 }
4253 if (shl != search_hl && cur != NULL)
4254 cur = cur->next;
4255 }
4256}
4257
4258/*
4259 * Prepare for 'hlsearch' and match highlighting in one window line.
4260 * Return TRUE if there is such highlighting and set "search_attr" to the
4261 * current highlight attribute.
4262 */
4263 int
4264prepare_search_hl_line(
4265 win_T *wp,
4266 linenr_T lnum,
4267 colnr_T mincol,
4268 char_u **line,
4269 match_T *search_hl,
4270 int *search_attr)
4271{
4272 matchitem_T *cur; // points to the match list
4273 match_T *shl; // points to search_hl or a match
4274 int shl_flag; // flag to indicate whether search_hl
4275 // has been processed or not
4276 int area_highlighting = FALSE;
4277
4278 /*
4279 * Handle highlighting the last used search pattern and matches.
4280 * Do this for both search_hl and the match list.
4281 * Do not use search_hl in a popup window.
4282 */
4283 cur = wp->w_match_head;
4284 shl_flag = WIN_IS_POPUP(wp);
4285 while (cur != NULL || shl_flag == FALSE)
4286 {
4287 if (shl_flag == FALSE)
4288 {
4289 shl = search_hl;
4290 shl_flag = TRUE;
4291 }
4292 else
4293 shl = &cur->hl;
4294 shl->startcol = MAXCOL;
4295 shl->endcol = MAXCOL;
4296 shl->attr_cur = 0;
4297 shl->is_addpos = FALSE;
4298 if (cur != NULL)
4299 cur->pos.cur = 0;
4300 next_search_hl(wp, search_hl, shl, lnum, mincol,
4301 shl == search_hl ? NULL : cur);
4302
4303 // Need to get the line again, a multi-line regexp may have made it
4304 // invalid.
4305 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4306
4307 if (shl->lnum != 0 && shl->lnum <= lnum)
4308 {
4309 if (shl->lnum == lnum)
4310 shl->startcol = shl->rm.startpos[0].col;
4311 else
4312 shl->startcol = 0;
4313 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4314 - shl->rm.startpos[0].lnum)
4315 shl->endcol = shl->rm.endpos[0].col;
4316 else
4317 shl->endcol = MAXCOL;
4318 // Highlight one character for an empty match.
4319 if (shl->startcol == shl->endcol)
4320 {
4321 if (has_mbyte && (*line)[shl->endcol] != NUL)
4322 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4323 else
4324 ++shl->endcol;
4325 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004326 if ((long)shl->startcol < mincol) // match at leftcol
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004327 {
4328 shl->attr_cur = shl->attr;
4329 *search_attr = shl->attr;
4330 }
4331 area_highlighting = TRUE;
4332 }
4333 if (shl != search_hl && cur != NULL)
4334 cur = cur->next;
4335 }
4336 return area_highlighting;
4337}
4338
4339/*
4340 * For a position in a line: Check for start/end of 'hlsearch' and other
4341 * matches.
4342 * After end, check for start/end of next match.
4343 * When another match, have to check for start again.
4344 * Watch out for matching an empty string!
Bram Moolenaar32aa1022019-11-02 22:54:41 +01004345 * Return the updated search_attr.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004346 */
4347 int
4348update_search_hl(
4349 win_T *wp,
4350 linenr_T lnum,
4351 colnr_T col,
4352 char_u **line,
4353 match_T *search_hl,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004354 int *has_match_conc UNUSED,
4355 int *match_conc UNUSED,
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004356 int did_line_attr,
4357 int lcs_eol_one)
4358{
4359 matchitem_T *cur; // points to the match list
4360 match_T *shl; // points to search_hl or a match
4361 int shl_flag; // flag to indicate whether search_hl
4362 // has been processed or not
4363 int pos_inprogress; // marks that position match search is in
4364 // progress
4365 int search_attr = 0;
4366
4367
4368 // Do this for 'search_hl' and the match list (ordered by priority).
4369 cur = wp->w_match_head;
4370 shl_flag = WIN_IS_POPUP(wp);
4371 while (cur != NULL || shl_flag == FALSE)
4372 {
4373 if (shl_flag == FALSE
4374 && ((cur != NULL
4375 && cur->priority > SEARCH_HL_PRIORITY)
4376 || cur == NULL))
4377 {
4378 shl = search_hl;
4379 shl_flag = TRUE;
4380 }
4381 else
4382 shl = &cur->hl;
4383 if (cur != NULL)
4384 cur->pos.cur = 0;
4385 pos_inprogress = TRUE;
4386 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4387 {
4388 if (shl->startcol != MAXCOL
4389 && col >= shl->startcol
4390 && col < shl->endcol)
4391 {
Bram Moolenaar1614a142019-10-06 22:00:13 +02004392 int next_col = col + mb_ptr2len(*line + col);
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004393
4394 if (shl->endcol < next_col)
4395 shl->endcol = next_col;
4396 shl->attr_cur = shl->attr;
4397# ifdef FEAT_CONCEAL
4398 // Match with the "Conceal" group results in hiding
4399 // the match.
4400 if (cur != NULL
4401 && shl != search_hl
4402 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4403 {
4404 *has_match_conc = col == shl->startcol ? 2 : 1;
4405 *match_conc = cur->conceal_char;
4406 }
4407 else
4408 *has_match_conc = *match_conc = 0;
4409# endif
4410 }
4411 else if (col == shl->endcol)
4412 {
4413 shl->attr_cur = 0;
4414 next_search_hl(wp, search_hl, shl, lnum, col,
4415 shl == search_hl ? NULL : cur);
4416 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4417
4418 // Need to get the line again, a multi-line regexp may have
4419 // made it invalid.
4420 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4421
4422 if (shl->lnum == lnum)
4423 {
4424 shl->startcol = shl->rm.startpos[0].col;
4425 if (shl->rm.endpos[0].lnum == 0)
4426 shl->endcol = shl->rm.endpos[0].col;
4427 else
4428 shl->endcol = MAXCOL;
4429
4430 if (shl->startcol == shl->endcol)
4431 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004432 // highlight empty match, try again after
4433 // it
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004434 if (has_mbyte)
4435 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4436 else
4437 ++shl->endcol;
4438 }
4439
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004440 // Loop to check if the match starts at the
4441 // current position
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004442 continue;
4443 }
4444 }
4445 break;
4446 }
4447 if (shl != search_hl && cur != NULL)
4448 cur = cur->next;
4449 }
4450
4451 // Use attributes from match with highest priority among 'search_hl' and
4452 // the match list.
4453 cur = wp->w_match_head;
4454 shl_flag = WIN_IS_POPUP(wp);
4455 while (cur != NULL || shl_flag == FALSE)
4456 {
4457 if (shl_flag == FALSE
4458 && ((cur != NULL
4459 && cur->priority > SEARCH_HL_PRIORITY)
4460 || cur == NULL))
4461 {
4462 shl = search_hl;
4463 shl_flag = TRUE;
4464 }
4465 else
4466 shl = &cur->hl;
4467 if (shl->attr_cur != 0)
4468 search_attr = shl->attr_cur;
4469 if (shl != search_hl && cur != NULL)
4470 cur = cur->next;
4471 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004472 // Only highlight one character after the last column.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004473 if (*(*line + col) == NUL && (did_line_attr >= 1
4474 || (wp->w_p_list && lcs_eol_one == -1)))
4475 search_attr = 0;
4476 return search_attr;
4477}
4478
4479 int
4480get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4481{
4482 long prevcol = curcol;
4483 int prevcol_hl_flag = FALSE;
4484 matchitem_T *cur; // points to the match list
4485
4486 // we're not really at that column when skipping some text
4487 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4488 ++prevcol;
4489
4490 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4491 prevcol_hl_flag = TRUE;
4492 else
4493 {
4494 cur = wp->w_match_head;
4495 while (cur != NULL)
4496 {
4497 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4498 {
4499 prevcol_hl_flag = TRUE;
4500 break;
4501 }
4502 cur = cur->next;
4503 }
4504 }
4505 return prevcol_hl_flag;
4506}
4507
4508/*
4509 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4510 * or match highlighting.
4511 */
4512 void
4513get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4514{
4515 matchitem_T *cur; // points to the match list
4516 match_T *shl; // points to search_hl or a match
4517 int shl_flag; // flag to indicate whether search_hl
4518 // has been processed or not
4519
4520 cur = wp->w_match_head;
4521 shl_flag = WIN_IS_POPUP(wp);
4522 while (cur != NULL || shl_flag == FALSE)
4523 {
4524 if (shl_flag == FALSE
4525 && ((cur != NULL
4526 && cur->priority > SEARCH_HL_PRIORITY)
4527 || cur == NULL))
4528 {
4529 shl = search_hl;
4530 shl_flag = TRUE;
4531 }
4532 else
4533 shl = &cur->hl;
4534 if (col - 1 == (long)shl->startcol
4535 && (shl == search_hl || !shl->is_addpos))
4536 *char_attr = shl->attr;
4537 if (shl != search_hl && cur != NULL)
4538 cur = cur->next;
4539 }
4540}
4541
4542#endif // FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004543
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004544#if defined(FEAT_EVAL) || defined(PROTO)
4545# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004546 static int
4547matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4548{
4549 dictitem_T *di;
4550
4551 if (tv->v_type != VAR_DICT)
4552 {
4553 emsg(_(e_dictreq));
4554 return FAIL;
4555 }
4556
4557 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4558 *conceal_char = dict_get_string(tv->vval.v_dict,
4559 (char_u *)"conceal", FALSE);
4560
4561 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4562 {
4563 *win = find_win_by_nr_or_id(&di->di_tv);
4564 if (*win == NULL)
4565 {
4566 emsg(_(e_invalwindow));
4567 return FAIL;
4568 }
4569 }
4570
4571 return OK;
4572}
4573#endif
4574
4575/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004576 * "clearmatches()" function
4577 */
4578 void
4579f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4580{
4581#ifdef FEAT_SEARCH_EXTRA
4582 win_T *win = get_optional_window(argvars, 0);
4583
4584 if (win != NULL)
4585 clear_matches(win);
4586#endif
4587}
4588
4589/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004590 * "getmatches()" function
4591 */
4592 void
4593f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4594{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004595# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004596 dict_T *dict;
4597 matchitem_T *cur;
4598 int i;
4599 win_T *win = get_optional_window(argvars, 0);
4600
4601 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4602 return;
4603
4604 cur = win->w_match_head;
4605 while (cur != NULL)
4606 {
4607 dict = dict_alloc();
4608 if (dict == NULL)
4609 return;
4610 if (cur->match.regprog == NULL)
4611 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004612 // match added with matchaddpos()
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004613 for (i = 0; i < MAXPOSMATCH; ++i)
4614 {
4615 llpos_T *llpos;
4616 char buf[30]; // use 30 to avoid compiler warning
4617 list_T *l;
4618
4619 llpos = &cur->pos.pos[i];
4620 if (llpos->lnum == 0)
4621 break;
4622 l = list_alloc();
4623 if (l == NULL)
4624 break;
4625 list_append_number(l, (varnumber_T)llpos->lnum);
4626 if (llpos->col > 0)
4627 {
4628 list_append_number(l, (varnumber_T)llpos->col);
4629 list_append_number(l, (varnumber_T)llpos->len);
4630 }
4631 sprintf(buf, "pos%d", i + 1);
4632 dict_add_list(dict, buf, l);
4633 }
4634 }
4635 else
4636 {
4637 dict_add_string(dict, "pattern", cur->pattern);
4638 }
4639 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4640 dict_add_number(dict, "priority", (long)cur->priority);
4641 dict_add_number(dict, "id", (long)cur->id);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004642# if defined(FEAT_CONCEAL)
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004643 if (cur->conceal_char)
4644 {
4645 char_u buf[MB_MAXBYTES + 1];
4646
4647 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4648 dict_add_string(dict, "conceal", (char_u *)&buf);
4649 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004650# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004651 list_append_dict(rettv->vval.v_list, dict);
4652 cur = cur->next;
4653 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004654# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004655}
4656
4657/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004658 * "setmatches()" function
4659 */
4660 void
4661f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4662{
4663#ifdef FEAT_SEARCH_EXTRA
4664 list_T *l;
4665 listitem_T *li;
4666 dict_T *d;
4667 list_T *s = NULL;
4668 win_T *win = get_optional_window(argvars, 1);
4669
4670 rettv->vval.v_number = -1;
4671 if (argvars[0].v_type != VAR_LIST)
4672 {
4673 emsg(_(e_listreq));
4674 return;
4675 }
4676 if (win == NULL)
4677 return;
4678
4679 if ((l = argvars[0].vval.v_list) != NULL)
4680 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004681 // To some extent make sure that we are dealing with a list from
4682 // "getmatches()".
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004683 li = l->lv_first;
4684 while (li != NULL)
4685 {
4686 if (li->li_tv.v_type != VAR_DICT
4687 || (d = li->li_tv.vval.v_dict) == NULL)
4688 {
4689 emsg(_(e_invarg));
4690 return;
4691 }
4692 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4693 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4694 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4695 && dict_find(d, (char_u *)"priority", -1) != NULL
4696 && dict_find(d, (char_u *)"id", -1) != NULL))
4697 {
4698 emsg(_(e_invarg));
4699 return;
4700 }
4701 li = li->li_next;
4702 }
4703
4704 clear_matches(win);
4705 li = l->lv_first;
4706 while (li != NULL)
4707 {
4708 int i = 0;
4709 char buf[30]; // use 30 to avoid compiler warning
4710 dictitem_T *di;
4711 char_u *group;
4712 int priority;
4713 int id;
4714 char_u *conceal;
4715
4716 d = li->li_tv.vval.v_dict;
4717 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4718 {
4719 if (s == NULL)
4720 {
4721 s = list_alloc();
4722 if (s == NULL)
4723 return;
4724 }
4725
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004726 // match from matchaddpos()
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004727 for (i = 1; i < 9; i++)
4728 {
4729 sprintf((char *)buf, (char *)"pos%d", i);
4730 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4731 {
4732 if (di->di_tv.v_type != VAR_LIST)
4733 return;
4734
4735 list_append_tv(s, &di->di_tv);
4736 s->lv_refcount++;
4737 }
4738 else
4739 break;
4740 }
4741 }
4742
4743 group = dict_get_string(d, (char_u *)"group", TRUE);
4744 priority = (int)dict_get_number(d, (char_u *)"priority");
4745 id = (int)dict_get_number(d, (char_u *)"id");
4746 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4747 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4748 : NULL;
4749 if (i == 0)
4750 {
4751 match_add(win, group,
4752 dict_get_string(d, (char_u *)"pattern", FALSE),
4753 priority, id, NULL, conceal);
4754 }
4755 else
4756 {
4757 match_add(win, group, NULL, priority, id, s, conceal);
4758 list_unref(s);
4759 s = NULL;
4760 }
4761 vim_free(group);
4762 vim_free(conceal);
4763
4764 li = li->li_next;
4765 }
4766 rettv->vval.v_number = 0;
4767 }
4768#endif
4769}
4770
4771/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004772 * "matchadd()" function
4773 */
4774 void
4775f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4776{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004777# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004778 char_u buf[NUMBUFLEN];
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004779 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4780 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4781 int prio = 10; // default priority
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004782 int id = -1;
4783 int error = FALSE;
4784 char_u *conceal_char = NULL;
4785 win_T *win = curwin;
4786
4787 rettv->vval.v_number = -1;
4788
4789 if (grp == NULL || pat == NULL)
4790 return;
4791 if (argvars[2].v_type != VAR_UNKNOWN)
4792 {
4793 prio = (int)tv_get_number_chk(&argvars[2], &error);
4794 if (argvars[3].v_type != VAR_UNKNOWN)
4795 {
4796 id = (int)tv_get_number_chk(&argvars[3], &error);
4797 if (argvars[4].v_type != VAR_UNKNOWN
4798 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4799 return;
4800 }
4801 }
4802 if (error == TRUE)
4803 return;
4804 if (id >= 1 && id <= 3)
4805 {
4806 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4807 return;
4808 }
4809
4810 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4811 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004812# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004813}
4814
4815/*
4816 * "matchaddpos()" function
4817 */
4818 void
4819f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4820{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004821# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004822 char_u buf[NUMBUFLEN];
4823 char_u *group;
4824 int prio = 10;
4825 int id = -1;
4826 int error = FALSE;
4827 list_T *l;
4828 char_u *conceal_char = NULL;
4829 win_T *win = curwin;
4830
4831 rettv->vval.v_number = -1;
4832
4833 group = tv_get_string_buf_chk(&argvars[0], buf);
4834 if (group == NULL)
4835 return;
4836
4837 if (argvars[1].v_type != VAR_LIST)
4838 {
4839 semsg(_(e_listarg), "matchaddpos()");
4840 return;
4841 }
4842 l = argvars[1].vval.v_list;
4843 if (l == NULL)
4844 return;
4845
4846 if (argvars[2].v_type != VAR_UNKNOWN)
4847 {
4848 prio = (int)tv_get_number_chk(&argvars[2], &error);
4849 if (argvars[3].v_type != VAR_UNKNOWN)
4850 {
4851 id = (int)tv_get_number_chk(&argvars[3], &error);
4852
4853 if (argvars[4].v_type != VAR_UNKNOWN
4854 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4855 return;
4856 }
4857 }
4858 if (error == TRUE)
4859 return;
4860
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004861 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004862 if (id == 1 || id == 2)
4863 {
4864 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4865 return;
4866 }
4867
4868 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4869 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004870# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004871}
4872
4873/*
4874 * "matcharg()" function
4875 */
4876 void
4877f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4878{
4879 if (rettv_list_alloc(rettv) == OK)
4880 {
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004881# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004882 int id = (int)tv_get_number(&argvars[0]);
4883 matchitem_T *m;
4884
4885 if (id >= 1 && id <= 3)
4886 {
4887 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4888 {
4889 list_append_string(rettv->vval.v_list,
4890 syn_id2name(m->hlg_id), -1);
4891 list_append_string(rettv->vval.v_list, m->pattern, -1);
4892 }
4893 else
4894 {
4895 list_append_string(rettv->vval.v_list, NULL, -1);
4896 list_append_string(rettv->vval.v_list, NULL, -1);
4897 }
4898 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004899# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004900 }
4901}
4902
4903/*
4904 * "matchdelete()" function
4905 */
4906 void
4907f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4908{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004909# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004910 win_T *win = get_optional_window(argvars, 1);
4911
4912 if (win == NULL)
4913 rettv->vval.v_number = -1;
4914 else
4915 rettv->vval.v_number = match_delete(win,
4916 (int)tv_get_number(&argvars[0]), TRUE);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004917# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004918}
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004919#endif
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004920
4921#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4922/*
4923 * ":[N]match {group} {pattern}"
4924 * Sets nextcmd to the start of the next command, if any. Also called when
4925 * skipping commands to find the next command.
4926 */
4927 void
4928ex_match(exarg_T *eap)
4929{
4930 char_u *p;
4931 char_u *g = NULL;
4932 char_u *end;
4933 int c;
4934 int id;
4935
4936 if (eap->line2 <= 3)
4937 id = eap->line2;
4938 else
4939 {
4940 emsg(_(e_invcmd));
4941 return;
4942 }
4943
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004944 // First clear any old pattern.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004945 if (!eap->skip)
4946 match_delete(curwin, id, FALSE);
4947
Bram Moolenaar1966c242020-04-20 22:42:32 +02004948 if (ends_excmd2(eap->cmd, eap->arg))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004949 end = eap->arg;
4950 else if ((STRNICMP(eap->arg, "none", 4) == 0
Bram Moolenaar1966c242020-04-20 22:42:32 +02004951 && (VIM_ISWHITE(eap->arg[4])
4952 || ends_excmd2(eap->arg, eap->arg + 4))))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004953 end = eap->arg + 4;
4954 else
4955 {
4956 p = skiptowhite(eap->arg);
4957 if (!eap->skip)
4958 g = vim_strnsave(eap->arg, (int)(p - eap->arg));
4959 p = skipwhite(p);
4960 if (*p == NUL)
4961 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004962 // There must be two arguments.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004963 vim_free(g);
4964 semsg(_(e_invarg2), eap->arg);
4965 return;
4966 }
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02004967 end = skip_regexp(p + 1, *p, TRUE);
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004968 if (!eap->skip)
4969 {
Bram Moolenaar1966c242020-04-20 22:42:32 +02004970 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004971 {
4972 vim_free(g);
4973 eap->errmsg = e_trailing;
4974 return;
4975 }
4976 if (*end != *p)
4977 {
4978 vim_free(g);
4979 semsg(_(e_invarg2), p);
4980 return;
4981 }
4982
4983 c = *end;
4984 *end = NUL;
4985 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
4986 vim_free(g);
4987 *end = c;
4988 }
4989 }
4990 eap->nextcmd = find_nextcmd(end);
4991}
4992#endif