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