blob: 5d123ac877e73cb42d6ee4d7c081294e11892af6 [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);
Bram Moolenaardf44a272020-06-07 20:49:05 +0200909 key = vim_strnsave_up(key_start, linep - key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200910 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 Moolenaarea563cc2020-06-05 19:36:57 +02002932 // Only use the underline/undercurl color when used, it may clear the
2933 // background color if not supported.
2934 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
2935 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
2936 else
2937 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002938 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2939 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2940 {
2941 // If both fg and bg are invalid fall back to the cterm colors.
2942 // Helps when the GUI only uses an attribute, e.g. undercurl.
2943 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2944 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2945 }
2946# endif
2947 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2948 }
2949}
2950
2951/*
2952 * Lookup a highlight group name and return its ID.
2953 * If it is not found, 0 is returned.
2954 */
2955 int
2956syn_name2id(char_u *name)
2957{
2958 int i;
2959 char_u name_u[200];
2960
2961 // Avoid using stricmp() too much, it's slow on some systems
2962 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
2963 // don't deserve to be found!
2964 vim_strncpy(name_u, name, 199);
2965 vim_strup(name_u);
2966 for (i = highlight_ga.ga_len; --i >= 0; )
2967 if (HL_TABLE()[i].sg_name_u != NULL
2968 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2969 break;
2970 return i + 1;
2971}
2972
2973/*
2974 * Lookup a highlight group name and return its attributes.
2975 * Return zero if not found.
2976 */
2977 int
2978syn_name2attr(char_u *name)
2979{
2980 int id = syn_name2id(name);
2981
2982 if (id != 0)
2983 return syn_id2attr(id);
2984 return 0;
2985}
2986
2987#if defined(FEAT_EVAL) || defined(PROTO)
2988/*
2989 * Return TRUE if highlight group "name" exists.
2990 */
2991 int
2992highlight_exists(char_u *name)
2993{
2994 return (syn_name2id(name) > 0);
2995}
2996
2997# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
2998/*
2999 * Return the name of highlight group "id".
3000 * When not a valid ID return an empty string.
3001 */
3002 char_u *
3003syn_id2name(int id)
3004{
3005 if (id <= 0 || id > highlight_ga.ga_len)
3006 return (char_u *)"";
3007 return HL_TABLE()[id - 1].sg_name;
3008}
3009# endif
3010#endif
3011
3012/*
3013 * Like syn_name2id(), but take a pointer + length argument.
3014 */
3015 int
3016syn_namen2id(char_u *linep, int len)
3017{
3018 char_u *name;
3019 int id = 0;
3020
3021 name = vim_strnsave(linep, len);
3022 if (name != NULL)
3023 {
3024 id = syn_name2id(name);
3025 vim_free(name);
3026 }
3027 return id;
3028}
3029
3030/*
3031 * Find highlight group name in the table and return its ID.
3032 * The argument is a pointer to the name and the length of the name.
3033 * If it doesn't exist yet, a new entry is created.
3034 * Return 0 for failure.
3035 */
3036 int
3037syn_check_group(char_u *pp, int len)
3038{
3039 int id;
3040 char_u *name;
3041
3042 name = vim_strnsave(pp, len);
3043 if (name == NULL)
3044 return 0;
3045
3046 id = syn_name2id(name);
3047 if (id == 0) // doesn't exist yet
3048 id = syn_add_group(name);
3049 else
3050 vim_free(name);
3051 return id;
3052}
3053
3054/*
3055 * Add new highlight group and return its ID.
3056 * "name" must be an allocated string, it will be consumed.
3057 * Return 0 for failure.
3058 */
3059 static int
3060syn_add_group(char_u *name)
3061{
3062 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003063 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003064
3065 // Check that the name is ASCII letters, digits and underscore.
3066 for (p = name; *p != NUL; ++p)
3067 {
3068 if (!vim_isprintc(*p))
3069 {
3070 emsg(_("E669: Unprintable character in group name"));
3071 vim_free(name);
3072 return 0;
3073 }
3074 else if (!ASCII_ISALNUM(*p) && *p != '_')
3075 {
3076 // This is an error, but since there previously was no check only
3077 // give a warning.
3078 msg_source(HL_ATTR(HLF_W));
3079 msg(_("W18: Invalid character in group name"));
3080 break;
3081 }
3082 }
3083
3084 /*
3085 * First call for this growarray: init growing array.
3086 */
3087 if (highlight_ga.ga_data == NULL)
3088 {
3089 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3090 highlight_ga.ga_growsize = 10;
3091 }
3092
3093 if (highlight_ga.ga_len >= MAX_HL_ID)
3094 {
3095 emsg(_("E849: Too many highlight and syntax groups"));
3096 vim_free(name);
3097 return 0;
3098 }
3099
3100 /*
3101 * Make room for at least one other syntax_highlight entry.
3102 */
3103 if (ga_grow(&highlight_ga, 1) == FAIL)
3104 {
3105 vim_free(name);
3106 return 0;
3107 }
3108
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003109 name_up = vim_strsave_up(name);
3110 if (name_up == NULL)
3111 {
3112 vim_free(name);
3113 return 0;
3114 }
3115
Bram Moolenaara80faa82020-04-12 19:37:17 +02003116 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003117 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003118 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003119#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3120 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3121 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003122 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003123#endif
3124 ++highlight_ga.ga_len;
3125
3126 return highlight_ga.ga_len; // ID is index plus one
3127}
3128
3129/*
3130 * When, just after calling syn_add_group(), an error is discovered, this
3131 * function deletes the new name.
3132 */
3133 static void
3134syn_unadd_group(void)
3135{
3136 --highlight_ga.ga_len;
3137 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3138 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3139}
3140
3141/*
3142 * Translate a group ID to highlight attributes.
3143 */
3144 int
3145syn_id2attr(int hl_id)
3146{
3147 int attr;
3148 hl_group_T *sgp;
3149
3150 hl_id = syn_get_final_id(hl_id);
3151 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3152
3153#ifdef FEAT_GUI
3154 /*
3155 * Only use GUI attr when the GUI is being used.
3156 */
3157 if (gui.in_use)
3158 attr = sgp->sg_gui_attr;
3159 else
3160#endif
3161 if (IS_CTERM)
3162 attr = sgp->sg_cterm_attr;
3163 else
3164 attr = sgp->sg_term_attr;
3165
3166 return attr;
3167}
3168
3169#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3170/*
3171 * Get the GUI colors and attributes for a group ID.
3172 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3173 */
3174 int
3175syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3176{
3177 hl_group_T *sgp;
3178
3179 hl_id = syn_get_final_id(hl_id);
3180 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3181
3182 *fgp = sgp->sg_gui_fg;
3183 *bgp = sgp->sg_gui_bg;
3184 return sgp->sg_gui;
3185}
3186#endif
3187
3188#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003189 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3190 && defined(FEAT_TERMGUICOLORS)) \
3191 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003192 void
3193syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3194{
3195 hl_group_T *sgp;
3196
3197 hl_id = syn_get_final_id(hl_id);
3198 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3199 *fgp = sgp->sg_cterm_fg - 1;
3200 *bgp = sgp->sg_cterm_bg - 1;
3201}
3202#endif
3203
3204/*
3205 * Translate a group ID to the final group ID (following links).
3206 */
3207 int
3208syn_get_final_id(int hl_id)
3209{
3210 int count;
3211 hl_group_T *sgp;
3212
3213 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3214 return 0; // Can be called from eval!!
3215
3216 /*
3217 * Follow links until there is no more.
3218 * Look out for loops! Break after 100 links.
3219 */
3220 for (count = 100; --count >= 0; )
3221 {
3222 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3223 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3224 break;
3225 hl_id = sgp->sg_link;
3226 }
3227
3228 return hl_id;
3229}
3230
3231#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3232/*
3233 * Call this function just after the GUI has started.
3234 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3235 * It finds the font and color handles for the highlighting groups.
3236 */
3237 void
3238highlight_gui_started(void)
3239{
3240 int idx;
3241
3242 // First get the colors from the "Normal" and "Menu" group, if set
3243 if (USE_24BIT)
3244 set_normal_colors();
3245
3246 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3247 gui_do_one_color(idx, FALSE, FALSE);
3248
3249 highlight_changed();
3250}
3251
3252 static void
3253gui_do_one_color(
3254 int idx,
3255 int do_menu UNUSED, // TRUE: might set the menu font
3256 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3257{
3258 int didit = FALSE;
3259
3260# ifdef FEAT_GUI
3261# ifdef FEAT_TERMGUICOLORS
3262 if (gui.in_use)
3263# endif
3264 if (HL_TABLE()[idx].sg_font_name != NULL)
3265 {
3266 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3267 do_tooltip, TRUE);
3268 didit = TRUE;
3269 }
3270# endif
3271 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3272 {
3273 HL_TABLE()[idx].sg_gui_fg =
3274 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3275 didit = TRUE;
3276 }
3277 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3278 {
3279 HL_TABLE()[idx].sg_gui_bg =
3280 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3281 didit = TRUE;
3282 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003283 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3284 {
3285 HL_TABLE()[idx].sg_gui_sp =
3286 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3287 didit = TRUE;
3288 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003289 if (didit) // need to get a new attr number
3290 set_hl_attr(idx);
3291}
3292#endif
3293
3294#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3295/*
3296 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3297 */
3298 static void
3299combine_stl_hlt(
3300 int id,
3301 int id_S,
3302 int id_alt,
3303 int hlcnt,
3304 int i,
3305 int hlf,
3306 int *table)
3307{
3308 hl_group_T *hlt = HL_TABLE();
3309
3310 if (id_alt == 0)
3311 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003312 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003313 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3314 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3315# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3316 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3317# endif
3318 }
3319 else
3320 mch_memmove(&hlt[hlcnt + i],
3321 &hlt[id_alt - 1],
3322 sizeof(hl_group_T));
3323 hlt[hlcnt + i].sg_link = 0;
3324
3325 hlt[hlcnt + i].sg_term ^=
3326 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3327 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3328 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3329 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3330 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3331 hlt[hlcnt + i].sg_cterm ^=
3332 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3333 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3334 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3335 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3336 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3337# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3338 hlt[hlcnt + i].sg_gui ^=
3339 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3340# endif
3341# ifdef FEAT_GUI
3342 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3343 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3344 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3345 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3346 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3347 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3348 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3349 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3350# ifdef FEAT_XFONTSET
3351 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3352 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3353# endif
3354# endif
3355 highlight_ga.ga_len = hlcnt + i + 1;
3356 set_hl_attr(hlcnt + i); // At long last we can apply
3357 table[i] = syn_id2attr(hlcnt + i + 1);
3358}
3359#endif
3360
3361/*
3362 * Translate the 'highlight' option into attributes in highlight_attr[] and
3363 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3364 * corresponding highlights to use on top of HLF_SNC is computed.
3365 * Called only when the 'highlight' option has been changed and upon first
3366 * screen redraw after any :highlight command.
3367 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3368 */
3369 int
3370highlight_changed(void)
3371{
3372 int hlf;
3373 int i;
3374 char_u *p;
3375 int attr;
3376 char_u *end;
3377 int id;
3378#ifdef USER_HIGHLIGHT
3379 char_u userhl[30]; // use 30 to avoid compiler warning
3380# ifdef FEAT_STL_OPT
3381 int id_S = -1;
3382 int id_SNC = 0;
3383# ifdef FEAT_TERMINAL
3384 int id_ST = 0;
3385 int id_STNC = 0;
3386# endif
3387 int hlcnt;
3388# endif
3389#endif
3390 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3391
3392 need_highlight_changed = FALSE;
3393
3394 /*
3395 * Clear all attributes.
3396 */
3397 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3398 highlight_attr[hlf] = 0;
3399
3400 /*
3401 * First set all attributes to their default value.
3402 * Then use the attributes from the 'highlight' option.
3403 */
3404 for (i = 0; i < 2; ++i)
3405 {
3406 if (i)
3407 p = p_hl;
3408 else
3409 p = get_highlight_default();
3410 if (p == NULL) // just in case
3411 continue;
3412
3413 while (*p)
3414 {
3415 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3416 if (hl_flags[hlf] == *p)
3417 break;
3418 ++p;
3419 if (hlf == (int)HLF_COUNT || *p == NUL)
3420 return FAIL;
3421
3422 /*
3423 * Allow several hl_flags to be combined, like "bu" for
3424 * bold-underlined.
3425 */
3426 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003427 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003428 {
3429 if (VIM_ISWHITE(*p)) // ignore white space
3430 continue;
3431
3432 if (attr > HL_ALL) // Combination with ':' is not allowed.
3433 return FAIL;
3434
3435 switch (*p)
3436 {
3437 case 'b': attr |= HL_BOLD;
3438 break;
3439 case 'i': attr |= HL_ITALIC;
3440 break;
3441 case '-':
3442 case 'n': // no highlighting
3443 break;
3444 case 'r': attr |= HL_INVERSE;
3445 break;
3446 case 's': attr |= HL_STANDOUT;
3447 break;
3448 case 'u': attr |= HL_UNDERLINE;
3449 break;
3450 case 'c': attr |= HL_UNDERCURL;
3451 break;
3452 case 't': attr |= HL_STRIKETHROUGH;
3453 break;
3454 case ':': ++p; // highlight group name
3455 if (attr || *p == NUL) // no combinations
3456 return FAIL;
3457 end = vim_strchr(p, ',');
3458 if (end == NULL)
3459 end = p + STRLEN(p);
3460 id = syn_check_group(p, (int)(end - p));
3461 if (id == 0)
3462 return FAIL;
3463 attr = syn_id2attr(id);
3464 p = end - 1;
3465#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3466 if (hlf == (int)HLF_SNC)
3467 id_SNC = syn_get_final_id(id);
3468# ifdef FEAT_TERMINAL
3469 else if (hlf == (int)HLF_ST)
3470 id_ST = syn_get_final_id(id);
3471 else if (hlf == (int)HLF_STNC)
3472 id_STNC = syn_get_final_id(id);
3473# endif
3474 else if (hlf == (int)HLF_S)
3475 id_S = syn_get_final_id(id);
3476#endif
3477 break;
3478 default: return FAIL;
3479 }
3480 }
3481 highlight_attr[hlf] = attr;
3482
3483 p = skip_to_option_part(p); // skip comma and spaces
3484 }
3485 }
3486
3487#ifdef USER_HIGHLIGHT
3488 /*
3489 * Setup the user highlights
3490 *
3491 * Temporarily utilize 28 more hl entries:
3492 * 9 for User1-User9 combined with StatusLineNC
3493 * 9 for User1-User9 combined with StatusLineTerm
3494 * 9 for User1-User9 combined with StatusLineTermNC
3495 * 1 for StatusLine default
3496 * Have to be in there simultaneously in case of table overflows in
3497 * get_attr_entry()
3498 */
3499# ifdef FEAT_STL_OPT
3500 if (ga_grow(&highlight_ga, 28) == FAIL)
3501 return FAIL;
3502 hlcnt = highlight_ga.ga_len;
3503 if (id_S == -1)
3504 {
3505 // Make sure id_S is always valid to simplify code below. Use the last
3506 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003507 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003508 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3509 id_S = hlcnt + 19;
3510 }
3511# endif
3512 for (i = 0; i < 9; i++)
3513 {
3514 sprintf((char *)userhl, "User%d", i + 1);
3515 id = syn_name2id(userhl);
3516 if (id == 0)
3517 {
3518 highlight_user[i] = 0;
3519# ifdef FEAT_STL_OPT
3520 highlight_stlnc[i] = 0;
3521# ifdef FEAT_TERMINAL
3522 highlight_stlterm[i] = 0;
3523 highlight_stltermnc[i] = 0;
3524# endif
3525# endif
3526 }
3527 else
3528 {
3529 highlight_user[i] = syn_id2attr(id);
3530# ifdef FEAT_STL_OPT
3531 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3532 HLF_SNC, highlight_stlnc);
3533# ifdef FEAT_TERMINAL
3534 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3535 HLF_ST, highlight_stlterm);
3536 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3537 HLF_STNC, highlight_stltermnc);
3538# endif
3539# endif
3540 }
3541 }
3542# ifdef FEAT_STL_OPT
3543 highlight_ga.ga_len = hlcnt;
3544# endif
3545
3546#endif // USER_HIGHLIGHT
3547
3548 return OK;
3549}
3550
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003551static void highlight_list(void);
3552static void highlight_list_two(int cnt, int attr);
3553
3554/*
3555 * Handle command line completion for :highlight command.
3556 */
3557 void
3558set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3559{
3560 char_u *p;
3561
3562 // Default: expand group names
3563 xp->xp_context = EXPAND_HIGHLIGHT;
3564 xp->xp_pattern = arg;
3565 include_link = 2;
3566 include_default = 1;
3567
3568 // (part of) subcommand already typed
3569 if (*arg != NUL)
3570 {
3571 p = skiptowhite(arg);
3572 if (*p != NUL) // past "default" or group name
3573 {
3574 include_default = 0;
3575 if (STRNCMP("default", arg, p - arg) == 0)
3576 {
3577 arg = skipwhite(p);
3578 xp->xp_pattern = arg;
3579 p = skiptowhite(arg);
3580 }
3581 if (*p != NUL) // past group name
3582 {
3583 include_link = 0;
3584 if (arg[1] == 'i' && arg[0] == 'N')
3585 highlight_list();
3586 if (STRNCMP("link", arg, p - arg) == 0
3587 || STRNCMP("clear", arg, p - arg) == 0)
3588 {
3589 xp->xp_pattern = skipwhite(p);
3590 p = skiptowhite(xp->xp_pattern);
3591 if (*p != NUL) // past first group name
3592 {
3593 xp->xp_pattern = skipwhite(p);
3594 p = skiptowhite(xp->xp_pattern);
3595 }
3596 }
3597 if (*p != NUL) // past group name(s)
3598 xp->xp_context = EXPAND_NOTHING;
3599 }
3600 }
3601 }
3602}
3603
3604/*
3605 * List highlighting matches in a nice way.
3606 */
3607 static void
3608highlight_list(void)
3609{
3610 int i;
3611
3612 for (i = 10; --i >= 0; )
3613 highlight_list_two(i, HL_ATTR(HLF_D));
3614 for (i = 40; --i >= 0; )
3615 highlight_list_two(99, 0);
3616}
3617
3618 static void
3619highlight_list_two(int cnt, int attr)
3620{
3621 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3622 msg_clr_eos();
3623 out_flush();
3624 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3625}
3626
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003627/*
3628 * Function given to ExpandGeneric() to obtain the list of group names.
3629 */
3630 char_u *
3631get_highlight_name(expand_T *xp UNUSED, int idx)
3632{
3633 return get_highlight_name_ext(xp, idx, TRUE);
3634}
3635
3636/*
3637 * Obtain a highlight group name.
3638 * When "skip_cleared" is TRUE don't return a cleared entry.
3639 */
3640 char_u *
3641get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3642{
3643 if (idx < 0)
3644 return NULL;
3645
3646 // Items are never removed from the table, skip the ones that were
3647 // cleared.
3648 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3649 return (char_u *)"";
3650
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003651 if (idx == highlight_ga.ga_len && include_none != 0)
3652 return (char_u *)"none";
3653 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3654 return (char_u *)"default";
3655 if (idx == highlight_ga.ga_len + include_none + include_default
3656 && include_link != 0)
3657 return (char_u *)"link";
3658 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3659 && include_link != 0)
3660 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003661 if (idx >= highlight_ga.ga_len)
3662 return NULL;
3663 return HL_TABLE()[idx].sg_name;
3664}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003665
3666#if defined(FEAT_GUI) || defined(PROTO)
3667/*
3668 * Free all the highlight group fonts.
3669 * Used when quitting for systems which need it.
3670 */
3671 void
3672free_highlight_fonts(void)
3673{
3674 int idx;
3675
3676 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3677 {
3678 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3679 HL_TABLE()[idx].sg_font = NOFONT;
3680# ifdef FEAT_XFONTSET
3681 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3682 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3683# endif
3684 }
3685
3686 gui_mch_free_font(gui.norm_font);
3687# ifdef FEAT_XFONTSET
3688 gui_mch_free_fontset(gui.fontset);
3689# endif
3690# ifndef FEAT_GUI_GTK
3691 gui_mch_free_font(gui.bold_font);
3692 gui_mch_free_font(gui.ital_font);
3693 gui_mch_free_font(gui.boldital_font);
3694# endif
3695}
3696#endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003697
3698
3699#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003700
3701# define SEARCH_HL_PRIORITY 0
3702
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003703/*
3704 * Add match to the match list of window 'wp'. The pattern 'pat' will be
3705 * highlighted with the group 'grp' with priority 'prio'.
3706 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3707 * If no particular ID is desired, -1 must be specified for 'id'.
3708 * Return ID of added match, -1 on failure.
3709 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003710 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003711match_add(
3712 win_T *wp,
3713 char_u *grp,
3714 char_u *pat,
3715 int prio,
3716 int id,
3717 list_T *pos_list,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003718 char_u *conceal_char UNUSED) // pointer to conceal replacement char
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003719{
3720 matchitem_T *cur;
3721 matchitem_T *prev;
3722 matchitem_T *m;
3723 int hlg_id;
3724 regprog_T *regprog = NULL;
3725 int rtype = SOME_VALID;
3726
3727 if (*grp == NUL || (pat != NULL && *pat == NUL))
3728 return -1;
3729 if (id < -1 || id == 0)
3730 {
Bram Moolenaar32aa1022019-11-02 22:54:41 +01003731 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"),
3732 id);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003733 return -1;
3734 }
3735 if (id != -1)
3736 {
3737 cur = wp->w_match_head;
3738 while (cur != NULL)
3739 {
3740 if (cur->id == id)
3741 {
3742 semsg(_("E801: ID already taken: %d"), id);
3743 return -1;
3744 }
3745 cur = cur->next;
3746 }
3747 }
3748 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3749 {
3750 semsg(_(e_nogroup), grp);
3751 return -1;
3752 }
3753 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3754 {
3755 semsg(_(e_invarg2), pat);
3756 return -1;
3757 }
3758
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003759 // Find available match ID.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003760 while (id == -1)
3761 {
3762 cur = wp->w_match_head;
3763 while (cur != NULL && cur->id != wp->w_next_match_id)
3764 cur = cur->next;
3765 if (cur == NULL)
3766 id = wp->w_next_match_id;
3767 wp->w_next_match_id++;
3768 }
3769
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003770 // Build new match.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003771 m = ALLOC_CLEAR_ONE(matchitem_T);
3772 m->id = id;
3773 m->priority = prio;
3774 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3775 m->hlg_id = hlg_id;
3776 m->match.regprog = regprog;
3777 m->match.rmm_ic = FALSE;
3778 m->match.rmm_maxcol = 0;
3779# if defined(FEAT_CONCEAL)
3780 m->conceal_char = 0;
3781 if (conceal_char != NULL)
3782 m->conceal_char = (*mb_ptr2char)(conceal_char);
3783# endif
3784
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003785 // Set up position matches
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003786 if (pos_list != NULL)
3787 {
3788 linenr_T toplnum = 0;
3789 linenr_T botlnum = 0;
3790 listitem_T *li;
3791 int i;
3792
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02003793 CHECK_LIST_MATERIALIZE(pos_list);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003794 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3795 i++, li = li->li_next)
3796 {
3797 linenr_T lnum = 0;
3798 colnr_T col = 0;
3799 int len = 1;
3800 list_T *subl;
3801 listitem_T *subli;
3802 int error = FALSE;
3803
3804 if (li->li_tv.v_type == VAR_LIST)
3805 {
3806 subl = li->li_tv.vval.v_list;
3807 if (subl == NULL)
3808 goto fail;
3809 subli = subl->lv_first;
3810 if (subli == NULL)
3811 goto fail;
3812 lnum = tv_get_number_chk(&subli->li_tv, &error);
3813 if (error == TRUE)
3814 goto fail;
3815 if (lnum == 0)
3816 {
3817 --i;
3818 continue;
3819 }
3820 m->pos.pos[i].lnum = lnum;
3821 subli = subli->li_next;
3822 if (subli != NULL)
3823 {
3824 col = tv_get_number_chk(&subli->li_tv, &error);
3825 if (error == TRUE)
3826 goto fail;
3827 subli = subli->li_next;
3828 if (subli != NULL)
3829 {
3830 len = tv_get_number_chk(&subli->li_tv, &error);
3831 if (error == TRUE)
3832 goto fail;
3833 }
3834 }
3835 m->pos.pos[i].col = col;
3836 m->pos.pos[i].len = len;
3837 }
3838 else if (li->li_tv.v_type == VAR_NUMBER)
3839 {
3840 if (li->li_tv.vval.v_number == 0)
3841 {
3842 --i;
3843 continue;
3844 }
3845 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3846 m->pos.pos[i].col = 0;
3847 m->pos.pos[i].len = 0;
3848 }
3849 else
3850 {
Bram Moolenaar1500a422019-12-24 15:38:21 +01003851 emsg(_("E290: List or number required"));
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003852 goto fail;
3853 }
3854 if (toplnum == 0 || lnum < toplnum)
3855 toplnum = lnum;
3856 if (botlnum == 0 || lnum >= botlnum)
3857 botlnum = lnum + 1;
3858 }
3859
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003860 // Calculate top and bottom lines for redrawing area
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003861 if (toplnum != 0)
3862 {
3863 if (wp->w_buffer->b_mod_set)
3864 {
3865 if (wp->w_buffer->b_mod_top > toplnum)
3866 wp->w_buffer->b_mod_top = toplnum;
3867 if (wp->w_buffer->b_mod_bot < botlnum)
3868 wp->w_buffer->b_mod_bot = botlnum;
3869 }
3870 else
3871 {
3872 wp->w_buffer->b_mod_set = TRUE;
3873 wp->w_buffer->b_mod_top = toplnum;
3874 wp->w_buffer->b_mod_bot = botlnum;
3875 wp->w_buffer->b_mod_xlines = 0;
3876 }
3877 m->pos.toplnum = toplnum;
3878 m->pos.botlnum = botlnum;
3879 rtype = VALID;
3880 }
3881 }
3882
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003883 // Insert new match. The match list is in ascending order with regard to
3884 // the match priorities.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003885 cur = wp->w_match_head;
3886 prev = cur;
3887 while (cur != NULL && prio >= cur->priority)
3888 {
3889 prev = cur;
3890 cur = cur->next;
3891 }
3892 if (cur == prev)
3893 wp->w_match_head = m;
3894 else
3895 prev->next = m;
3896 m->next = cur;
3897
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003898 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003899 return id;
3900
3901fail:
3902 vim_free(m);
3903 return -1;
3904}
3905
3906/*
3907 * Delete match with ID 'id' in the match list of window 'wp'.
3908 * Print error messages if 'perr' is TRUE.
3909 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003910 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003911match_delete(win_T *wp, int id, int perr)
3912{
3913 matchitem_T *cur = wp->w_match_head;
3914 matchitem_T *prev = cur;
3915 int rtype = SOME_VALID;
3916
3917 if (id < 1)
3918 {
3919 if (perr == TRUE)
3920 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3921 id);
3922 return -1;
3923 }
3924 while (cur != NULL && cur->id != id)
3925 {
3926 prev = cur;
3927 cur = cur->next;
3928 }
3929 if (cur == NULL)
3930 {
3931 if (perr == TRUE)
3932 semsg(_("E803: ID not found: %d"), id);
3933 return -1;
3934 }
3935 if (cur == prev)
3936 wp->w_match_head = cur->next;
3937 else
3938 prev->next = cur->next;
3939 vim_regfree(cur->match.regprog);
3940 vim_free(cur->pattern);
3941 if (cur->pos.toplnum != 0)
3942 {
3943 if (wp->w_buffer->b_mod_set)
3944 {
3945 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3946 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3947 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3948 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3949 }
3950 else
3951 {
3952 wp->w_buffer->b_mod_set = TRUE;
3953 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3954 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3955 wp->w_buffer->b_mod_xlines = 0;
3956 }
3957 rtype = VALID;
3958 }
3959 vim_free(cur);
Bram Moolenaar06029a82019-07-24 14:25:26 +02003960 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003961 return 0;
3962}
3963
3964/*
3965 * Delete all matches in the match list of window 'wp'.
3966 */
3967 void
3968clear_matches(win_T *wp)
3969{
3970 matchitem_T *m;
3971
3972 while (wp->w_match_head != NULL)
3973 {
3974 m = wp->w_match_head->next;
3975 vim_regfree(wp->w_match_head->match.regprog);
3976 vim_free(wp->w_match_head->pattern);
3977 vim_free(wp->w_match_head);
3978 wp->w_match_head = m;
3979 }
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003980 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003981}
3982
3983/*
3984 * Get match from ID 'id' in window 'wp'.
3985 * Return NULL if match not found.
3986 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003987 static matchitem_T *
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003988get_match(win_T *wp, int id)
3989{
3990 matchitem_T *cur = wp->w_match_head;
3991
3992 while (cur != NULL && cur->id != id)
3993 cur = cur->next;
3994 return cur;
3995}
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003996
3997/*
3998 * Init for calling prepare_search_hl().
3999 */
4000 void
4001init_search_hl(win_T *wp, match_T *search_hl)
4002{
4003 matchitem_T *cur;
4004
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004005 // Setup for match and 'hlsearch' highlighting. Disable any previous
4006 // match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004007 cur = wp->w_match_head;
4008 while (cur != NULL)
4009 {
4010 cur->hl.rm = cur->match;
4011 if (cur->hlg_id == 0)
4012 cur->hl.attr = 0;
4013 else
4014 cur->hl.attr = syn_id2attr(cur->hlg_id);
4015 cur->hl.buf = wp->w_buffer;
4016 cur->hl.lnum = 0;
4017 cur->hl.first_lnum = 0;
4018# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004019 // Set the time limit to 'redrawtime'.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004020 profile_setlimit(p_rdt, &(cur->hl.tm));
4021# endif
4022 cur = cur->next;
4023 }
4024 search_hl->buf = wp->w_buffer;
4025 search_hl->lnum = 0;
4026 search_hl->first_lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004027 // time limit is set at the toplevel, for all windows
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004028}
4029
4030/*
4031 * If there is a match fill "shl" and return one.
4032 * Return zero otherwise.
4033 */
4034 static int
4035next_search_hl_pos(
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004036 match_T *shl, // points to a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004037 linenr_T lnum,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004038 posmatch_T *posmatch, // match positions
4039 colnr_T mincol) // minimal column for a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004040{
4041 int i;
4042 int found = -1;
4043
4044 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
4045 {
4046 llpos_T *pos = &posmatch->pos[i];
4047
4048 if (pos->lnum == 0)
4049 break;
4050 if (pos->len == 0 && pos->col < mincol)
4051 continue;
4052 if (pos->lnum == lnum)
4053 {
4054 if (found >= 0)
4055 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004056 // if this match comes before the one at "found" then swap
4057 // them
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004058 if (pos->col < posmatch->pos[found].col)
4059 {
4060 llpos_T tmp = *pos;
4061
4062 *pos = posmatch->pos[found];
4063 posmatch->pos[found] = tmp;
4064 }
4065 }
4066 else
4067 found = i;
4068 }
4069 }
4070 posmatch->cur = 0;
4071 if (found >= 0)
4072 {
4073 colnr_T start = posmatch->pos[found].col == 0
4074 ? 0 : posmatch->pos[found].col - 1;
4075 colnr_T end = posmatch->pos[found].col == 0
4076 ? MAXCOL : start + posmatch->pos[found].len;
4077
4078 shl->lnum = lnum;
4079 shl->rm.startpos[0].lnum = 0;
4080 shl->rm.startpos[0].col = start;
4081 shl->rm.endpos[0].lnum = 0;
4082 shl->rm.endpos[0].col = end;
4083 shl->is_addpos = TRUE;
4084 posmatch->cur = found + 1;
4085 return 1;
4086 }
4087 return 0;
4088}
4089
4090/*
4091 * Search for a next 'hlsearch' or match.
4092 * Uses shl->buf.
4093 * Sets shl->lnum and shl->rm contents.
4094 * Note: Assumes a previous match is always before "lnum", unless
4095 * shl->lnum is zero.
4096 * Careful: Any pointers for buffer lines will become invalid.
4097 */
4098 static void
4099next_search_hl(
4100 win_T *win,
4101 match_T *search_hl,
4102 match_T *shl, // points to search_hl or a match
4103 linenr_T lnum,
4104 colnr_T mincol, // minimal column for a match
4105 matchitem_T *cur) // to retrieve match positions if any
4106{
4107 linenr_T l;
4108 colnr_T matchcol;
4109 long nmatched;
Bram Moolenaar53989552019-12-23 22:59:18 +01004110 int called_emsg_before = called_emsg;
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004111
4112 // for :{range}s/pat only highlight inside the range
4113 if (lnum < search_first_line || lnum > search_last_line)
4114 {
4115 shl->lnum = 0;
4116 return;
4117 }
4118
4119 if (shl->lnum != 0)
4120 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004121 // Check for three situations:
4122 // 1. If the "lnum" is below a previous match, start a new search.
4123 // 2. If the previous match includes "mincol", use it.
4124 // 3. Continue after the previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004125 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4126 if (lnum > l)
4127 shl->lnum = 0;
4128 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4129 return;
4130 }
4131
4132 /*
4133 * Repeat searching for a match until one is found that includes "mincol"
4134 * or none is found in this line.
4135 */
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004136 for (;;)
4137 {
4138# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004139 // Stop searching after passing the time limit.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004140 if (profile_passed_limit(&(shl->tm)))
4141 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004142 shl->lnum = 0; // no match found in time
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004143 break;
4144 }
4145# endif
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004146 // Three situations:
4147 // 1. No useful previous match: search from start of line.
4148 // 2. Not Vi compatible or empty match: continue at next character.
4149 // Break the loop if this is beyond the end of the line.
4150 // 3. Vi compatible searching: continue at end of previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004151 if (shl->lnum == 0)
4152 matchcol = 0;
4153 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4154 || (shl->rm.endpos[0].lnum == 0
4155 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4156 {
4157 char_u *ml;
4158
4159 matchcol = shl->rm.startpos[0].col;
4160 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4161 if (*ml == NUL)
4162 {
4163 ++matchcol;
4164 shl->lnum = 0;
4165 break;
4166 }
4167 if (has_mbyte)
4168 matchcol += mb_ptr2len(ml);
4169 else
4170 ++matchcol;
4171 }
4172 else
4173 matchcol = shl->rm.endpos[0].col;
4174
4175 shl->lnum = lnum;
4176 if (shl->rm.regprog != NULL)
4177 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004178 // Remember whether shl->rm is using a copy of the regprog in
4179 // cur->match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004180 int regprog_is_copy = (shl != search_hl && cur != NULL
4181 && shl == &cur->hl
4182 && cur->match.regprog == cur->hl.rm.regprog);
4183 int timed_out = FALSE;
4184
4185 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4186 matchcol,
4187#ifdef FEAT_RELTIME
4188 &(shl->tm), &timed_out
4189#else
4190 NULL, NULL
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004191#endif
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004192 );
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004193 // Copy the regprog, in case it got freed and recompiled.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004194 if (regprog_is_copy)
4195 cur->match.regprog = cur->hl.rm.regprog;
4196
Bram Moolenaar53989552019-12-23 22:59:18 +01004197 if (called_emsg > called_emsg_before || got_int || timed_out)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004198 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004199 // Error while handling regexp: stop using this regexp.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004200 if (shl == search_hl)
4201 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004202 // don't free regprog in the match list, it's a copy
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004203 vim_regfree(shl->rm.regprog);
4204 set_no_hlsearch(TRUE);
4205 }
4206 shl->rm.regprog = NULL;
4207 shl->lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004208 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004209 break;
4210 }
4211 }
4212 else if (cur != NULL)
4213 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4214 else
4215 nmatched = 0;
4216 if (nmatched == 0)
4217 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004218 shl->lnum = 0; // no match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004219 break;
4220 }
4221 if (shl->rm.startpos[0].lnum > 0
4222 || shl->rm.startpos[0].col >= mincol
4223 || nmatched > 1
4224 || shl->rm.endpos[0].col > mincol)
4225 {
4226 shl->lnum += shl->rm.startpos[0].lnum;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004227 break; // useful match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004228 }
4229 }
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004230}
4231
4232/*
4233 * Advance to the match in window "wp" line "lnum" or past it.
4234 */
4235 void
4236prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4237{
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004238 matchitem_T *cur; // points to the match list
4239 match_T *shl; // points to search_hl or a match
4240 int shl_flag; // flag to indicate whether search_hl
4241 // has been processed or not
4242 int pos_inprogress; // marks that position match search is
4243 // in progress
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004244 int n;
4245
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004246 // When using a multi-line pattern, start searching at the top
4247 // of the window or just after a closed fold.
4248 // Do this both for search_hl and the match list.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004249 cur = wp->w_match_head;
4250 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4251 while (cur != NULL || shl_flag == FALSE)
4252 {
4253 if (shl_flag == FALSE)
4254 {
4255 shl = search_hl;
4256 shl_flag = TRUE;
4257 }
4258 else
4259 shl = &cur->hl;
4260 if (shl->rm.regprog != NULL
4261 && shl->lnum == 0
4262 && re_multiline(shl->rm.regprog))
4263 {
4264 if (shl->first_lnum == 0)
4265 {
4266# ifdef FEAT_FOLDING
4267 for (shl->first_lnum = lnum;
4268 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4269 if (hasFoldingWin(wp, shl->first_lnum - 1,
4270 NULL, NULL, TRUE, NULL))
4271 break;
4272# else
4273 shl->first_lnum = wp->w_topline;
4274# endif
4275 }
4276 if (cur != NULL)
4277 cur->pos.cur = 0;
4278 pos_inprogress = TRUE;
4279 n = 0;
4280 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4281 || (cur != NULL && pos_inprogress)))
4282 {
4283 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4284 shl == search_hl ? NULL : cur);
4285 pos_inprogress = cur == NULL || cur->pos.cur == 0
4286 ? FALSE : TRUE;
4287 if (shl->lnum != 0)
4288 {
4289 shl->first_lnum = shl->lnum
4290 + shl->rm.endpos[0].lnum
4291 - shl->rm.startpos[0].lnum;
4292 n = shl->rm.endpos[0].col;
4293 }
4294 else
4295 {
4296 ++shl->first_lnum;
4297 n = 0;
4298 }
4299 }
4300 }
4301 if (shl != search_hl && cur != NULL)
4302 cur = cur->next;
4303 }
4304}
4305
4306/*
4307 * Prepare for 'hlsearch' and match highlighting in one window line.
4308 * Return TRUE if there is such highlighting and set "search_attr" to the
4309 * current highlight attribute.
4310 */
4311 int
4312prepare_search_hl_line(
4313 win_T *wp,
4314 linenr_T lnum,
4315 colnr_T mincol,
4316 char_u **line,
4317 match_T *search_hl,
4318 int *search_attr)
4319{
4320 matchitem_T *cur; // points to the match list
4321 match_T *shl; // points to search_hl or a match
4322 int shl_flag; // flag to indicate whether search_hl
4323 // has been processed or not
4324 int area_highlighting = FALSE;
4325
4326 /*
4327 * Handle highlighting the last used search pattern and matches.
4328 * Do this for both search_hl and the match list.
4329 * Do not use search_hl in a popup window.
4330 */
4331 cur = wp->w_match_head;
4332 shl_flag = WIN_IS_POPUP(wp);
4333 while (cur != NULL || shl_flag == FALSE)
4334 {
4335 if (shl_flag == FALSE)
4336 {
4337 shl = search_hl;
4338 shl_flag = TRUE;
4339 }
4340 else
4341 shl = &cur->hl;
4342 shl->startcol = MAXCOL;
4343 shl->endcol = MAXCOL;
4344 shl->attr_cur = 0;
4345 shl->is_addpos = FALSE;
4346 if (cur != NULL)
4347 cur->pos.cur = 0;
4348 next_search_hl(wp, search_hl, shl, lnum, mincol,
4349 shl == search_hl ? NULL : cur);
4350
4351 // Need to get the line again, a multi-line regexp may have made it
4352 // invalid.
4353 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4354
4355 if (shl->lnum != 0 && shl->lnum <= lnum)
4356 {
4357 if (shl->lnum == lnum)
4358 shl->startcol = shl->rm.startpos[0].col;
4359 else
4360 shl->startcol = 0;
4361 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4362 - shl->rm.startpos[0].lnum)
4363 shl->endcol = shl->rm.endpos[0].col;
4364 else
4365 shl->endcol = MAXCOL;
4366 // Highlight one character for an empty match.
4367 if (shl->startcol == shl->endcol)
4368 {
4369 if (has_mbyte && (*line)[shl->endcol] != NUL)
4370 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4371 else
4372 ++shl->endcol;
4373 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004374 if ((long)shl->startcol < mincol) // match at leftcol
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004375 {
4376 shl->attr_cur = shl->attr;
4377 *search_attr = shl->attr;
4378 }
4379 area_highlighting = TRUE;
4380 }
4381 if (shl != search_hl && cur != NULL)
4382 cur = cur->next;
4383 }
4384 return area_highlighting;
4385}
4386
4387/*
4388 * For a position in a line: Check for start/end of 'hlsearch' and other
4389 * matches.
4390 * After end, check for start/end of next match.
4391 * When another match, have to check for start again.
4392 * Watch out for matching an empty string!
Bram Moolenaar32aa1022019-11-02 22:54:41 +01004393 * Return the updated search_attr.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004394 */
4395 int
4396update_search_hl(
4397 win_T *wp,
4398 linenr_T lnum,
4399 colnr_T col,
4400 char_u **line,
4401 match_T *search_hl,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004402 int *has_match_conc UNUSED,
4403 int *match_conc UNUSED,
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004404 int did_line_attr,
4405 int lcs_eol_one)
4406{
4407 matchitem_T *cur; // points to the match list
4408 match_T *shl; // points to search_hl or a match
4409 int shl_flag; // flag to indicate whether search_hl
4410 // has been processed or not
4411 int pos_inprogress; // marks that position match search is in
4412 // progress
4413 int search_attr = 0;
4414
4415
4416 // Do this for 'search_hl' and the match list (ordered by priority).
4417 cur = wp->w_match_head;
4418 shl_flag = WIN_IS_POPUP(wp);
4419 while (cur != NULL || shl_flag == FALSE)
4420 {
4421 if (shl_flag == FALSE
4422 && ((cur != NULL
4423 && cur->priority > SEARCH_HL_PRIORITY)
4424 || cur == NULL))
4425 {
4426 shl = search_hl;
4427 shl_flag = TRUE;
4428 }
4429 else
4430 shl = &cur->hl;
4431 if (cur != NULL)
4432 cur->pos.cur = 0;
4433 pos_inprogress = TRUE;
4434 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4435 {
4436 if (shl->startcol != MAXCOL
4437 && col >= shl->startcol
4438 && col < shl->endcol)
4439 {
Bram Moolenaar1614a142019-10-06 22:00:13 +02004440 int next_col = col + mb_ptr2len(*line + col);
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004441
4442 if (shl->endcol < next_col)
4443 shl->endcol = next_col;
4444 shl->attr_cur = shl->attr;
4445# ifdef FEAT_CONCEAL
4446 // Match with the "Conceal" group results in hiding
4447 // the match.
4448 if (cur != NULL
4449 && shl != search_hl
4450 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4451 {
4452 *has_match_conc = col == shl->startcol ? 2 : 1;
4453 *match_conc = cur->conceal_char;
4454 }
4455 else
4456 *has_match_conc = *match_conc = 0;
4457# endif
4458 }
4459 else if (col == shl->endcol)
4460 {
4461 shl->attr_cur = 0;
4462 next_search_hl(wp, search_hl, shl, lnum, col,
4463 shl == search_hl ? NULL : cur);
4464 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4465
4466 // Need to get the line again, a multi-line regexp may have
4467 // made it invalid.
4468 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4469
4470 if (shl->lnum == lnum)
4471 {
4472 shl->startcol = shl->rm.startpos[0].col;
4473 if (shl->rm.endpos[0].lnum == 0)
4474 shl->endcol = shl->rm.endpos[0].col;
4475 else
4476 shl->endcol = MAXCOL;
4477
4478 if (shl->startcol == shl->endcol)
4479 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004480 // highlight empty match, try again after
4481 // it
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004482 if (has_mbyte)
4483 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4484 else
4485 ++shl->endcol;
4486 }
4487
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004488 // Loop to check if the match starts at the
4489 // current position
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004490 continue;
4491 }
4492 }
4493 break;
4494 }
4495 if (shl != search_hl && cur != NULL)
4496 cur = cur->next;
4497 }
4498
4499 // Use attributes from match with highest priority among 'search_hl' and
4500 // the match list.
4501 cur = wp->w_match_head;
4502 shl_flag = WIN_IS_POPUP(wp);
4503 while (cur != NULL || shl_flag == FALSE)
4504 {
4505 if (shl_flag == FALSE
4506 && ((cur != NULL
4507 && cur->priority > SEARCH_HL_PRIORITY)
4508 || cur == NULL))
4509 {
4510 shl = search_hl;
4511 shl_flag = TRUE;
4512 }
4513 else
4514 shl = &cur->hl;
4515 if (shl->attr_cur != 0)
4516 search_attr = shl->attr_cur;
4517 if (shl != search_hl && cur != NULL)
4518 cur = cur->next;
4519 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004520 // Only highlight one character after the last column.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004521 if (*(*line + col) == NUL && (did_line_attr >= 1
4522 || (wp->w_p_list && lcs_eol_one == -1)))
4523 search_attr = 0;
4524 return search_attr;
4525}
4526
4527 int
4528get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4529{
4530 long prevcol = curcol;
4531 int prevcol_hl_flag = FALSE;
4532 matchitem_T *cur; // points to the match list
4533
4534 // we're not really at that column when skipping some text
4535 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4536 ++prevcol;
4537
4538 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4539 prevcol_hl_flag = TRUE;
4540 else
4541 {
4542 cur = wp->w_match_head;
4543 while (cur != NULL)
4544 {
4545 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4546 {
4547 prevcol_hl_flag = TRUE;
4548 break;
4549 }
4550 cur = cur->next;
4551 }
4552 }
4553 return prevcol_hl_flag;
4554}
4555
4556/*
4557 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4558 * or match highlighting.
4559 */
4560 void
4561get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4562{
4563 matchitem_T *cur; // points to the match list
4564 match_T *shl; // points to search_hl or a match
4565 int shl_flag; // flag to indicate whether search_hl
4566 // has been processed or not
4567
4568 cur = wp->w_match_head;
4569 shl_flag = WIN_IS_POPUP(wp);
4570 while (cur != NULL || shl_flag == FALSE)
4571 {
4572 if (shl_flag == FALSE
4573 && ((cur != NULL
4574 && cur->priority > SEARCH_HL_PRIORITY)
4575 || cur == NULL))
4576 {
4577 shl = search_hl;
4578 shl_flag = TRUE;
4579 }
4580 else
4581 shl = &cur->hl;
4582 if (col - 1 == (long)shl->startcol
4583 && (shl == search_hl || !shl->is_addpos))
4584 *char_attr = shl->attr;
4585 if (shl != search_hl && cur != NULL)
4586 cur = cur->next;
4587 }
4588}
4589
4590#endif // FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004591
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004592#if defined(FEAT_EVAL) || defined(PROTO)
4593# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004594 static int
4595matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4596{
4597 dictitem_T *di;
4598
4599 if (tv->v_type != VAR_DICT)
4600 {
4601 emsg(_(e_dictreq));
4602 return FAIL;
4603 }
4604
4605 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4606 *conceal_char = dict_get_string(tv->vval.v_dict,
4607 (char_u *)"conceal", FALSE);
4608
4609 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4610 {
4611 *win = find_win_by_nr_or_id(&di->di_tv);
4612 if (*win == NULL)
4613 {
4614 emsg(_(e_invalwindow));
4615 return FAIL;
4616 }
4617 }
4618
4619 return OK;
4620}
4621#endif
4622
4623/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004624 * "clearmatches()" function
4625 */
4626 void
4627f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4628{
4629#ifdef FEAT_SEARCH_EXTRA
4630 win_T *win = get_optional_window(argvars, 0);
4631
4632 if (win != NULL)
4633 clear_matches(win);
4634#endif
4635}
4636
4637/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004638 * "getmatches()" function
4639 */
4640 void
4641f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4642{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004643# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004644 dict_T *dict;
4645 matchitem_T *cur;
4646 int i;
4647 win_T *win = get_optional_window(argvars, 0);
4648
4649 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4650 return;
4651
4652 cur = win->w_match_head;
4653 while (cur != NULL)
4654 {
4655 dict = dict_alloc();
4656 if (dict == NULL)
4657 return;
4658 if (cur->match.regprog == NULL)
4659 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004660 // match added with matchaddpos()
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004661 for (i = 0; i < MAXPOSMATCH; ++i)
4662 {
4663 llpos_T *llpos;
4664 char buf[30]; // use 30 to avoid compiler warning
4665 list_T *l;
4666
4667 llpos = &cur->pos.pos[i];
4668 if (llpos->lnum == 0)
4669 break;
4670 l = list_alloc();
4671 if (l == NULL)
4672 break;
4673 list_append_number(l, (varnumber_T)llpos->lnum);
4674 if (llpos->col > 0)
4675 {
4676 list_append_number(l, (varnumber_T)llpos->col);
4677 list_append_number(l, (varnumber_T)llpos->len);
4678 }
4679 sprintf(buf, "pos%d", i + 1);
4680 dict_add_list(dict, buf, l);
4681 }
4682 }
4683 else
4684 {
4685 dict_add_string(dict, "pattern", cur->pattern);
4686 }
4687 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4688 dict_add_number(dict, "priority", (long)cur->priority);
4689 dict_add_number(dict, "id", (long)cur->id);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004690# if defined(FEAT_CONCEAL)
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004691 if (cur->conceal_char)
4692 {
4693 char_u buf[MB_MAXBYTES + 1];
4694
4695 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4696 dict_add_string(dict, "conceal", (char_u *)&buf);
4697 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004698# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004699 list_append_dict(rettv->vval.v_list, dict);
4700 cur = cur->next;
4701 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004702# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004703}
4704
4705/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004706 * "setmatches()" function
4707 */
4708 void
4709f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4710{
4711#ifdef FEAT_SEARCH_EXTRA
4712 list_T *l;
4713 listitem_T *li;
4714 dict_T *d;
4715 list_T *s = NULL;
4716 win_T *win = get_optional_window(argvars, 1);
4717
4718 rettv->vval.v_number = -1;
4719 if (argvars[0].v_type != VAR_LIST)
4720 {
4721 emsg(_(e_listreq));
4722 return;
4723 }
4724 if (win == NULL)
4725 return;
4726
4727 if ((l = argvars[0].vval.v_list) != NULL)
4728 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004729 // To some extent make sure that we are dealing with a list from
4730 // "getmatches()".
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004731 li = l->lv_first;
4732 while (li != NULL)
4733 {
4734 if (li->li_tv.v_type != VAR_DICT
4735 || (d = li->li_tv.vval.v_dict) == NULL)
4736 {
4737 emsg(_(e_invarg));
4738 return;
4739 }
4740 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4741 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4742 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4743 && dict_find(d, (char_u *)"priority", -1) != NULL
4744 && dict_find(d, (char_u *)"id", -1) != NULL))
4745 {
4746 emsg(_(e_invarg));
4747 return;
4748 }
4749 li = li->li_next;
4750 }
4751
4752 clear_matches(win);
4753 li = l->lv_first;
4754 while (li != NULL)
4755 {
4756 int i = 0;
4757 char buf[30]; // use 30 to avoid compiler warning
4758 dictitem_T *di;
4759 char_u *group;
4760 int priority;
4761 int id;
4762 char_u *conceal;
4763
4764 d = li->li_tv.vval.v_dict;
4765 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4766 {
4767 if (s == NULL)
4768 {
4769 s = list_alloc();
4770 if (s == NULL)
4771 return;
4772 }
4773
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004774 // match from matchaddpos()
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004775 for (i = 1; i < 9; i++)
4776 {
4777 sprintf((char *)buf, (char *)"pos%d", i);
4778 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4779 {
4780 if (di->di_tv.v_type != VAR_LIST)
4781 return;
4782
4783 list_append_tv(s, &di->di_tv);
4784 s->lv_refcount++;
4785 }
4786 else
4787 break;
4788 }
4789 }
4790
4791 group = dict_get_string(d, (char_u *)"group", TRUE);
4792 priority = (int)dict_get_number(d, (char_u *)"priority");
4793 id = (int)dict_get_number(d, (char_u *)"id");
4794 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4795 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4796 : NULL;
4797 if (i == 0)
4798 {
4799 match_add(win, group,
4800 dict_get_string(d, (char_u *)"pattern", FALSE),
4801 priority, id, NULL, conceal);
4802 }
4803 else
4804 {
4805 match_add(win, group, NULL, priority, id, s, conceal);
4806 list_unref(s);
4807 s = NULL;
4808 }
4809 vim_free(group);
4810 vim_free(conceal);
4811
4812 li = li->li_next;
4813 }
4814 rettv->vval.v_number = 0;
4815 }
4816#endif
4817}
4818
4819/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004820 * "matchadd()" function
4821 */
4822 void
4823f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4824{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004825# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004826 char_u buf[NUMBUFLEN];
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004827 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4828 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4829 int prio = 10; // default priority
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004830 int id = -1;
4831 int error = FALSE;
4832 char_u *conceal_char = NULL;
4833 win_T *win = curwin;
4834
4835 rettv->vval.v_number = -1;
4836
4837 if (grp == NULL || pat == NULL)
4838 return;
4839 if (argvars[2].v_type != VAR_UNKNOWN)
4840 {
4841 prio = (int)tv_get_number_chk(&argvars[2], &error);
4842 if (argvars[3].v_type != VAR_UNKNOWN)
4843 {
4844 id = (int)tv_get_number_chk(&argvars[3], &error);
4845 if (argvars[4].v_type != VAR_UNKNOWN
4846 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4847 return;
4848 }
4849 }
4850 if (error == TRUE)
4851 return;
4852 if (id >= 1 && id <= 3)
4853 {
4854 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4855 return;
4856 }
4857
4858 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4859 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004860# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004861}
4862
4863/*
4864 * "matchaddpos()" function
4865 */
4866 void
4867f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4868{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004869# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004870 char_u buf[NUMBUFLEN];
4871 char_u *group;
4872 int prio = 10;
4873 int id = -1;
4874 int error = FALSE;
4875 list_T *l;
4876 char_u *conceal_char = NULL;
4877 win_T *win = curwin;
4878
4879 rettv->vval.v_number = -1;
4880
4881 group = tv_get_string_buf_chk(&argvars[0], buf);
4882 if (group == NULL)
4883 return;
4884
4885 if (argvars[1].v_type != VAR_LIST)
4886 {
4887 semsg(_(e_listarg), "matchaddpos()");
4888 return;
4889 }
4890 l = argvars[1].vval.v_list;
4891 if (l == NULL)
4892 return;
4893
4894 if (argvars[2].v_type != VAR_UNKNOWN)
4895 {
4896 prio = (int)tv_get_number_chk(&argvars[2], &error);
4897 if (argvars[3].v_type != VAR_UNKNOWN)
4898 {
4899 id = (int)tv_get_number_chk(&argvars[3], &error);
4900
4901 if (argvars[4].v_type != VAR_UNKNOWN
4902 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4903 return;
4904 }
4905 }
4906 if (error == TRUE)
4907 return;
4908
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004909 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004910 if (id == 1 || id == 2)
4911 {
4912 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4913 return;
4914 }
4915
4916 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4917 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004918# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004919}
4920
4921/*
4922 * "matcharg()" function
4923 */
4924 void
4925f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4926{
4927 if (rettv_list_alloc(rettv) == OK)
4928 {
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004929# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004930 int id = (int)tv_get_number(&argvars[0]);
4931 matchitem_T *m;
4932
4933 if (id >= 1 && id <= 3)
4934 {
4935 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4936 {
4937 list_append_string(rettv->vval.v_list,
4938 syn_id2name(m->hlg_id), -1);
4939 list_append_string(rettv->vval.v_list, m->pattern, -1);
4940 }
4941 else
4942 {
4943 list_append_string(rettv->vval.v_list, NULL, -1);
4944 list_append_string(rettv->vval.v_list, NULL, -1);
4945 }
4946 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004947# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004948 }
4949}
4950
4951/*
4952 * "matchdelete()" function
4953 */
4954 void
4955f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4956{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004957# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004958 win_T *win = get_optional_window(argvars, 1);
4959
4960 if (win == NULL)
4961 rettv->vval.v_number = -1;
4962 else
4963 rettv->vval.v_number = match_delete(win,
4964 (int)tv_get_number(&argvars[0]), TRUE);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004965# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004966}
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004967#endif
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004968
4969#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4970/*
4971 * ":[N]match {group} {pattern}"
4972 * Sets nextcmd to the start of the next command, if any. Also called when
4973 * skipping commands to find the next command.
4974 */
4975 void
4976ex_match(exarg_T *eap)
4977{
4978 char_u *p;
4979 char_u *g = NULL;
4980 char_u *end;
4981 int c;
4982 int id;
4983
4984 if (eap->line2 <= 3)
4985 id = eap->line2;
4986 else
4987 {
4988 emsg(_(e_invcmd));
4989 return;
4990 }
4991
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004992 // First clear any old pattern.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004993 if (!eap->skip)
4994 match_delete(curwin, id, FALSE);
4995
Bram Moolenaar1966c242020-04-20 22:42:32 +02004996 if (ends_excmd2(eap->cmd, eap->arg))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004997 end = eap->arg;
4998 else if ((STRNICMP(eap->arg, "none", 4) == 0
Bram Moolenaar1966c242020-04-20 22:42:32 +02004999 && (VIM_ISWHITE(eap->arg[4])
5000 || ends_excmd2(eap->arg, eap->arg + 4))))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02005001 end = eap->arg + 4;
5002 else
5003 {
5004 p = skiptowhite(eap->arg);
5005 if (!eap->skip)
5006 g = vim_strnsave(eap->arg, (int)(p - eap->arg));
5007 p = skipwhite(p);
5008 if (*p == NUL)
5009 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02005010 // There must be two arguments.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02005011 vim_free(g);
5012 semsg(_(e_invarg2), eap->arg);
5013 return;
5014 }
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005015 end = skip_regexp(p + 1, *p, TRUE);
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02005016 if (!eap->skip)
5017 {
Bram Moolenaar1966c242020-04-20 22:42:32 +02005018 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02005019 {
5020 vim_free(g);
5021 eap->errmsg = e_trailing;
5022 return;
5023 }
5024 if (*end != *p)
5025 {
5026 vim_free(g);
5027 semsg(_(e_invarg2), p);
5028 return;
5029 }
5030
5031 c = *end;
5032 *end = NUL;
5033 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
5034 vim_free(g);
5035 *end = c;
5036 }
5037 }
5038 eap->nextcmd = find_nextcmd(end);
5039}
5040#endif