blob: 6ddd44a827dff2505007f647f25cb44c6fc06db2 [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.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020012 */
13
14#include "vim.h"
15
16#define SG_TERM 1 // term has been set
17#define SG_CTERM 2 // cterm has been set
18#define SG_GUI 4 // gui has been set
19#define SG_LINK 8 // link has been set
20
21/*
22 * The "term", "cterm" and "gui" arguments can be any combination of the
23 * following names, separated by commas (but no spaces!).
24 */
25static char *(hl_name_table[]) =
26 {"bold", "standout", "underline", "undercurl",
27 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
28static int hl_attr_table[] =
29 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
30#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
31
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020032/*
33 * Structure that stores information about a highlight group.
34 * The ID of a highlight group is also called group ID. It is the index in
35 * the highlight_ga array PLUS ONE.
36 */
37typedef struct
38{
39 char_u *sg_name; // highlight group name
40 char_u *sg_name_u; // uppercase of sg_name
41 int sg_cleared; // "hi clear" was used
42// for normal terminals
43 int sg_term; // "term=" highlighting attributes
44 char_u *sg_start; // terminal string for start highl
45 char_u *sg_stop; // terminal string for stop highl
46 int sg_term_attr; // Screen attr for term mode
47// for color terminals
48 int sg_cterm; // "cterm=" highlighting attr
49 int sg_cterm_bold; // bold attr was set for light color
50 int sg_cterm_fg; // terminal fg color number + 1
51 int sg_cterm_bg; // terminal bg color number + 1
Bram Moolenaare023e882020-05-31 16:42:30 +020052 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020053 int sg_cterm_attr; // Screen attr for color term mode
54// for when using the GUI
55#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
56 guicolor_T sg_gui_fg; // GUI foreground color handle
57 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020058 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020059#endif
60#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020061 GuiFont sg_font; // GUI font handle
62#ifdef FEAT_XFONTSET
63 GuiFontset sg_fontset; // GUI fontset handle
64#endif
65 char_u *sg_font_name; // GUI font or fontset name
66 int sg_gui_attr; // Screen attr for GUI mode
67#endif
68#if defined(FEAT_GUI) || defined(FEAT_EVAL)
69// Store the sp color name for the GUI or synIDattr()
70 int sg_gui; // "gui=" highlighting attributes
71 char_u *sg_gui_fg_name;// GUI foreground color name
72 char_u *sg_gui_bg_name;// GUI background color name
73 char_u *sg_gui_sp_name;// GUI special color name
74#endif
75 int sg_link; // link to this highlight group ID
76 int sg_set; // combination of SG_* flags
77#ifdef FEAT_EVAL
78 sctx_T sg_script_ctx; // script in which the group was last set
79#endif
80} hl_group_T;
81
82// highlight groups for 'highlight' option
83static garray_T highlight_ga;
84#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
85
86/*
87 * An attribute number is the index in attr_table plus ATTR_OFF.
88 */
89#define ATTR_OFF (HL_ALL + 1)
90
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020091static void syn_unadd_group(void);
92static void set_hl_attr(int idx);
93static void highlight_list_one(int id);
94static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
95static int syn_add_group(char_u *name);
96static int hl_has_settings(int idx, int check_link);
97static void highlight_clear(int idx);
98
99#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
100static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
101#endif
102#ifdef FEAT_GUI
103static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
104static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
105#endif
106
107/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200108 * The default highlight groups. These are compiled-in for fast startup and
109 * they still work when the runtime files can't be found.
110 * When making changes here, also change runtime/colors/default.vim!
111 * The #ifdefs are needed to reduce the amount of static data. Helps to make
112 * the 16 bit DOS (museum) version compile.
113 */
114#if defined(FEAT_GUI) || defined(FEAT_EVAL)
115# define CENT(a, b) b
116#else
117# define CENT(a, b) a
118#endif
119static char *(highlight_init_both[]) = {
120 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
121 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
122 CENT("IncSearch term=reverse cterm=reverse",
123 "IncSearch term=reverse cterm=reverse gui=reverse"),
124 CENT("ModeMsg term=bold cterm=bold",
125 "ModeMsg term=bold cterm=bold gui=bold"),
126 CENT("NonText term=bold ctermfg=Blue",
127 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
128 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
129 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
130 CENT("StatusLineNC term=reverse cterm=reverse",
131 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
132 "default link EndOfBuffer NonText",
133 CENT("VertSplit term=reverse cterm=reverse",
134 "VertSplit term=reverse cterm=reverse gui=reverse"),
135#ifdef FEAT_CLIPBOARD
136 CENT("VisualNOS term=underline,bold cterm=underline,bold",
137 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
138#endif
139#ifdef FEAT_DIFF
140 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
141 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
142#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200143 CENT("PmenuSbar ctermbg=Grey",
144 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200145 CENT("TabLineSel term=bold cterm=bold",
146 "TabLineSel term=bold cterm=bold gui=bold"),
147 CENT("TabLineFill term=reverse cterm=reverse",
148 "TabLineFill term=reverse cterm=reverse gui=reverse"),
149#ifdef FEAT_GUI
150 "Cursor guibg=fg guifg=bg",
151 "lCursor guibg=fg guifg=bg", // should be different, but what?
152#endif
153 "default link QuickFixLine Search",
154 CENT("Normal cterm=NONE", "Normal gui=NONE"),
155 NULL
156};
157
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200158// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200159static char *(highlight_init_light[]) = {
160 CENT("Directory term=bold ctermfg=DarkBlue",
161 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
162 CENT("LineNr term=underline ctermfg=Brown",
163 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200164 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
165 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200166 CENT("MoreMsg term=bold ctermfg=DarkGreen",
167 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
168 CENT("Question term=standout ctermfg=DarkGreen",
169 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
170 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
171 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
172#ifdef FEAT_SPELL
173 CENT("SpellBad term=reverse ctermbg=LightRed",
174 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
175 CENT("SpellCap term=reverse ctermbg=LightBlue",
176 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
177 CENT("SpellRare term=reverse ctermbg=LightMagenta",
178 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
179 CENT("SpellLocal term=underline ctermbg=Cyan",
180 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
181#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200182 CENT("PmenuThumb ctermbg=Black",
183 "PmenuThumb ctermbg=Black guibg=Black"),
184 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
185 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
186 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
187 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200188 CENT("SpecialKey term=bold ctermfg=DarkBlue",
189 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
190 CENT("Title term=bold ctermfg=DarkMagenta",
191 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
192 CENT("WarningMsg term=standout ctermfg=DarkRed",
193 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
194#ifdef FEAT_WILDMENU
195 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
196 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
197#endif
198#ifdef FEAT_FOLDING
199 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
200 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
201 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
202 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
203#endif
204#ifdef FEAT_SIGNS
205 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
206 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
207#endif
208 CENT("Visual term=reverse",
209 "Visual term=reverse guibg=LightGrey"),
210#ifdef FEAT_DIFF
211 CENT("DiffAdd term=bold ctermbg=LightBlue",
212 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
213 CENT("DiffChange term=bold ctermbg=LightMagenta",
214 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
215 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
216 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
217#endif
218 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
219 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
220#ifdef FEAT_SYN_HL
221 CENT("CursorColumn term=reverse ctermbg=LightGrey",
222 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
223 CENT("CursorLine term=underline cterm=underline",
224 "CursorLine term=underline cterm=underline guibg=Grey90"),
225 CENT("ColorColumn term=reverse ctermbg=LightRed",
226 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
227#endif
228#ifdef FEAT_CONCEAL
229 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
230 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
231#endif
232 CENT("MatchParen term=reverse ctermbg=Cyan",
233 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
234#ifdef FEAT_TERMINAL
235 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
236 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
237 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
238 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
239#endif
240#ifdef FEAT_MENU
241 CENT("ToolbarLine term=underline ctermbg=LightGrey",
242 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
243 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
244 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
245#endif
246 NULL
247};
248
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200249// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200250static char *(highlight_init_dark[]) = {
251 CENT("Directory term=bold ctermfg=LightCyan",
252 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
253 CENT("LineNr term=underline ctermfg=Yellow",
254 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200255 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
256 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200257 CENT("MoreMsg term=bold ctermfg=LightGreen",
258 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
259 CENT("Question term=standout ctermfg=LightGreen",
260 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
261 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
262 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
263 CENT("SpecialKey term=bold ctermfg=LightBlue",
264 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
265#ifdef FEAT_SPELL
266 CENT("SpellBad term=reverse ctermbg=Red",
267 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
268 CENT("SpellCap term=reverse ctermbg=Blue",
269 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
270 CENT("SpellRare term=reverse ctermbg=Magenta",
271 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
272 CENT("SpellLocal term=underline ctermbg=Cyan",
273 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
274#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200275 CENT("PmenuThumb ctermbg=White",
276 "PmenuThumb ctermbg=White guibg=White"),
277 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
278 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
279 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
280 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200281 CENT("Title term=bold ctermfg=LightMagenta",
282 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
283 CENT("WarningMsg term=standout ctermfg=LightRed",
284 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
285#ifdef FEAT_WILDMENU
286 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
287 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
288#endif
289#ifdef FEAT_FOLDING
290 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
291 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
292 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
293 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
294#endif
295#ifdef FEAT_SIGNS
296 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
297 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
298#endif
299 CENT("Visual term=reverse",
300 "Visual term=reverse guibg=DarkGrey"),
301#ifdef FEAT_DIFF
302 CENT("DiffAdd term=bold ctermbg=DarkBlue",
303 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
304 CENT("DiffChange term=bold ctermbg=DarkMagenta",
305 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
306 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
307 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
308#endif
309 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
310 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
311#ifdef FEAT_SYN_HL
312 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
313 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
314 CENT("CursorLine term=underline cterm=underline",
315 "CursorLine term=underline cterm=underline guibg=Grey40"),
316 CENT("ColorColumn term=reverse ctermbg=DarkRed",
317 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
318#endif
319 CENT("MatchParen term=reverse ctermbg=DarkCyan",
320 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
321#ifdef FEAT_CONCEAL
322 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
323 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
324#endif
325#ifdef FEAT_TERMINAL
326 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
327 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
328 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
329 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
330#endif
331#ifdef FEAT_MENU
332 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
333 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
334 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
335 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
336#endif
337 NULL
338};
339
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200340/*
341 * Returns the number of highlight groups.
342 */
343 int
344highlight_num_groups(void)
345{
346 return highlight_ga.ga_len;
347}
348
349/*
350 * Returns the name of a highlight group.
351 */
352 char_u *
353highlight_group_name(int id)
354{
355 return HL_TABLE()[id].sg_name;
356}
357
358/*
359 * Returns the ID of the link to a highlight group.
360 */
361 int
362highlight_link_id(int id)
363{
364 return HL_TABLE()[id].sg_link;
365}
366
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200367 void
368init_highlight(
369 int both, // include groups where 'bg' doesn't matter
370 int reset) // clear group first
371{
372 int i;
373 char **pp;
374 static int had_both = FALSE;
375#ifdef FEAT_EVAL
376 char_u *p;
377
378 /*
379 * Try finding the color scheme file. Used when a color file was loaded
380 * and 'background' or 't_Co' is changed.
381 */
382 p = get_var_value((char_u *)"g:colors_name");
383 if (p != NULL)
384 {
385 // The value of g:colors_name could be freed when sourcing the script,
386 // making "p" invalid, so copy it.
387 char_u *copy_p = vim_strsave(p);
388 int r;
389
390 if (copy_p != NULL)
391 {
392 r = load_colors(copy_p);
393 vim_free(copy_p);
394 if (r == OK)
395 return;
396 }
397 }
398
399#endif
400
401 /*
402 * Didn't use a color file, use the compiled-in colors.
403 */
404 if (both)
405 {
406 had_both = TRUE;
407 pp = highlight_init_both;
408 for (i = 0; pp[i] != NULL; ++i)
409 do_highlight((char_u *)pp[i], reset, TRUE);
410 }
411 else if (!had_both)
412 // Don't do anything before the call with both == TRUE from main().
413 // Not everything has been setup then, and that call will overrule
414 // everything anyway.
415 return;
416
417 if (*p_bg == 'l')
418 pp = highlight_init_light;
419 else
420 pp = highlight_init_dark;
421 for (i = 0; pp[i] != NULL; ++i)
422 do_highlight((char_u *)pp[i], reset, TRUE);
423
424 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
425 // depend on the number of colors available.
426 // With 8 colors brown is equal to yellow, need to use black for Search fg
427 // to avoid Statement highlighted text disappears.
428 // Clear the attributes, needed when changing the t_Co value.
429 if (t_colors > 8)
430 do_highlight((char_u *)(*p_bg == 'l'
431 ? "Visual cterm=NONE ctermbg=LightGrey"
432 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
433 else
434 {
435 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
436 FALSE, TRUE);
437 if (*p_bg == 'l')
438 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
439 }
440
441#ifdef FEAT_SYN_HL
442 /*
443 * If syntax highlighting is enabled load the highlighting for it.
444 */
445 if (get_var_value((char_u *)"g:syntax_on") != NULL)
446 {
447 static int recursive = 0;
448
449 if (recursive >= 5)
450 emsg(_("E679: recursive loop loading syncolor.vim"));
451 else
452 {
453 ++recursive;
454 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
455 --recursive;
456 }
457 }
458#endif
459}
460
461/*
462 * Load color file "name".
463 * Return OK for success, FAIL for failure.
464 */
465 int
466load_colors(char_u *name)
467{
468 char_u *buf;
469 int retval = FAIL;
470 static int recursive = FALSE;
471
472 // When being called recursively, this is probably because setting
473 // 'background' caused the highlighting to be reloaded. This means it is
474 // working, thus we should return OK.
475 if (recursive)
476 return OK;
477
478 recursive = TRUE;
479 buf = alloc(STRLEN(name) + 12);
480 if (buf != NULL)
481 {
482 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
483 curbuf->b_fname, FALSE, curbuf);
484 sprintf((char *)buf, "colors/%s.vim", name);
485 retval = source_runtime(buf, DIP_START + DIP_OPT);
486 vim_free(buf);
487 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
488 }
489 recursive = FALSE;
490
491 return retval;
492}
493
494static char *(color_names[28]) = {
495 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
496 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
497 "Gray", "Grey", "LightGray", "LightGrey",
498 "DarkGray", "DarkGrey",
499 "Blue", "LightBlue", "Green", "LightGreen",
500 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
501 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
502 // indices:
503 // 0, 1, 2, 3,
504 // 4, 5, 6, 7,
505 // 8, 9, 10, 11,
506 // 12, 13,
507 // 14, 15, 16, 17,
508 // 18, 19, 20, 21, 22,
509 // 23, 24, 25, 26, 27
510static int color_numbers_16[28] = {0, 1, 2, 3,
511 4, 5, 6, 6,
512 7, 7, 7, 7,
513 8, 8,
514 9, 9, 10, 10,
515 11, 11, 12, 12, 13,
516 13, 14, 14, 15, -1};
517// for xterm with 88 colors...
518static int color_numbers_88[28] = {0, 4, 2, 6,
519 1, 5, 32, 72,
520 84, 84, 7, 7,
521 82, 82,
522 12, 43, 10, 61,
523 14, 63, 9, 74, 13,
524 75, 11, 78, 15, -1};
525// for xterm with 256 colors...
526static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200527 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200528 248, 248, 7, 7,
529 242, 242,
530 12, 81, 10, 121,
531 14, 159, 9, 224, 13,
532 225, 11, 229, 15, -1};
533// for terminals with less than 16 colors...
534static int color_numbers_8[28] = {0, 4, 2, 6,
535 1, 5, 3, 3,
536 7, 7, 7, 7,
537 0+8, 0+8,
538 4+8, 4+8, 2+8, 2+8,
539 6+8, 6+8, 1+8, 1+8, 5+8,
540 5+8, 3+8, 3+8, 7+8, -1};
541
542/*
543 * Lookup the "cterm" value to be used for color with index "idx" in
544 * color_names[].
545 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
546 * colors, otherwise it will be unchanged.
547 */
548 int
549lookup_color(int idx, int foreground, int *boldp)
550{
551 int color = color_numbers_16[idx];
552 char_u *p;
553
554 // Use the _16 table to check if it's a valid color name.
555 if (color < 0)
556 return -1;
557
558 if (t_colors == 8)
559 {
560 // t_Co is 8: use the 8 colors table
561#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100562 // On qnx, the 8 & 16 color arrays are the same
563 if (STRNCMP(T_NAME, "qansi", 5) == 0)
564 color = color_numbers_16[idx];
565 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200566#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100567 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200568 if (foreground)
569 {
570 // set/reset bold attribute to get light foreground
571 // colors (on some terminals, e.g. "linux")
572 if (color & 8)
573 *boldp = TRUE;
574 else
575 *boldp = FALSE;
576 }
577 color &= 7; // truncate to 8 colors
578 }
579 else if (t_colors == 16 || t_colors == 88
580 || t_colors >= 256)
581 {
582 /*
583 * Guess: if the termcap entry ends in 'm', it is
584 * probably an xterm-like terminal. Use the changed
585 * order for colors.
586 */
587 if (*T_CAF != NUL)
588 p = T_CAF;
589 else
590 p = T_CSF;
591 if (*p != NUL && (t_colors > 256
592 || *(p + STRLEN(p) - 1) == 'm'))
593 {
594 if (t_colors == 88)
595 color = color_numbers_88[idx];
596 else if (t_colors >= 256)
597 color = color_numbers_256[idx];
598 else
599 color = color_numbers_8[idx];
600 }
601#ifdef FEAT_TERMRESPONSE
602 if (t_colors >= 256 && color == 15 && is_mac_terminal)
603 // Terminal.app has a bug: 15 is light grey. Use white
604 // from the color cube instead.
605 color = 231;
606#endif
607 }
608 return color;
609}
610
611/*
612 * Handle the ":highlight .." command.
613 * When using ":hi clear" this is called recursively for each group with
614 * "forceit" and "init" both TRUE.
615 */
616 void
617do_highlight(
618 char_u *line,
619 int forceit,
620 int init) // TRUE when called for initializing
621{
622 char_u *name_end;
623 char_u *p;
624 char_u *linep;
625 char_u *key_start;
626 char_u *arg_start;
627 char_u *key = NULL, *arg = NULL;
628 long i;
629 int off;
630 int len;
631 int attr;
632 int id;
633 int idx;
634 hl_group_T item_before;
635 int did_change = FALSE;
636 int dodefault = FALSE;
637 int doclear = FALSE;
638 int dolink = FALSE;
639 int error = FALSE;
640 int color;
641 int is_normal_group = FALSE; // "Normal" group
642#ifdef FEAT_TERMINAL
643 int is_terminal_group = FALSE; // "Terminal" group
644#endif
645#ifdef FEAT_GUI_X11
646 int is_menu_group = FALSE; // "Menu" group
647 int is_scrollbar_group = FALSE; // "Scrollbar" group
648 int is_tooltip_group = FALSE; // "Tooltip" group
649 int do_colors = FALSE; // need to update colors?
650#else
651# define is_menu_group 0
652# define is_tooltip_group 0
653#endif
654#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
655 int did_highlight_changed = FALSE;
656#endif
657
658 /*
659 * If no argument, list current highlighting.
660 */
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +0200661 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200662 {
663 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
664 // TODO: only call when the group has attributes set
665 highlight_list_one((int)i);
666 return;
667 }
668
669 /*
670 * Isolate the name.
671 */
672 name_end = skiptowhite(line);
673 linep = skipwhite(name_end);
674
675 /*
676 * Check for "default" argument.
677 */
678 if (STRNCMP(line, "default", name_end - line) == 0)
679 {
680 dodefault = TRUE;
681 line = linep;
682 name_end = skiptowhite(line);
683 linep = skipwhite(name_end);
684 }
685
686 /*
687 * Check for "clear" or "link" argument.
688 */
689 if (STRNCMP(line, "clear", name_end - line) == 0)
690 doclear = TRUE;
691 if (STRNCMP(line, "link", name_end - line) == 0)
692 dolink = TRUE;
693
694 /*
695 * ":highlight {group-name}": list highlighting for one group.
696 */
Bram Moolenaar1966c242020-04-20 22:42:32 +0200697 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200698 {
699 id = syn_namen2id(line, (int)(name_end - line));
700 if (id == 0)
701 semsg(_("E411: highlight group not found: %s"), line);
702 else
703 highlight_list_one(id);
704 return;
705 }
706
707 /*
708 * Handle ":highlight link {from} {to}" command.
709 */
710 if (dolink)
711 {
712 char_u *from_start = linep;
713 char_u *from_end;
714 char_u *to_start;
715 char_u *to_end;
716 int from_id;
717 int to_id;
718
719 from_end = skiptowhite(from_start);
720 to_start = skipwhite(from_end);
721 to_end = skiptowhite(to_start);
722
Bram Moolenaar1966c242020-04-20 22:42:32 +0200723 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200724 {
725 semsg(_("E412: Not enough arguments: \":highlight link %s\""),
726 from_start);
727 return;
728 }
729
Bram Moolenaar1966c242020-04-20 22:42:32 +0200730 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200731 {
732 semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
733 return;
734 }
735
736 from_id = syn_check_group(from_start, (int)(from_end - from_start));
737 if (STRNCMP(to_start, "NONE", 4) == 0)
738 to_id = 0;
739 else
740 to_id = syn_check_group(to_start, (int)(to_end - to_start));
741
742 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
743 {
744 /*
745 * Don't allow a link when there already is some highlighting
746 * for the group, unless '!' is used
747 */
748 if (to_id > 0 && !forceit && !init
749 && hl_has_settings(from_id - 1, dodefault))
750 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100751 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200752 emsg(_("E414: group has settings, highlight link ignored"));
753 }
754 else if (HL_TABLE()[from_id - 1].sg_link != to_id
755#ifdef FEAT_EVAL
756 || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
757 != current_sctx.sc_sid
758#endif
759 || HL_TABLE()[from_id - 1].sg_cleared)
760 {
761 if (!init)
762 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
763 HL_TABLE()[from_id - 1].sg_link = to_id;
764#ifdef FEAT_EVAL
765 HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100766 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200767#endif
768 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
769 redraw_all_later(SOME_VALID);
770
771 // Only call highlight_changed() once after multiple changes.
772 need_highlight_changed = TRUE;
773 }
774 }
775
776 return;
777 }
778
779 if (doclear)
780 {
781 /*
782 * ":highlight clear [group]" command.
783 */
Bram Moolenaar1966c242020-04-20 22:42:32 +0200784 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200785 {
786#ifdef FEAT_GUI
787 // First, we do not destroy the old values, but allocate the new
788 // ones and update the display. THEN we destroy the old values.
789 // If we destroy the old values first, then the old values
790 // (such as GuiFont's or GuiFontset's) will still be displayed but
791 // invalid because they were free'd.
792 if (gui.in_use)
793 {
794# ifdef FEAT_BEVAL_TIP
795 gui_init_tooltip_font();
796# endif
797# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
798 gui_init_menu_font();
799# endif
800 }
801# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
802 gui_mch_def_colors();
803# endif
804# ifdef FEAT_GUI_X11
805# ifdef FEAT_MENU
806
807 // This only needs to be done when there is no Menu highlight
808 // group defined by default, which IS currently the case.
809 gui_mch_new_menu_colors();
810# endif
811 if (gui.in_use)
812 {
813 gui_new_scrollbar_colors();
814# ifdef FEAT_BEVAL_GUI
815 gui_mch_new_tooltip_colors();
816# endif
817# ifdef FEAT_MENU
818 gui_mch_new_menu_font();
819# endif
820 }
821# endif
822
823 // Ok, we're done allocating the new default graphics items.
824 // The screen should already be refreshed at this point.
825 // It is now Ok to clear out the old data.
826#endif
827#ifdef FEAT_EVAL
Bram Moolenaar1966c242020-04-20 22:42:32 +0200828 do_unlet((char_u *)"g:colors_name", TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200829#endif
830 restore_cterm_colors();
831
832 /*
833 * Clear all default highlight groups and load the defaults.
834 */
835 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
836 highlight_clear(idx);
837 init_highlight(TRUE, TRUE);
838#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
839 if (USE_24BIT)
840 highlight_gui_started();
841 else
842#endif
843 highlight_changed();
844 redraw_later_clear();
845 return;
846 }
Bram Moolenaar1966c242020-04-20 22:42:32 +0200847 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200848 name_end = skiptowhite(line);
849 linep = skipwhite(name_end);
850 }
851
852 /*
853 * Find the group name in the table. If it does not exist yet, add it.
854 */
855 id = syn_check_group(line, (int)(name_end - line));
856 if (id == 0) // failed (out of memory)
857 return;
858 idx = id - 1; // index is ID minus one
859
860 // Return if "default" was used and the group already has settings.
861 if (dodefault && hl_has_settings(idx, TRUE))
862 return;
863
864 // Make a copy so we can check if any attribute actually changed.
865 item_before = HL_TABLE()[idx];
866
867 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
868 is_normal_group = TRUE;
869#ifdef FEAT_TERMINAL
870 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
871 is_terminal_group = TRUE;
872#endif
873#ifdef FEAT_GUI_X11
874 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
875 is_menu_group = TRUE;
876 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
877 is_scrollbar_group = TRUE;
878 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
879 is_tooltip_group = TRUE;
880#endif
881
882 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
883 if (doclear || (forceit && init))
884 {
885 highlight_clear(idx);
886 if (!doclear)
887 HL_TABLE()[idx].sg_set = 0;
888 }
889
890 if (!doclear)
Bram Moolenaar1966c242020-04-20 22:42:32 +0200891 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200892 {
893 key_start = linep;
894 if (*linep == '=')
895 {
896 semsg(_("E415: unexpected equal sign: %s"), key_start);
897 error = TRUE;
898 break;
899 }
900
901 /*
902 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
903 * "guibg").
904 */
905 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
906 ++linep;
907 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +0200908 key = vim_strnsave_up(key_start, linep - key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200909 if (key == NULL)
910 {
911 error = TRUE;
912 break;
913 }
914 linep = skipwhite(linep);
915
916 if (STRCMP(key, "NONE") == 0)
917 {
918 if (!init || HL_TABLE()[idx].sg_set == 0)
919 {
920 if (!init)
921 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
922 highlight_clear(idx);
923 }
924 continue;
925 }
926
927 /*
928 * Check for the equal sign.
929 */
930 if (*linep != '=')
931 {
932 semsg(_("E416: missing equal sign: %s"), key_start);
933 error = TRUE;
934 break;
935 }
936 ++linep;
937
938 /*
939 * Isolate the argument.
940 */
941 linep = skipwhite(linep);
942 if (*linep == '\'') // guifg='color name'
943 {
944 arg_start = ++linep;
945 linep = vim_strchr(linep, '\'');
946 if (linep == NULL)
947 {
948 semsg(_(e_invarg2), key_start);
949 error = TRUE;
950 break;
951 }
952 }
953 else
954 {
955 arg_start = linep;
956 linep = skiptowhite(linep);
957 }
958 if (linep == arg_start)
959 {
960 semsg(_("E417: missing argument: %s"), key_start);
961 error = TRUE;
962 break;
963 }
964 vim_free(arg);
Bram Moolenaar71ccd032020-06-12 22:59:11 +0200965 arg = vim_strnsave(arg_start, linep - arg_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200966 if (arg == NULL)
967 {
968 error = TRUE;
969 break;
970 }
971 if (*linep == '\'')
972 ++linep;
973
974 /*
975 * Store the argument.
976 */
977 if ( STRCMP(key, "TERM") == 0
978 || STRCMP(key, "CTERM") == 0
979 || STRCMP(key, "GUI") == 0)
980 {
981 attr = 0;
982 off = 0;
983 while (arg[off] != NUL)
984 {
985 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
986 {
987 len = (int)STRLEN(hl_name_table[i]);
988 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
989 {
990 attr |= hl_attr_table[i];
991 off += len;
992 break;
993 }
994 }
995 if (i < 0)
996 {
997 semsg(_("E418: Illegal value: %s"), arg);
998 error = TRUE;
999 break;
1000 }
1001 if (arg[off] == ',') // another one follows
1002 ++off;
1003 }
1004 if (error)
1005 break;
1006 if (*key == 'T')
1007 {
1008 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
1009 {
1010 if (!init)
1011 HL_TABLE()[idx].sg_set |= SG_TERM;
1012 HL_TABLE()[idx].sg_term = attr;
1013 }
1014 }
1015 else if (*key == 'C')
1016 {
1017 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1018 {
1019 if (!init)
1020 HL_TABLE()[idx].sg_set |= SG_CTERM;
1021 HL_TABLE()[idx].sg_cterm = attr;
1022 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1023 }
1024 }
1025#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1026 else
1027 {
1028 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1029 {
1030 if (!init)
1031 HL_TABLE()[idx].sg_set |= SG_GUI;
1032 HL_TABLE()[idx].sg_gui = attr;
1033 }
1034 }
1035#endif
1036 }
1037 else if (STRCMP(key, "FONT") == 0)
1038 {
1039 // in non-GUI fonts are simply ignored
1040#ifdef FEAT_GUI
1041 if (HL_TABLE()[idx].sg_font_name != NULL
1042 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
1043 {
1044 // Font name didn't change, ignore.
1045 }
1046 else if (!gui.shell_created)
1047 {
1048 // GUI not started yet, always accept the name.
1049 vim_free(HL_TABLE()[idx].sg_font_name);
1050 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1051 did_change = TRUE;
1052 }
1053 else
1054 {
1055 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
1056# ifdef FEAT_XFONTSET
1057 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
1058# endif
1059 // First, save the current font/fontset.
1060 // Then try to allocate the font/fontset.
1061 // If the allocation fails, HL_TABLE()[idx].sg_font OR
1062 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
1063
1064 HL_TABLE()[idx].sg_font = NOFONT;
1065# ifdef FEAT_XFONTSET
1066 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1067# endif
1068 hl_do_font(idx, arg, is_normal_group, is_menu_group,
1069 is_tooltip_group, FALSE);
1070
1071# ifdef FEAT_XFONTSET
1072 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1073 {
1074 // New fontset was accepted. Free the old one, if there
1075 // was one.
1076 gui_mch_free_fontset(temp_sg_fontset);
1077 vim_free(HL_TABLE()[idx].sg_font_name);
1078 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1079 did_change = TRUE;
1080 }
1081 else
1082 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
1083# endif
1084 if (HL_TABLE()[idx].sg_font != NOFONT)
1085 {
1086 // New font was accepted. Free the old one, if there was
1087 // one.
1088 gui_mch_free_font(temp_sg_font);
1089 vim_free(HL_TABLE()[idx].sg_font_name);
1090 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1091 did_change = TRUE;
1092 }
1093 else
1094 HL_TABLE()[idx].sg_font = temp_sg_font;
1095 }
1096#endif
1097 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001098 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0
1099 || STRCMP(key, "CTERMUL") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001100 {
1101 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1102 {
1103 if (!init)
1104 HL_TABLE()[idx].sg_set |= SG_CTERM;
1105
1106 // When setting the foreground color, and previously the "bold"
1107 // flag was set for a light color, reset it now
1108 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1109 {
1110 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1111 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1112 }
1113
1114 if (VIM_ISDIGIT(*arg))
1115 color = atoi((char *)arg);
1116 else if (STRICMP(arg, "fg") == 0)
1117 {
1118 if (cterm_normal_fg_color)
1119 color = cterm_normal_fg_color - 1;
1120 else
1121 {
1122 emsg(_("E419: FG color unknown"));
1123 error = TRUE;
1124 break;
1125 }
1126 }
1127 else if (STRICMP(arg, "bg") == 0)
1128 {
1129 if (cterm_normal_bg_color > 0)
1130 color = cterm_normal_bg_color - 1;
1131 else
1132 {
1133 emsg(_("E420: BG color unknown"));
1134 error = TRUE;
1135 break;
1136 }
1137 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001138 else if (STRICMP(arg, "ul") == 0)
1139 {
1140 if (cterm_normal_ul_color > 0)
1141 color = cterm_normal_ul_color - 1;
1142 else
1143 {
1144 emsg(_("E453: UL color unknown"));
1145 error = TRUE;
1146 break;
1147 }
1148 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001149 else
1150 {
1151 int bold = MAYBE;
1152
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001153 // reduce calls to STRICMP a bit, it can be slow
1154 off = TOUPPER_ASC(*arg);
1155 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
1156 if (off == color_names[i][0]
1157 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1158 break;
1159 if (i < 0)
1160 {
1161 semsg(_("E421: Color name or number not recognized: %s"), key_start);
1162 error = TRUE;
1163 break;
1164 }
1165
1166 color = lookup_color(i, key[5] == 'F', &bold);
1167
1168 // set/reset bold attribute to get light foreground
1169 // colors (on some terminals, e.g. "linux")
1170 if (bold == TRUE)
1171 {
1172 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1173 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1174 }
1175 else if (bold == FALSE)
1176 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1177 }
1178
1179 // Add one to the argument, to avoid zero. Zero is used for
1180 // "NONE", then "color" is -1.
1181 if (key[5] == 'F')
1182 {
1183 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1184 if (is_normal_group)
1185 {
1186 cterm_normal_fg_color = color + 1;
1187 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
1188#ifdef FEAT_GUI
1189 // Don't do this if the GUI is used.
1190 if (!gui.in_use && !gui.starting)
1191#endif
1192 {
1193 must_redraw = CLEAR;
1194 if (termcap_active && color >= 0)
1195 term_fg_color(color);
1196 }
1197 }
1198 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001199 else if (key[5] == 'B')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001200 {
1201 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1202 if (is_normal_group)
1203 {
1204 cterm_normal_bg_color = color + 1;
1205#ifdef FEAT_GUI
1206 // Don't mess with 'background' if the GUI is used.
1207 if (!gui.in_use && !gui.starting)
1208#endif
1209 {
1210 must_redraw = CLEAR;
1211 if (color >= 0)
1212 {
1213 int dark = -1;
1214
1215 if (termcap_active)
1216 term_bg_color(color);
1217 if (t_colors < 16)
1218 dark = (color == 0 || color == 4);
1219 // Limit the heuristic to the standard 16 colors
1220 else if (color < 16)
1221 dark = (color < 7 || color == 8);
1222 // Set the 'background' option if the value is
1223 // wrong.
1224 if (dark != -1
1225 && dark != (*p_bg == 'd')
1226 && !option_was_set((char_u *)"bg"))
1227 {
1228 set_option_value((char_u *)"bg", 0L,
1229 (char_u *)(dark ? "dark" : "light"), 0);
1230 reset_option_was_set((char_u *)"bg");
1231 }
1232 }
1233 }
1234 }
1235 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001236 else // ctermul
1237 {
1238 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1239 if (is_normal_group)
1240 {
1241 cterm_normal_ul_color = color + 1;
1242#ifdef FEAT_GUI
1243 // Don't do this if the GUI is used.
1244 if (!gui.in_use && !gui.starting)
1245#endif
1246 {
1247 must_redraw = CLEAR;
1248 if (termcap_active && color >= 0)
1249 term_ul_color(color);
1250 }
1251 }
1252 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001253 }
1254 }
1255 else if (STRCMP(key, "GUIFG") == 0)
1256 {
1257#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1258 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
1259
1260 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1261 {
1262 if (!init)
1263 HL_TABLE()[idx].sg_set |= SG_GUI;
1264
1265# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1266 // In GUI guifg colors are only used when recognized
1267 i = color_name2handle(arg);
1268 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1269 {
1270 HL_TABLE()[idx].sg_gui_fg = i;
1271# endif
1272 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1273 {
1274 vim_free(*namep);
1275 if (STRCMP(arg, "NONE") != 0)
1276 *namep = vim_strsave(arg);
1277 else
1278 *namep = NULL;
1279 did_change = TRUE;
1280 }
1281# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1282# ifdef FEAT_GUI_X11
1283 if (is_menu_group && gui.menu_fg_pixel != i)
1284 {
1285 gui.menu_fg_pixel = i;
1286 do_colors = TRUE;
1287 }
1288 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1289 {
1290 gui.scroll_fg_pixel = i;
1291 do_colors = TRUE;
1292 }
1293# ifdef FEAT_BEVAL_GUI
1294 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1295 {
1296 gui.tooltip_fg_pixel = i;
1297 do_colors = TRUE;
1298 }
1299# endif
1300# endif
1301 }
1302# endif
1303 }
1304#endif
1305 }
1306 else if (STRCMP(key, "GUIBG") == 0)
1307 {
1308#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1309 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
1310
1311 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1312 {
1313 if (!init)
1314 HL_TABLE()[idx].sg_set |= SG_GUI;
1315
1316# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1317 // In GUI guifg colors are only used when recognized
1318 i = color_name2handle(arg);
1319 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1320 {
1321 HL_TABLE()[idx].sg_gui_bg = i;
1322# endif
1323 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1324 {
1325 vim_free(*namep);
1326 if (STRCMP(arg, "NONE") != 0)
1327 *namep = vim_strsave(arg);
1328 else
1329 *namep = NULL;
1330 did_change = TRUE;
1331 }
1332# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1333# ifdef FEAT_GUI_X11
1334 if (is_menu_group && gui.menu_bg_pixel != i)
1335 {
1336 gui.menu_bg_pixel = i;
1337 do_colors = TRUE;
1338 }
1339 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1340 {
1341 gui.scroll_bg_pixel = i;
1342 do_colors = TRUE;
1343 }
1344# ifdef FEAT_BEVAL_GUI
1345 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1346 {
1347 gui.tooltip_bg_pixel = i;
1348 do_colors = TRUE;
1349 }
1350# endif
1351# endif
1352 }
1353# endif
1354 }
1355#endif
1356 }
1357 else if (STRCMP(key, "GUISP") == 0)
1358 {
1359#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1360 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
1361
1362 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1363 {
1364 if (!init)
1365 HL_TABLE()[idx].sg_set |= SG_GUI;
1366
Bram Moolenaare023e882020-05-31 16:42:30 +02001367# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1368 // In GUI guisp colors are only used when recognized
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001369 i = color_name2handle(arg);
Bram Moolenaare023e882020-05-31 16:42:30 +02001370 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001371 {
1372 HL_TABLE()[idx].sg_gui_sp = i;
1373# endif
1374 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1375 {
1376 vim_free(*namep);
1377 if (STRCMP(arg, "NONE") != 0)
1378 *namep = vim_strsave(arg);
1379 else
1380 *namep = NULL;
1381 did_change = TRUE;
1382 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001383# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001384 }
1385# endif
1386 }
1387#endif
1388 }
1389 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1390 {
1391 char_u buf[100];
1392 char_u *tname;
1393
1394 if (!init)
1395 HL_TABLE()[idx].sg_set |= SG_TERM;
1396
1397 /*
1398 * The "start" and "stop" arguments can be a literal escape
1399 * sequence, or a comma separated list of terminal codes.
1400 */
1401 if (STRNCMP(arg, "t_", 2) == 0)
1402 {
1403 off = 0;
1404 buf[0] = 0;
1405 while (arg[off] != NUL)
1406 {
1407 // Isolate one termcap name
1408 for (len = 0; arg[off + len] &&
1409 arg[off + len] != ','; ++len)
1410 ;
1411 tname = vim_strnsave(arg + off, len);
1412 if (tname == NULL) // out of memory
1413 {
1414 error = TRUE;
1415 break;
1416 }
1417 // lookup the escape sequence for the item
1418 p = get_term_code(tname);
1419 vim_free(tname);
1420 if (p == NULL) // ignore non-existing things
1421 p = (char_u *)"";
1422
1423 // Append it to the already found stuff
1424 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1425 {
1426 semsg(_("E422: terminal code too long: %s"), arg);
1427 error = TRUE;
1428 break;
1429 }
1430 STRCAT(buf, p);
1431
1432 // Advance to the next item
1433 off += len;
1434 if (arg[off] == ',') // another one follows
1435 ++off;
1436 }
1437 }
1438 else
1439 {
1440 /*
1441 * Copy characters from arg[] to buf[], translating <> codes.
1442 */
1443 for (p = arg, off = 0; off < 100 - 6 && *p; )
1444 {
Bram Moolenaarebe9d342020-05-30 21:52:54 +02001445 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001446 if (len > 0) // recognized special char
1447 off += len;
1448 else // copy as normal char
1449 buf[off++] = *p++;
1450 }
1451 buf[off] = NUL;
1452 }
1453 if (error)
1454 break;
1455
1456 if (STRCMP(buf, "NONE") == 0) // resetting the value
1457 p = NULL;
1458 else
1459 p = vim_strsave(buf);
1460 if (key[2] == 'A')
1461 {
1462 vim_free(HL_TABLE()[idx].sg_start);
1463 HL_TABLE()[idx].sg_start = p;
1464 }
1465 else
1466 {
1467 vim_free(HL_TABLE()[idx].sg_stop);
1468 HL_TABLE()[idx].sg_stop = p;
1469 }
1470 }
1471 else
1472 {
1473 semsg(_("E423: Illegal argument: %s"), key_start);
1474 error = TRUE;
1475 break;
1476 }
1477 HL_TABLE()[idx].sg_cleared = FALSE;
1478
1479 /*
1480 * When highlighting has been given for a group, don't link it.
1481 */
1482 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1483 HL_TABLE()[idx].sg_link = 0;
1484
1485 /*
1486 * Continue with next argument.
1487 */
1488 linep = skipwhite(linep);
1489 }
1490
1491 /*
1492 * If there is an error, and it's a new entry, remove it from the table.
1493 */
1494 if (error && idx == highlight_ga.ga_len)
1495 syn_unadd_group();
1496 else
1497 {
1498 if (is_normal_group)
1499 {
1500 HL_TABLE()[idx].sg_term_attr = 0;
1501 HL_TABLE()[idx].sg_cterm_attr = 0;
1502#ifdef FEAT_GUI
1503 HL_TABLE()[idx].sg_gui_attr = 0;
1504 /*
1505 * Need to update all groups, because they might be using "bg"
1506 * and/or "fg", which have been changed now.
1507 */
1508#endif
1509#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1510 if (USE_24BIT)
1511 {
1512 highlight_gui_started();
1513 did_highlight_changed = TRUE;
1514 redraw_all_later(NOT_VALID);
1515 }
1516#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001517#ifdef FEAT_VTP
1518 control_console_color_rgb();
1519#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001520 }
1521#ifdef FEAT_TERMINAL
1522 else if (is_terminal_group)
1523 set_terminal_default_colors(
1524 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
1525#endif
1526#ifdef FEAT_GUI_X11
1527# ifdef FEAT_MENU
1528 else if (is_menu_group)
1529 {
1530 if (gui.in_use && do_colors)
1531 gui_mch_new_menu_colors();
1532 }
1533# endif
1534 else if (is_scrollbar_group)
1535 {
1536 if (gui.in_use && do_colors)
1537 gui_new_scrollbar_colors();
1538 else
1539 set_hl_attr(idx);
1540 }
1541# ifdef FEAT_BEVAL_GUI
1542 else if (is_tooltip_group)
1543 {
1544 if (gui.in_use && do_colors)
1545 gui_mch_new_tooltip_colors();
1546 }
1547# endif
1548#endif
1549 else
1550 set_hl_attr(idx);
1551#ifdef FEAT_EVAL
1552 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001553 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001554#endif
1555 }
1556
1557 vim_free(key);
1558 vim_free(arg);
1559
1560 // Only call highlight_changed() once, after a sequence of highlight
1561 // commands, and only if an attribute actually changed.
1562 if ((did_change
1563 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1564#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1565 && !did_highlight_changed
1566#endif
1567 )
1568 {
1569 // Do not trigger a redraw when highlighting is changed while
1570 // redrawing. This may happen when evaluating 'statusline' changes the
1571 // StatusLine group.
1572 if (!updating_screen)
1573 redraw_all_later(NOT_VALID);
1574 need_highlight_changed = TRUE;
1575 }
1576}
1577
1578#if defined(EXITFREE) || defined(PROTO)
1579 void
1580free_highlight(void)
1581{
1582 int i;
1583
1584 for (i = 0; i < highlight_ga.ga_len; ++i)
1585 {
1586 highlight_clear(i);
1587 vim_free(HL_TABLE()[i].sg_name);
1588 vim_free(HL_TABLE()[i].sg_name_u);
1589 }
1590 ga_clear(&highlight_ga);
1591}
1592#endif
1593
1594/*
1595 * Reset the cterm colors to what they were before Vim was started, if
1596 * possible. Otherwise reset them to zero.
1597 */
1598 void
1599restore_cterm_colors(void)
1600{
1601#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1602 // Since t_me has been set, this probably means that the user
1603 // wants to use this as default colors. Need to reset default
1604 // background/foreground colors.
1605 mch_set_normal_colors();
1606#else
1607# ifdef VIMDLL
1608 if (!gui.in_use)
1609 {
1610 mch_set_normal_colors();
1611 return;
1612 }
1613# endif
1614 cterm_normal_fg_color = 0;
1615 cterm_normal_fg_bold = 0;
1616 cterm_normal_bg_color = 0;
1617# ifdef FEAT_TERMGUICOLORS
1618 cterm_normal_fg_gui_color = INVALCOLOR;
1619 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001620 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001621# endif
1622#endif
1623}
1624
1625/*
1626 * Return TRUE if highlight group "idx" has any settings.
1627 * When "check_link" is TRUE also check for an existing link.
1628 */
1629 static int
1630hl_has_settings(int idx, int check_link)
1631{
1632 return ( HL_TABLE()[idx].sg_term_attr != 0
1633 || HL_TABLE()[idx].sg_cterm_attr != 0
1634 || HL_TABLE()[idx].sg_cterm_fg != 0
1635 || HL_TABLE()[idx].sg_cterm_bg != 0
1636#ifdef FEAT_GUI
1637 || HL_TABLE()[idx].sg_gui_attr != 0
1638 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1639 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1640 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1641 || HL_TABLE()[idx].sg_font_name != NULL
1642#endif
1643 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1644}
1645
1646/*
1647 * Clear highlighting for one group.
1648 */
1649 static void
1650highlight_clear(int idx)
1651{
1652 HL_TABLE()[idx].sg_cleared = TRUE;
1653
1654 HL_TABLE()[idx].sg_term = 0;
1655 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1656 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1657 HL_TABLE()[idx].sg_term_attr = 0;
1658 HL_TABLE()[idx].sg_cterm = 0;
1659 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1660 HL_TABLE()[idx].sg_cterm_fg = 0;
1661 HL_TABLE()[idx].sg_cterm_bg = 0;
1662 HL_TABLE()[idx].sg_cterm_attr = 0;
1663#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1664 HL_TABLE()[idx].sg_gui = 0;
1665 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1666 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1667 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1668#endif
1669#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1670 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1671 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001672 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001673#endif
1674#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001675 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1676 HL_TABLE()[idx].sg_font = NOFONT;
1677# ifdef FEAT_XFONTSET
1678 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1679 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1680# endif
1681 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1682 HL_TABLE()[idx].sg_gui_attr = 0;
1683#endif
1684#ifdef FEAT_EVAL
1685 // Clear the script ID only when there is no link, since that is not
1686 // cleared.
1687 if (HL_TABLE()[idx].sg_link == 0)
1688 {
1689 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
1690 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
1691 }
1692#endif
1693}
1694
1695#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1696/*
1697 * Set the normal foreground and background colors according to the "Normal"
1698 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1699 * "Tooltip" colors.
1700 */
1701 void
1702set_normal_colors(void)
1703{
1704# ifdef FEAT_GUI
1705# ifdef FEAT_TERMGUICOLORS
1706 if (gui.in_use)
1707# endif
1708 {
1709 if (set_group_colors((char_u *)"Normal",
1710 &gui.norm_pixel, &gui.back_pixel,
1711 FALSE, TRUE, FALSE))
1712 {
1713 gui_mch_new_colors();
1714 must_redraw = CLEAR;
1715 }
1716# ifdef FEAT_GUI_X11
1717 if (set_group_colors((char_u *)"Menu",
1718 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1719 TRUE, FALSE, FALSE))
1720 {
1721# ifdef FEAT_MENU
1722 gui_mch_new_menu_colors();
1723# endif
1724 must_redraw = CLEAR;
1725 }
1726# ifdef FEAT_BEVAL_GUI
1727 if (set_group_colors((char_u *)"Tooltip",
1728 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1729 FALSE, FALSE, TRUE))
1730 {
1731# ifdef FEAT_TOOLBAR
1732 gui_mch_new_tooltip_colors();
1733# endif
1734 must_redraw = CLEAR;
1735 }
1736# endif
1737 if (set_group_colors((char_u *)"Scrollbar",
1738 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1739 FALSE, FALSE, FALSE))
1740 {
1741 gui_new_scrollbar_colors();
1742 must_redraw = CLEAR;
1743 }
1744# endif
1745 }
1746# endif
1747# ifdef FEAT_TERMGUICOLORS
1748# ifdef FEAT_GUI
1749 else
1750# endif
1751 {
1752 int idx;
1753
1754 idx = syn_name2id((char_u *)"Normal") - 1;
1755 if (idx >= 0)
1756 {
1757 gui_do_one_color(idx, FALSE, FALSE);
1758
1759 // If the normal fg or bg color changed a complete redraw is
1760 // required.
1761 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1762 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1763 {
1764 // if the GUI color is INVALCOLOR then we use the default cterm
1765 // color
1766 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1767 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1768 must_redraw = CLEAR;
1769 }
1770 }
1771 }
1772# endif
1773}
1774#endif
1775
1776#if defined(FEAT_GUI) || defined(PROTO)
1777/*
1778 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1779 */
1780 static int
1781set_group_colors(
1782 char_u *name,
1783 guicolor_T *fgp,
1784 guicolor_T *bgp,
1785 int do_menu,
1786 int use_norm,
1787 int do_tooltip)
1788{
1789 int idx;
1790
1791 idx = syn_name2id(name) - 1;
1792 if (idx >= 0)
1793 {
1794 gui_do_one_color(idx, do_menu, do_tooltip);
1795
1796 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1797 *fgp = HL_TABLE()[idx].sg_gui_fg;
1798 else if (use_norm)
1799 *fgp = gui.def_norm_pixel;
1800 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1801 *bgp = HL_TABLE()[idx].sg_gui_bg;
1802 else if (use_norm)
1803 *bgp = gui.def_back_pixel;
1804 return TRUE;
1805 }
1806 return FALSE;
1807}
1808
1809/*
1810 * Get the font of the "Normal" group.
1811 * Returns "" when it's not found or not set.
1812 */
1813 char_u *
1814hl_get_font_name(void)
1815{
1816 int id;
1817 char_u *s;
1818
1819 id = syn_name2id((char_u *)"Normal");
1820 if (id > 0)
1821 {
1822 s = HL_TABLE()[id - 1].sg_font_name;
1823 if (s != NULL)
1824 return s;
1825 }
1826 return (char_u *)"";
1827}
1828
1829/*
1830 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
1831 * actually chosen to be used.
1832 */
1833 void
1834hl_set_font_name(char_u *font_name)
1835{
1836 int id;
1837
1838 id = syn_name2id((char_u *)"Normal");
1839 if (id > 0)
1840 {
1841 vim_free(HL_TABLE()[id - 1].sg_font_name);
1842 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
1843 }
1844}
1845
1846/*
1847 * Set background color for "Normal" group. Called by gui_set_bg_color()
1848 * when the color is known.
1849 */
1850 void
1851hl_set_bg_color_name(
1852 char_u *name) // must have been allocated
1853{
1854 int id;
1855
1856 if (name != NULL)
1857 {
1858 id = syn_name2id((char_u *)"Normal");
1859 if (id > 0)
1860 {
1861 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
1862 HL_TABLE()[id - 1].sg_gui_bg_name = name;
1863 }
1864 }
1865}
1866
1867/*
1868 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
1869 * when the color is known.
1870 */
1871 void
1872hl_set_fg_color_name(
1873 char_u *name) // must have been allocated
1874{
1875 int id;
1876
1877 if (name != NULL)
1878 {
1879 id = syn_name2id((char_u *)"Normal");
1880 if (id > 0)
1881 {
1882 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
1883 HL_TABLE()[id - 1].sg_gui_fg_name = name;
1884 }
1885 }
1886}
1887
1888/*
1889 * Return the handle for a font name.
1890 * Returns NOFONT when failed.
1891 */
1892 static GuiFont
1893font_name2handle(char_u *name)
1894{
1895 if (STRCMP(name, "NONE") == 0)
1896 return NOFONT;
1897
1898 return gui_mch_get_font(name, TRUE);
1899}
1900
1901# ifdef FEAT_XFONTSET
1902/*
1903 * Return the handle for a fontset name.
1904 * Returns NOFONTSET when failed.
1905 */
1906 static GuiFontset
1907fontset_name2handle(char_u *name, int fixed_width)
1908{
1909 if (STRCMP(name, "NONE") == 0)
1910 return NOFONTSET;
1911
1912 return gui_mch_get_fontset(name, TRUE, fixed_width);
1913}
1914# endif
1915
1916/*
1917 * Get the font or fontset for one highlight group.
1918 */
1919 static void
1920hl_do_font(
1921 int idx,
1922 char_u *arg,
1923 int do_normal, // set normal font
1924 int do_menu UNUSED, // set menu font
1925 int do_tooltip UNUSED, // set tooltip font
1926 int free_font) // free current font/fontset
1927{
1928# ifdef FEAT_XFONTSET
1929 // If 'guifontset' is not empty, first try using the name as a
1930 // fontset. If that doesn't work, use it as a font name.
1931 if (*p_guifontset != NUL
1932# ifdef FONTSET_ALWAYS
1933 || do_menu
1934# endif
1935# ifdef FEAT_BEVAL_TIP
1936 // In Athena & Motif, the Tooltip highlight group is always a fontset
1937 || do_tooltip
1938# endif
1939 )
1940 {
1941 if (free_font)
1942 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1943 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
1944# ifdef FONTSET_ALWAYS
1945 || do_menu
1946# endif
1947# ifdef FEAT_BEVAL_TIP
1948 || do_tooltip
1949# endif
1950 );
1951 }
1952 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1953 {
1954 // If it worked and it's the Normal group, use it as the normal
1955 // fontset. Same for the Menu group.
1956 if (do_normal)
1957 gui_init_font(arg, TRUE);
1958# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1959 if (do_menu)
1960 {
1961# ifdef FONTSET_ALWAYS
1962 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
1963# else
1964 // YIKES! This is a bug waiting to crash the program
1965 gui.menu_font = HL_TABLE()[idx].sg_fontset;
1966# endif
1967 gui_mch_new_menu_font();
1968 }
1969# ifdef FEAT_BEVAL_GUI
1970 if (do_tooltip)
1971 {
1972 // The Athena widget set cannot currently handle switching between
1973 // displaying a single font and a fontset.
1974 // If the XtNinternational resource is set to True at widget
1975 // creation, then a fontset is always used, otherwise an
1976 // XFontStruct is used.
1977 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
1978 gui_mch_new_tooltip_font();
1979 }
1980# endif
1981# endif
1982 }
1983 else
1984# endif
1985 {
1986 if (free_font)
1987 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1988 HL_TABLE()[idx].sg_font = font_name2handle(arg);
1989 // If it worked and it's the Normal group, use it as the
1990 // normal font. Same for the Menu group.
1991 if (HL_TABLE()[idx].sg_font != NOFONT)
1992 {
1993 if (do_normal)
1994 gui_init_font(arg, FALSE);
1995#ifndef FONTSET_ALWAYS
1996# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1997 if (do_menu)
1998 {
1999 gui.menu_font = HL_TABLE()[idx].sg_font;
2000 gui_mch_new_menu_font();
2001 }
2002# endif
2003#endif
2004 }
2005 }
2006}
2007
2008#endif // FEAT_GUI
2009
2010#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2011/*
2012 * Return the handle for a color name.
2013 * Returns INVALCOLOR when failed.
2014 */
2015 guicolor_T
2016color_name2handle(char_u *name)
2017{
2018 if (STRCMP(name, "NONE") == 0)
2019 return INVALCOLOR;
2020
2021 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2022 {
2023#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2024 if (gui.in_use)
2025#endif
2026#ifdef FEAT_GUI
2027 return gui.norm_pixel;
2028#endif
2029#ifdef FEAT_TERMGUICOLORS
2030 if (cterm_normal_fg_gui_color != INVALCOLOR)
2031 return cterm_normal_fg_gui_color;
2032 // Guess that the foreground is black or white.
2033 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2034#endif
2035 }
2036 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2037 {
2038#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2039 if (gui.in_use)
2040#endif
2041#ifdef FEAT_GUI
2042 return gui.back_pixel;
2043#endif
2044#ifdef FEAT_TERMGUICOLORS
2045 if (cterm_normal_bg_gui_color != INVALCOLOR)
2046 return cterm_normal_bg_gui_color;
2047 // Guess that the background is white or black.
2048 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2049#endif
2050 }
2051
2052 return GUI_GET_COLOR(name);
2053}
2054#endif
2055
2056/*
2057 * Table with the specifications for an attribute number.
2058 * Note that this table is used by ALL buffers. This is required because the
2059 * GUI can redraw at any time for any buffer.
2060 */
2061static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2062
2063#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2064
2065static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2066
2067#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2068
2069#ifdef FEAT_GUI
2070static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2071
2072#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2073#endif
2074
2075/*
2076 * Return the attr number for a set of colors and font.
2077 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2078 * if the combination is new.
2079 * Return 0 for error (no more room).
2080 */
2081 static int
2082get_attr_entry(garray_T *table, attrentry_T *aep)
2083{
2084 int i;
2085 attrentry_T *taep;
2086 static int recursive = FALSE;
2087
2088 /*
2089 * Init the table, in case it wasn't done yet.
2090 */
2091 table->ga_itemsize = sizeof(attrentry_T);
2092 table->ga_growsize = 7;
2093
2094 /*
2095 * Try to find an entry with the same specifications.
2096 */
2097 for (i = 0; i < table->ga_len; ++i)
2098 {
2099 taep = &(((attrentry_T *)table->ga_data)[i]);
2100 if ( aep->ae_attr == taep->ae_attr
2101 && (
2102#ifdef FEAT_GUI
2103 (table == &gui_attr_table
2104 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2105 && aep->ae_u.gui.bg_color
2106 == taep->ae_u.gui.bg_color
2107 && aep->ae_u.gui.sp_color
2108 == taep->ae_u.gui.sp_color
2109 && aep->ae_u.gui.font == taep->ae_u.gui.font
2110# ifdef FEAT_XFONTSET
2111 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2112# endif
2113 ))
2114 ||
2115#endif
2116 (table == &term_attr_table
2117 && (aep->ae_u.term.start == NULL)
2118 == (taep->ae_u.term.start == NULL)
2119 && (aep->ae_u.term.start == NULL
2120 || STRCMP(aep->ae_u.term.start,
2121 taep->ae_u.term.start) == 0)
2122 && (aep->ae_u.term.stop == NULL)
2123 == (taep->ae_u.term.stop == NULL)
2124 && (aep->ae_u.term.stop == NULL
2125 || STRCMP(aep->ae_u.term.stop,
2126 taep->ae_u.term.stop) == 0))
2127 || (table == &cterm_attr_table
2128 && aep->ae_u.cterm.fg_color
2129 == taep->ae_u.cterm.fg_color
2130 && aep->ae_u.cterm.bg_color
2131 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002132 && aep->ae_u.cterm.ul_color
2133 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002134#ifdef FEAT_TERMGUICOLORS
2135 && aep->ae_u.cterm.fg_rgb
2136 == taep->ae_u.cterm.fg_rgb
2137 && aep->ae_u.cterm.bg_rgb
2138 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002139 && aep->ae_u.cterm.ul_rgb
2140 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002141#endif
2142 )))
2143
2144 return i + ATTR_OFF;
2145 }
2146
2147 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2148 {
2149 /*
2150 * Running out of attribute entries! remove all attributes, and
2151 * compute new ones for all groups.
2152 * When called recursively, we are really out of numbers.
2153 */
2154 if (recursive)
2155 {
2156 emsg(_("E424: Too many different highlighting attributes in use"));
2157 return 0;
2158 }
2159 recursive = TRUE;
2160
2161 clear_hl_tables();
2162
2163 must_redraw = CLEAR;
2164
2165 for (i = 0; i < highlight_ga.ga_len; ++i)
2166 set_hl_attr(i);
2167
2168 recursive = FALSE;
2169 }
2170
2171 /*
2172 * This is a new combination of colors and font, add an entry.
2173 */
2174 if (ga_grow(table, 1) == FAIL)
2175 return 0;
2176
2177 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002178 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002179 taep->ae_attr = aep->ae_attr;
2180#ifdef FEAT_GUI
2181 if (table == &gui_attr_table)
2182 {
2183 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2184 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2185 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2186 taep->ae_u.gui.font = aep->ae_u.gui.font;
2187# ifdef FEAT_XFONTSET
2188 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2189# endif
2190 }
2191#endif
2192 if (table == &term_attr_table)
2193 {
2194 if (aep->ae_u.term.start == NULL)
2195 taep->ae_u.term.start = NULL;
2196 else
2197 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2198 if (aep->ae_u.term.stop == NULL)
2199 taep->ae_u.term.stop = NULL;
2200 else
2201 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2202 }
2203 else if (table == &cterm_attr_table)
2204 {
2205 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2206 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002207 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002208#ifdef FEAT_TERMGUICOLORS
2209 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2210 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002211 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002212#endif
2213 }
2214 ++table->ga_len;
2215 return (table->ga_len - 1 + ATTR_OFF);
2216}
2217
2218#if defined(FEAT_TERMINAL) || defined(PROTO)
2219/*
2220 * Get an attribute index for a cterm entry.
2221 * Uses an existing entry when possible or adds one when needed.
2222 */
2223 int
2224get_cterm_attr_idx(int attr, int fg, int bg)
2225{
2226 attrentry_T at_en;
2227
Bram Moolenaara80faa82020-04-12 19:37:17 +02002228 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002229#ifdef FEAT_TERMGUICOLORS
2230 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2231 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002232 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002233#endif
2234 at_en.ae_attr = attr;
2235 at_en.ae_u.cterm.fg_color = fg;
2236 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002237 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002238 return get_attr_entry(&cterm_attr_table, &at_en);
2239}
2240#endif
2241
2242#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2243/*
2244 * Get an attribute index for a 'termguicolors' entry.
2245 * Uses an existing entry when possible or adds one when needed.
2246 */
2247 int
2248get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2249{
2250 attrentry_T at_en;
2251
Bram Moolenaara80faa82020-04-12 19:37:17 +02002252 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002253 at_en.ae_attr = attr;
2254 if (fg == INVALCOLOR && bg == INVALCOLOR)
2255 {
2256 // If both GUI colors are not set fall back to the cterm colors. Helps
2257 // if the GUI only has an attribute, such as undercurl.
2258 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2259 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2260 }
2261 else
2262 {
2263 at_en.ae_u.cterm.fg_rgb = fg;
2264 at_en.ae_u.cterm.bg_rgb = bg;
2265 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002266 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002267 return get_attr_entry(&cterm_attr_table, &at_en);
2268}
2269#endif
2270
2271#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2272/*
2273 * Get an attribute index for a cterm entry.
2274 * Uses an existing entry when possible or adds one when needed.
2275 */
2276 int
2277get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2278{
2279 attrentry_T at_en;
2280
Bram Moolenaara80faa82020-04-12 19:37:17 +02002281 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002282 at_en.ae_attr = attr;
2283 at_en.ae_u.gui.fg_color = fg;
2284 at_en.ae_u.gui.bg_color = bg;
2285 return get_attr_entry(&gui_attr_table, &at_en);
2286}
2287#endif
2288
2289/*
2290 * Clear all highlight tables.
2291 */
2292 void
2293clear_hl_tables(void)
2294{
2295 int i;
2296 attrentry_T *taep;
2297
2298#ifdef FEAT_GUI
2299 ga_clear(&gui_attr_table);
2300#endif
2301 for (i = 0; i < term_attr_table.ga_len; ++i)
2302 {
2303 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2304 vim_free(taep->ae_u.term.start);
2305 vim_free(taep->ae_u.term.stop);
2306 }
2307 ga_clear(&term_attr_table);
2308 ga_clear(&cterm_attr_table);
2309}
2310
2311/*
2312 * Combine special attributes (e.g., for spelling) with other attributes
2313 * (e.g., for syntax highlighting).
2314 * "prim_attr" overrules "char_attr".
2315 * This creates a new group when required.
2316 * Since we expect there to be few spelling mistakes we don't cache the
2317 * result.
2318 * Return the resulting attributes.
2319 */
2320 int
2321hl_combine_attr(int char_attr, int prim_attr)
2322{
2323 attrentry_T *char_aep = NULL;
2324 attrentry_T *spell_aep;
2325 attrentry_T new_en;
2326
2327 if (char_attr == 0)
2328 return prim_attr;
2329 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2330 return ATTR_COMBINE(char_attr, prim_attr);
2331#ifdef FEAT_GUI
2332 if (gui.in_use)
2333 {
2334 if (char_attr > HL_ALL)
2335 char_aep = syn_gui_attr2entry(char_attr);
2336 if (char_aep != NULL)
2337 new_en = *char_aep;
2338 else
2339 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002340 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002341 new_en.ae_u.gui.fg_color = INVALCOLOR;
2342 new_en.ae_u.gui.bg_color = INVALCOLOR;
2343 new_en.ae_u.gui.sp_color = INVALCOLOR;
2344 if (char_attr <= HL_ALL)
2345 new_en.ae_attr = char_attr;
2346 }
2347
2348 if (prim_attr <= HL_ALL)
2349 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2350 else
2351 {
2352 spell_aep = syn_gui_attr2entry(prim_attr);
2353 if (spell_aep != NULL)
2354 {
2355 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2356 spell_aep->ae_attr);
2357 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2358 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2359 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2360 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2361 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2362 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2363 if (spell_aep->ae_u.gui.font != NOFONT)
2364 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2365# ifdef FEAT_XFONTSET
2366 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2367 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2368# endif
2369 }
2370 }
2371 return get_attr_entry(&gui_attr_table, &new_en);
2372 }
2373#endif
2374
2375 if (IS_CTERM)
2376 {
2377 if (char_attr > HL_ALL)
2378 char_aep = syn_cterm_attr2entry(char_attr);
2379 if (char_aep != NULL)
2380 new_en = *char_aep;
2381 else
2382 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002383 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002384#ifdef FEAT_TERMGUICOLORS
2385 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2386 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002387 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002388#endif
2389 if (char_attr <= HL_ALL)
2390 new_en.ae_attr = char_attr;
2391 }
2392
2393 if (prim_attr <= HL_ALL)
2394 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2395 else
2396 {
2397 spell_aep = syn_cterm_attr2entry(prim_attr);
2398 if (spell_aep != NULL)
2399 {
2400 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2401 spell_aep->ae_attr);
2402 if (spell_aep->ae_u.cterm.fg_color > 0)
2403 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2404 if (spell_aep->ae_u.cterm.bg_color > 0)
2405 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002406 if (spell_aep->ae_u.cterm.ul_color > 0)
2407 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002408#ifdef FEAT_TERMGUICOLORS
2409 // If both fg and bg are not set fall back to cterm colors.
2410 // Helps for SpellBad which uses undercurl in the GUI.
2411 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2412 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2413 {
2414 if (spell_aep->ae_u.cterm.fg_color > 0)
2415 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2416 if (spell_aep->ae_u.cterm.bg_color > 0)
2417 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2418 }
2419 else
2420 {
2421 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2422 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2423 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2424 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2425 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002426 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2427 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002428#endif
2429 }
2430 }
2431 return get_attr_entry(&cterm_attr_table, &new_en);
2432 }
2433
2434 if (char_attr > HL_ALL)
2435 char_aep = syn_term_attr2entry(char_attr);
2436 if (char_aep != NULL)
2437 new_en = *char_aep;
2438 else
2439 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002440 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002441 if (char_attr <= HL_ALL)
2442 new_en.ae_attr = char_attr;
2443 }
2444
2445 if (prim_attr <= HL_ALL)
2446 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2447 else
2448 {
2449 spell_aep = syn_term_attr2entry(prim_attr);
2450 if (spell_aep != NULL)
2451 {
2452 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2453 if (spell_aep->ae_u.term.start != NULL)
2454 {
2455 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2456 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2457 }
2458 }
2459 }
2460 return get_attr_entry(&term_attr_table, &new_en);
2461}
2462
2463#ifdef FEAT_GUI
2464 attrentry_T *
2465syn_gui_attr2entry(int attr)
2466{
2467 attr -= ATTR_OFF;
2468 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2469 return NULL;
2470 return &(GUI_ATTR_ENTRY(attr));
2471}
2472#endif
2473
2474/*
2475 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2476 * Only to be used when "attr" > HL_ALL.
2477 */
2478 int
2479syn_attr2attr(int attr)
2480{
2481 attrentry_T *aep;
2482
2483#ifdef FEAT_GUI
2484 if (gui.in_use)
2485 aep = syn_gui_attr2entry(attr);
2486 else
2487#endif
2488 if (IS_CTERM)
2489 aep = syn_cterm_attr2entry(attr);
2490 else
2491 aep = syn_term_attr2entry(attr);
2492
2493 if (aep == NULL) // highlighting not set
2494 return 0;
2495 return aep->ae_attr;
2496}
2497
2498
2499 attrentry_T *
2500syn_term_attr2entry(int attr)
2501{
2502 attr -= ATTR_OFF;
2503 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2504 return NULL;
2505 return &(TERM_ATTR_ENTRY(attr));
2506}
2507
2508 attrentry_T *
2509syn_cterm_attr2entry(int attr)
2510{
2511 attr -= ATTR_OFF;
2512 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2513 return NULL;
2514 return &(CTERM_ATTR_ENTRY(attr));
2515}
2516
2517#define LIST_ATTR 1
2518#define LIST_STRING 2
2519#define LIST_INT 3
2520
2521 static void
2522highlight_list_one(int id)
2523{
2524 hl_group_T *sgp;
2525 int didh = FALSE;
2526
2527 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2528
2529 if (message_filtered(sgp->sg_name))
2530 return;
2531
2532 didh = highlight_list_arg(id, didh, LIST_ATTR,
2533 sgp->sg_term, NULL, "term");
2534 didh = highlight_list_arg(id, didh, LIST_STRING,
2535 0, sgp->sg_start, "start");
2536 didh = highlight_list_arg(id, didh, LIST_STRING,
2537 0, sgp->sg_stop, "stop");
2538
2539 didh = highlight_list_arg(id, didh, LIST_ATTR,
2540 sgp->sg_cterm, NULL, "cterm");
2541 didh = highlight_list_arg(id, didh, LIST_INT,
2542 sgp->sg_cterm_fg, NULL, "ctermfg");
2543 didh = highlight_list_arg(id, didh, LIST_INT,
2544 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002545 didh = highlight_list_arg(id, didh, LIST_INT,
2546 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002547
2548#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2549 didh = highlight_list_arg(id, didh, LIST_ATTR,
2550 sgp->sg_gui, NULL, "gui");
2551 didh = highlight_list_arg(id, didh, LIST_STRING,
2552 0, sgp->sg_gui_fg_name, "guifg");
2553 didh = highlight_list_arg(id, didh, LIST_STRING,
2554 0, sgp->sg_gui_bg_name, "guibg");
2555 didh = highlight_list_arg(id, didh, LIST_STRING,
2556 0, sgp->sg_gui_sp_name, "guisp");
2557#endif
2558#ifdef FEAT_GUI
2559 didh = highlight_list_arg(id, didh, LIST_STRING,
2560 0, sgp->sg_font_name, "font");
2561#endif
2562
2563 if (sgp->sg_link && !got_int)
2564 {
2565 (void)syn_list_header(didh, 9999, id);
2566 didh = TRUE;
2567 msg_puts_attr("links to", HL_ATTR(HLF_D));
2568 msg_putchar(' ');
2569 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2570 }
2571
2572 if (!didh)
2573 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2574#ifdef FEAT_EVAL
2575 if (p_verbose > 0)
2576 last_set_msg(sgp->sg_script_ctx);
2577#endif
2578}
2579
2580 static int
2581highlight_list_arg(
2582 int id,
2583 int didh,
2584 int type,
2585 int iarg,
2586 char_u *sarg,
2587 char *name)
2588{
2589 char_u buf[100];
2590 char_u *ts;
2591 int i;
2592
2593 if (got_int)
2594 return FALSE;
2595 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2596 {
2597 ts = buf;
2598 if (type == LIST_INT)
2599 sprintf((char *)buf, "%d", iarg - 1);
2600 else if (type == LIST_STRING)
2601 ts = sarg;
2602 else // type == LIST_ATTR
2603 {
2604 buf[0] = NUL;
2605 for (i = 0; hl_attr_table[i] != 0; ++i)
2606 {
2607 if (iarg & hl_attr_table[i])
2608 {
2609 if (buf[0] != NUL)
2610 vim_strcat(buf, (char_u *)",", 100);
2611 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2612 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2613 }
2614 }
2615 }
2616
2617 (void)syn_list_header(didh,
2618 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2619 didh = TRUE;
2620 if (!got_int)
2621 {
2622 if (*name != NUL)
2623 {
2624 msg_puts_attr(name, HL_ATTR(HLF_D));
2625 msg_puts_attr("=", HL_ATTR(HLF_D));
2626 }
2627 msg_outtrans(ts);
2628 }
2629 }
2630 return didh;
2631}
2632
2633#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2634/*
2635 * Return "1" if highlight group "id" has attribute "flag".
2636 * Return NULL otherwise.
2637 */
2638 char_u *
2639highlight_has_attr(
2640 int id,
2641 int flag,
2642 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2643{
2644 int attr;
2645
2646 if (id <= 0 || id > highlight_ga.ga_len)
2647 return NULL;
2648
2649#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2650 if (modec == 'g')
2651 attr = HL_TABLE()[id - 1].sg_gui;
2652 else
2653#endif
2654 if (modec == 'c')
2655 attr = HL_TABLE()[id - 1].sg_cterm;
2656 else
2657 attr = HL_TABLE()[id - 1].sg_term;
2658
2659 if (attr & flag)
2660 return (char_u *)"1";
2661 return NULL;
2662}
2663#endif
2664
2665#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2666/*
2667 * Return color name of highlight group "id".
2668 */
2669 char_u *
2670highlight_color(
2671 int id,
2672 char_u *what, // "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#"
2673 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2674{
2675 static char_u name[20];
2676 int n;
2677 int fg = FALSE;
2678 int sp = FALSE;
2679 int font = FALSE;
2680
2681 if (id <= 0 || id > highlight_ga.ga_len)
2682 return NULL;
2683
2684 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2685 fg = TRUE;
2686 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2687 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2688 font = TRUE;
2689 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2690 sp = TRUE;
2691 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2692 return NULL;
2693 if (modec == 'g')
2694 {
2695# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2696# ifdef FEAT_GUI
2697 // return font name
2698 if (font)
2699 return HL_TABLE()[id - 1].sg_font_name;
2700# endif
2701
2702 // return #RRGGBB form (only possible when GUI is running)
2703 if ((USE_24BIT) && what[2] == '#')
2704 {
2705 guicolor_T color;
2706 long_u rgb;
2707 static char_u buf[10];
2708
2709 if (fg)
2710 color = HL_TABLE()[id - 1].sg_gui_fg;
2711 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002712 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002713 else
2714 color = HL_TABLE()[id - 1].sg_gui_bg;
2715 if (color == INVALCOLOR)
2716 return NULL;
2717 rgb = (long_u)GUI_MCH_GET_RGB(color);
2718 sprintf((char *)buf, "#%02x%02x%02x",
2719 (unsigned)(rgb >> 16),
2720 (unsigned)(rgb >> 8) & 255,
2721 (unsigned)rgb & 255);
2722 return buf;
2723 }
2724# endif
2725 if (fg)
2726 return (HL_TABLE()[id - 1].sg_gui_fg_name);
2727 if (sp)
2728 return (HL_TABLE()[id - 1].sg_gui_sp_name);
2729 return (HL_TABLE()[id - 1].sg_gui_bg_name);
2730 }
2731 if (font || sp)
2732 return NULL;
2733 if (modec == 'c')
2734 {
2735 if (fg)
2736 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
2737 else
2738 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2739 if (n < 0)
2740 return NULL;
2741 sprintf((char *)name, "%d", n);
2742 return name;
2743 }
2744 // term doesn't have color
2745 return NULL;
2746}
2747#endif
2748
2749#if (defined(FEAT_SYN_HL) \
2750 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2751 && defined(FEAT_PRINTER)) || defined(PROTO)
2752/*
2753 * Return color name of highlight group "id" as RGB value.
2754 */
2755 long_u
2756highlight_gui_color_rgb(
2757 int id,
2758 int fg) // TRUE = fg, FALSE = bg
2759{
2760 guicolor_T color;
2761
2762 if (id <= 0 || id > highlight_ga.ga_len)
2763 return 0L;
2764
2765 if (fg)
2766 color = HL_TABLE()[id - 1].sg_gui_fg;
2767 else
2768 color = HL_TABLE()[id - 1].sg_gui_bg;
2769
2770 if (color == INVALCOLOR)
2771 return 0L;
2772
2773 return GUI_MCH_GET_RGB(color);
2774}
2775#endif
2776
2777/*
2778 * Output the syntax list header.
2779 * Return TRUE when started a new line.
2780 */
2781 int
2782syn_list_header(
2783 int did_header, // did header already
2784 int outlen, // length of string that comes
2785 int id) // highlight group id
2786{
2787 int endcol = 19;
2788 int newline = TRUE;
2789 int name_col = 0;
2790
2791 if (!did_header)
2792 {
2793 msg_putchar('\n');
2794 if (got_int)
2795 return TRUE;
2796 msg_outtrans(HL_TABLE()[id - 1].sg_name);
2797 name_col = msg_col;
2798 endcol = 15;
2799 }
2800 else if (msg_col + outlen + 1 >= Columns)
2801 {
2802 msg_putchar('\n');
2803 if (got_int)
2804 return TRUE;
2805 }
2806 else
2807 {
2808 if (msg_col >= endcol) // wrap around is like starting a new line
2809 newline = FALSE;
2810 }
2811
2812 if (msg_col >= endcol) // output at least one space
2813 endcol = msg_col + 1;
2814 if (Columns <= endcol) // avoid hang for tiny window
2815 endcol = Columns - 1;
2816
2817 msg_advance(endcol);
2818
2819 // Show "xxx" with the attributes.
2820 if (!did_header)
2821 {
2822 if (endcol == Columns - 1 && endcol <= name_col)
2823 msg_putchar(' ');
2824 msg_puts_attr("xxx", syn_id2attr(id));
2825 msg_putchar(' ');
2826 }
2827
2828 return newline;
2829}
2830
2831/*
2832 * Set the attribute numbers for a highlight group.
2833 * Called after one of the attributes has changed.
2834 */
2835 static void
2836set_hl_attr(
2837 int idx) // index in array
2838{
2839 attrentry_T at_en;
2840 hl_group_T *sgp = HL_TABLE() + idx;
2841
2842 // The "Normal" group doesn't need an attribute number
2843 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
2844 return;
2845
2846#ifdef FEAT_GUI
2847 /*
2848 * For the GUI mode: If there are other than "normal" highlighting
2849 * attributes, need to allocate an attr number.
2850 */
2851 if (sgp->sg_gui_fg == INVALCOLOR
2852 && sgp->sg_gui_bg == INVALCOLOR
2853 && sgp->sg_gui_sp == INVALCOLOR
2854 && sgp->sg_font == NOFONT
2855# ifdef FEAT_XFONTSET
2856 && sgp->sg_fontset == NOFONTSET
2857# endif
2858 )
2859 {
2860 sgp->sg_gui_attr = sgp->sg_gui;
2861 }
2862 else
2863 {
2864 at_en.ae_attr = sgp->sg_gui;
2865 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
2866 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
2867 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
2868 at_en.ae_u.gui.font = sgp->sg_font;
2869# ifdef FEAT_XFONTSET
2870 at_en.ae_u.gui.fontset = sgp->sg_fontset;
2871# endif
2872 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
2873 }
2874#endif
2875 /*
2876 * For the term mode: If there are other than "normal" highlighting
2877 * attributes, need to allocate an attr number.
2878 */
2879 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
2880 sgp->sg_term_attr = sgp->sg_term;
2881 else
2882 {
2883 at_en.ae_attr = sgp->sg_term;
2884 at_en.ae_u.term.start = sgp->sg_start;
2885 at_en.ae_u.term.stop = sgp->sg_stop;
2886 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
2887 }
2888
2889 /*
2890 * For the color term mode: If there are other than "normal"
2891 * highlighting attributes, need to allocate an attr number.
2892 */
Bram Moolenaare023e882020-05-31 16:42:30 +02002893 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002894# ifdef FEAT_TERMGUICOLORS
2895 && sgp->sg_gui_fg == INVALCOLOR
2896 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02002897 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002898# endif
2899 )
2900 sgp->sg_cterm_attr = sgp->sg_cterm;
2901 else
2902 {
2903 at_en.ae_attr = sgp->sg_cterm;
2904 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
2905 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02002906 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002907# ifdef FEAT_TERMGUICOLORS
2908# ifdef MSWIN
2909# ifdef VIMDLL
2910 // Only when not using the GUI.
2911 if (!gui.in_use && !gui.starting)
2912# endif
2913 {
2914 int id;
2915 guicolor_T fg, bg;
2916
2917 id = syn_name2id((char_u *)"Normal");
2918 if (id > 0)
2919 {
2920 syn_id2colors(id, &fg, &bg);
2921 if (sgp->sg_gui_fg == INVALCOLOR)
2922 sgp->sg_gui_fg = fg;
2923 if (sgp->sg_gui_bg == INVALCOLOR)
2924 sgp->sg_gui_bg = bg;
2925 }
2926
2927 }
2928# endif
2929 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
2930 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02002931 // Only use the underline/undercurl color when used, it may clear the
2932 // background color if not supported.
2933 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
2934 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
2935 else
2936 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002937 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2938 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2939 {
2940 // If both fg and bg are invalid fall back to the cterm colors.
2941 // Helps when the GUI only uses an attribute, e.g. undercurl.
2942 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2943 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2944 }
2945# endif
2946 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2947 }
2948}
2949
2950/*
2951 * Lookup a highlight group name and return its ID.
2952 * If it is not found, 0 is returned.
2953 */
2954 int
2955syn_name2id(char_u *name)
2956{
2957 int i;
2958 char_u name_u[200];
2959
2960 // Avoid using stricmp() too much, it's slow on some systems
2961 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
2962 // don't deserve to be found!
2963 vim_strncpy(name_u, name, 199);
2964 vim_strup(name_u);
2965 for (i = highlight_ga.ga_len; --i >= 0; )
2966 if (HL_TABLE()[i].sg_name_u != NULL
2967 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2968 break;
2969 return i + 1;
2970}
2971
2972/*
2973 * Lookup a highlight group name and return its attributes.
2974 * Return zero if not found.
2975 */
2976 int
2977syn_name2attr(char_u *name)
2978{
2979 int id = syn_name2id(name);
2980
2981 if (id != 0)
2982 return syn_id2attr(id);
2983 return 0;
2984}
2985
2986#if defined(FEAT_EVAL) || defined(PROTO)
2987/*
2988 * Return TRUE if highlight group "name" exists.
2989 */
2990 int
2991highlight_exists(char_u *name)
2992{
2993 return (syn_name2id(name) > 0);
2994}
2995
2996# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
2997/*
2998 * Return the name of highlight group "id".
2999 * When not a valid ID return an empty string.
3000 */
3001 char_u *
3002syn_id2name(int id)
3003{
3004 if (id <= 0 || id > highlight_ga.ga_len)
3005 return (char_u *)"";
3006 return HL_TABLE()[id - 1].sg_name;
3007}
3008# endif
3009#endif
3010
3011/*
3012 * Like syn_name2id(), but take a pointer + length argument.
3013 */
3014 int
3015syn_namen2id(char_u *linep, int len)
3016{
3017 char_u *name;
3018 int id = 0;
3019
3020 name = vim_strnsave(linep, len);
3021 if (name != NULL)
3022 {
3023 id = syn_name2id(name);
3024 vim_free(name);
3025 }
3026 return id;
3027}
3028
3029/*
3030 * Find highlight group name in the table and return its ID.
3031 * The argument is a pointer to the name and the length of the name.
3032 * If it doesn't exist yet, a new entry is created.
3033 * Return 0 for failure.
3034 */
3035 int
3036syn_check_group(char_u *pp, int len)
3037{
3038 int id;
3039 char_u *name;
3040
3041 name = vim_strnsave(pp, len);
3042 if (name == NULL)
3043 return 0;
3044
3045 id = syn_name2id(name);
3046 if (id == 0) // doesn't exist yet
3047 id = syn_add_group(name);
3048 else
3049 vim_free(name);
3050 return id;
3051}
3052
3053/*
3054 * Add new highlight group and return its ID.
3055 * "name" must be an allocated string, it will be consumed.
3056 * Return 0 for failure.
3057 */
3058 static int
3059syn_add_group(char_u *name)
3060{
3061 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003062 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003063
3064 // Check that the name is ASCII letters, digits and underscore.
3065 for (p = name; *p != NUL; ++p)
3066 {
3067 if (!vim_isprintc(*p))
3068 {
3069 emsg(_("E669: Unprintable character in group name"));
3070 vim_free(name);
3071 return 0;
3072 }
3073 else if (!ASCII_ISALNUM(*p) && *p != '_')
3074 {
3075 // This is an error, but since there previously was no check only
3076 // give a warning.
3077 msg_source(HL_ATTR(HLF_W));
3078 msg(_("W18: Invalid character in group name"));
3079 break;
3080 }
3081 }
3082
3083 /*
3084 * First call for this growarray: init growing array.
3085 */
3086 if (highlight_ga.ga_data == NULL)
3087 {
3088 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3089 highlight_ga.ga_growsize = 10;
3090 }
3091
3092 if (highlight_ga.ga_len >= MAX_HL_ID)
3093 {
3094 emsg(_("E849: Too many highlight and syntax groups"));
3095 vim_free(name);
3096 return 0;
3097 }
3098
3099 /*
3100 * Make room for at least one other syntax_highlight entry.
3101 */
3102 if (ga_grow(&highlight_ga, 1) == FAIL)
3103 {
3104 vim_free(name);
3105 return 0;
3106 }
3107
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003108 name_up = vim_strsave_up(name);
3109 if (name_up == NULL)
3110 {
3111 vim_free(name);
3112 return 0;
3113 }
3114
Bram Moolenaara80faa82020-04-12 19:37:17 +02003115 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003116 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003117 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003118#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3119 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3120 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003121 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003122#endif
3123 ++highlight_ga.ga_len;
3124
3125 return highlight_ga.ga_len; // ID is index plus one
3126}
3127
3128/*
3129 * When, just after calling syn_add_group(), an error is discovered, this
3130 * function deletes the new name.
3131 */
3132 static void
3133syn_unadd_group(void)
3134{
3135 --highlight_ga.ga_len;
3136 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3137 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3138}
3139
3140/*
3141 * Translate a group ID to highlight attributes.
3142 */
3143 int
3144syn_id2attr(int hl_id)
3145{
3146 int attr;
3147 hl_group_T *sgp;
3148
3149 hl_id = syn_get_final_id(hl_id);
3150 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3151
3152#ifdef FEAT_GUI
3153 /*
3154 * Only use GUI attr when the GUI is being used.
3155 */
3156 if (gui.in_use)
3157 attr = sgp->sg_gui_attr;
3158 else
3159#endif
3160 if (IS_CTERM)
3161 attr = sgp->sg_cterm_attr;
3162 else
3163 attr = sgp->sg_term_attr;
3164
3165 return attr;
3166}
3167
3168#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3169/*
3170 * Get the GUI colors and attributes for a group ID.
3171 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3172 */
3173 int
3174syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3175{
3176 hl_group_T *sgp;
3177
3178 hl_id = syn_get_final_id(hl_id);
3179 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3180
3181 *fgp = sgp->sg_gui_fg;
3182 *bgp = sgp->sg_gui_bg;
3183 return sgp->sg_gui;
3184}
3185#endif
3186
3187#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003188 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3189 && defined(FEAT_TERMGUICOLORS)) \
3190 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003191 void
3192syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3193{
3194 hl_group_T *sgp;
3195
3196 hl_id = syn_get_final_id(hl_id);
3197 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3198 *fgp = sgp->sg_cterm_fg - 1;
3199 *bgp = sgp->sg_cterm_bg - 1;
3200}
3201#endif
3202
3203/*
3204 * Translate a group ID to the final group ID (following links).
3205 */
3206 int
3207syn_get_final_id(int hl_id)
3208{
3209 int count;
3210 hl_group_T *sgp;
3211
3212 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3213 return 0; // Can be called from eval!!
3214
3215 /*
3216 * Follow links until there is no more.
3217 * Look out for loops! Break after 100 links.
3218 */
3219 for (count = 100; --count >= 0; )
3220 {
3221 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3222 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3223 break;
3224 hl_id = sgp->sg_link;
3225 }
3226
3227 return hl_id;
3228}
3229
3230#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3231/*
3232 * Call this function just after the GUI has started.
3233 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3234 * It finds the font and color handles for the highlighting groups.
3235 */
3236 void
3237highlight_gui_started(void)
3238{
3239 int idx;
3240
3241 // First get the colors from the "Normal" and "Menu" group, if set
3242 if (USE_24BIT)
3243 set_normal_colors();
3244
3245 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3246 gui_do_one_color(idx, FALSE, FALSE);
3247
3248 highlight_changed();
3249}
3250
3251 static void
3252gui_do_one_color(
3253 int idx,
3254 int do_menu UNUSED, // TRUE: might set the menu font
3255 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3256{
3257 int didit = FALSE;
3258
3259# ifdef FEAT_GUI
3260# ifdef FEAT_TERMGUICOLORS
3261 if (gui.in_use)
3262# endif
3263 if (HL_TABLE()[idx].sg_font_name != NULL)
3264 {
3265 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3266 do_tooltip, TRUE);
3267 didit = TRUE;
3268 }
3269# endif
3270 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3271 {
3272 HL_TABLE()[idx].sg_gui_fg =
3273 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3274 didit = TRUE;
3275 }
3276 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3277 {
3278 HL_TABLE()[idx].sg_gui_bg =
3279 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3280 didit = TRUE;
3281 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003282 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3283 {
3284 HL_TABLE()[idx].sg_gui_sp =
3285 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3286 didit = TRUE;
3287 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003288 if (didit) // need to get a new attr number
3289 set_hl_attr(idx);
3290}
3291#endif
3292
3293#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3294/*
3295 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3296 */
3297 static void
3298combine_stl_hlt(
3299 int id,
3300 int id_S,
3301 int id_alt,
3302 int hlcnt,
3303 int i,
3304 int hlf,
3305 int *table)
3306{
3307 hl_group_T *hlt = HL_TABLE();
3308
3309 if (id_alt == 0)
3310 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003311 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003312 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3313 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3314# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3315 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3316# endif
3317 }
3318 else
3319 mch_memmove(&hlt[hlcnt + i],
3320 &hlt[id_alt - 1],
3321 sizeof(hl_group_T));
3322 hlt[hlcnt + i].sg_link = 0;
3323
3324 hlt[hlcnt + i].sg_term ^=
3325 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3326 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3327 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3328 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3329 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3330 hlt[hlcnt + i].sg_cterm ^=
3331 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3332 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3333 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3334 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3335 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3336# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3337 hlt[hlcnt + i].sg_gui ^=
3338 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3339# endif
3340# ifdef FEAT_GUI
3341 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3342 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3343 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3344 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3345 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3346 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3347 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3348 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3349# ifdef FEAT_XFONTSET
3350 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3351 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3352# endif
3353# endif
3354 highlight_ga.ga_len = hlcnt + i + 1;
3355 set_hl_attr(hlcnt + i); // At long last we can apply
3356 table[i] = syn_id2attr(hlcnt + i + 1);
3357}
3358#endif
3359
3360/*
3361 * Translate the 'highlight' option into attributes in highlight_attr[] and
3362 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3363 * corresponding highlights to use on top of HLF_SNC is computed.
3364 * Called only when the 'highlight' option has been changed and upon first
3365 * screen redraw after any :highlight command.
3366 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3367 */
3368 int
3369highlight_changed(void)
3370{
3371 int hlf;
3372 int i;
3373 char_u *p;
3374 int attr;
3375 char_u *end;
3376 int id;
3377#ifdef USER_HIGHLIGHT
3378 char_u userhl[30]; // use 30 to avoid compiler warning
3379# ifdef FEAT_STL_OPT
3380 int id_S = -1;
3381 int id_SNC = 0;
3382# ifdef FEAT_TERMINAL
3383 int id_ST = 0;
3384 int id_STNC = 0;
3385# endif
3386 int hlcnt;
3387# endif
3388#endif
3389 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3390
3391 need_highlight_changed = FALSE;
3392
3393 /*
3394 * Clear all attributes.
3395 */
3396 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3397 highlight_attr[hlf] = 0;
3398
3399 /*
3400 * First set all attributes to their default value.
3401 * Then use the attributes from the 'highlight' option.
3402 */
3403 for (i = 0; i < 2; ++i)
3404 {
3405 if (i)
3406 p = p_hl;
3407 else
3408 p = get_highlight_default();
3409 if (p == NULL) // just in case
3410 continue;
3411
3412 while (*p)
3413 {
3414 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3415 if (hl_flags[hlf] == *p)
3416 break;
3417 ++p;
3418 if (hlf == (int)HLF_COUNT || *p == NUL)
3419 return FAIL;
3420
3421 /*
3422 * Allow several hl_flags to be combined, like "bu" for
3423 * bold-underlined.
3424 */
3425 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003426 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003427 {
3428 if (VIM_ISWHITE(*p)) // ignore white space
3429 continue;
3430
3431 if (attr > HL_ALL) // Combination with ':' is not allowed.
3432 return FAIL;
3433
3434 switch (*p)
3435 {
3436 case 'b': attr |= HL_BOLD;
3437 break;
3438 case 'i': attr |= HL_ITALIC;
3439 break;
3440 case '-':
3441 case 'n': // no highlighting
3442 break;
3443 case 'r': attr |= HL_INVERSE;
3444 break;
3445 case 's': attr |= HL_STANDOUT;
3446 break;
3447 case 'u': attr |= HL_UNDERLINE;
3448 break;
3449 case 'c': attr |= HL_UNDERCURL;
3450 break;
3451 case 't': attr |= HL_STRIKETHROUGH;
3452 break;
3453 case ':': ++p; // highlight group name
3454 if (attr || *p == NUL) // no combinations
3455 return FAIL;
3456 end = vim_strchr(p, ',');
3457 if (end == NULL)
3458 end = p + STRLEN(p);
3459 id = syn_check_group(p, (int)(end - p));
3460 if (id == 0)
3461 return FAIL;
3462 attr = syn_id2attr(id);
3463 p = end - 1;
3464#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3465 if (hlf == (int)HLF_SNC)
3466 id_SNC = syn_get_final_id(id);
3467# ifdef FEAT_TERMINAL
3468 else if (hlf == (int)HLF_ST)
3469 id_ST = syn_get_final_id(id);
3470 else if (hlf == (int)HLF_STNC)
3471 id_STNC = syn_get_final_id(id);
3472# endif
3473 else if (hlf == (int)HLF_S)
3474 id_S = syn_get_final_id(id);
3475#endif
3476 break;
3477 default: return FAIL;
3478 }
3479 }
3480 highlight_attr[hlf] = attr;
3481
3482 p = skip_to_option_part(p); // skip comma and spaces
3483 }
3484 }
3485
3486#ifdef USER_HIGHLIGHT
3487 /*
3488 * Setup the user highlights
3489 *
3490 * Temporarily utilize 28 more hl entries:
3491 * 9 for User1-User9 combined with StatusLineNC
3492 * 9 for User1-User9 combined with StatusLineTerm
3493 * 9 for User1-User9 combined with StatusLineTermNC
3494 * 1 for StatusLine default
3495 * Have to be in there simultaneously in case of table overflows in
3496 * get_attr_entry()
3497 */
3498# ifdef FEAT_STL_OPT
3499 if (ga_grow(&highlight_ga, 28) == FAIL)
3500 return FAIL;
3501 hlcnt = highlight_ga.ga_len;
3502 if (id_S == -1)
3503 {
3504 // Make sure id_S is always valid to simplify code below. Use the last
3505 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003506 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003507 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3508 id_S = hlcnt + 19;
3509 }
3510# endif
3511 for (i = 0; i < 9; i++)
3512 {
3513 sprintf((char *)userhl, "User%d", i + 1);
3514 id = syn_name2id(userhl);
3515 if (id == 0)
3516 {
3517 highlight_user[i] = 0;
3518# ifdef FEAT_STL_OPT
3519 highlight_stlnc[i] = 0;
3520# ifdef FEAT_TERMINAL
3521 highlight_stlterm[i] = 0;
3522 highlight_stltermnc[i] = 0;
3523# endif
3524# endif
3525 }
3526 else
3527 {
3528 highlight_user[i] = syn_id2attr(id);
3529# ifdef FEAT_STL_OPT
3530 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3531 HLF_SNC, highlight_stlnc);
3532# ifdef FEAT_TERMINAL
3533 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3534 HLF_ST, highlight_stlterm);
3535 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3536 HLF_STNC, highlight_stltermnc);
3537# endif
3538# endif
3539 }
3540 }
3541# ifdef FEAT_STL_OPT
3542 highlight_ga.ga_len = hlcnt;
3543# endif
3544
3545#endif // USER_HIGHLIGHT
3546
3547 return OK;
3548}
3549
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003550static void highlight_list(void);
3551static void highlight_list_two(int cnt, int attr);
3552
3553/*
3554 * Handle command line completion for :highlight command.
3555 */
3556 void
3557set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3558{
3559 char_u *p;
3560
3561 // Default: expand group names
3562 xp->xp_context = EXPAND_HIGHLIGHT;
3563 xp->xp_pattern = arg;
3564 include_link = 2;
3565 include_default = 1;
3566
3567 // (part of) subcommand already typed
3568 if (*arg != NUL)
3569 {
3570 p = skiptowhite(arg);
3571 if (*p != NUL) // past "default" or group name
3572 {
3573 include_default = 0;
3574 if (STRNCMP("default", arg, p - arg) == 0)
3575 {
3576 arg = skipwhite(p);
3577 xp->xp_pattern = arg;
3578 p = skiptowhite(arg);
3579 }
3580 if (*p != NUL) // past group name
3581 {
3582 include_link = 0;
3583 if (arg[1] == 'i' && arg[0] == 'N')
3584 highlight_list();
3585 if (STRNCMP("link", arg, p - arg) == 0
3586 || STRNCMP("clear", arg, p - arg) == 0)
3587 {
3588 xp->xp_pattern = skipwhite(p);
3589 p = skiptowhite(xp->xp_pattern);
3590 if (*p != NUL) // past first group name
3591 {
3592 xp->xp_pattern = skipwhite(p);
3593 p = skiptowhite(xp->xp_pattern);
3594 }
3595 }
3596 if (*p != NUL) // past group name(s)
3597 xp->xp_context = EXPAND_NOTHING;
3598 }
3599 }
3600 }
3601}
3602
3603/*
3604 * List highlighting matches in a nice way.
3605 */
3606 static void
3607highlight_list(void)
3608{
3609 int i;
3610
3611 for (i = 10; --i >= 0; )
3612 highlight_list_two(i, HL_ATTR(HLF_D));
3613 for (i = 40; --i >= 0; )
3614 highlight_list_two(99, 0);
3615}
3616
3617 static void
3618highlight_list_two(int cnt, int attr)
3619{
3620 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3621 msg_clr_eos();
3622 out_flush();
3623 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3624}
3625
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003626/*
3627 * Function given to ExpandGeneric() to obtain the list of group names.
3628 */
3629 char_u *
3630get_highlight_name(expand_T *xp UNUSED, int idx)
3631{
3632 return get_highlight_name_ext(xp, idx, TRUE);
3633}
3634
3635/*
3636 * Obtain a highlight group name.
3637 * When "skip_cleared" is TRUE don't return a cleared entry.
3638 */
3639 char_u *
3640get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3641{
3642 if (idx < 0)
3643 return NULL;
3644
3645 // Items are never removed from the table, skip the ones that were
3646 // cleared.
3647 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3648 return (char_u *)"";
3649
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003650 if (idx == highlight_ga.ga_len && include_none != 0)
3651 return (char_u *)"none";
3652 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3653 return (char_u *)"default";
3654 if (idx == highlight_ga.ga_len + include_none + include_default
3655 && include_link != 0)
3656 return (char_u *)"link";
3657 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3658 && include_link != 0)
3659 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003660 if (idx >= highlight_ga.ga_len)
3661 return NULL;
3662 return HL_TABLE()[idx].sg_name;
3663}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003664
3665#if defined(FEAT_GUI) || defined(PROTO)
3666/*
3667 * Free all the highlight group fonts.
3668 * Used when quitting for systems which need it.
3669 */
3670 void
3671free_highlight_fonts(void)
3672{
3673 int idx;
3674
3675 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3676 {
3677 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3678 HL_TABLE()[idx].sg_font = NOFONT;
3679# ifdef FEAT_XFONTSET
3680 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3681 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3682# endif
3683 }
3684
3685 gui_mch_free_font(gui.norm_font);
3686# ifdef FEAT_XFONTSET
3687 gui_mch_free_fontset(gui.fontset);
3688# endif
3689# ifndef FEAT_GUI_GTK
3690 gui_mch_free_font(gui.bold_font);
3691 gui_mch_free_font(gui.ital_font);
3692 gui_mch_free_font(gui.boldital_font);
3693# endif
3694}
3695#endif