blob: 64f37a4e8d61c1828d816cfa45fa665a80e99510 [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 Moolenaar459fd782019-10-13 16:43:39 +02001415 len = trans_special(&p, buf + off, FALSE, FALSE,
1416 TRUE, NULL);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001417 if (len > 0) // recognized special char
1418 off += len;
1419 else // copy as normal char
1420 buf[off++] = *p++;
1421 }
1422 buf[off] = NUL;
1423 }
1424 if (error)
1425 break;
1426
1427 if (STRCMP(buf, "NONE") == 0) // resetting the value
1428 p = NULL;
1429 else
1430 p = vim_strsave(buf);
1431 if (key[2] == 'A')
1432 {
1433 vim_free(HL_TABLE()[idx].sg_start);
1434 HL_TABLE()[idx].sg_start = p;
1435 }
1436 else
1437 {
1438 vim_free(HL_TABLE()[idx].sg_stop);
1439 HL_TABLE()[idx].sg_stop = p;
1440 }
1441 }
1442 else
1443 {
1444 semsg(_("E423: Illegal argument: %s"), key_start);
1445 error = TRUE;
1446 break;
1447 }
1448 HL_TABLE()[idx].sg_cleared = FALSE;
1449
1450 /*
1451 * When highlighting has been given for a group, don't link it.
1452 */
1453 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1454 HL_TABLE()[idx].sg_link = 0;
1455
1456 /*
1457 * Continue with next argument.
1458 */
1459 linep = skipwhite(linep);
1460 }
1461
1462 /*
1463 * If there is an error, and it's a new entry, remove it from the table.
1464 */
1465 if (error && idx == highlight_ga.ga_len)
1466 syn_unadd_group();
1467 else
1468 {
1469 if (is_normal_group)
1470 {
1471 HL_TABLE()[idx].sg_term_attr = 0;
1472 HL_TABLE()[idx].sg_cterm_attr = 0;
1473#ifdef FEAT_GUI
1474 HL_TABLE()[idx].sg_gui_attr = 0;
1475 /*
1476 * Need to update all groups, because they might be using "bg"
1477 * and/or "fg", which have been changed now.
1478 */
1479#endif
1480#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1481 if (USE_24BIT)
1482 {
1483 highlight_gui_started();
1484 did_highlight_changed = TRUE;
1485 redraw_all_later(NOT_VALID);
1486 }
1487#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001488#ifdef FEAT_VTP
1489 control_console_color_rgb();
1490#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001491 }
1492#ifdef FEAT_TERMINAL
1493 else if (is_terminal_group)
1494 set_terminal_default_colors(
1495 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
1496#endif
1497#ifdef FEAT_GUI_X11
1498# ifdef FEAT_MENU
1499 else if (is_menu_group)
1500 {
1501 if (gui.in_use && do_colors)
1502 gui_mch_new_menu_colors();
1503 }
1504# endif
1505 else if (is_scrollbar_group)
1506 {
1507 if (gui.in_use && do_colors)
1508 gui_new_scrollbar_colors();
1509 else
1510 set_hl_attr(idx);
1511 }
1512# ifdef FEAT_BEVAL_GUI
1513 else if (is_tooltip_group)
1514 {
1515 if (gui.in_use && do_colors)
1516 gui_mch_new_tooltip_colors();
1517 }
1518# endif
1519#endif
1520 else
1521 set_hl_attr(idx);
1522#ifdef FEAT_EVAL
1523 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001524 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001525#endif
1526 }
1527
1528 vim_free(key);
1529 vim_free(arg);
1530
1531 // Only call highlight_changed() once, after a sequence of highlight
1532 // commands, and only if an attribute actually changed.
1533 if ((did_change
1534 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1535#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1536 && !did_highlight_changed
1537#endif
1538 )
1539 {
1540 // Do not trigger a redraw when highlighting is changed while
1541 // redrawing. This may happen when evaluating 'statusline' changes the
1542 // StatusLine group.
1543 if (!updating_screen)
1544 redraw_all_later(NOT_VALID);
1545 need_highlight_changed = TRUE;
1546 }
1547}
1548
1549#if defined(EXITFREE) || defined(PROTO)
1550 void
1551free_highlight(void)
1552{
1553 int i;
1554
1555 for (i = 0; i < highlight_ga.ga_len; ++i)
1556 {
1557 highlight_clear(i);
1558 vim_free(HL_TABLE()[i].sg_name);
1559 vim_free(HL_TABLE()[i].sg_name_u);
1560 }
1561 ga_clear(&highlight_ga);
1562}
1563#endif
1564
1565/*
1566 * Reset the cterm colors to what they were before Vim was started, if
1567 * possible. Otherwise reset them to zero.
1568 */
1569 void
1570restore_cterm_colors(void)
1571{
1572#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1573 // Since t_me has been set, this probably means that the user
1574 // wants to use this as default colors. Need to reset default
1575 // background/foreground colors.
1576 mch_set_normal_colors();
1577#else
1578# ifdef VIMDLL
1579 if (!gui.in_use)
1580 {
1581 mch_set_normal_colors();
1582 return;
1583 }
1584# endif
1585 cterm_normal_fg_color = 0;
1586 cterm_normal_fg_bold = 0;
1587 cterm_normal_bg_color = 0;
1588# ifdef FEAT_TERMGUICOLORS
1589 cterm_normal_fg_gui_color = INVALCOLOR;
1590 cterm_normal_bg_gui_color = INVALCOLOR;
1591# endif
1592#endif
1593}
1594
1595/*
1596 * Return TRUE if highlight group "idx" has any settings.
1597 * When "check_link" is TRUE also check for an existing link.
1598 */
1599 static int
1600hl_has_settings(int idx, int check_link)
1601{
1602 return ( HL_TABLE()[idx].sg_term_attr != 0
1603 || HL_TABLE()[idx].sg_cterm_attr != 0
1604 || HL_TABLE()[idx].sg_cterm_fg != 0
1605 || HL_TABLE()[idx].sg_cterm_bg != 0
1606#ifdef FEAT_GUI
1607 || HL_TABLE()[idx].sg_gui_attr != 0
1608 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1609 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1610 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1611 || HL_TABLE()[idx].sg_font_name != NULL
1612#endif
1613 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1614}
1615
1616/*
1617 * Clear highlighting for one group.
1618 */
1619 static void
1620highlight_clear(int idx)
1621{
1622 HL_TABLE()[idx].sg_cleared = TRUE;
1623
1624 HL_TABLE()[idx].sg_term = 0;
1625 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1626 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1627 HL_TABLE()[idx].sg_term_attr = 0;
1628 HL_TABLE()[idx].sg_cterm = 0;
1629 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1630 HL_TABLE()[idx].sg_cterm_fg = 0;
1631 HL_TABLE()[idx].sg_cterm_bg = 0;
1632 HL_TABLE()[idx].sg_cterm_attr = 0;
1633#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1634 HL_TABLE()[idx].sg_gui = 0;
1635 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1636 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1637 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1638#endif
1639#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1640 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1641 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
1642#endif
1643#ifdef FEAT_GUI
1644 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
1645 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1646 HL_TABLE()[idx].sg_font = NOFONT;
1647# ifdef FEAT_XFONTSET
1648 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1649 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1650# endif
1651 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1652 HL_TABLE()[idx].sg_gui_attr = 0;
1653#endif
1654#ifdef FEAT_EVAL
1655 // Clear the script ID only when there is no link, since that is not
1656 // cleared.
1657 if (HL_TABLE()[idx].sg_link == 0)
1658 {
1659 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
1660 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
1661 }
1662#endif
1663}
1664
1665#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1666/*
1667 * Set the normal foreground and background colors according to the "Normal"
1668 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1669 * "Tooltip" colors.
1670 */
1671 void
1672set_normal_colors(void)
1673{
1674# ifdef FEAT_GUI
1675# ifdef FEAT_TERMGUICOLORS
1676 if (gui.in_use)
1677# endif
1678 {
1679 if (set_group_colors((char_u *)"Normal",
1680 &gui.norm_pixel, &gui.back_pixel,
1681 FALSE, TRUE, FALSE))
1682 {
1683 gui_mch_new_colors();
1684 must_redraw = CLEAR;
1685 }
1686# ifdef FEAT_GUI_X11
1687 if (set_group_colors((char_u *)"Menu",
1688 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1689 TRUE, FALSE, FALSE))
1690 {
1691# ifdef FEAT_MENU
1692 gui_mch_new_menu_colors();
1693# endif
1694 must_redraw = CLEAR;
1695 }
1696# ifdef FEAT_BEVAL_GUI
1697 if (set_group_colors((char_u *)"Tooltip",
1698 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1699 FALSE, FALSE, TRUE))
1700 {
1701# ifdef FEAT_TOOLBAR
1702 gui_mch_new_tooltip_colors();
1703# endif
1704 must_redraw = CLEAR;
1705 }
1706# endif
1707 if (set_group_colors((char_u *)"Scrollbar",
1708 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1709 FALSE, FALSE, FALSE))
1710 {
1711 gui_new_scrollbar_colors();
1712 must_redraw = CLEAR;
1713 }
1714# endif
1715 }
1716# endif
1717# ifdef FEAT_TERMGUICOLORS
1718# ifdef FEAT_GUI
1719 else
1720# endif
1721 {
1722 int idx;
1723
1724 idx = syn_name2id((char_u *)"Normal") - 1;
1725 if (idx >= 0)
1726 {
1727 gui_do_one_color(idx, FALSE, FALSE);
1728
1729 // If the normal fg or bg color changed a complete redraw is
1730 // required.
1731 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1732 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1733 {
1734 // if the GUI color is INVALCOLOR then we use the default cterm
1735 // color
1736 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1737 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1738 must_redraw = CLEAR;
1739 }
1740 }
1741 }
1742# endif
1743}
1744#endif
1745
1746#if defined(FEAT_GUI) || defined(PROTO)
1747/*
1748 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1749 */
1750 static int
1751set_group_colors(
1752 char_u *name,
1753 guicolor_T *fgp,
1754 guicolor_T *bgp,
1755 int do_menu,
1756 int use_norm,
1757 int do_tooltip)
1758{
1759 int idx;
1760
1761 idx = syn_name2id(name) - 1;
1762 if (idx >= 0)
1763 {
1764 gui_do_one_color(idx, do_menu, do_tooltip);
1765
1766 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1767 *fgp = HL_TABLE()[idx].sg_gui_fg;
1768 else if (use_norm)
1769 *fgp = gui.def_norm_pixel;
1770 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1771 *bgp = HL_TABLE()[idx].sg_gui_bg;
1772 else if (use_norm)
1773 *bgp = gui.def_back_pixel;
1774 return TRUE;
1775 }
1776 return FALSE;
1777}
1778
1779/*
1780 * Get the font of the "Normal" group.
1781 * Returns "" when it's not found or not set.
1782 */
1783 char_u *
1784hl_get_font_name(void)
1785{
1786 int id;
1787 char_u *s;
1788
1789 id = syn_name2id((char_u *)"Normal");
1790 if (id > 0)
1791 {
1792 s = HL_TABLE()[id - 1].sg_font_name;
1793 if (s != NULL)
1794 return s;
1795 }
1796 return (char_u *)"";
1797}
1798
1799/*
1800 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
1801 * actually chosen to be used.
1802 */
1803 void
1804hl_set_font_name(char_u *font_name)
1805{
1806 int id;
1807
1808 id = syn_name2id((char_u *)"Normal");
1809 if (id > 0)
1810 {
1811 vim_free(HL_TABLE()[id - 1].sg_font_name);
1812 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
1813 }
1814}
1815
1816/*
1817 * Set background color for "Normal" group. Called by gui_set_bg_color()
1818 * when the color is known.
1819 */
1820 void
1821hl_set_bg_color_name(
1822 char_u *name) // must have been allocated
1823{
1824 int id;
1825
1826 if (name != NULL)
1827 {
1828 id = syn_name2id((char_u *)"Normal");
1829 if (id > 0)
1830 {
1831 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
1832 HL_TABLE()[id - 1].sg_gui_bg_name = name;
1833 }
1834 }
1835}
1836
1837/*
1838 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
1839 * when the color is known.
1840 */
1841 void
1842hl_set_fg_color_name(
1843 char_u *name) // must have been allocated
1844{
1845 int id;
1846
1847 if (name != NULL)
1848 {
1849 id = syn_name2id((char_u *)"Normal");
1850 if (id > 0)
1851 {
1852 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
1853 HL_TABLE()[id - 1].sg_gui_fg_name = name;
1854 }
1855 }
1856}
1857
1858/*
1859 * Return the handle for a font name.
1860 * Returns NOFONT when failed.
1861 */
1862 static GuiFont
1863font_name2handle(char_u *name)
1864{
1865 if (STRCMP(name, "NONE") == 0)
1866 return NOFONT;
1867
1868 return gui_mch_get_font(name, TRUE);
1869}
1870
1871# ifdef FEAT_XFONTSET
1872/*
1873 * Return the handle for a fontset name.
1874 * Returns NOFONTSET when failed.
1875 */
1876 static GuiFontset
1877fontset_name2handle(char_u *name, int fixed_width)
1878{
1879 if (STRCMP(name, "NONE") == 0)
1880 return NOFONTSET;
1881
1882 return gui_mch_get_fontset(name, TRUE, fixed_width);
1883}
1884# endif
1885
1886/*
1887 * Get the font or fontset for one highlight group.
1888 */
1889 static void
1890hl_do_font(
1891 int idx,
1892 char_u *arg,
1893 int do_normal, // set normal font
1894 int do_menu UNUSED, // set menu font
1895 int do_tooltip UNUSED, // set tooltip font
1896 int free_font) // free current font/fontset
1897{
1898# ifdef FEAT_XFONTSET
1899 // If 'guifontset' is not empty, first try using the name as a
1900 // fontset. If that doesn't work, use it as a font name.
1901 if (*p_guifontset != NUL
1902# ifdef FONTSET_ALWAYS
1903 || do_menu
1904# endif
1905# ifdef FEAT_BEVAL_TIP
1906 // In Athena & Motif, the Tooltip highlight group is always a fontset
1907 || do_tooltip
1908# endif
1909 )
1910 {
1911 if (free_font)
1912 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1913 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
1914# ifdef FONTSET_ALWAYS
1915 || do_menu
1916# endif
1917# ifdef FEAT_BEVAL_TIP
1918 || do_tooltip
1919# endif
1920 );
1921 }
1922 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1923 {
1924 // If it worked and it's the Normal group, use it as the normal
1925 // fontset. Same for the Menu group.
1926 if (do_normal)
1927 gui_init_font(arg, TRUE);
1928# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1929 if (do_menu)
1930 {
1931# ifdef FONTSET_ALWAYS
1932 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
1933# else
1934 // YIKES! This is a bug waiting to crash the program
1935 gui.menu_font = HL_TABLE()[idx].sg_fontset;
1936# endif
1937 gui_mch_new_menu_font();
1938 }
1939# ifdef FEAT_BEVAL_GUI
1940 if (do_tooltip)
1941 {
1942 // The Athena widget set cannot currently handle switching between
1943 // displaying a single font and a fontset.
1944 // If the XtNinternational resource is set to True at widget
1945 // creation, then a fontset is always used, otherwise an
1946 // XFontStruct is used.
1947 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
1948 gui_mch_new_tooltip_font();
1949 }
1950# endif
1951# endif
1952 }
1953 else
1954# endif
1955 {
1956 if (free_font)
1957 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1958 HL_TABLE()[idx].sg_font = font_name2handle(arg);
1959 // If it worked and it's the Normal group, use it as the
1960 // normal font. Same for the Menu group.
1961 if (HL_TABLE()[idx].sg_font != NOFONT)
1962 {
1963 if (do_normal)
1964 gui_init_font(arg, FALSE);
1965#ifndef FONTSET_ALWAYS
1966# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1967 if (do_menu)
1968 {
1969 gui.menu_font = HL_TABLE()[idx].sg_font;
1970 gui_mch_new_menu_font();
1971 }
1972# endif
1973#endif
1974 }
1975 }
1976}
1977
1978#endif // FEAT_GUI
1979
1980#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1981/*
1982 * Return the handle for a color name.
1983 * Returns INVALCOLOR when failed.
1984 */
1985 guicolor_T
1986color_name2handle(char_u *name)
1987{
1988 if (STRCMP(name, "NONE") == 0)
1989 return INVALCOLOR;
1990
1991 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
1992 {
1993#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
1994 if (gui.in_use)
1995#endif
1996#ifdef FEAT_GUI
1997 return gui.norm_pixel;
1998#endif
1999#ifdef FEAT_TERMGUICOLORS
2000 if (cterm_normal_fg_gui_color != INVALCOLOR)
2001 return cterm_normal_fg_gui_color;
2002 // Guess that the foreground is black or white.
2003 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2004#endif
2005 }
2006 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2007 {
2008#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2009 if (gui.in_use)
2010#endif
2011#ifdef FEAT_GUI
2012 return gui.back_pixel;
2013#endif
2014#ifdef FEAT_TERMGUICOLORS
2015 if (cterm_normal_bg_gui_color != INVALCOLOR)
2016 return cterm_normal_bg_gui_color;
2017 // Guess that the background is white or black.
2018 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2019#endif
2020 }
2021
2022 return GUI_GET_COLOR(name);
2023}
2024#endif
2025
2026/*
2027 * Table with the specifications for an attribute number.
2028 * Note that this table is used by ALL buffers. This is required because the
2029 * GUI can redraw at any time for any buffer.
2030 */
2031static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2032
2033#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2034
2035static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2036
2037#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2038
2039#ifdef FEAT_GUI
2040static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2041
2042#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2043#endif
2044
2045/*
2046 * Return the attr number for a set of colors and font.
2047 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2048 * if the combination is new.
2049 * Return 0 for error (no more room).
2050 */
2051 static int
2052get_attr_entry(garray_T *table, attrentry_T *aep)
2053{
2054 int i;
2055 attrentry_T *taep;
2056 static int recursive = FALSE;
2057
2058 /*
2059 * Init the table, in case it wasn't done yet.
2060 */
2061 table->ga_itemsize = sizeof(attrentry_T);
2062 table->ga_growsize = 7;
2063
2064 /*
2065 * Try to find an entry with the same specifications.
2066 */
2067 for (i = 0; i < table->ga_len; ++i)
2068 {
2069 taep = &(((attrentry_T *)table->ga_data)[i]);
2070 if ( aep->ae_attr == taep->ae_attr
2071 && (
2072#ifdef FEAT_GUI
2073 (table == &gui_attr_table
2074 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2075 && aep->ae_u.gui.bg_color
2076 == taep->ae_u.gui.bg_color
2077 && aep->ae_u.gui.sp_color
2078 == taep->ae_u.gui.sp_color
2079 && aep->ae_u.gui.font == taep->ae_u.gui.font
2080# ifdef FEAT_XFONTSET
2081 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2082# endif
2083 ))
2084 ||
2085#endif
2086 (table == &term_attr_table
2087 && (aep->ae_u.term.start == NULL)
2088 == (taep->ae_u.term.start == NULL)
2089 && (aep->ae_u.term.start == NULL
2090 || STRCMP(aep->ae_u.term.start,
2091 taep->ae_u.term.start) == 0)
2092 && (aep->ae_u.term.stop == NULL)
2093 == (taep->ae_u.term.stop == NULL)
2094 && (aep->ae_u.term.stop == NULL
2095 || STRCMP(aep->ae_u.term.stop,
2096 taep->ae_u.term.stop) == 0))
2097 || (table == &cterm_attr_table
2098 && aep->ae_u.cterm.fg_color
2099 == taep->ae_u.cterm.fg_color
2100 && aep->ae_u.cterm.bg_color
2101 == taep->ae_u.cterm.bg_color
2102#ifdef FEAT_TERMGUICOLORS
2103 && aep->ae_u.cterm.fg_rgb
2104 == taep->ae_u.cterm.fg_rgb
2105 && aep->ae_u.cterm.bg_rgb
2106 == taep->ae_u.cterm.bg_rgb
2107#endif
2108 )))
2109
2110 return i + ATTR_OFF;
2111 }
2112
2113 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2114 {
2115 /*
2116 * Running out of attribute entries! remove all attributes, and
2117 * compute new ones for all groups.
2118 * When called recursively, we are really out of numbers.
2119 */
2120 if (recursive)
2121 {
2122 emsg(_("E424: Too many different highlighting attributes in use"));
2123 return 0;
2124 }
2125 recursive = TRUE;
2126
2127 clear_hl_tables();
2128
2129 must_redraw = CLEAR;
2130
2131 for (i = 0; i < highlight_ga.ga_len; ++i)
2132 set_hl_attr(i);
2133
2134 recursive = FALSE;
2135 }
2136
2137 /*
2138 * This is a new combination of colors and font, add an entry.
2139 */
2140 if (ga_grow(table, 1) == FAIL)
2141 return 0;
2142
2143 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002144 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002145 taep->ae_attr = aep->ae_attr;
2146#ifdef FEAT_GUI
2147 if (table == &gui_attr_table)
2148 {
2149 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2150 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2151 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2152 taep->ae_u.gui.font = aep->ae_u.gui.font;
2153# ifdef FEAT_XFONTSET
2154 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2155# endif
2156 }
2157#endif
2158 if (table == &term_attr_table)
2159 {
2160 if (aep->ae_u.term.start == NULL)
2161 taep->ae_u.term.start = NULL;
2162 else
2163 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2164 if (aep->ae_u.term.stop == NULL)
2165 taep->ae_u.term.stop = NULL;
2166 else
2167 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2168 }
2169 else if (table == &cterm_attr_table)
2170 {
2171 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2172 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
2173#ifdef FEAT_TERMGUICOLORS
2174 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2175 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
2176#endif
2177 }
2178 ++table->ga_len;
2179 return (table->ga_len - 1 + ATTR_OFF);
2180}
2181
2182#if defined(FEAT_TERMINAL) || defined(PROTO)
2183/*
2184 * Get an attribute index for a cterm entry.
2185 * Uses an existing entry when possible or adds one when needed.
2186 */
2187 int
2188get_cterm_attr_idx(int attr, int fg, int bg)
2189{
2190 attrentry_T at_en;
2191
Bram Moolenaara80faa82020-04-12 19:37:17 +02002192 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002193#ifdef FEAT_TERMGUICOLORS
2194 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2195 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2196#endif
2197 at_en.ae_attr = attr;
2198 at_en.ae_u.cterm.fg_color = fg;
2199 at_en.ae_u.cterm.bg_color = bg;
2200 return get_attr_entry(&cterm_attr_table, &at_en);
2201}
2202#endif
2203
2204#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2205/*
2206 * Get an attribute index for a 'termguicolors' entry.
2207 * Uses an existing entry when possible or adds one when needed.
2208 */
2209 int
2210get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2211{
2212 attrentry_T at_en;
2213
Bram Moolenaara80faa82020-04-12 19:37:17 +02002214 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002215 at_en.ae_attr = attr;
2216 if (fg == INVALCOLOR && bg == INVALCOLOR)
2217 {
2218 // If both GUI colors are not set fall back to the cterm colors. Helps
2219 // if the GUI only has an attribute, such as undercurl.
2220 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2221 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2222 }
2223 else
2224 {
2225 at_en.ae_u.cterm.fg_rgb = fg;
2226 at_en.ae_u.cterm.bg_rgb = bg;
2227 }
2228 return get_attr_entry(&cterm_attr_table, &at_en);
2229}
2230#endif
2231
2232#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2233/*
2234 * Get an attribute index for a cterm entry.
2235 * Uses an existing entry when possible or adds one when needed.
2236 */
2237 int
2238get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2239{
2240 attrentry_T at_en;
2241
Bram Moolenaara80faa82020-04-12 19:37:17 +02002242 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002243 at_en.ae_attr = attr;
2244 at_en.ae_u.gui.fg_color = fg;
2245 at_en.ae_u.gui.bg_color = bg;
2246 return get_attr_entry(&gui_attr_table, &at_en);
2247}
2248#endif
2249
2250/*
2251 * Clear all highlight tables.
2252 */
2253 void
2254clear_hl_tables(void)
2255{
2256 int i;
2257 attrentry_T *taep;
2258
2259#ifdef FEAT_GUI
2260 ga_clear(&gui_attr_table);
2261#endif
2262 for (i = 0; i < term_attr_table.ga_len; ++i)
2263 {
2264 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2265 vim_free(taep->ae_u.term.start);
2266 vim_free(taep->ae_u.term.stop);
2267 }
2268 ga_clear(&term_attr_table);
2269 ga_clear(&cterm_attr_table);
2270}
2271
2272/*
2273 * Combine special attributes (e.g., for spelling) with other attributes
2274 * (e.g., for syntax highlighting).
2275 * "prim_attr" overrules "char_attr".
2276 * This creates a new group when required.
2277 * Since we expect there to be few spelling mistakes we don't cache the
2278 * result.
2279 * Return the resulting attributes.
2280 */
2281 int
2282hl_combine_attr(int char_attr, int prim_attr)
2283{
2284 attrentry_T *char_aep = NULL;
2285 attrentry_T *spell_aep;
2286 attrentry_T new_en;
2287
2288 if (char_attr == 0)
2289 return prim_attr;
2290 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2291 return ATTR_COMBINE(char_attr, prim_attr);
2292#ifdef FEAT_GUI
2293 if (gui.in_use)
2294 {
2295 if (char_attr > HL_ALL)
2296 char_aep = syn_gui_attr2entry(char_attr);
2297 if (char_aep != NULL)
2298 new_en = *char_aep;
2299 else
2300 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002301 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002302 new_en.ae_u.gui.fg_color = INVALCOLOR;
2303 new_en.ae_u.gui.bg_color = INVALCOLOR;
2304 new_en.ae_u.gui.sp_color = INVALCOLOR;
2305 if (char_attr <= HL_ALL)
2306 new_en.ae_attr = char_attr;
2307 }
2308
2309 if (prim_attr <= HL_ALL)
2310 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2311 else
2312 {
2313 spell_aep = syn_gui_attr2entry(prim_attr);
2314 if (spell_aep != NULL)
2315 {
2316 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2317 spell_aep->ae_attr);
2318 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2319 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2320 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2321 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2322 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2323 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2324 if (spell_aep->ae_u.gui.font != NOFONT)
2325 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2326# ifdef FEAT_XFONTSET
2327 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2328 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2329# endif
2330 }
2331 }
2332 return get_attr_entry(&gui_attr_table, &new_en);
2333 }
2334#endif
2335
2336 if (IS_CTERM)
2337 {
2338 if (char_attr > HL_ALL)
2339 char_aep = syn_cterm_attr2entry(char_attr);
2340 if (char_aep != NULL)
2341 new_en = *char_aep;
2342 else
2343 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002344 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002345#ifdef FEAT_TERMGUICOLORS
2346 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2347 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2348#endif
2349 if (char_attr <= HL_ALL)
2350 new_en.ae_attr = char_attr;
2351 }
2352
2353 if (prim_attr <= HL_ALL)
2354 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2355 else
2356 {
2357 spell_aep = syn_cterm_attr2entry(prim_attr);
2358 if (spell_aep != NULL)
2359 {
2360 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2361 spell_aep->ae_attr);
2362 if (spell_aep->ae_u.cterm.fg_color > 0)
2363 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2364 if (spell_aep->ae_u.cterm.bg_color > 0)
2365 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
2366#ifdef FEAT_TERMGUICOLORS
2367 // If both fg and bg are not set fall back to cterm colors.
2368 // Helps for SpellBad which uses undercurl in the GUI.
2369 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2370 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2371 {
2372 if (spell_aep->ae_u.cterm.fg_color > 0)
2373 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2374 if (spell_aep->ae_u.cterm.bg_color > 0)
2375 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2376 }
2377 else
2378 {
2379 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2380 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2381 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2382 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2383 }
2384#endif
2385 }
2386 }
2387 return get_attr_entry(&cterm_attr_table, &new_en);
2388 }
2389
2390 if (char_attr > HL_ALL)
2391 char_aep = syn_term_attr2entry(char_attr);
2392 if (char_aep != NULL)
2393 new_en = *char_aep;
2394 else
2395 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002396 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002397 if (char_attr <= HL_ALL)
2398 new_en.ae_attr = char_attr;
2399 }
2400
2401 if (prim_attr <= HL_ALL)
2402 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2403 else
2404 {
2405 spell_aep = syn_term_attr2entry(prim_attr);
2406 if (spell_aep != NULL)
2407 {
2408 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2409 if (spell_aep->ae_u.term.start != NULL)
2410 {
2411 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2412 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2413 }
2414 }
2415 }
2416 return get_attr_entry(&term_attr_table, &new_en);
2417}
2418
2419#ifdef FEAT_GUI
2420 attrentry_T *
2421syn_gui_attr2entry(int attr)
2422{
2423 attr -= ATTR_OFF;
2424 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2425 return NULL;
2426 return &(GUI_ATTR_ENTRY(attr));
2427}
2428#endif
2429
2430/*
2431 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2432 * Only to be used when "attr" > HL_ALL.
2433 */
2434 int
2435syn_attr2attr(int attr)
2436{
2437 attrentry_T *aep;
2438
2439#ifdef FEAT_GUI
2440 if (gui.in_use)
2441 aep = syn_gui_attr2entry(attr);
2442 else
2443#endif
2444 if (IS_CTERM)
2445 aep = syn_cterm_attr2entry(attr);
2446 else
2447 aep = syn_term_attr2entry(attr);
2448
2449 if (aep == NULL) // highlighting not set
2450 return 0;
2451 return aep->ae_attr;
2452}
2453
2454
2455 attrentry_T *
2456syn_term_attr2entry(int attr)
2457{
2458 attr -= ATTR_OFF;
2459 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2460 return NULL;
2461 return &(TERM_ATTR_ENTRY(attr));
2462}
2463
2464 attrentry_T *
2465syn_cterm_attr2entry(int attr)
2466{
2467 attr -= ATTR_OFF;
2468 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2469 return NULL;
2470 return &(CTERM_ATTR_ENTRY(attr));
2471}
2472
2473#define LIST_ATTR 1
2474#define LIST_STRING 2
2475#define LIST_INT 3
2476
2477 static void
2478highlight_list_one(int id)
2479{
2480 hl_group_T *sgp;
2481 int didh = FALSE;
2482
2483 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2484
2485 if (message_filtered(sgp->sg_name))
2486 return;
2487
2488 didh = highlight_list_arg(id, didh, LIST_ATTR,
2489 sgp->sg_term, NULL, "term");
2490 didh = highlight_list_arg(id, didh, LIST_STRING,
2491 0, sgp->sg_start, "start");
2492 didh = highlight_list_arg(id, didh, LIST_STRING,
2493 0, sgp->sg_stop, "stop");
2494
2495 didh = highlight_list_arg(id, didh, LIST_ATTR,
2496 sgp->sg_cterm, NULL, "cterm");
2497 didh = highlight_list_arg(id, didh, LIST_INT,
2498 sgp->sg_cterm_fg, NULL, "ctermfg");
2499 didh = highlight_list_arg(id, didh, LIST_INT,
2500 sgp->sg_cterm_bg, NULL, "ctermbg");
2501
2502#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2503 didh = highlight_list_arg(id, didh, LIST_ATTR,
2504 sgp->sg_gui, NULL, "gui");
2505 didh = highlight_list_arg(id, didh, LIST_STRING,
2506 0, sgp->sg_gui_fg_name, "guifg");
2507 didh = highlight_list_arg(id, didh, LIST_STRING,
2508 0, sgp->sg_gui_bg_name, "guibg");
2509 didh = highlight_list_arg(id, didh, LIST_STRING,
2510 0, sgp->sg_gui_sp_name, "guisp");
2511#endif
2512#ifdef FEAT_GUI
2513 didh = highlight_list_arg(id, didh, LIST_STRING,
2514 0, sgp->sg_font_name, "font");
2515#endif
2516
2517 if (sgp->sg_link && !got_int)
2518 {
2519 (void)syn_list_header(didh, 9999, id);
2520 didh = TRUE;
2521 msg_puts_attr("links to", HL_ATTR(HLF_D));
2522 msg_putchar(' ');
2523 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2524 }
2525
2526 if (!didh)
2527 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2528#ifdef FEAT_EVAL
2529 if (p_verbose > 0)
2530 last_set_msg(sgp->sg_script_ctx);
2531#endif
2532}
2533
2534 static int
2535highlight_list_arg(
2536 int id,
2537 int didh,
2538 int type,
2539 int iarg,
2540 char_u *sarg,
2541 char *name)
2542{
2543 char_u buf[100];
2544 char_u *ts;
2545 int i;
2546
2547 if (got_int)
2548 return FALSE;
2549 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2550 {
2551 ts = buf;
2552 if (type == LIST_INT)
2553 sprintf((char *)buf, "%d", iarg - 1);
2554 else if (type == LIST_STRING)
2555 ts = sarg;
2556 else // type == LIST_ATTR
2557 {
2558 buf[0] = NUL;
2559 for (i = 0; hl_attr_table[i] != 0; ++i)
2560 {
2561 if (iarg & hl_attr_table[i])
2562 {
2563 if (buf[0] != NUL)
2564 vim_strcat(buf, (char_u *)",", 100);
2565 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2566 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2567 }
2568 }
2569 }
2570
2571 (void)syn_list_header(didh,
2572 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2573 didh = TRUE;
2574 if (!got_int)
2575 {
2576 if (*name != NUL)
2577 {
2578 msg_puts_attr(name, HL_ATTR(HLF_D));
2579 msg_puts_attr("=", HL_ATTR(HLF_D));
2580 }
2581 msg_outtrans(ts);
2582 }
2583 }
2584 return didh;
2585}
2586
2587#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2588/*
2589 * Return "1" if highlight group "id" has attribute "flag".
2590 * Return NULL otherwise.
2591 */
2592 char_u *
2593highlight_has_attr(
2594 int id,
2595 int flag,
2596 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2597{
2598 int attr;
2599
2600 if (id <= 0 || id > highlight_ga.ga_len)
2601 return NULL;
2602
2603#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2604 if (modec == 'g')
2605 attr = HL_TABLE()[id - 1].sg_gui;
2606 else
2607#endif
2608 if (modec == 'c')
2609 attr = HL_TABLE()[id - 1].sg_cterm;
2610 else
2611 attr = HL_TABLE()[id - 1].sg_term;
2612
2613 if (attr & flag)
2614 return (char_u *)"1";
2615 return NULL;
2616}
2617#endif
2618
2619#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2620/*
2621 * Return color name of highlight group "id".
2622 */
2623 char_u *
2624highlight_color(
2625 int id,
2626 char_u *what, // "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#"
2627 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2628{
2629 static char_u name[20];
2630 int n;
2631 int fg = FALSE;
2632 int sp = FALSE;
2633 int font = FALSE;
2634
2635 if (id <= 0 || id > highlight_ga.ga_len)
2636 return NULL;
2637
2638 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2639 fg = TRUE;
2640 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2641 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2642 font = TRUE;
2643 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2644 sp = TRUE;
2645 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2646 return NULL;
2647 if (modec == 'g')
2648 {
2649# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2650# ifdef FEAT_GUI
2651 // return font name
2652 if (font)
2653 return HL_TABLE()[id - 1].sg_font_name;
2654# endif
2655
2656 // return #RRGGBB form (only possible when GUI is running)
2657 if ((USE_24BIT) && what[2] == '#')
2658 {
2659 guicolor_T color;
2660 long_u rgb;
2661 static char_u buf[10];
2662
2663 if (fg)
2664 color = HL_TABLE()[id - 1].sg_gui_fg;
2665 else if (sp)
2666# ifdef FEAT_GUI
2667 color = HL_TABLE()[id - 1].sg_gui_sp;
2668# else
2669 color = INVALCOLOR;
2670# endif
2671 else
2672 color = HL_TABLE()[id - 1].sg_gui_bg;
2673 if (color == INVALCOLOR)
2674 return NULL;
2675 rgb = (long_u)GUI_MCH_GET_RGB(color);
2676 sprintf((char *)buf, "#%02x%02x%02x",
2677 (unsigned)(rgb >> 16),
2678 (unsigned)(rgb >> 8) & 255,
2679 (unsigned)rgb & 255);
2680 return buf;
2681 }
2682# endif
2683 if (fg)
2684 return (HL_TABLE()[id - 1].sg_gui_fg_name);
2685 if (sp)
2686 return (HL_TABLE()[id - 1].sg_gui_sp_name);
2687 return (HL_TABLE()[id - 1].sg_gui_bg_name);
2688 }
2689 if (font || sp)
2690 return NULL;
2691 if (modec == 'c')
2692 {
2693 if (fg)
2694 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
2695 else
2696 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2697 if (n < 0)
2698 return NULL;
2699 sprintf((char *)name, "%d", n);
2700 return name;
2701 }
2702 // term doesn't have color
2703 return NULL;
2704}
2705#endif
2706
2707#if (defined(FEAT_SYN_HL) \
2708 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2709 && defined(FEAT_PRINTER)) || defined(PROTO)
2710/*
2711 * Return color name of highlight group "id" as RGB value.
2712 */
2713 long_u
2714highlight_gui_color_rgb(
2715 int id,
2716 int fg) // TRUE = fg, FALSE = bg
2717{
2718 guicolor_T color;
2719
2720 if (id <= 0 || id > highlight_ga.ga_len)
2721 return 0L;
2722
2723 if (fg)
2724 color = HL_TABLE()[id - 1].sg_gui_fg;
2725 else
2726 color = HL_TABLE()[id - 1].sg_gui_bg;
2727
2728 if (color == INVALCOLOR)
2729 return 0L;
2730
2731 return GUI_MCH_GET_RGB(color);
2732}
2733#endif
2734
2735/*
2736 * Output the syntax list header.
2737 * Return TRUE when started a new line.
2738 */
2739 int
2740syn_list_header(
2741 int did_header, // did header already
2742 int outlen, // length of string that comes
2743 int id) // highlight group id
2744{
2745 int endcol = 19;
2746 int newline = TRUE;
2747 int name_col = 0;
2748
2749 if (!did_header)
2750 {
2751 msg_putchar('\n');
2752 if (got_int)
2753 return TRUE;
2754 msg_outtrans(HL_TABLE()[id - 1].sg_name);
2755 name_col = msg_col;
2756 endcol = 15;
2757 }
2758 else if (msg_col + outlen + 1 >= Columns)
2759 {
2760 msg_putchar('\n');
2761 if (got_int)
2762 return TRUE;
2763 }
2764 else
2765 {
2766 if (msg_col >= endcol) // wrap around is like starting a new line
2767 newline = FALSE;
2768 }
2769
2770 if (msg_col >= endcol) // output at least one space
2771 endcol = msg_col + 1;
2772 if (Columns <= endcol) // avoid hang for tiny window
2773 endcol = Columns - 1;
2774
2775 msg_advance(endcol);
2776
2777 // Show "xxx" with the attributes.
2778 if (!did_header)
2779 {
2780 if (endcol == Columns - 1 && endcol <= name_col)
2781 msg_putchar(' ');
2782 msg_puts_attr("xxx", syn_id2attr(id));
2783 msg_putchar(' ');
2784 }
2785
2786 return newline;
2787}
2788
2789/*
2790 * Set the attribute numbers for a highlight group.
2791 * Called after one of the attributes has changed.
2792 */
2793 static void
2794set_hl_attr(
2795 int idx) // index in array
2796{
2797 attrentry_T at_en;
2798 hl_group_T *sgp = HL_TABLE() + idx;
2799
2800 // The "Normal" group doesn't need an attribute number
2801 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
2802 return;
2803
2804#ifdef FEAT_GUI
2805 /*
2806 * For the GUI mode: If there are other than "normal" highlighting
2807 * attributes, need to allocate an attr number.
2808 */
2809 if (sgp->sg_gui_fg == INVALCOLOR
2810 && sgp->sg_gui_bg == INVALCOLOR
2811 && sgp->sg_gui_sp == INVALCOLOR
2812 && sgp->sg_font == NOFONT
2813# ifdef FEAT_XFONTSET
2814 && sgp->sg_fontset == NOFONTSET
2815# endif
2816 )
2817 {
2818 sgp->sg_gui_attr = sgp->sg_gui;
2819 }
2820 else
2821 {
2822 at_en.ae_attr = sgp->sg_gui;
2823 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
2824 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
2825 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
2826 at_en.ae_u.gui.font = sgp->sg_font;
2827# ifdef FEAT_XFONTSET
2828 at_en.ae_u.gui.fontset = sgp->sg_fontset;
2829# endif
2830 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
2831 }
2832#endif
2833 /*
2834 * For the term mode: If there are other than "normal" highlighting
2835 * attributes, need to allocate an attr number.
2836 */
2837 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
2838 sgp->sg_term_attr = sgp->sg_term;
2839 else
2840 {
2841 at_en.ae_attr = sgp->sg_term;
2842 at_en.ae_u.term.start = sgp->sg_start;
2843 at_en.ae_u.term.stop = sgp->sg_stop;
2844 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
2845 }
2846
2847 /*
2848 * For the color term mode: If there are other than "normal"
2849 * highlighting attributes, need to allocate an attr number.
2850 */
2851 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
2852# ifdef FEAT_TERMGUICOLORS
2853 && sgp->sg_gui_fg == INVALCOLOR
2854 && sgp->sg_gui_bg == INVALCOLOR
2855# endif
2856 )
2857 sgp->sg_cterm_attr = sgp->sg_cterm;
2858 else
2859 {
2860 at_en.ae_attr = sgp->sg_cterm;
2861 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
2862 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
2863# ifdef FEAT_TERMGUICOLORS
2864# ifdef MSWIN
2865# ifdef VIMDLL
2866 // Only when not using the GUI.
2867 if (!gui.in_use && !gui.starting)
2868# endif
2869 {
2870 int id;
2871 guicolor_T fg, bg;
2872
2873 id = syn_name2id((char_u *)"Normal");
2874 if (id > 0)
2875 {
2876 syn_id2colors(id, &fg, &bg);
2877 if (sgp->sg_gui_fg == INVALCOLOR)
2878 sgp->sg_gui_fg = fg;
2879 if (sgp->sg_gui_bg == INVALCOLOR)
2880 sgp->sg_gui_bg = bg;
2881 }
2882
2883 }
2884# endif
2885 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
2886 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
2887 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2888 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2889 {
2890 // If both fg and bg are invalid fall back to the cterm colors.
2891 // Helps when the GUI only uses an attribute, e.g. undercurl.
2892 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2893 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2894 }
2895# endif
2896 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2897 }
2898}
2899
2900/*
2901 * Lookup a highlight group name and return its ID.
2902 * If it is not found, 0 is returned.
2903 */
2904 int
2905syn_name2id(char_u *name)
2906{
2907 int i;
2908 char_u name_u[200];
2909
2910 // Avoid using stricmp() too much, it's slow on some systems
2911 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
2912 // don't deserve to be found!
2913 vim_strncpy(name_u, name, 199);
2914 vim_strup(name_u);
2915 for (i = highlight_ga.ga_len; --i >= 0; )
2916 if (HL_TABLE()[i].sg_name_u != NULL
2917 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2918 break;
2919 return i + 1;
2920}
2921
2922/*
2923 * Lookup a highlight group name and return its attributes.
2924 * Return zero if not found.
2925 */
2926 int
2927syn_name2attr(char_u *name)
2928{
2929 int id = syn_name2id(name);
2930
2931 if (id != 0)
2932 return syn_id2attr(id);
2933 return 0;
2934}
2935
2936#if defined(FEAT_EVAL) || defined(PROTO)
2937/*
2938 * Return TRUE if highlight group "name" exists.
2939 */
2940 int
2941highlight_exists(char_u *name)
2942{
2943 return (syn_name2id(name) > 0);
2944}
2945
2946# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
2947/*
2948 * Return the name of highlight group "id".
2949 * When not a valid ID return an empty string.
2950 */
2951 char_u *
2952syn_id2name(int id)
2953{
2954 if (id <= 0 || id > highlight_ga.ga_len)
2955 return (char_u *)"";
2956 return HL_TABLE()[id - 1].sg_name;
2957}
2958# endif
2959#endif
2960
2961/*
2962 * Like syn_name2id(), but take a pointer + length argument.
2963 */
2964 int
2965syn_namen2id(char_u *linep, int len)
2966{
2967 char_u *name;
2968 int id = 0;
2969
2970 name = vim_strnsave(linep, len);
2971 if (name != NULL)
2972 {
2973 id = syn_name2id(name);
2974 vim_free(name);
2975 }
2976 return id;
2977}
2978
2979/*
2980 * Find highlight group name in the table and return its ID.
2981 * The argument is a pointer to the name and the length of the name.
2982 * If it doesn't exist yet, a new entry is created.
2983 * Return 0 for failure.
2984 */
2985 int
2986syn_check_group(char_u *pp, int len)
2987{
2988 int id;
2989 char_u *name;
2990
2991 name = vim_strnsave(pp, len);
2992 if (name == NULL)
2993 return 0;
2994
2995 id = syn_name2id(name);
2996 if (id == 0) // doesn't exist yet
2997 id = syn_add_group(name);
2998 else
2999 vim_free(name);
3000 return id;
3001}
3002
3003/*
3004 * Add new highlight group and return its ID.
3005 * "name" must be an allocated string, it will be consumed.
3006 * Return 0 for failure.
3007 */
3008 static int
3009syn_add_group(char_u *name)
3010{
3011 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003012 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003013
3014 // Check that the name is ASCII letters, digits and underscore.
3015 for (p = name; *p != NUL; ++p)
3016 {
3017 if (!vim_isprintc(*p))
3018 {
3019 emsg(_("E669: Unprintable character in group name"));
3020 vim_free(name);
3021 return 0;
3022 }
3023 else if (!ASCII_ISALNUM(*p) && *p != '_')
3024 {
3025 // This is an error, but since there previously was no check only
3026 // give a warning.
3027 msg_source(HL_ATTR(HLF_W));
3028 msg(_("W18: Invalid character in group name"));
3029 break;
3030 }
3031 }
3032
3033 /*
3034 * First call for this growarray: init growing array.
3035 */
3036 if (highlight_ga.ga_data == NULL)
3037 {
3038 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3039 highlight_ga.ga_growsize = 10;
3040 }
3041
3042 if (highlight_ga.ga_len >= MAX_HL_ID)
3043 {
3044 emsg(_("E849: Too many highlight and syntax groups"));
3045 vim_free(name);
3046 return 0;
3047 }
3048
3049 /*
3050 * Make room for at least one other syntax_highlight entry.
3051 */
3052 if (ga_grow(&highlight_ga, 1) == FAIL)
3053 {
3054 vim_free(name);
3055 return 0;
3056 }
3057
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003058 name_up = vim_strsave_up(name);
3059 if (name_up == NULL)
3060 {
3061 vim_free(name);
3062 return 0;
3063 }
3064
Bram Moolenaara80faa82020-04-12 19:37:17 +02003065 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003066 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003067 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003068#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3069 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3070 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
3071# ifdef FEAT_GUI
3072 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
3073# endif
3074#endif
3075 ++highlight_ga.ga_len;
3076
3077 return highlight_ga.ga_len; // ID is index plus one
3078}
3079
3080/*
3081 * When, just after calling syn_add_group(), an error is discovered, this
3082 * function deletes the new name.
3083 */
3084 static void
3085syn_unadd_group(void)
3086{
3087 --highlight_ga.ga_len;
3088 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3089 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3090}
3091
3092/*
3093 * Translate a group ID to highlight attributes.
3094 */
3095 int
3096syn_id2attr(int hl_id)
3097{
3098 int attr;
3099 hl_group_T *sgp;
3100
3101 hl_id = syn_get_final_id(hl_id);
3102 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3103
3104#ifdef FEAT_GUI
3105 /*
3106 * Only use GUI attr when the GUI is being used.
3107 */
3108 if (gui.in_use)
3109 attr = sgp->sg_gui_attr;
3110 else
3111#endif
3112 if (IS_CTERM)
3113 attr = sgp->sg_cterm_attr;
3114 else
3115 attr = sgp->sg_term_attr;
3116
3117 return attr;
3118}
3119
3120#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3121/*
3122 * Get the GUI colors and attributes for a group ID.
3123 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3124 */
3125 int
3126syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3127{
3128 hl_group_T *sgp;
3129
3130 hl_id = syn_get_final_id(hl_id);
3131 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3132
3133 *fgp = sgp->sg_gui_fg;
3134 *bgp = sgp->sg_gui_bg;
3135 return sgp->sg_gui;
3136}
3137#endif
3138
3139#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003140 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3141 && defined(FEAT_TERMGUICOLORS)) \
3142 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003143 void
3144syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3145{
3146 hl_group_T *sgp;
3147
3148 hl_id = syn_get_final_id(hl_id);
3149 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3150 *fgp = sgp->sg_cterm_fg - 1;
3151 *bgp = sgp->sg_cterm_bg - 1;
3152}
3153#endif
3154
3155/*
3156 * Translate a group ID to the final group ID (following links).
3157 */
3158 int
3159syn_get_final_id(int hl_id)
3160{
3161 int count;
3162 hl_group_T *sgp;
3163
3164 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3165 return 0; // Can be called from eval!!
3166
3167 /*
3168 * Follow links until there is no more.
3169 * Look out for loops! Break after 100 links.
3170 */
3171 for (count = 100; --count >= 0; )
3172 {
3173 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3174 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3175 break;
3176 hl_id = sgp->sg_link;
3177 }
3178
3179 return hl_id;
3180}
3181
3182#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3183/*
3184 * Call this function just after the GUI has started.
3185 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3186 * It finds the font and color handles for the highlighting groups.
3187 */
3188 void
3189highlight_gui_started(void)
3190{
3191 int idx;
3192
3193 // First get the colors from the "Normal" and "Menu" group, if set
3194 if (USE_24BIT)
3195 set_normal_colors();
3196
3197 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3198 gui_do_one_color(idx, FALSE, FALSE);
3199
3200 highlight_changed();
3201}
3202
3203 static void
3204gui_do_one_color(
3205 int idx,
3206 int do_menu UNUSED, // TRUE: might set the menu font
3207 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3208{
3209 int didit = FALSE;
3210
3211# ifdef FEAT_GUI
3212# ifdef FEAT_TERMGUICOLORS
3213 if (gui.in_use)
3214# endif
3215 if (HL_TABLE()[idx].sg_font_name != NULL)
3216 {
3217 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3218 do_tooltip, TRUE);
3219 didit = TRUE;
3220 }
3221# endif
3222 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3223 {
3224 HL_TABLE()[idx].sg_gui_fg =
3225 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3226 didit = TRUE;
3227 }
3228 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3229 {
3230 HL_TABLE()[idx].sg_gui_bg =
3231 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3232 didit = TRUE;
3233 }
3234# ifdef FEAT_GUI
3235 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3236 {
3237 HL_TABLE()[idx].sg_gui_sp =
3238 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3239 didit = TRUE;
3240 }
3241# endif
3242 if (didit) // need to get a new attr number
3243 set_hl_attr(idx);
3244}
3245#endif
3246
3247#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3248/*
3249 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3250 */
3251 static void
3252combine_stl_hlt(
3253 int id,
3254 int id_S,
3255 int id_alt,
3256 int hlcnt,
3257 int i,
3258 int hlf,
3259 int *table)
3260{
3261 hl_group_T *hlt = HL_TABLE();
3262
3263 if (id_alt == 0)
3264 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003265 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003266 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3267 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3268# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3269 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3270# endif
3271 }
3272 else
3273 mch_memmove(&hlt[hlcnt + i],
3274 &hlt[id_alt - 1],
3275 sizeof(hl_group_T));
3276 hlt[hlcnt + i].sg_link = 0;
3277
3278 hlt[hlcnt + i].sg_term ^=
3279 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3280 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3281 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3282 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3283 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3284 hlt[hlcnt + i].sg_cterm ^=
3285 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3286 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3287 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3288 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3289 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3290# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3291 hlt[hlcnt + i].sg_gui ^=
3292 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3293# endif
3294# ifdef FEAT_GUI
3295 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3296 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3297 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3298 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3299 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3300 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3301 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3302 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3303# ifdef FEAT_XFONTSET
3304 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3305 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3306# endif
3307# endif
3308 highlight_ga.ga_len = hlcnt + i + 1;
3309 set_hl_attr(hlcnt + i); // At long last we can apply
3310 table[i] = syn_id2attr(hlcnt + i + 1);
3311}
3312#endif
3313
3314/*
3315 * Translate the 'highlight' option into attributes in highlight_attr[] and
3316 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3317 * corresponding highlights to use on top of HLF_SNC is computed.
3318 * Called only when the 'highlight' option has been changed and upon first
3319 * screen redraw after any :highlight command.
3320 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3321 */
3322 int
3323highlight_changed(void)
3324{
3325 int hlf;
3326 int i;
3327 char_u *p;
3328 int attr;
3329 char_u *end;
3330 int id;
3331#ifdef USER_HIGHLIGHT
3332 char_u userhl[30]; // use 30 to avoid compiler warning
3333# ifdef FEAT_STL_OPT
3334 int id_S = -1;
3335 int id_SNC = 0;
3336# ifdef FEAT_TERMINAL
3337 int id_ST = 0;
3338 int id_STNC = 0;
3339# endif
3340 int hlcnt;
3341# endif
3342#endif
3343 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3344
3345 need_highlight_changed = FALSE;
3346
3347 /*
3348 * Clear all attributes.
3349 */
3350 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3351 highlight_attr[hlf] = 0;
3352
3353 /*
3354 * First set all attributes to their default value.
3355 * Then use the attributes from the 'highlight' option.
3356 */
3357 for (i = 0; i < 2; ++i)
3358 {
3359 if (i)
3360 p = p_hl;
3361 else
3362 p = get_highlight_default();
3363 if (p == NULL) // just in case
3364 continue;
3365
3366 while (*p)
3367 {
3368 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3369 if (hl_flags[hlf] == *p)
3370 break;
3371 ++p;
3372 if (hlf == (int)HLF_COUNT || *p == NUL)
3373 return FAIL;
3374
3375 /*
3376 * Allow several hl_flags to be combined, like "bu" for
3377 * bold-underlined.
3378 */
3379 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003380 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003381 {
3382 if (VIM_ISWHITE(*p)) // ignore white space
3383 continue;
3384
3385 if (attr > HL_ALL) // Combination with ':' is not allowed.
3386 return FAIL;
3387
3388 switch (*p)
3389 {
3390 case 'b': attr |= HL_BOLD;
3391 break;
3392 case 'i': attr |= HL_ITALIC;
3393 break;
3394 case '-':
3395 case 'n': // no highlighting
3396 break;
3397 case 'r': attr |= HL_INVERSE;
3398 break;
3399 case 's': attr |= HL_STANDOUT;
3400 break;
3401 case 'u': attr |= HL_UNDERLINE;
3402 break;
3403 case 'c': attr |= HL_UNDERCURL;
3404 break;
3405 case 't': attr |= HL_STRIKETHROUGH;
3406 break;
3407 case ':': ++p; // highlight group name
3408 if (attr || *p == NUL) // no combinations
3409 return FAIL;
3410 end = vim_strchr(p, ',');
3411 if (end == NULL)
3412 end = p + STRLEN(p);
3413 id = syn_check_group(p, (int)(end - p));
3414 if (id == 0)
3415 return FAIL;
3416 attr = syn_id2attr(id);
3417 p = end - 1;
3418#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3419 if (hlf == (int)HLF_SNC)
3420 id_SNC = syn_get_final_id(id);
3421# ifdef FEAT_TERMINAL
3422 else if (hlf == (int)HLF_ST)
3423 id_ST = syn_get_final_id(id);
3424 else if (hlf == (int)HLF_STNC)
3425 id_STNC = syn_get_final_id(id);
3426# endif
3427 else if (hlf == (int)HLF_S)
3428 id_S = syn_get_final_id(id);
3429#endif
3430 break;
3431 default: return FAIL;
3432 }
3433 }
3434 highlight_attr[hlf] = attr;
3435
3436 p = skip_to_option_part(p); // skip comma and spaces
3437 }
3438 }
3439
3440#ifdef USER_HIGHLIGHT
3441 /*
3442 * Setup the user highlights
3443 *
3444 * Temporarily utilize 28 more hl entries:
3445 * 9 for User1-User9 combined with StatusLineNC
3446 * 9 for User1-User9 combined with StatusLineTerm
3447 * 9 for User1-User9 combined with StatusLineTermNC
3448 * 1 for StatusLine default
3449 * Have to be in there simultaneously in case of table overflows in
3450 * get_attr_entry()
3451 */
3452# ifdef FEAT_STL_OPT
3453 if (ga_grow(&highlight_ga, 28) == FAIL)
3454 return FAIL;
3455 hlcnt = highlight_ga.ga_len;
3456 if (id_S == -1)
3457 {
3458 // Make sure id_S is always valid to simplify code below. Use the last
3459 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003460 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003461 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3462 id_S = hlcnt + 19;
3463 }
3464# endif
3465 for (i = 0; i < 9; i++)
3466 {
3467 sprintf((char *)userhl, "User%d", i + 1);
3468 id = syn_name2id(userhl);
3469 if (id == 0)
3470 {
3471 highlight_user[i] = 0;
3472# ifdef FEAT_STL_OPT
3473 highlight_stlnc[i] = 0;
3474# ifdef FEAT_TERMINAL
3475 highlight_stlterm[i] = 0;
3476 highlight_stltermnc[i] = 0;
3477# endif
3478# endif
3479 }
3480 else
3481 {
3482 highlight_user[i] = syn_id2attr(id);
3483# ifdef FEAT_STL_OPT
3484 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3485 HLF_SNC, highlight_stlnc);
3486# ifdef FEAT_TERMINAL
3487 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3488 HLF_ST, highlight_stlterm);
3489 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3490 HLF_STNC, highlight_stltermnc);
3491# endif
3492# endif
3493 }
3494 }
3495# ifdef FEAT_STL_OPT
3496 highlight_ga.ga_len = hlcnt;
3497# endif
3498
3499#endif // USER_HIGHLIGHT
3500
3501 return OK;
3502}
3503
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003504static void highlight_list(void);
3505static void highlight_list_two(int cnt, int attr);
3506
3507/*
3508 * Handle command line completion for :highlight command.
3509 */
3510 void
3511set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3512{
3513 char_u *p;
3514
3515 // Default: expand group names
3516 xp->xp_context = EXPAND_HIGHLIGHT;
3517 xp->xp_pattern = arg;
3518 include_link = 2;
3519 include_default = 1;
3520
3521 // (part of) subcommand already typed
3522 if (*arg != NUL)
3523 {
3524 p = skiptowhite(arg);
3525 if (*p != NUL) // past "default" or group name
3526 {
3527 include_default = 0;
3528 if (STRNCMP("default", arg, p - arg) == 0)
3529 {
3530 arg = skipwhite(p);
3531 xp->xp_pattern = arg;
3532 p = skiptowhite(arg);
3533 }
3534 if (*p != NUL) // past group name
3535 {
3536 include_link = 0;
3537 if (arg[1] == 'i' && arg[0] == 'N')
3538 highlight_list();
3539 if (STRNCMP("link", arg, p - arg) == 0
3540 || STRNCMP("clear", arg, p - arg) == 0)
3541 {
3542 xp->xp_pattern = skipwhite(p);
3543 p = skiptowhite(xp->xp_pattern);
3544 if (*p != NUL) // past first group name
3545 {
3546 xp->xp_pattern = skipwhite(p);
3547 p = skiptowhite(xp->xp_pattern);
3548 }
3549 }
3550 if (*p != NUL) // past group name(s)
3551 xp->xp_context = EXPAND_NOTHING;
3552 }
3553 }
3554 }
3555}
3556
3557/*
3558 * List highlighting matches in a nice way.
3559 */
3560 static void
3561highlight_list(void)
3562{
3563 int i;
3564
3565 for (i = 10; --i >= 0; )
3566 highlight_list_two(i, HL_ATTR(HLF_D));
3567 for (i = 40; --i >= 0; )
3568 highlight_list_two(99, 0);
3569}
3570
3571 static void
3572highlight_list_two(int cnt, int attr)
3573{
3574 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3575 msg_clr_eos();
3576 out_flush();
3577 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3578}
3579
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003580/*
3581 * Function given to ExpandGeneric() to obtain the list of group names.
3582 */
3583 char_u *
3584get_highlight_name(expand_T *xp UNUSED, int idx)
3585{
3586 return get_highlight_name_ext(xp, idx, TRUE);
3587}
3588
3589/*
3590 * Obtain a highlight group name.
3591 * When "skip_cleared" is TRUE don't return a cleared entry.
3592 */
3593 char_u *
3594get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3595{
3596 if (idx < 0)
3597 return NULL;
3598
3599 // Items are never removed from the table, skip the ones that were
3600 // cleared.
3601 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3602 return (char_u *)"";
3603
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003604 if (idx == highlight_ga.ga_len && include_none != 0)
3605 return (char_u *)"none";
3606 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3607 return (char_u *)"default";
3608 if (idx == highlight_ga.ga_len + include_none + include_default
3609 && include_link != 0)
3610 return (char_u *)"link";
3611 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3612 && include_link != 0)
3613 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003614 if (idx >= highlight_ga.ga_len)
3615 return NULL;
3616 return HL_TABLE()[idx].sg_name;
3617}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003618
3619#if defined(FEAT_GUI) || defined(PROTO)
3620/*
3621 * Free all the highlight group fonts.
3622 * Used when quitting for systems which need it.
3623 */
3624 void
3625free_highlight_fonts(void)
3626{
3627 int idx;
3628
3629 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3630 {
3631 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3632 HL_TABLE()[idx].sg_font = NOFONT;
3633# ifdef FEAT_XFONTSET
3634 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3635 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3636# endif
3637 }
3638
3639 gui_mch_free_font(gui.norm_font);
3640# ifdef FEAT_XFONTSET
3641 gui_mch_free_fontset(gui.fontset);
3642# endif
3643# ifndef FEAT_GUI_GTK
3644 gui_mch_free_font(gui.bold_font);
3645 gui_mch_free_font(gui.ital_font);
3646 gui_mch_free_font(gui.boldital_font);
3647# endif
3648}
3649#endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003650
3651
3652#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003653
3654# define SEARCH_HL_PRIORITY 0
3655
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003656/*
3657 * Add match to the match list of window 'wp'. The pattern 'pat' will be
3658 * highlighted with the group 'grp' with priority 'prio'.
3659 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3660 * If no particular ID is desired, -1 must be specified for 'id'.
3661 * Return ID of added match, -1 on failure.
3662 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003663 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003664match_add(
3665 win_T *wp,
3666 char_u *grp,
3667 char_u *pat,
3668 int prio,
3669 int id,
3670 list_T *pos_list,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003671 char_u *conceal_char UNUSED) // pointer to conceal replacement char
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003672{
3673 matchitem_T *cur;
3674 matchitem_T *prev;
3675 matchitem_T *m;
3676 int hlg_id;
3677 regprog_T *regprog = NULL;
3678 int rtype = SOME_VALID;
3679
3680 if (*grp == NUL || (pat != NULL && *pat == NUL))
3681 return -1;
3682 if (id < -1 || id == 0)
3683 {
Bram Moolenaar32aa1022019-11-02 22:54:41 +01003684 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"),
3685 id);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003686 return -1;
3687 }
3688 if (id != -1)
3689 {
3690 cur = wp->w_match_head;
3691 while (cur != NULL)
3692 {
3693 if (cur->id == id)
3694 {
3695 semsg(_("E801: ID already taken: %d"), id);
3696 return -1;
3697 }
3698 cur = cur->next;
3699 }
3700 }
3701 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3702 {
3703 semsg(_(e_nogroup), grp);
3704 return -1;
3705 }
3706 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3707 {
3708 semsg(_(e_invarg2), pat);
3709 return -1;
3710 }
3711
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003712 // Find available match ID.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003713 while (id == -1)
3714 {
3715 cur = wp->w_match_head;
3716 while (cur != NULL && cur->id != wp->w_next_match_id)
3717 cur = cur->next;
3718 if (cur == NULL)
3719 id = wp->w_next_match_id;
3720 wp->w_next_match_id++;
3721 }
3722
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003723 // Build new match.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003724 m = ALLOC_CLEAR_ONE(matchitem_T);
3725 m->id = id;
3726 m->priority = prio;
3727 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3728 m->hlg_id = hlg_id;
3729 m->match.regprog = regprog;
3730 m->match.rmm_ic = FALSE;
3731 m->match.rmm_maxcol = 0;
3732# if defined(FEAT_CONCEAL)
3733 m->conceal_char = 0;
3734 if (conceal_char != NULL)
3735 m->conceal_char = (*mb_ptr2char)(conceal_char);
3736# endif
3737
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003738 // Set up position matches
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003739 if (pos_list != NULL)
3740 {
3741 linenr_T toplnum = 0;
3742 linenr_T botlnum = 0;
3743 listitem_T *li;
3744 int i;
3745
Bram Moolenaar50985eb2020-01-27 22:09:39 +01003746 range_list_materialize(pos_list);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003747 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3748 i++, li = li->li_next)
3749 {
3750 linenr_T lnum = 0;
3751 colnr_T col = 0;
3752 int len = 1;
3753 list_T *subl;
3754 listitem_T *subli;
3755 int error = FALSE;
3756
3757 if (li->li_tv.v_type == VAR_LIST)
3758 {
3759 subl = li->li_tv.vval.v_list;
3760 if (subl == NULL)
3761 goto fail;
3762 subli = subl->lv_first;
3763 if (subli == NULL)
3764 goto fail;
3765 lnum = tv_get_number_chk(&subli->li_tv, &error);
3766 if (error == TRUE)
3767 goto fail;
3768 if (lnum == 0)
3769 {
3770 --i;
3771 continue;
3772 }
3773 m->pos.pos[i].lnum = lnum;
3774 subli = subli->li_next;
3775 if (subli != NULL)
3776 {
3777 col = tv_get_number_chk(&subli->li_tv, &error);
3778 if (error == TRUE)
3779 goto fail;
3780 subli = subli->li_next;
3781 if (subli != NULL)
3782 {
3783 len = tv_get_number_chk(&subli->li_tv, &error);
3784 if (error == TRUE)
3785 goto fail;
3786 }
3787 }
3788 m->pos.pos[i].col = col;
3789 m->pos.pos[i].len = len;
3790 }
3791 else if (li->li_tv.v_type == VAR_NUMBER)
3792 {
3793 if (li->li_tv.vval.v_number == 0)
3794 {
3795 --i;
3796 continue;
3797 }
3798 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3799 m->pos.pos[i].col = 0;
3800 m->pos.pos[i].len = 0;
3801 }
3802 else
3803 {
Bram Moolenaar1500a422019-12-24 15:38:21 +01003804 emsg(_("E290: List or number required"));
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003805 goto fail;
3806 }
3807 if (toplnum == 0 || lnum < toplnum)
3808 toplnum = lnum;
3809 if (botlnum == 0 || lnum >= botlnum)
3810 botlnum = lnum + 1;
3811 }
3812
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003813 // Calculate top and bottom lines for redrawing area
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003814 if (toplnum != 0)
3815 {
3816 if (wp->w_buffer->b_mod_set)
3817 {
3818 if (wp->w_buffer->b_mod_top > toplnum)
3819 wp->w_buffer->b_mod_top = toplnum;
3820 if (wp->w_buffer->b_mod_bot < botlnum)
3821 wp->w_buffer->b_mod_bot = botlnum;
3822 }
3823 else
3824 {
3825 wp->w_buffer->b_mod_set = TRUE;
3826 wp->w_buffer->b_mod_top = toplnum;
3827 wp->w_buffer->b_mod_bot = botlnum;
3828 wp->w_buffer->b_mod_xlines = 0;
3829 }
3830 m->pos.toplnum = toplnum;
3831 m->pos.botlnum = botlnum;
3832 rtype = VALID;
3833 }
3834 }
3835
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003836 // Insert new match. The match list is in ascending order with regard to
3837 // the match priorities.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003838 cur = wp->w_match_head;
3839 prev = cur;
3840 while (cur != NULL && prio >= cur->priority)
3841 {
3842 prev = cur;
3843 cur = cur->next;
3844 }
3845 if (cur == prev)
3846 wp->w_match_head = m;
3847 else
3848 prev->next = m;
3849 m->next = cur;
3850
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003851 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003852 return id;
3853
3854fail:
3855 vim_free(m);
3856 return -1;
3857}
3858
3859/*
3860 * Delete match with ID 'id' in the match list of window 'wp'.
3861 * Print error messages if 'perr' is TRUE.
3862 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003863 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003864match_delete(win_T *wp, int id, int perr)
3865{
3866 matchitem_T *cur = wp->w_match_head;
3867 matchitem_T *prev = cur;
3868 int rtype = SOME_VALID;
3869
3870 if (id < 1)
3871 {
3872 if (perr == TRUE)
3873 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3874 id);
3875 return -1;
3876 }
3877 while (cur != NULL && cur->id != id)
3878 {
3879 prev = cur;
3880 cur = cur->next;
3881 }
3882 if (cur == NULL)
3883 {
3884 if (perr == TRUE)
3885 semsg(_("E803: ID not found: %d"), id);
3886 return -1;
3887 }
3888 if (cur == prev)
3889 wp->w_match_head = cur->next;
3890 else
3891 prev->next = cur->next;
3892 vim_regfree(cur->match.regprog);
3893 vim_free(cur->pattern);
3894 if (cur->pos.toplnum != 0)
3895 {
3896 if (wp->w_buffer->b_mod_set)
3897 {
3898 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3899 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3900 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3901 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3902 }
3903 else
3904 {
3905 wp->w_buffer->b_mod_set = TRUE;
3906 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3907 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3908 wp->w_buffer->b_mod_xlines = 0;
3909 }
3910 rtype = VALID;
3911 }
3912 vim_free(cur);
Bram Moolenaar06029a82019-07-24 14:25:26 +02003913 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003914 return 0;
3915}
3916
3917/*
3918 * Delete all matches in the match list of window 'wp'.
3919 */
3920 void
3921clear_matches(win_T *wp)
3922{
3923 matchitem_T *m;
3924
3925 while (wp->w_match_head != NULL)
3926 {
3927 m = wp->w_match_head->next;
3928 vim_regfree(wp->w_match_head->match.regprog);
3929 vim_free(wp->w_match_head->pattern);
3930 vim_free(wp->w_match_head);
3931 wp->w_match_head = m;
3932 }
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003933 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003934}
3935
3936/*
3937 * Get match from ID 'id' in window 'wp'.
3938 * Return NULL if match not found.
3939 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003940 static matchitem_T *
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003941get_match(win_T *wp, int id)
3942{
3943 matchitem_T *cur = wp->w_match_head;
3944
3945 while (cur != NULL && cur->id != id)
3946 cur = cur->next;
3947 return cur;
3948}
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003949
3950/*
3951 * Init for calling prepare_search_hl().
3952 */
3953 void
3954init_search_hl(win_T *wp, match_T *search_hl)
3955{
3956 matchitem_T *cur;
3957
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003958 // Setup for match and 'hlsearch' highlighting. Disable any previous
3959 // match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003960 cur = wp->w_match_head;
3961 while (cur != NULL)
3962 {
3963 cur->hl.rm = cur->match;
3964 if (cur->hlg_id == 0)
3965 cur->hl.attr = 0;
3966 else
3967 cur->hl.attr = syn_id2attr(cur->hlg_id);
3968 cur->hl.buf = wp->w_buffer;
3969 cur->hl.lnum = 0;
3970 cur->hl.first_lnum = 0;
3971# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003972 // Set the time limit to 'redrawtime'.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003973 profile_setlimit(p_rdt, &(cur->hl.tm));
3974# endif
3975 cur = cur->next;
3976 }
3977 search_hl->buf = wp->w_buffer;
3978 search_hl->lnum = 0;
3979 search_hl->first_lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003980 // time limit is set at the toplevel, for all windows
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003981}
3982
3983/*
3984 * If there is a match fill "shl" and return one.
3985 * Return zero otherwise.
3986 */
3987 static int
3988next_search_hl_pos(
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003989 match_T *shl, // points to a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003990 linenr_T lnum,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003991 posmatch_T *posmatch, // match positions
3992 colnr_T mincol) // minimal column for a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003993{
3994 int i;
3995 int found = -1;
3996
3997 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
3998 {
3999 llpos_T *pos = &posmatch->pos[i];
4000
4001 if (pos->lnum == 0)
4002 break;
4003 if (pos->len == 0 && pos->col < mincol)
4004 continue;
4005 if (pos->lnum == lnum)
4006 {
4007 if (found >= 0)
4008 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004009 // if this match comes before the one at "found" then swap
4010 // them
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004011 if (pos->col < posmatch->pos[found].col)
4012 {
4013 llpos_T tmp = *pos;
4014
4015 *pos = posmatch->pos[found];
4016 posmatch->pos[found] = tmp;
4017 }
4018 }
4019 else
4020 found = i;
4021 }
4022 }
4023 posmatch->cur = 0;
4024 if (found >= 0)
4025 {
4026 colnr_T start = posmatch->pos[found].col == 0
4027 ? 0 : posmatch->pos[found].col - 1;
4028 colnr_T end = posmatch->pos[found].col == 0
4029 ? MAXCOL : start + posmatch->pos[found].len;
4030
4031 shl->lnum = lnum;
4032 shl->rm.startpos[0].lnum = 0;
4033 shl->rm.startpos[0].col = start;
4034 shl->rm.endpos[0].lnum = 0;
4035 shl->rm.endpos[0].col = end;
4036 shl->is_addpos = TRUE;
4037 posmatch->cur = found + 1;
4038 return 1;
4039 }
4040 return 0;
4041}
4042
4043/*
4044 * Search for a next 'hlsearch' or match.
4045 * Uses shl->buf.
4046 * Sets shl->lnum and shl->rm contents.
4047 * Note: Assumes a previous match is always before "lnum", unless
4048 * shl->lnum is zero.
4049 * Careful: Any pointers for buffer lines will become invalid.
4050 */
4051 static void
4052next_search_hl(
4053 win_T *win,
4054 match_T *search_hl,
4055 match_T *shl, // points to search_hl or a match
4056 linenr_T lnum,
4057 colnr_T mincol, // minimal column for a match
4058 matchitem_T *cur) // to retrieve match positions if any
4059{
4060 linenr_T l;
4061 colnr_T matchcol;
4062 long nmatched;
Bram Moolenaar53989552019-12-23 22:59:18 +01004063 int called_emsg_before = called_emsg;
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004064
4065 // for :{range}s/pat only highlight inside the range
4066 if (lnum < search_first_line || lnum > search_last_line)
4067 {
4068 shl->lnum = 0;
4069 return;
4070 }
4071
4072 if (shl->lnum != 0)
4073 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004074 // Check for three situations:
4075 // 1. If the "lnum" is below a previous match, start a new search.
4076 // 2. If the previous match includes "mincol", use it.
4077 // 3. Continue after the previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004078 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4079 if (lnum > l)
4080 shl->lnum = 0;
4081 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4082 return;
4083 }
4084
4085 /*
4086 * Repeat searching for a match until one is found that includes "mincol"
4087 * or none is found in this line.
4088 */
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004089 for (;;)
4090 {
4091# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004092 // Stop searching after passing the time limit.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004093 if (profile_passed_limit(&(shl->tm)))
4094 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004095 shl->lnum = 0; // no match found in time
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004096 break;
4097 }
4098# endif
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004099 // Three situations:
4100 // 1. No useful previous match: search from start of line.
4101 // 2. Not Vi compatible or empty match: continue at next character.
4102 // Break the loop if this is beyond the end of the line.
4103 // 3. Vi compatible searching: continue at end of previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004104 if (shl->lnum == 0)
4105 matchcol = 0;
4106 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4107 || (shl->rm.endpos[0].lnum == 0
4108 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4109 {
4110 char_u *ml;
4111
4112 matchcol = shl->rm.startpos[0].col;
4113 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4114 if (*ml == NUL)
4115 {
4116 ++matchcol;
4117 shl->lnum = 0;
4118 break;
4119 }
4120 if (has_mbyte)
4121 matchcol += mb_ptr2len(ml);
4122 else
4123 ++matchcol;
4124 }
4125 else
4126 matchcol = shl->rm.endpos[0].col;
4127
4128 shl->lnum = lnum;
4129 if (shl->rm.regprog != NULL)
4130 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004131 // Remember whether shl->rm is using a copy of the regprog in
4132 // cur->match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004133 int regprog_is_copy = (shl != search_hl && cur != NULL
4134 && shl == &cur->hl
4135 && cur->match.regprog == cur->hl.rm.regprog);
4136 int timed_out = FALSE;
4137
4138 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4139 matchcol,
4140#ifdef FEAT_RELTIME
4141 &(shl->tm), &timed_out
4142#else
4143 NULL, NULL
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004144#endif
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004145 );
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004146 // Copy the regprog, in case it got freed and recompiled.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004147 if (regprog_is_copy)
4148 cur->match.regprog = cur->hl.rm.regprog;
4149
Bram Moolenaar53989552019-12-23 22:59:18 +01004150 if (called_emsg > called_emsg_before || got_int || timed_out)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004151 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004152 // Error while handling regexp: stop using this regexp.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004153 if (shl == search_hl)
4154 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004155 // don't free regprog in the match list, it's a copy
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004156 vim_regfree(shl->rm.regprog);
4157 set_no_hlsearch(TRUE);
4158 }
4159 shl->rm.regprog = NULL;
4160 shl->lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004161 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004162 break;
4163 }
4164 }
4165 else if (cur != NULL)
4166 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4167 else
4168 nmatched = 0;
4169 if (nmatched == 0)
4170 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004171 shl->lnum = 0; // no match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004172 break;
4173 }
4174 if (shl->rm.startpos[0].lnum > 0
4175 || shl->rm.startpos[0].col >= mincol
4176 || nmatched > 1
4177 || shl->rm.endpos[0].col > mincol)
4178 {
4179 shl->lnum += shl->rm.startpos[0].lnum;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004180 break; // useful match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004181 }
4182 }
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004183}
4184
4185/*
4186 * Advance to the match in window "wp" line "lnum" or past it.
4187 */
4188 void
4189prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4190{
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004191 matchitem_T *cur; // points to the match list
4192 match_T *shl; // points to search_hl or a match
4193 int shl_flag; // flag to indicate whether search_hl
4194 // has been processed or not
4195 int pos_inprogress; // marks that position match search is
4196 // in progress
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004197 int n;
4198
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004199 // When using a multi-line pattern, start searching at the top
4200 // of the window or just after a closed fold.
4201 // Do this both for search_hl and the match list.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004202 cur = wp->w_match_head;
4203 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4204 while (cur != NULL || shl_flag == FALSE)
4205 {
4206 if (shl_flag == FALSE)
4207 {
4208 shl = search_hl;
4209 shl_flag = TRUE;
4210 }
4211 else
4212 shl = &cur->hl;
4213 if (shl->rm.regprog != NULL
4214 && shl->lnum == 0
4215 && re_multiline(shl->rm.regprog))
4216 {
4217 if (shl->first_lnum == 0)
4218 {
4219# ifdef FEAT_FOLDING
4220 for (shl->first_lnum = lnum;
4221 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4222 if (hasFoldingWin(wp, shl->first_lnum - 1,
4223 NULL, NULL, TRUE, NULL))
4224 break;
4225# else
4226 shl->first_lnum = wp->w_topline;
4227# endif
4228 }
4229 if (cur != NULL)
4230 cur->pos.cur = 0;
4231 pos_inprogress = TRUE;
4232 n = 0;
4233 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4234 || (cur != NULL && pos_inprogress)))
4235 {
4236 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4237 shl == search_hl ? NULL : cur);
4238 pos_inprogress = cur == NULL || cur->pos.cur == 0
4239 ? FALSE : TRUE;
4240 if (shl->lnum != 0)
4241 {
4242 shl->first_lnum = shl->lnum
4243 + shl->rm.endpos[0].lnum
4244 - shl->rm.startpos[0].lnum;
4245 n = shl->rm.endpos[0].col;
4246 }
4247 else
4248 {
4249 ++shl->first_lnum;
4250 n = 0;
4251 }
4252 }
4253 }
4254 if (shl != search_hl && cur != NULL)
4255 cur = cur->next;
4256 }
4257}
4258
4259/*
4260 * Prepare for 'hlsearch' and match highlighting in one window line.
4261 * Return TRUE if there is such highlighting and set "search_attr" to the
4262 * current highlight attribute.
4263 */
4264 int
4265prepare_search_hl_line(
4266 win_T *wp,
4267 linenr_T lnum,
4268 colnr_T mincol,
4269 char_u **line,
4270 match_T *search_hl,
4271 int *search_attr)
4272{
4273 matchitem_T *cur; // points to the match list
4274 match_T *shl; // points to search_hl or a match
4275 int shl_flag; // flag to indicate whether search_hl
4276 // has been processed or not
4277 int area_highlighting = FALSE;
4278
4279 /*
4280 * Handle highlighting the last used search pattern and matches.
4281 * Do this for both search_hl and the match list.
4282 * Do not use search_hl in a popup window.
4283 */
4284 cur = wp->w_match_head;
4285 shl_flag = WIN_IS_POPUP(wp);
4286 while (cur != NULL || shl_flag == FALSE)
4287 {
4288 if (shl_flag == FALSE)
4289 {
4290 shl = search_hl;
4291 shl_flag = TRUE;
4292 }
4293 else
4294 shl = &cur->hl;
4295 shl->startcol = MAXCOL;
4296 shl->endcol = MAXCOL;
4297 shl->attr_cur = 0;
4298 shl->is_addpos = FALSE;
4299 if (cur != NULL)
4300 cur->pos.cur = 0;
4301 next_search_hl(wp, search_hl, shl, lnum, mincol,
4302 shl == search_hl ? NULL : cur);
4303
4304 // Need to get the line again, a multi-line regexp may have made it
4305 // invalid.
4306 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4307
4308 if (shl->lnum != 0 && shl->lnum <= lnum)
4309 {
4310 if (shl->lnum == lnum)
4311 shl->startcol = shl->rm.startpos[0].col;
4312 else
4313 shl->startcol = 0;
4314 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4315 - shl->rm.startpos[0].lnum)
4316 shl->endcol = shl->rm.endpos[0].col;
4317 else
4318 shl->endcol = MAXCOL;
4319 // Highlight one character for an empty match.
4320 if (shl->startcol == shl->endcol)
4321 {
4322 if (has_mbyte && (*line)[shl->endcol] != NUL)
4323 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4324 else
4325 ++shl->endcol;
4326 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004327 if ((long)shl->startcol < mincol) // match at leftcol
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004328 {
4329 shl->attr_cur = shl->attr;
4330 *search_attr = shl->attr;
4331 }
4332 area_highlighting = TRUE;
4333 }
4334 if (shl != search_hl && cur != NULL)
4335 cur = cur->next;
4336 }
4337 return area_highlighting;
4338}
4339
4340/*
4341 * For a position in a line: Check for start/end of 'hlsearch' and other
4342 * matches.
4343 * After end, check for start/end of next match.
4344 * When another match, have to check for start again.
4345 * Watch out for matching an empty string!
Bram Moolenaar32aa1022019-11-02 22:54:41 +01004346 * Return the updated search_attr.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004347 */
4348 int
4349update_search_hl(
4350 win_T *wp,
4351 linenr_T lnum,
4352 colnr_T col,
4353 char_u **line,
4354 match_T *search_hl,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004355 int *has_match_conc UNUSED,
4356 int *match_conc UNUSED,
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004357 int did_line_attr,
4358 int lcs_eol_one)
4359{
4360 matchitem_T *cur; // points to the match list
4361 match_T *shl; // points to search_hl or a match
4362 int shl_flag; // flag to indicate whether search_hl
4363 // has been processed or not
4364 int pos_inprogress; // marks that position match search is in
4365 // progress
4366 int search_attr = 0;
4367
4368
4369 // Do this for 'search_hl' and the match list (ordered by priority).
4370 cur = wp->w_match_head;
4371 shl_flag = WIN_IS_POPUP(wp);
4372 while (cur != NULL || shl_flag == FALSE)
4373 {
4374 if (shl_flag == FALSE
4375 && ((cur != NULL
4376 && cur->priority > SEARCH_HL_PRIORITY)
4377 || cur == NULL))
4378 {
4379 shl = search_hl;
4380 shl_flag = TRUE;
4381 }
4382 else
4383 shl = &cur->hl;
4384 if (cur != NULL)
4385 cur->pos.cur = 0;
4386 pos_inprogress = TRUE;
4387 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4388 {
4389 if (shl->startcol != MAXCOL
4390 && col >= shl->startcol
4391 && col < shl->endcol)
4392 {
Bram Moolenaar1614a142019-10-06 22:00:13 +02004393 int next_col = col + mb_ptr2len(*line + col);
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004394
4395 if (shl->endcol < next_col)
4396 shl->endcol = next_col;
4397 shl->attr_cur = shl->attr;
4398# ifdef FEAT_CONCEAL
4399 // Match with the "Conceal" group results in hiding
4400 // the match.
4401 if (cur != NULL
4402 && shl != search_hl
4403 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4404 {
4405 *has_match_conc = col == shl->startcol ? 2 : 1;
4406 *match_conc = cur->conceal_char;
4407 }
4408 else
4409 *has_match_conc = *match_conc = 0;
4410# endif
4411 }
4412 else if (col == shl->endcol)
4413 {
4414 shl->attr_cur = 0;
4415 next_search_hl(wp, search_hl, shl, lnum, col,
4416 shl == search_hl ? NULL : cur);
4417 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4418
4419 // Need to get the line again, a multi-line regexp may have
4420 // made it invalid.
4421 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4422
4423 if (shl->lnum == lnum)
4424 {
4425 shl->startcol = shl->rm.startpos[0].col;
4426 if (shl->rm.endpos[0].lnum == 0)
4427 shl->endcol = shl->rm.endpos[0].col;
4428 else
4429 shl->endcol = MAXCOL;
4430
4431 if (shl->startcol == shl->endcol)
4432 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004433 // highlight empty match, try again after
4434 // it
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004435 if (has_mbyte)
4436 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4437 else
4438 ++shl->endcol;
4439 }
4440
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004441 // Loop to check if the match starts at the
4442 // current position
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004443 continue;
4444 }
4445 }
4446 break;
4447 }
4448 if (shl != search_hl && cur != NULL)
4449 cur = cur->next;
4450 }
4451
4452 // Use attributes from match with highest priority among 'search_hl' and
4453 // the match list.
4454 cur = wp->w_match_head;
4455 shl_flag = WIN_IS_POPUP(wp);
4456 while (cur != NULL || shl_flag == FALSE)
4457 {
4458 if (shl_flag == FALSE
4459 && ((cur != NULL
4460 && cur->priority > SEARCH_HL_PRIORITY)
4461 || cur == NULL))
4462 {
4463 shl = search_hl;
4464 shl_flag = TRUE;
4465 }
4466 else
4467 shl = &cur->hl;
4468 if (shl->attr_cur != 0)
4469 search_attr = shl->attr_cur;
4470 if (shl != search_hl && cur != NULL)
4471 cur = cur->next;
4472 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004473 // Only highlight one character after the last column.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004474 if (*(*line + col) == NUL && (did_line_attr >= 1
4475 || (wp->w_p_list && lcs_eol_one == -1)))
4476 search_attr = 0;
4477 return search_attr;
4478}
4479
4480 int
4481get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4482{
4483 long prevcol = curcol;
4484 int prevcol_hl_flag = FALSE;
4485 matchitem_T *cur; // points to the match list
4486
4487 // we're not really at that column when skipping some text
4488 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4489 ++prevcol;
4490
4491 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4492 prevcol_hl_flag = TRUE;
4493 else
4494 {
4495 cur = wp->w_match_head;
4496 while (cur != NULL)
4497 {
4498 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4499 {
4500 prevcol_hl_flag = TRUE;
4501 break;
4502 }
4503 cur = cur->next;
4504 }
4505 }
4506 return prevcol_hl_flag;
4507}
4508
4509/*
4510 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4511 * or match highlighting.
4512 */
4513 void
4514get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4515{
4516 matchitem_T *cur; // points to the match list
4517 match_T *shl; // points to search_hl or a match
4518 int shl_flag; // flag to indicate whether search_hl
4519 // has been processed or not
4520
4521 cur = wp->w_match_head;
4522 shl_flag = WIN_IS_POPUP(wp);
4523 while (cur != NULL || shl_flag == FALSE)
4524 {
4525 if (shl_flag == FALSE
4526 && ((cur != NULL
4527 && cur->priority > SEARCH_HL_PRIORITY)
4528 || cur == NULL))
4529 {
4530 shl = search_hl;
4531 shl_flag = TRUE;
4532 }
4533 else
4534 shl = &cur->hl;
4535 if (col - 1 == (long)shl->startcol
4536 && (shl == search_hl || !shl->is_addpos))
4537 *char_attr = shl->attr;
4538 if (shl != search_hl && cur != NULL)
4539 cur = cur->next;
4540 }
4541}
4542
4543#endif // FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004544
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004545#if defined(FEAT_EVAL) || defined(PROTO)
4546# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004547 static int
4548matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4549{
4550 dictitem_T *di;
4551
4552 if (tv->v_type != VAR_DICT)
4553 {
4554 emsg(_(e_dictreq));
4555 return FAIL;
4556 }
4557
4558 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4559 *conceal_char = dict_get_string(tv->vval.v_dict,
4560 (char_u *)"conceal", FALSE);
4561
4562 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4563 {
4564 *win = find_win_by_nr_or_id(&di->di_tv);
4565 if (*win == NULL)
4566 {
4567 emsg(_(e_invalwindow));
4568 return FAIL;
4569 }
4570 }
4571
4572 return OK;
4573}
4574#endif
4575
4576/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004577 * "clearmatches()" function
4578 */
4579 void
4580f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4581{
4582#ifdef FEAT_SEARCH_EXTRA
4583 win_T *win = get_optional_window(argvars, 0);
4584
4585 if (win != NULL)
4586 clear_matches(win);
4587#endif
4588}
4589
4590/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004591 * "getmatches()" function
4592 */
4593 void
4594f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4595{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004596# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004597 dict_T *dict;
4598 matchitem_T *cur;
4599 int i;
4600 win_T *win = get_optional_window(argvars, 0);
4601
4602 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4603 return;
4604
4605 cur = win->w_match_head;
4606 while (cur != NULL)
4607 {
4608 dict = dict_alloc();
4609 if (dict == NULL)
4610 return;
4611 if (cur->match.regprog == NULL)
4612 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004613 // match added with matchaddpos()
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004614 for (i = 0; i < MAXPOSMATCH; ++i)
4615 {
4616 llpos_T *llpos;
4617 char buf[30]; // use 30 to avoid compiler warning
4618 list_T *l;
4619
4620 llpos = &cur->pos.pos[i];
4621 if (llpos->lnum == 0)
4622 break;
4623 l = list_alloc();
4624 if (l == NULL)
4625 break;
4626 list_append_number(l, (varnumber_T)llpos->lnum);
4627 if (llpos->col > 0)
4628 {
4629 list_append_number(l, (varnumber_T)llpos->col);
4630 list_append_number(l, (varnumber_T)llpos->len);
4631 }
4632 sprintf(buf, "pos%d", i + 1);
4633 dict_add_list(dict, buf, l);
4634 }
4635 }
4636 else
4637 {
4638 dict_add_string(dict, "pattern", cur->pattern);
4639 }
4640 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4641 dict_add_number(dict, "priority", (long)cur->priority);
4642 dict_add_number(dict, "id", (long)cur->id);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004643# if defined(FEAT_CONCEAL)
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004644 if (cur->conceal_char)
4645 {
4646 char_u buf[MB_MAXBYTES + 1];
4647
4648 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4649 dict_add_string(dict, "conceal", (char_u *)&buf);
4650 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004651# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004652 list_append_dict(rettv->vval.v_list, dict);
4653 cur = cur->next;
4654 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004655# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004656}
4657
4658/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004659 * "setmatches()" function
4660 */
4661 void
4662f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4663{
4664#ifdef FEAT_SEARCH_EXTRA
4665 list_T *l;
4666 listitem_T *li;
4667 dict_T *d;
4668 list_T *s = NULL;
4669 win_T *win = get_optional_window(argvars, 1);
4670
4671 rettv->vval.v_number = -1;
4672 if (argvars[0].v_type != VAR_LIST)
4673 {
4674 emsg(_(e_listreq));
4675 return;
4676 }
4677 if (win == NULL)
4678 return;
4679
4680 if ((l = argvars[0].vval.v_list) != NULL)
4681 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004682 // To some extent make sure that we are dealing with a list from
4683 // "getmatches()".
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004684 li = l->lv_first;
4685 while (li != NULL)
4686 {
4687 if (li->li_tv.v_type != VAR_DICT
4688 || (d = li->li_tv.vval.v_dict) == NULL)
4689 {
4690 emsg(_(e_invarg));
4691 return;
4692 }
4693 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4694 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4695 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4696 && dict_find(d, (char_u *)"priority", -1) != NULL
4697 && dict_find(d, (char_u *)"id", -1) != NULL))
4698 {
4699 emsg(_(e_invarg));
4700 return;
4701 }
4702 li = li->li_next;
4703 }
4704
4705 clear_matches(win);
4706 li = l->lv_first;
4707 while (li != NULL)
4708 {
4709 int i = 0;
4710 char buf[30]; // use 30 to avoid compiler warning
4711 dictitem_T *di;
4712 char_u *group;
4713 int priority;
4714 int id;
4715 char_u *conceal;
4716
4717 d = li->li_tv.vval.v_dict;
4718 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4719 {
4720 if (s == NULL)
4721 {
4722 s = list_alloc();
4723 if (s == NULL)
4724 return;
4725 }
4726
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004727 // match from matchaddpos()
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004728 for (i = 1; i < 9; i++)
4729 {
4730 sprintf((char *)buf, (char *)"pos%d", i);
4731 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4732 {
4733 if (di->di_tv.v_type != VAR_LIST)
4734 return;
4735
4736 list_append_tv(s, &di->di_tv);
4737 s->lv_refcount++;
4738 }
4739 else
4740 break;
4741 }
4742 }
4743
4744 group = dict_get_string(d, (char_u *)"group", TRUE);
4745 priority = (int)dict_get_number(d, (char_u *)"priority");
4746 id = (int)dict_get_number(d, (char_u *)"id");
4747 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4748 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4749 : NULL;
4750 if (i == 0)
4751 {
4752 match_add(win, group,
4753 dict_get_string(d, (char_u *)"pattern", FALSE),
4754 priority, id, NULL, conceal);
4755 }
4756 else
4757 {
4758 match_add(win, group, NULL, priority, id, s, conceal);
4759 list_unref(s);
4760 s = NULL;
4761 }
4762 vim_free(group);
4763 vim_free(conceal);
4764
4765 li = li->li_next;
4766 }
4767 rettv->vval.v_number = 0;
4768 }
4769#endif
4770}
4771
4772/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004773 * "matchadd()" function
4774 */
4775 void
4776f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4777{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004778# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004779 char_u buf[NUMBUFLEN];
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004780 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4781 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4782 int prio = 10; // default priority
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004783 int id = -1;
4784 int error = FALSE;
4785 char_u *conceal_char = NULL;
4786 win_T *win = curwin;
4787
4788 rettv->vval.v_number = -1;
4789
4790 if (grp == NULL || pat == NULL)
4791 return;
4792 if (argvars[2].v_type != VAR_UNKNOWN)
4793 {
4794 prio = (int)tv_get_number_chk(&argvars[2], &error);
4795 if (argvars[3].v_type != VAR_UNKNOWN)
4796 {
4797 id = (int)tv_get_number_chk(&argvars[3], &error);
4798 if (argvars[4].v_type != VAR_UNKNOWN
4799 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4800 return;
4801 }
4802 }
4803 if (error == TRUE)
4804 return;
4805 if (id >= 1 && id <= 3)
4806 {
4807 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4808 return;
4809 }
4810
4811 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4812 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004813# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004814}
4815
4816/*
4817 * "matchaddpos()" function
4818 */
4819 void
4820f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4821{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004822# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004823 char_u buf[NUMBUFLEN];
4824 char_u *group;
4825 int prio = 10;
4826 int id = -1;
4827 int error = FALSE;
4828 list_T *l;
4829 char_u *conceal_char = NULL;
4830 win_T *win = curwin;
4831
4832 rettv->vval.v_number = -1;
4833
4834 group = tv_get_string_buf_chk(&argvars[0], buf);
4835 if (group == NULL)
4836 return;
4837
4838 if (argvars[1].v_type != VAR_LIST)
4839 {
4840 semsg(_(e_listarg), "matchaddpos()");
4841 return;
4842 }
4843 l = argvars[1].vval.v_list;
4844 if (l == NULL)
4845 return;
4846
4847 if (argvars[2].v_type != VAR_UNKNOWN)
4848 {
4849 prio = (int)tv_get_number_chk(&argvars[2], &error);
4850 if (argvars[3].v_type != VAR_UNKNOWN)
4851 {
4852 id = (int)tv_get_number_chk(&argvars[3], &error);
4853
4854 if (argvars[4].v_type != VAR_UNKNOWN
4855 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4856 return;
4857 }
4858 }
4859 if (error == TRUE)
4860 return;
4861
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004862 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004863 if (id == 1 || id == 2)
4864 {
4865 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4866 return;
4867 }
4868
4869 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4870 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004871# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004872}
4873
4874/*
4875 * "matcharg()" function
4876 */
4877 void
4878f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4879{
4880 if (rettv_list_alloc(rettv) == OK)
4881 {
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004882# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004883 int id = (int)tv_get_number(&argvars[0]);
4884 matchitem_T *m;
4885
4886 if (id >= 1 && id <= 3)
4887 {
4888 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4889 {
4890 list_append_string(rettv->vval.v_list,
4891 syn_id2name(m->hlg_id), -1);
4892 list_append_string(rettv->vval.v_list, m->pattern, -1);
4893 }
4894 else
4895 {
4896 list_append_string(rettv->vval.v_list, NULL, -1);
4897 list_append_string(rettv->vval.v_list, NULL, -1);
4898 }
4899 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004900# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004901 }
4902}
4903
4904/*
4905 * "matchdelete()" function
4906 */
4907 void
4908f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4909{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004910# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004911 win_T *win = get_optional_window(argvars, 1);
4912
4913 if (win == NULL)
4914 rettv->vval.v_number = -1;
4915 else
4916 rettv->vval.v_number = match_delete(win,
4917 (int)tv_get_number(&argvars[0]), TRUE);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004918# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004919}
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004920#endif
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004921
4922#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4923/*
4924 * ":[N]match {group} {pattern}"
4925 * Sets nextcmd to the start of the next command, if any. Also called when
4926 * skipping commands to find the next command.
4927 */
4928 void
4929ex_match(exarg_T *eap)
4930{
4931 char_u *p;
4932 char_u *g = NULL;
4933 char_u *end;
4934 int c;
4935 int id;
4936
4937 if (eap->line2 <= 3)
4938 id = eap->line2;
4939 else
4940 {
4941 emsg(_(e_invcmd));
4942 return;
4943 }
4944
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004945 // First clear any old pattern.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004946 if (!eap->skip)
4947 match_delete(curwin, id, FALSE);
4948
Bram Moolenaar1966c242020-04-20 22:42:32 +02004949 if (ends_excmd2(eap->cmd, eap->arg))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004950 end = eap->arg;
4951 else if ((STRNICMP(eap->arg, "none", 4) == 0
Bram Moolenaar1966c242020-04-20 22:42:32 +02004952 && (VIM_ISWHITE(eap->arg[4])
4953 || ends_excmd2(eap->arg, eap->arg + 4))))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004954 end = eap->arg + 4;
4955 else
4956 {
4957 p = skiptowhite(eap->arg);
4958 if (!eap->skip)
4959 g = vim_strnsave(eap->arg, (int)(p - eap->arg));
4960 p = skipwhite(p);
4961 if (*p == NUL)
4962 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004963 // There must be two arguments.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004964 vim_free(g);
4965 semsg(_(e_invarg2), eap->arg);
4966 return;
4967 }
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02004968 end = skip_regexp(p + 1, *p, TRUE);
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004969 if (!eap->skip)
4970 {
Bram Moolenaar1966c242020-04-20 22:42:32 +02004971 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004972 {
4973 vim_free(g);
4974 eap->errmsg = e_trailing;
4975 return;
4976 }
4977 if (*end != *p)
4978 {
4979 vim_free(g);
4980 semsg(_(e_invarg2), p);
4981 return;
4982 }
4983
4984 c = *end;
4985 *end = NUL;
4986 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
4987 vim_free(g);
4988 *end = c;
4989 }
4990 }
4991 eap->nextcmd = find_nextcmd(end);
4992}
4993#endif