blob: 0175079245aa2af9e7da200161414ea3f2752bce [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 {
Bram Moolenaar05eb5b92020-09-16 15:43:21 +0200732 semsg(_("E413: Too many arguments: \":highlight link %s\""),
733 from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200734 return;
735 }
736
737 from_id = syn_check_group(from_start, (int)(from_end - from_start));
738 if (STRNCMP(to_start, "NONE", 4) == 0)
739 to_id = 0;
740 else
741 to_id = syn_check_group(to_start, (int)(to_end - to_start));
742
743 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
744 {
745 /*
746 * Don't allow a link when there already is some highlighting
747 * for the group, unless '!' is used
748 */
749 if (to_id > 0 && !forceit && !init
750 && hl_has_settings(from_id - 1, dodefault))
751 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100752 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200753 emsg(_("E414: group has settings, highlight link ignored"));
754 }
755 else if (HL_TABLE()[from_id - 1].sg_link != to_id
756#ifdef FEAT_EVAL
757 || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
758 != current_sctx.sc_sid
759#endif
760 || HL_TABLE()[from_id - 1].sg_cleared)
761 {
762 if (!init)
763 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
764 HL_TABLE()[from_id - 1].sg_link = to_id;
765#ifdef FEAT_EVAL
766 HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100767 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200768#endif
769 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
770 redraw_all_later(SOME_VALID);
771
772 // Only call highlight_changed() once after multiple changes.
773 need_highlight_changed = TRUE;
774 }
775 }
776
777 return;
778 }
779
780 if (doclear)
781 {
782 /*
783 * ":highlight clear [group]" command.
784 */
Bram Moolenaar1966c242020-04-20 22:42:32 +0200785 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200786 {
787#ifdef FEAT_GUI
788 // First, we do not destroy the old values, but allocate the new
789 // ones and update the display. THEN we destroy the old values.
790 // If we destroy the old values first, then the old values
791 // (such as GuiFont's or GuiFontset's) will still be displayed but
792 // invalid because they were free'd.
793 if (gui.in_use)
794 {
795# ifdef FEAT_BEVAL_TIP
796 gui_init_tooltip_font();
797# endif
798# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
799 gui_init_menu_font();
800# endif
801 }
802# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
803 gui_mch_def_colors();
804# endif
805# ifdef FEAT_GUI_X11
806# ifdef FEAT_MENU
807
808 // This only needs to be done when there is no Menu highlight
809 // group defined by default, which IS currently the case.
810 gui_mch_new_menu_colors();
811# endif
812 if (gui.in_use)
813 {
814 gui_new_scrollbar_colors();
815# ifdef FEAT_BEVAL_GUI
816 gui_mch_new_tooltip_colors();
817# endif
818# ifdef FEAT_MENU
819 gui_mch_new_menu_font();
820# endif
821 }
822# endif
823
824 // Ok, we're done allocating the new default graphics items.
825 // The screen should already be refreshed at this point.
826 // It is now Ok to clear out the old data.
827#endif
828#ifdef FEAT_EVAL
Bram Moolenaar1966c242020-04-20 22:42:32 +0200829 do_unlet((char_u *)"g:colors_name", TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200830#endif
831 restore_cterm_colors();
832
833 /*
834 * Clear all default highlight groups and load the defaults.
835 */
836 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
837 highlight_clear(idx);
838 init_highlight(TRUE, TRUE);
839#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
840 if (USE_24BIT)
841 highlight_gui_started();
842 else
843#endif
844 highlight_changed();
845 redraw_later_clear();
846 return;
847 }
Bram Moolenaar1966c242020-04-20 22:42:32 +0200848 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200849 name_end = skiptowhite(line);
850 linep = skipwhite(name_end);
851 }
852
853 /*
854 * Find the group name in the table. If it does not exist yet, add it.
855 */
856 id = syn_check_group(line, (int)(name_end - line));
857 if (id == 0) // failed (out of memory)
858 return;
859 idx = id - 1; // index is ID minus one
860
861 // Return if "default" was used and the group already has settings.
862 if (dodefault && hl_has_settings(idx, TRUE))
863 return;
864
865 // Make a copy so we can check if any attribute actually changed.
866 item_before = HL_TABLE()[idx];
867
868 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
869 is_normal_group = TRUE;
870#ifdef FEAT_TERMINAL
871 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
872 is_terminal_group = TRUE;
873#endif
874#ifdef FEAT_GUI_X11
875 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
876 is_menu_group = TRUE;
877 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
878 is_scrollbar_group = TRUE;
879 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
880 is_tooltip_group = TRUE;
881#endif
882
883 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
884 if (doclear || (forceit && init))
885 {
886 highlight_clear(idx);
887 if (!doclear)
888 HL_TABLE()[idx].sg_set = 0;
889 }
890
891 if (!doclear)
Bram Moolenaar1966c242020-04-20 22:42:32 +0200892 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200893 {
894 key_start = linep;
895 if (*linep == '=')
896 {
897 semsg(_("E415: unexpected equal sign: %s"), key_start);
898 error = TRUE;
899 break;
900 }
901
902 /*
903 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
904 * "guibg").
905 */
906 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
907 ++linep;
908 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +0200909 key = vim_strnsave_up(key_start, linep - key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200910 if (key == NULL)
911 {
912 error = TRUE;
913 break;
914 }
915 linep = skipwhite(linep);
916
917 if (STRCMP(key, "NONE") == 0)
918 {
919 if (!init || HL_TABLE()[idx].sg_set == 0)
920 {
921 if (!init)
922 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
923 highlight_clear(idx);
924 }
925 continue;
926 }
927
928 /*
929 * Check for the equal sign.
930 */
931 if (*linep != '=')
932 {
933 semsg(_("E416: missing equal sign: %s"), key_start);
934 error = TRUE;
935 break;
936 }
937 ++linep;
938
939 /*
940 * Isolate the argument.
941 */
942 linep = skipwhite(linep);
943 if (*linep == '\'') // guifg='color name'
944 {
945 arg_start = ++linep;
946 linep = vim_strchr(linep, '\'');
947 if (linep == NULL)
948 {
949 semsg(_(e_invarg2), key_start);
950 error = TRUE;
951 break;
952 }
953 }
954 else
955 {
956 arg_start = linep;
957 linep = skiptowhite(linep);
958 }
959 if (linep == arg_start)
960 {
961 semsg(_("E417: missing argument: %s"), key_start);
962 error = TRUE;
963 break;
964 }
965 vim_free(arg);
Bram Moolenaar71ccd032020-06-12 22:59:11 +0200966 arg = vim_strnsave(arg_start, linep - arg_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200967 if (arg == NULL)
968 {
969 error = TRUE;
970 break;
971 }
972 if (*linep == '\'')
973 ++linep;
974
975 /*
976 * Store the argument.
977 */
978 if ( STRCMP(key, "TERM") == 0
979 || STRCMP(key, "CTERM") == 0
980 || STRCMP(key, "GUI") == 0)
981 {
982 attr = 0;
983 off = 0;
984 while (arg[off] != NUL)
985 {
986 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
987 {
988 len = (int)STRLEN(hl_name_table[i]);
989 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
990 {
991 attr |= hl_attr_table[i];
992 off += len;
993 break;
994 }
995 }
996 if (i < 0)
997 {
998 semsg(_("E418: Illegal value: %s"), arg);
999 error = TRUE;
1000 break;
1001 }
1002 if (arg[off] == ',') // another one follows
1003 ++off;
1004 }
1005 if (error)
1006 break;
1007 if (*key == 'T')
1008 {
1009 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
1010 {
1011 if (!init)
1012 HL_TABLE()[idx].sg_set |= SG_TERM;
1013 HL_TABLE()[idx].sg_term = attr;
1014 }
1015 }
1016 else if (*key == 'C')
1017 {
1018 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1019 {
1020 if (!init)
1021 HL_TABLE()[idx].sg_set |= SG_CTERM;
1022 HL_TABLE()[idx].sg_cterm = attr;
1023 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1024 }
1025 }
1026#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1027 else
1028 {
1029 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1030 {
1031 if (!init)
1032 HL_TABLE()[idx].sg_set |= SG_GUI;
1033 HL_TABLE()[idx].sg_gui = attr;
1034 }
1035 }
1036#endif
1037 }
1038 else if (STRCMP(key, "FONT") == 0)
1039 {
1040 // in non-GUI fonts are simply ignored
1041#ifdef FEAT_GUI
1042 if (HL_TABLE()[idx].sg_font_name != NULL
1043 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
1044 {
1045 // Font name didn't change, ignore.
1046 }
1047 else if (!gui.shell_created)
1048 {
1049 // GUI not started yet, always accept the name.
1050 vim_free(HL_TABLE()[idx].sg_font_name);
1051 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1052 did_change = TRUE;
1053 }
1054 else
1055 {
1056 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
1057# ifdef FEAT_XFONTSET
1058 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
1059# endif
1060 // First, save the current font/fontset.
1061 // Then try to allocate the font/fontset.
1062 // If the allocation fails, HL_TABLE()[idx].sg_font OR
1063 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
1064
1065 HL_TABLE()[idx].sg_font = NOFONT;
1066# ifdef FEAT_XFONTSET
1067 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1068# endif
1069 hl_do_font(idx, arg, is_normal_group, is_menu_group,
1070 is_tooltip_group, FALSE);
1071
1072# ifdef FEAT_XFONTSET
1073 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1074 {
1075 // New fontset was accepted. Free the old one, if there
1076 // was one.
1077 gui_mch_free_fontset(temp_sg_fontset);
1078 vim_free(HL_TABLE()[idx].sg_font_name);
1079 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1080 did_change = TRUE;
1081 }
1082 else
1083 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
1084# endif
1085 if (HL_TABLE()[idx].sg_font != NOFONT)
1086 {
1087 // New font was accepted. Free the old one, if there was
1088 // one.
1089 gui_mch_free_font(temp_sg_font);
1090 vim_free(HL_TABLE()[idx].sg_font_name);
1091 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1092 did_change = TRUE;
1093 }
1094 else
1095 HL_TABLE()[idx].sg_font = temp_sg_font;
1096 }
1097#endif
1098 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001099 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0
1100 || STRCMP(key, "CTERMUL") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001101 {
1102 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1103 {
1104 if (!init)
1105 HL_TABLE()[idx].sg_set |= SG_CTERM;
1106
1107 // When setting the foreground color, and previously the "bold"
1108 // flag was set for a light color, reset it now
1109 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1110 {
1111 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1112 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1113 }
1114
1115 if (VIM_ISDIGIT(*arg))
1116 color = atoi((char *)arg);
1117 else if (STRICMP(arg, "fg") == 0)
1118 {
1119 if (cterm_normal_fg_color)
1120 color = cterm_normal_fg_color - 1;
1121 else
1122 {
1123 emsg(_("E419: FG color unknown"));
1124 error = TRUE;
1125 break;
1126 }
1127 }
1128 else if (STRICMP(arg, "bg") == 0)
1129 {
1130 if (cterm_normal_bg_color > 0)
1131 color = cterm_normal_bg_color - 1;
1132 else
1133 {
1134 emsg(_("E420: BG color unknown"));
1135 error = TRUE;
1136 break;
1137 }
1138 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001139 else if (STRICMP(arg, "ul") == 0)
1140 {
1141 if (cterm_normal_ul_color > 0)
1142 color = cterm_normal_ul_color - 1;
1143 else
1144 {
1145 emsg(_("E453: UL color unknown"));
1146 error = TRUE;
1147 break;
1148 }
1149 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001150 else
1151 {
1152 int bold = MAYBE;
1153
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001154 // reduce calls to STRICMP a bit, it can be slow
1155 off = TOUPPER_ASC(*arg);
1156 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
1157 if (off == color_names[i][0]
1158 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1159 break;
1160 if (i < 0)
1161 {
1162 semsg(_("E421: Color name or number not recognized: %s"), key_start);
1163 error = TRUE;
1164 break;
1165 }
1166
1167 color = lookup_color(i, key[5] == 'F', &bold);
1168
1169 // set/reset bold attribute to get light foreground
1170 // colors (on some terminals, e.g. "linux")
1171 if (bold == TRUE)
1172 {
1173 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1174 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1175 }
1176 else if (bold == FALSE)
1177 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1178 }
1179
1180 // Add one to the argument, to avoid zero. Zero is used for
1181 // "NONE", then "color" is -1.
1182 if (key[5] == 'F')
1183 {
1184 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1185 if (is_normal_group)
1186 {
1187 cterm_normal_fg_color = color + 1;
1188 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
1189#ifdef FEAT_GUI
1190 // Don't do this if the GUI is used.
1191 if (!gui.in_use && !gui.starting)
1192#endif
1193 {
1194 must_redraw = CLEAR;
1195 if (termcap_active && color >= 0)
1196 term_fg_color(color);
1197 }
1198 }
1199 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001200 else if (key[5] == 'B')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001201 {
1202 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1203 if (is_normal_group)
1204 {
1205 cterm_normal_bg_color = color + 1;
1206#ifdef FEAT_GUI
1207 // Don't mess with 'background' if the GUI is used.
1208 if (!gui.in_use && !gui.starting)
1209#endif
1210 {
1211 must_redraw = CLEAR;
1212 if (color >= 0)
1213 {
1214 int dark = -1;
1215
1216 if (termcap_active)
1217 term_bg_color(color);
1218 if (t_colors < 16)
1219 dark = (color == 0 || color == 4);
1220 // Limit the heuristic to the standard 16 colors
1221 else if (color < 16)
1222 dark = (color < 7 || color == 8);
1223 // Set the 'background' option if the value is
1224 // wrong.
1225 if (dark != -1
1226 && dark != (*p_bg == 'd')
1227 && !option_was_set((char_u *)"bg"))
1228 {
1229 set_option_value((char_u *)"bg", 0L,
1230 (char_u *)(dark ? "dark" : "light"), 0);
1231 reset_option_was_set((char_u *)"bg");
1232 }
1233 }
1234 }
1235 }
1236 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001237 else // ctermul
1238 {
1239 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1240 if (is_normal_group)
1241 {
1242 cterm_normal_ul_color = color + 1;
1243#ifdef FEAT_GUI
1244 // Don't do this if the GUI is used.
1245 if (!gui.in_use && !gui.starting)
1246#endif
1247 {
1248 must_redraw = CLEAR;
1249 if (termcap_active && color >= 0)
1250 term_ul_color(color);
1251 }
1252 }
1253 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001254 }
1255 }
1256 else if (STRCMP(key, "GUIFG") == 0)
1257 {
1258#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1259 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
1260
1261 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1262 {
1263 if (!init)
1264 HL_TABLE()[idx].sg_set |= SG_GUI;
1265
1266# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1267 // In GUI guifg colors are only used when recognized
1268 i = color_name2handle(arg);
1269 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1270 {
1271 HL_TABLE()[idx].sg_gui_fg = i;
1272# endif
1273 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1274 {
1275 vim_free(*namep);
1276 if (STRCMP(arg, "NONE") != 0)
1277 *namep = vim_strsave(arg);
1278 else
1279 *namep = NULL;
1280 did_change = TRUE;
1281 }
1282# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1283# ifdef FEAT_GUI_X11
1284 if (is_menu_group && gui.menu_fg_pixel != i)
1285 {
1286 gui.menu_fg_pixel = i;
1287 do_colors = TRUE;
1288 }
1289 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1290 {
1291 gui.scroll_fg_pixel = i;
1292 do_colors = TRUE;
1293 }
1294# ifdef FEAT_BEVAL_GUI
1295 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1296 {
1297 gui.tooltip_fg_pixel = i;
1298 do_colors = TRUE;
1299 }
1300# endif
1301# endif
1302 }
1303# endif
1304 }
1305#endif
1306 }
1307 else if (STRCMP(key, "GUIBG") == 0)
1308 {
1309#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1310 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
1311
1312 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1313 {
1314 if (!init)
1315 HL_TABLE()[idx].sg_set |= SG_GUI;
1316
1317# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1318 // In GUI guifg colors are only used when recognized
1319 i = color_name2handle(arg);
1320 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1321 {
1322 HL_TABLE()[idx].sg_gui_bg = i;
1323# endif
1324 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1325 {
1326 vim_free(*namep);
1327 if (STRCMP(arg, "NONE") != 0)
1328 *namep = vim_strsave(arg);
1329 else
1330 *namep = NULL;
1331 did_change = TRUE;
1332 }
1333# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1334# ifdef FEAT_GUI_X11
1335 if (is_menu_group && gui.menu_bg_pixel != i)
1336 {
1337 gui.menu_bg_pixel = i;
1338 do_colors = TRUE;
1339 }
1340 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1341 {
1342 gui.scroll_bg_pixel = i;
1343 do_colors = TRUE;
1344 }
1345# ifdef FEAT_BEVAL_GUI
1346 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1347 {
1348 gui.tooltip_bg_pixel = i;
1349 do_colors = TRUE;
1350 }
1351# endif
1352# endif
1353 }
1354# endif
1355 }
1356#endif
1357 }
1358 else if (STRCMP(key, "GUISP") == 0)
1359 {
1360#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1361 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
1362
1363 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1364 {
1365 if (!init)
1366 HL_TABLE()[idx].sg_set |= SG_GUI;
1367
Bram Moolenaare023e882020-05-31 16:42:30 +02001368# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1369 // In GUI guisp colors are only used when recognized
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001370 i = color_name2handle(arg);
Bram Moolenaare023e882020-05-31 16:42:30 +02001371 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001372 {
1373 HL_TABLE()[idx].sg_gui_sp = i;
1374# endif
1375 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1376 {
1377 vim_free(*namep);
1378 if (STRCMP(arg, "NONE") != 0)
1379 *namep = vim_strsave(arg);
1380 else
1381 *namep = NULL;
1382 did_change = TRUE;
1383 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001384# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001385 }
1386# endif
1387 }
1388#endif
1389 }
1390 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1391 {
1392 char_u buf[100];
1393 char_u *tname;
1394
1395 if (!init)
1396 HL_TABLE()[idx].sg_set |= SG_TERM;
1397
1398 /*
1399 * The "start" and "stop" arguments can be a literal escape
1400 * sequence, or a comma separated list of terminal codes.
1401 */
1402 if (STRNCMP(arg, "t_", 2) == 0)
1403 {
1404 off = 0;
1405 buf[0] = 0;
1406 while (arg[off] != NUL)
1407 {
1408 // Isolate one termcap name
1409 for (len = 0; arg[off + len] &&
1410 arg[off + len] != ','; ++len)
1411 ;
1412 tname = vim_strnsave(arg + off, len);
1413 if (tname == NULL) // out of memory
1414 {
1415 error = TRUE;
1416 break;
1417 }
1418 // lookup the escape sequence for the item
1419 p = get_term_code(tname);
1420 vim_free(tname);
1421 if (p == NULL) // ignore non-existing things
1422 p = (char_u *)"";
1423
1424 // Append it to the already found stuff
1425 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1426 {
1427 semsg(_("E422: terminal code too long: %s"), arg);
1428 error = TRUE;
1429 break;
1430 }
1431 STRCAT(buf, p);
1432
1433 // Advance to the next item
1434 off += len;
1435 if (arg[off] == ',') // another one follows
1436 ++off;
1437 }
1438 }
1439 else
1440 {
1441 /*
1442 * Copy characters from arg[] to buf[], translating <> codes.
1443 */
1444 for (p = arg, off = 0; off < 100 - 6 && *p; )
1445 {
Bram Moolenaarebe9d342020-05-30 21:52:54 +02001446 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001447 if (len > 0) // recognized special char
1448 off += len;
1449 else // copy as normal char
1450 buf[off++] = *p++;
1451 }
1452 buf[off] = NUL;
1453 }
1454 if (error)
1455 break;
1456
1457 if (STRCMP(buf, "NONE") == 0) // resetting the value
1458 p = NULL;
1459 else
1460 p = vim_strsave(buf);
1461 if (key[2] == 'A')
1462 {
1463 vim_free(HL_TABLE()[idx].sg_start);
1464 HL_TABLE()[idx].sg_start = p;
1465 }
1466 else
1467 {
1468 vim_free(HL_TABLE()[idx].sg_stop);
1469 HL_TABLE()[idx].sg_stop = p;
1470 }
1471 }
1472 else
1473 {
1474 semsg(_("E423: Illegal argument: %s"), key_start);
1475 error = TRUE;
1476 break;
1477 }
1478 HL_TABLE()[idx].sg_cleared = FALSE;
1479
1480 /*
1481 * When highlighting has been given for a group, don't link it.
1482 */
1483 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1484 HL_TABLE()[idx].sg_link = 0;
1485
1486 /*
1487 * Continue with next argument.
1488 */
1489 linep = skipwhite(linep);
1490 }
1491
1492 /*
1493 * If there is an error, and it's a new entry, remove it from the table.
1494 */
1495 if (error && idx == highlight_ga.ga_len)
1496 syn_unadd_group();
1497 else
1498 {
1499 if (is_normal_group)
1500 {
1501 HL_TABLE()[idx].sg_term_attr = 0;
1502 HL_TABLE()[idx].sg_cterm_attr = 0;
1503#ifdef FEAT_GUI
1504 HL_TABLE()[idx].sg_gui_attr = 0;
1505 /*
1506 * Need to update all groups, because they might be using "bg"
1507 * and/or "fg", which have been changed now.
1508 */
1509#endif
1510#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1511 if (USE_24BIT)
1512 {
1513 highlight_gui_started();
1514 did_highlight_changed = TRUE;
1515 redraw_all_later(NOT_VALID);
1516 }
1517#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001518#ifdef FEAT_VTP
1519 control_console_color_rgb();
1520#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001521 }
1522#ifdef FEAT_TERMINAL
1523 else if (is_terminal_group)
1524 set_terminal_default_colors(
1525 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
1526#endif
1527#ifdef FEAT_GUI_X11
1528# ifdef FEAT_MENU
1529 else if (is_menu_group)
1530 {
1531 if (gui.in_use && do_colors)
1532 gui_mch_new_menu_colors();
1533 }
1534# endif
1535 else if (is_scrollbar_group)
1536 {
1537 if (gui.in_use && do_colors)
1538 gui_new_scrollbar_colors();
1539 else
1540 set_hl_attr(idx);
1541 }
1542# ifdef FEAT_BEVAL_GUI
1543 else if (is_tooltip_group)
1544 {
1545 if (gui.in_use && do_colors)
1546 gui_mch_new_tooltip_colors();
1547 }
1548# endif
1549#endif
1550 else
1551 set_hl_attr(idx);
1552#ifdef FEAT_EVAL
1553 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001554 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001555#endif
1556 }
1557
1558 vim_free(key);
1559 vim_free(arg);
1560
1561 // Only call highlight_changed() once, after a sequence of highlight
1562 // commands, and only if an attribute actually changed.
1563 if ((did_change
1564 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1565#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1566 && !did_highlight_changed
1567#endif
1568 )
1569 {
1570 // Do not trigger a redraw when highlighting is changed while
1571 // redrawing. This may happen when evaluating 'statusline' changes the
1572 // StatusLine group.
1573 if (!updating_screen)
1574 redraw_all_later(NOT_VALID);
1575 need_highlight_changed = TRUE;
1576 }
1577}
1578
1579#if defined(EXITFREE) || defined(PROTO)
1580 void
1581free_highlight(void)
1582{
1583 int i;
1584
1585 for (i = 0; i < highlight_ga.ga_len; ++i)
1586 {
1587 highlight_clear(i);
1588 vim_free(HL_TABLE()[i].sg_name);
1589 vim_free(HL_TABLE()[i].sg_name_u);
1590 }
1591 ga_clear(&highlight_ga);
1592}
1593#endif
1594
1595/*
1596 * Reset the cterm colors to what they were before Vim was started, if
1597 * possible. Otherwise reset them to zero.
1598 */
1599 void
1600restore_cterm_colors(void)
1601{
1602#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1603 // Since t_me has been set, this probably means that the user
1604 // wants to use this as default colors. Need to reset default
1605 // background/foreground colors.
1606 mch_set_normal_colors();
1607#else
1608# ifdef VIMDLL
1609 if (!gui.in_use)
1610 {
1611 mch_set_normal_colors();
1612 return;
1613 }
1614# endif
1615 cterm_normal_fg_color = 0;
1616 cterm_normal_fg_bold = 0;
1617 cterm_normal_bg_color = 0;
1618# ifdef FEAT_TERMGUICOLORS
1619 cterm_normal_fg_gui_color = INVALCOLOR;
1620 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001621 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001622# endif
1623#endif
1624}
1625
1626/*
1627 * Return TRUE if highlight group "idx" has any settings.
1628 * When "check_link" is TRUE also check for an existing link.
1629 */
1630 static int
1631hl_has_settings(int idx, int check_link)
1632{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001633 return HL_TABLE()[idx].sg_cleared == 0
1634 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001635 || HL_TABLE()[idx].sg_cterm_attr != 0
1636 || HL_TABLE()[idx].sg_cterm_fg != 0
1637 || HL_TABLE()[idx].sg_cterm_bg != 0
1638#ifdef FEAT_GUI
1639 || HL_TABLE()[idx].sg_gui_attr != 0
1640 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1641 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1642 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1643 || HL_TABLE()[idx].sg_font_name != NULL
1644#endif
1645 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1646}
1647
1648/*
1649 * Clear highlighting for one group.
1650 */
1651 static void
1652highlight_clear(int idx)
1653{
1654 HL_TABLE()[idx].sg_cleared = TRUE;
1655
1656 HL_TABLE()[idx].sg_term = 0;
1657 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1658 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1659 HL_TABLE()[idx].sg_term_attr = 0;
1660 HL_TABLE()[idx].sg_cterm = 0;
1661 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1662 HL_TABLE()[idx].sg_cterm_fg = 0;
1663 HL_TABLE()[idx].sg_cterm_bg = 0;
1664 HL_TABLE()[idx].sg_cterm_attr = 0;
1665#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1666 HL_TABLE()[idx].sg_gui = 0;
1667 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1668 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1669 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1670#endif
1671#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1672 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1673 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001674 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001675#endif
1676#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001677 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1678 HL_TABLE()[idx].sg_font = NOFONT;
1679# ifdef FEAT_XFONTSET
1680 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1681 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1682# endif
1683 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1684 HL_TABLE()[idx].sg_gui_attr = 0;
1685#endif
1686#ifdef FEAT_EVAL
1687 // Clear the script ID only when there is no link, since that is not
1688 // cleared.
1689 if (HL_TABLE()[idx].sg_link == 0)
1690 {
1691 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
1692 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
1693 }
1694#endif
1695}
1696
1697#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1698/*
1699 * Set the normal foreground and background colors according to the "Normal"
1700 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1701 * "Tooltip" colors.
1702 */
1703 void
1704set_normal_colors(void)
1705{
1706# ifdef FEAT_GUI
1707# ifdef FEAT_TERMGUICOLORS
1708 if (gui.in_use)
1709# endif
1710 {
1711 if (set_group_colors((char_u *)"Normal",
1712 &gui.norm_pixel, &gui.back_pixel,
1713 FALSE, TRUE, FALSE))
1714 {
1715 gui_mch_new_colors();
1716 must_redraw = CLEAR;
1717 }
1718# ifdef FEAT_GUI_X11
1719 if (set_group_colors((char_u *)"Menu",
1720 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1721 TRUE, FALSE, FALSE))
1722 {
1723# ifdef FEAT_MENU
1724 gui_mch_new_menu_colors();
1725# endif
1726 must_redraw = CLEAR;
1727 }
1728# ifdef FEAT_BEVAL_GUI
1729 if (set_group_colors((char_u *)"Tooltip",
1730 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1731 FALSE, FALSE, TRUE))
1732 {
1733# ifdef FEAT_TOOLBAR
1734 gui_mch_new_tooltip_colors();
1735# endif
1736 must_redraw = CLEAR;
1737 }
1738# endif
1739 if (set_group_colors((char_u *)"Scrollbar",
1740 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1741 FALSE, FALSE, FALSE))
1742 {
1743 gui_new_scrollbar_colors();
1744 must_redraw = CLEAR;
1745 }
1746# endif
1747 }
1748# endif
1749# ifdef FEAT_TERMGUICOLORS
1750# ifdef FEAT_GUI
1751 else
1752# endif
1753 {
1754 int idx;
1755
1756 idx = syn_name2id((char_u *)"Normal") - 1;
1757 if (idx >= 0)
1758 {
1759 gui_do_one_color(idx, FALSE, FALSE);
1760
1761 // If the normal fg or bg color changed a complete redraw is
1762 // required.
1763 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1764 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1765 {
1766 // if the GUI color is INVALCOLOR then we use the default cterm
1767 // color
1768 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1769 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1770 must_redraw = CLEAR;
1771 }
1772 }
1773 }
1774# endif
1775}
1776#endif
1777
1778#if defined(FEAT_GUI) || defined(PROTO)
1779/*
1780 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1781 */
1782 static int
1783set_group_colors(
1784 char_u *name,
1785 guicolor_T *fgp,
1786 guicolor_T *bgp,
1787 int do_menu,
1788 int use_norm,
1789 int do_tooltip)
1790{
1791 int idx;
1792
1793 idx = syn_name2id(name) - 1;
1794 if (idx >= 0)
1795 {
1796 gui_do_one_color(idx, do_menu, do_tooltip);
1797
1798 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1799 *fgp = HL_TABLE()[idx].sg_gui_fg;
1800 else if (use_norm)
1801 *fgp = gui.def_norm_pixel;
1802 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1803 *bgp = HL_TABLE()[idx].sg_gui_bg;
1804 else if (use_norm)
1805 *bgp = gui.def_back_pixel;
1806 return TRUE;
1807 }
1808 return FALSE;
1809}
1810
1811/*
1812 * Get the font of the "Normal" group.
1813 * Returns "" when it's not found or not set.
1814 */
1815 char_u *
1816hl_get_font_name(void)
1817{
1818 int id;
1819 char_u *s;
1820
1821 id = syn_name2id((char_u *)"Normal");
1822 if (id > 0)
1823 {
1824 s = HL_TABLE()[id - 1].sg_font_name;
1825 if (s != NULL)
1826 return s;
1827 }
1828 return (char_u *)"";
1829}
1830
1831/*
1832 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
1833 * actually chosen to be used.
1834 */
1835 void
1836hl_set_font_name(char_u *font_name)
1837{
1838 int id;
1839
1840 id = syn_name2id((char_u *)"Normal");
1841 if (id > 0)
1842 {
1843 vim_free(HL_TABLE()[id - 1].sg_font_name);
1844 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
1845 }
1846}
1847
1848/*
1849 * Set background color for "Normal" group. Called by gui_set_bg_color()
1850 * when the color is known.
1851 */
1852 void
1853hl_set_bg_color_name(
1854 char_u *name) // must have been allocated
1855{
1856 int id;
1857
1858 if (name != NULL)
1859 {
1860 id = syn_name2id((char_u *)"Normal");
1861 if (id > 0)
1862 {
1863 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
1864 HL_TABLE()[id - 1].sg_gui_bg_name = name;
1865 }
1866 }
1867}
1868
1869/*
1870 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
1871 * when the color is known.
1872 */
1873 void
1874hl_set_fg_color_name(
1875 char_u *name) // must have been allocated
1876{
1877 int id;
1878
1879 if (name != NULL)
1880 {
1881 id = syn_name2id((char_u *)"Normal");
1882 if (id > 0)
1883 {
1884 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
1885 HL_TABLE()[id - 1].sg_gui_fg_name = name;
1886 }
1887 }
1888}
1889
1890/*
1891 * Return the handle for a font name.
1892 * Returns NOFONT when failed.
1893 */
1894 static GuiFont
1895font_name2handle(char_u *name)
1896{
1897 if (STRCMP(name, "NONE") == 0)
1898 return NOFONT;
1899
1900 return gui_mch_get_font(name, TRUE);
1901}
1902
1903# ifdef FEAT_XFONTSET
1904/*
1905 * Return the handle for a fontset name.
1906 * Returns NOFONTSET when failed.
1907 */
1908 static GuiFontset
1909fontset_name2handle(char_u *name, int fixed_width)
1910{
1911 if (STRCMP(name, "NONE") == 0)
1912 return NOFONTSET;
1913
1914 return gui_mch_get_fontset(name, TRUE, fixed_width);
1915}
1916# endif
1917
1918/*
1919 * Get the font or fontset for one highlight group.
1920 */
1921 static void
1922hl_do_font(
1923 int idx,
1924 char_u *arg,
1925 int do_normal, // set normal font
1926 int do_menu UNUSED, // set menu font
1927 int do_tooltip UNUSED, // set tooltip font
1928 int free_font) // free current font/fontset
1929{
1930# ifdef FEAT_XFONTSET
1931 // If 'guifontset' is not empty, first try using the name as a
1932 // fontset. If that doesn't work, use it as a font name.
1933 if (*p_guifontset != NUL
1934# ifdef FONTSET_ALWAYS
1935 || do_menu
1936# endif
1937# ifdef FEAT_BEVAL_TIP
1938 // In Athena & Motif, the Tooltip highlight group is always a fontset
1939 || do_tooltip
1940# endif
1941 )
1942 {
1943 if (free_font)
1944 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1945 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
1946# ifdef FONTSET_ALWAYS
1947 || do_menu
1948# endif
1949# ifdef FEAT_BEVAL_TIP
1950 || do_tooltip
1951# endif
1952 );
1953 }
1954 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1955 {
1956 // If it worked and it's the Normal group, use it as the normal
1957 // fontset. Same for the Menu group.
1958 if (do_normal)
1959 gui_init_font(arg, TRUE);
1960# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1961 if (do_menu)
1962 {
1963# ifdef FONTSET_ALWAYS
1964 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
1965# else
1966 // YIKES! This is a bug waiting to crash the program
1967 gui.menu_font = HL_TABLE()[idx].sg_fontset;
1968# endif
1969 gui_mch_new_menu_font();
1970 }
1971# ifdef FEAT_BEVAL_GUI
1972 if (do_tooltip)
1973 {
1974 // The Athena widget set cannot currently handle switching between
1975 // displaying a single font and a fontset.
1976 // If the XtNinternational resource is set to True at widget
1977 // creation, then a fontset is always used, otherwise an
1978 // XFontStruct is used.
1979 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
1980 gui_mch_new_tooltip_font();
1981 }
1982# endif
1983# endif
1984 }
1985 else
1986# endif
1987 {
1988 if (free_font)
1989 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1990 HL_TABLE()[idx].sg_font = font_name2handle(arg);
1991 // If it worked and it's the Normal group, use it as the
1992 // normal font. Same for the Menu group.
1993 if (HL_TABLE()[idx].sg_font != NOFONT)
1994 {
1995 if (do_normal)
1996 gui_init_font(arg, FALSE);
1997#ifndef FONTSET_ALWAYS
1998# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1999 if (do_menu)
2000 {
2001 gui.menu_font = HL_TABLE()[idx].sg_font;
2002 gui_mch_new_menu_font();
2003 }
2004# endif
2005#endif
2006 }
2007 }
2008}
2009
2010#endif // FEAT_GUI
2011
2012#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2013/*
2014 * Return the handle for a color name.
2015 * Returns INVALCOLOR when failed.
2016 */
2017 guicolor_T
2018color_name2handle(char_u *name)
2019{
2020 if (STRCMP(name, "NONE") == 0)
2021 return INVALCOLOR;
2022
2023 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2024 {
2025#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2026 if (gui.in_use)
2027#endif
2028#ifdef FEAT_GUI
2029 return gui.norm_pixel;
2030#endif
2031#ifdef FEAT_TERMGUICOLORS
2032 if (cterm_normal_fg_gui_color != INVALCOLOR)
2033 return cterm_normal_fg_gui_color;
2034 // Guess that the foreground is black or white.
2035 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2036#endif
2037 }
2038 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2039 {
2040#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2041 if (gui.in_use)
2042#endif
2043#ifdef FEAT_GUI
2044 return gui.back_pixel;
2045#endif
2046#ifdef FEAT_TERMGUICOLORS
2047 if (cterm_normal_bg_gui_color != INVALCOLOR)
2048 return cterm_normal_bg_gui_color;
2049 // Guess that the background is white or black.
2050 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2051#endif
2052 }
2053
2054 return GUI_GET_COLOR(name);
2055}
2056#endif
2057
2058/*
2059 * Table with the specifications for an attribute number.
2060 * Note that this table is used by ALL buffers. This is required because the
2061 * GUI can redraw at any time for any buffer.
2062 */
2063static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2064
2065#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2066
2067static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2068
2069#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2070
2071#ifdef FEAT_GUI
2072static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2073
2074#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2075#endif
2076
2077/*
2078 * Return the attr number for a set of colors and font.
2079 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2080 * if the combination is new.
2081 * Return 0 for error (no more room).
2082 */
2083 static int
2084get_attr_entry(garray_T *table, attrentry_T *aep)
2085{
2086 int i;
2087 attrentry_T *taep;
2088 static int recursive = FALSE;
2089
2090 /*
2091 * Init the table, in case it wasn't done yet.
2092 */
2093 table->ga_itemsize = sizeof(attrentry_T);
2094 table->ga_growsize = 7;
2095
2096 /*
2097 * Try to find an entry with the same specifications.
2098 */
2099 for (i = 0; i < table->ga_len; ++i)
2100 {
2101 taep = &(((attrentry_T *)table->ga_data)[i]);
2102 if ( aep->ae_attr == taep->ae_attr
2103 && (
2104#ifdef FEAT_GUI
2105 (table == &gui_attr_table
2106 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2107 && aep->ae_u.gui.bg_color
2108 == taep->ae_u.gui.bg_color
2109 && aep->ae_u.gui.sp_color
2110 == taep->ae_u.gui.sp_color
2111 && aep->ae_u.gui.font == taep->ae_u.gui.font
2112# ifdef FEAT_XFONTSET
2113 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2114# endif
2115 ))
2116 ||
2117#endif
2118 (table == &term_attr_table
2119 && (aep->ae_u.term.start == NULL)
2120 == (taep->ae_u.term.start == NULL)
2121 && (aep->ae_u.term.start == NULL
2122 || STRCMP(aep->ae_u.term.start,
2123 taep->ae_u.term.start) == 0)
2124 && (aep->ae_u.term.stop == NULL)
2125 == (taep->ae_u.term.stop == NULL)
2126 && (aep->ae_u.term.stop == NULL
2127 || STRCMP(aep->ae_u.term.stop,
2128 taep->ae_u.term.stop) == 0))
2129 || (table == &cterm_attr_table
2130 && aep->ae_u.cterm.fg_color
2131 == taep->ae_u.cterm.fg_color
2132 && aep->ae_u.cterm.bg_color
2133 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002134 && aep->ae_u.cterm.ul_color
2135 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002136#ifdef FEAT_TERMGUICOLORS
2137 && aep->ae_u.cterm.fg_rgb
2138 == taep->ae_u.cterm.fg_rgb
2139 && aep->ae_u.cterm.bg_rgb
2140 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002141 && aep->ae_u.cterm.ul_rgb
2142 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002143#endif
2144 )))
2145
2146 return i + ATTR_OFF;
2147 }
2148
2149 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2150 {
2151 /*
2152 * Running out of attribute entries! remove all attributes, and
2153 * compute new ones for all groups.
2154 * When called recursively, we are really out of numbers.
2155 */
2156 if (recursive)
2157 {
2158 emsg(_("E424: Too many different highlighting attributes in use"));
2159 return 0;
2160 }
2161 recursive = TRUE;
2162
2163 clear_hl_tables();
2164
2165 must_redraw = CLEAR;
2166
2167 for (i = 0; i < highlight_ga.ga_len; ++i)
2168 set_hl_attr(i);
2169
2170 recursive = FALSE;
2171 }
2172
2173 /*
2174 * This is a new combination of colors and font, add an entry.
2175 */
2176 if (ga_grow(table, 1) == FAIL)
2177 return 0;
2178
2179 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002180 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002181 taep->ae_attr = aep->ae_attr;
2182#ifdef FEAT_GUI
2183 if (table == &gui_attr_table)
2184 {
2185 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2186 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2187 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2188 taep->ae_u.gui.font = aep->ae_u.gui.font;
2189# ifdef FEAT_XFONTSET
2190 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2191# endif
2192 }
2193#endif
2194 if (table == &term_attr_table)
2195 {
2196 if (aep->ae_u.term.start == NULL)
2197 taep->ae_u.term.start = NULL;
2198 else
2199 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2200 if (aep->ae_u.term.stop == NULL)
2201 taep->ae_u.term.stop = NULL;
2202 else
2203 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2204 }
2205 else if (table == &cterm_attr_table)
2206 {
2207 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2208 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002209 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002210#ifdef FEAT_TERMGUICOLORS
2211 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2212 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002213 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002214#endif
2215 }
2216 ++table->ga_len;
2217 return (table->ga_len - 1 + ATTR_OFF);
2218}
2219
2220#if defined(FEAT_TERMINAL) || defined(PROTO)
2221/*
2222 * Get an attribute index for a cterm entry.
2223 * Uses an existing entry when possible or adds one when needed.
2224 */
2225 int
2226get_cterm_attr_idx(int attr, int fg, int bg)
2227{
2228 attrentry_T at_en;
2229
Bram Moolenaara80faa82020-04-12 19:37:17 +02002230 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002231#ifdef FEAT_TERMGUICOLORS
2232 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2233 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002234 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002235#endif
2236 at_en.ae_attr = attr;
2237 at_en.ae_u.cterm.fg_color = fg;
2238 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002239 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002240 return get_attr_entry(&cterm_attr_table, &at_en);
2241}
2242#endif
2243
2244#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2245/*
2246 * Get an attribute index for a 'termguicolors' entry.
2247 * Uses an existing entry when possible or adds one when needed.
2248 */
2249 int
2250get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2251{
2252 attrentry_T at_en;
2253
Bram Moolenaara80faa82020-04-12 19:37:17 +02002254 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002255 at_en.ae_attr = attr;
2256 if (fg == INVALCOLOR && bg == INVALCOLOR)
2257 {
2258 // If both GUI colors are not set fall back to the cterm colors. Helps
2259 // if the GUI only has an attribute, such as undercurl.
2260 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2261 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2262 }
2263 else
2264 {
2265 at_en.ae_u.cterm.fg_rgb = fg;
2266 at_en.ae_u.cterm.bg_rgb = bg;
2267 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002268 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002269 return get_attr_entry(&cterm_attr_table, &at_en);
2270}
2271#endif
2272
2273#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2274/*
2275 * Get an attribute index for a cterm entry.
2276 * Uses an existing entry when possible or adds one when needed.
2277 */
2278 int
2279get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2280{
2281 attrentry_T at_en;
2282
Bram Moolenaara80faa82020-04-12 19:37:17 +02002283 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002284 at_en.ae_attr = attr;
2285 at_en.ae_u.gui.fg_color = fg;
2286 at_en.ae_u.gui.bg_color = bg;
2287 return get_attr_entry(&gui_attr_table, &at_en);
2288}
2289#endif
2290
2291/*
2292 * Clear all highlight tables.
2293 */
2294 void
2295clear_hl_tables(void)
2296{
2297 int i;
2298 attrentry_T *taep;
2299
2300#ifdef FEAT_GUI
2301 ga_clear(&gui_attr_table);
2302#endif
2303 for (i = 0; i < term_attr_table.ga_len; ++i)
2304 {
2305 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2306 vim_free(taep->ae_u.term.start);
2307 vim_free(taep->ae_u.term.stop);
2308 }
2309 ga_clear(&term_attr_table);
2310 ga_clear(&cterm_attr_table);
2311}
2312
2313/*
2314 * Combine special attributes (e.g., for spelling) with other attributes
2315 * (e.g., for syntax highlighting).
2316 * "prim_attr" overrules "char_attr".
2317 * This creates a new group when required.
2318 * Since we expect there to be few spelling mistakes we don't cache the
2319 * result.
2320 * Return the resulting attributes.
2321 */
2322 int
2323hl_combine_attr(int char_attr, int prim_attr)
2324{
2325 attrentry_T *char_aep = NULL;
2326 attrentry_T *spell_aep;
2327 attrentry_T new_en;
2328
2329 if (char_attr == 0)
2330 return prim_attr;
2331 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2332 return ATTR_COMBINE(char_attr, prim_attr);
2333#ifdef FEAT_GUI
2334 if (gui.in_use)
2335 {
2336 if (char_attr > HL_ALL)
2337 char_aep = syn_gui_attr2entry(char_attr);
2338 if (char_aep != NULL)
2339 new_en = *char_aep;
2340 else
2341 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002342 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002343 new_en.ae_u.gui.fg_color = INVALCOLOR;
2344 new_en.ae_u.gui.bg_color = INVALCOLOR;
2345 new_en.ae_u.gui.sp_color = INVALCOLOR;
2346 if (char_attr <= HL_ALL)
2347 new_en.ae_attr = char_attr;
2348 }
2349
2350 if (prim_attr <= HL_ALL)
2351 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2352 else
2353 {
2354 spell_aep = syn_gui_attr2entry(prim_attr);
2355 if (spell_aep != NULL)
2356 {
2357 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2358 spell_aep->ae_attr);
2359 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2360 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2361 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2362 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2363 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2364 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2365 if (spell_aep->ae_u.gui.font != NOFONT)
2366 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2367# ifdef FEAT_XFONTSET
2368 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2369 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2370# endif
2371 }
2372 }
2373 return get_attr_entry(&gui_attr_table, &new_en);
2374 }
2375#endif
2376
2377 if (IS_CTERM)
2378 {
2379 if (char_attr > HL_ALL)
2380 char_aep = syn_cterm_attr2entry(char_attr);
2381 if (char_aep != NULL)
2382 new_en = *char_aep;
2383 else
2384 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002385 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002386#ifdef FEAT_TERMGUICOLORS
2387 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2388 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002389 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002390#endif
2391 if (char_attr <= HL_ALL)
2392 new_en.ae_attr = char_attr;
2393 }
2394
2395 if (prim_attr <= HL_ALL)
2396 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2397 else
2398 {
2399 spell_aep = syn_cterm_attr2entry(prim_attr);
2400 if (spell_aep != NULL)
2401 {
2402 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2403 spell_aep->ae_attr);
2404 if (spell_aep->ae_u.cterm.fg_color > 0)
2405 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2406 if (spell_aep->ae_u.cterm.bg_color > 0)
2407 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002408 if (spell_aep->ae_u.cterm.ul_color > 0)
2409 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002410#ifdef FEAT_TERMGUICOLORS
2411 // If both fg and bg are not set fall back to cterm colors.
2412 // Helps for SpellBad which uses undercurl in the GUI.
2413 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2414 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2415 {
2416 if (spell_aep->ae_u.cterm.fg_color > 0)
2417 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2418 if (spell_aep->ae_u.cterm.bg_color > 0)
2419 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2420 }
2421 else
2422 {
2423 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2424 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2425 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2426 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2427 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002428 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2429 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002430#endif
2431 }
2432 }
2433 return get_attr_entry(&cterm_attr_table, &new_en);
2434 }
2435
2436 if (char_attr > HL_ALL)
2437 char_aep = syn_term_attr2entry(char_attr);
2438 if (char_aep != NULL)
2439 new_en = *char_aep;
2440 else
2441 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002442 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002443 if (char_attr <= HL_ALL)
2444 new_en.ae_attr = char_attr;
2445 }
2446
2447 if (prim_attr <= HL_ALL)
2448 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2449 else
2450 {
2451 spell_aep = syn_term_attr2entry(prim_attr);
2452 if (spell_aep != NULL)
2453 {
2454 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2455 if (spell_aep->ae_u.term.start != NULL)
2456 {
2457 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2458 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2459 }
2460 }
2461 }
2462 return get_attr_entry(&term_attr_table, &new_en);
2463}
2464
2465#ifdef FEAT_GUI
2466 attrentry_T *
2467syn_gui_attr2entry(int attr)
2468{
2469 attr -= ATTR_OFF;
2470 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2471 return NULL;
2472 return &(GUI_ATTR_ENTRY(attr));
2473}
2474#endif
2475
2476/*
2477 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2478 * Only to be used when "attr" > HL_ALL.
2479 */
2480 int
2481syn_attr2attr(int attr)
2482{
2483 attrentry_T *aep;
2484
2485#ifdef FEAT_GUI
2486 if (gui.in_use)
2487 aep = syn_gui_attr2entry(attr);
2488 else
2489#endif
2490 if (IS_CTERM)
2491 aep = syn_cterm_attr2entry(attr);
2492 else
2493 aep = syn_term_attr2entry(attr);
2494
2495 if (aep == NULL) // highlighting not set
2496 return 0;
2497 return aep->ae_attr;
2498}
2499
2500
2501 attrentry_T *
2502syn_term_attr2entry(int attr)
2503{
2504 attr -= ATTR_OFF;
2505 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2506 return NULL;
2507 return &(TERM_ATTR_ENTRY(attr));
2508}
2509
2510 attrentry_T *
2511syn_cterm_attr2entry(int attr)
2512{
2513 attr -= ATTR_OFF;
2514 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2515 return NULL;
2516 return &(CTERM_ATTR_ENTRY(attr));
2517}
2518
2519#define LIST_ATTR 1
2520#define LIST_STRING 2
2521#define LIST_INT 3
2522
2523 static void
2524highlight_list_one(int id)
2525{
2526 hl_group_T *sgp;
2527 int didh = FALSE;
2528
2529 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2530
2531 if (message_filtered(sgp->sg_name))
2532 return;
2533
2534 didh = highlight_list_arg(id, didh, LIST_ATTR,
2535 sgp->sg_term, NULL, "term");
2536 didh = highlight_list_arg(id, didh, LIST_STRING,
2537 0, sgp->sg_start, "start");
2538 didh = highlight_list_arg(id, didh, LIST_STRING,
2539 0, sgp->sg_stop, "stop");
2540
2541 didh = highlight_list_arg(id, didh, LIST_ATTR,
2542 sgp->sg_cterm, NULL, "cterm");
2543 didh = highlight_list_arg(id, didh, LIST_INT,
2544 sgp->sg_cterm_fg, NULL, "ctermfg");
2545 didh = highlight_list_arg(id, didh, LIST_INT,
2546 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002547 didh = highlight_list_arg(id, didh, LIST_INT,
2548 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002549
2550#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2551 didh = highlight_list_arg(id, didh, LIST_ATTR,
2552 sgp->sg_gui, NULL, "gui");
2553 didh = highlight_list_arg(id, didh, LIST_STRING,
2554 0, sgp->sg_gui_fg_name, "guifg");
2555 didh = highlight_list_arg(id, didh, LIST_STRING,
2556 0, sgp->sg_gui_bg_name, "guibg");
2557 didh = highlight_list_arg(id, didh, LIST_STRING,
2558 0, sgp->sg_gui_sp_name, "guisp");
2559#endif
2560#ifdef FEAT_GUI
2561 didh = highlight_list_arg(id, didh, LIST_STRING,
2562 0, sgp->sg_font_name, "font");
2563#endif
2564
2565 if (sgp->sg_link && !got_int)
2566 {
2567 (void)syn_list_header(didh, 9999, id);
2568 didh = TRUE;
2569 msg_puts_attr("links to", HL_ATTR(HLF_D));
2570 msg_putchar(' ');
2571 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2572 }
2573
2574 if (!didh)
2575 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2576#ifdef FEAT_EVAL
2577 if (p_verbose > 0)
2578 last_set_msg(sgp->sg_script_ctx);
2579#endif
2580}
2581
2582 static int
2583highlight_list_arg(
2584 int id,
2585 int didh,
2586 int type,
2587 int iarg,
2588 char_u *sarg,
2589 char *name)
2590{
2591 char_u buf[100];
2592 char_u *ts;
2593 int i;
2594
2595 if (got_int)
2596 return FALSE;
2597 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2598 {
2599 ts = buf;
2600 if (type == LIST_INT)
2601 sprintf((char *)buf, "%d", iarg - 1);
2602 else if (type == LIST_STRING)
2603 ts = sarg;
2604 else // type == LIST_ATTR
2605 {
2606 buf[0] = NUL;
2607 for (i = 0; hl_attr_table[i] != 0; ++i)
2608 {
2609 if (iarg & hl_attr_table[i])
2610 {
2611 if (buf[0] != NUL)
2612 vim_strcat(buf, (char_u *)",", 100);
2613 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2614 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2615 }
2616 }
2617 }
2618
2619 (void)syn_list_header(didh,
2620 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2621 didh = TRUE;
2622 if (!got_int)
2623 {
2624 if (*name != NUL)
2625 {
2626 msg_puts_attr(name, HL_ATTR(HLF_D));
2627 msg_puts_attr("=", HL_ATTR(HLF_D));
2628 }
2629 msg_outtrans(ts);
2630 }
2631 }
2632 return didh;
2633}
2634
2635#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2636/*
2637 * Return "1" if highlight group "id" has attribute "flag".
2638 * Return NULL otherwise.
2639 */
2640 char_u *
2641highlight_has_attr(
2642 int id,
2643 int flag,
2644 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2645{
2646 int attr;
2647
2648 if (id <= 0 || id > highlight_ga.ga_len)
2649 return NULL;
2650
2651#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2652 if (modec == 'g')
2653 attr = HL_TABLE()[id - 1].sg_gui;
2654 else
2655#endif
2656 if (modec == 'c')
2657 attr = HL_TABLE()[id - 1].sg_cterm;
2658 else
2659 attr = HL_TABLE()[id - 1].sg_term;
2660
2661 if (attr & flag)
2662 return (char_u *)"1";
2663 return NULL;
2664}
2665#endif
2666
2667#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2668/*
2669 * Return color name of highlight group "id".
2670 */
2671 char_u *
2672highlight_color(
2673 int id,
2674 char_u *what, // "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#"
2675 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2676{
2677 static char_u name[20];
2678 int n;
2679 int fg = FALSE;
2680 int sp = FALSE;
2681 int font = FALSE;
2682
2683 if (id <= 0 || id > highlight_ga.ga_len)
2684 return NULL;
2685
2686 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2687 fg = TRUE;
2688 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2689 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2690 font = TRUE;
2691 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2692 sp = TRUE;
2693 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2694 return NULL;
2695 if (modec == 'g')
2696 {
2697# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2698# ifdef FEAT_GUI
2699 // return font name
2700 if (font)
2701 return HL_TABLE()[id - 1].sg_font_name;
2702# endif
2703
2704 // return #RRGGBB form (only possible when GUI is running)
2705 if ((USE_24BIT) && what[2] == '#')
2706 {
2707 guicolor_T color;
2708 long_u rgb;
2709 static char_u buf[10];
2710
2711 if (fg)
2712 color = HL_TABLE()[id - 1].sg_gui_fg;
2713 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002714 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002715 else
2716 color = HL_TABLE()[id - 1].sg_gui_bg;
2717 if (color == INVALCOLOR)
2718 return NULL;
2719 rgb = (long_u)GUI_MCH_GET_RGB(color);
2720 sprintf((char *)buf, "#%02x%02x%02x",
2721 (unsigned)(rgb >> 16),
2722 (unsigned)(rgb >> 8) & 255,
2723 (unsigned)rgb & 255);
2724 return buf;
2725 }
2726# endif
2727 if (fg)
2728 return (HL_TABLE()[id - 1].sg_gui_fg_name);
2729 if (sp)
2730 return (HL_TABLE()[id - 1].sg_gui_sp_name);
2731 return (HL_TABLE()[id - 1].sg_gui_bg_name);
2732 }
2733 if (font || sp)
2734 return NULL;
2735 if (modec == 'c')
2736 {
2737 if (fg)
2738 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
2739 else
2740 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2741 if (n < 0)
2742 return NULL;
2743 sprintf((char *)name, "%d", n);
2744 return name;
2745 }
2746 // term doesn't have color
2747 return NULL;
2748}
2749#endif
2750
2751#if (defined(FEAT_SYN_HL) \
2752 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2753 && defined(FEAT_PRINTER)) || defined(PROTO)
2754/*
2755 * Return color name of highlight group "id" as RGB value.
2756 */
2757 long_u
2758highlight_gui_color_rgb(
2759 int id,
2760 int fg) // TRUE = fg, FALSE = bg
2761{
2762 guicolor_T color;
2763
2764 if (id <= 0 || id > highlight_ga.ga_len)
2765 return 0L;
2766
2767 if (fg)
2768 color = HL_TABLE()[id - 1].sg_gui_fg;
2769 else
2770 color = HL_TABLE()[id - 1].sg_gui_bg;
2771
2772 if (color == INVALCOLOR)
2773 return 0L;
2774
2775 return GUI_MCH_GET_RGB(color);
2776}
2777#endif
2778
2779/*
2780 * Output the syntax list header.
2781 * Return TRUE when started a new line.
2782 */
2783 int
2784syn_list_header(
2785 int did_header, // did header already
2786 int outlen, // length of string that comes
2787 int id) // highlight group id
2788{
2789 int endcol = 19;
2790 int newline = TRUE;
2791 int name_col = 0;
2792
2793 if (!did_header)
2794 {
2795 msg_putchar('\n');
2796 if (got_int)
2797 return TRUE;
2798 msg_outtrans(HL_TABLE()[id - 1].sg_name);
2799 name_col = msg_col;
2800 endcol = 15;
2801 }
2802 else if (msg_col + outlen + 1 >= Columns)
2803 {
2804 msg_putchar('\n');
2805 if (got_int)
2806 return TRUE;
2807 }
2808 else
2809 {
2810 if (msg_col >= endcol) // wrap around is like starting a new line
2811 newline = FALSE;
2812 }
2813
2814 if (msg_col >= endcol) // output at least one space
2815 endcol = msg_col + 1;
2816 if (Columns <= endcol) // avoid hang for tiny window
2817 endcol = Columns - 1;
2818
2819 msg_advance(endcol);
2820
2821 // Show "xxx" with the attributes.
2822 if (!did_header)
2823 {
2824 if (endcol == Columns - 1 && endcol <= name_col)
2825 msg_putchar(' ');
2826 msg_puts_attr("xxx", syn_id2attr(id));
2827 msg_putchar(' ');
2828 }
2829
2830 return newline;
2831}
2832
2833/*
2834 * Set the attribute numbers for a highlight group.
2835 * Called after one of the attributes has changed.
2836 */
2837 static void
2838set_hl_attr(
2839 int idx) // index in array
2840{
2841 attrentry_T at_en;
2842 hl_group_T *sgp = HL_TABLE() + idx;
2843
2844 // The "Normal" group doesn't need an attribute number
2845 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
2846 return;
2847
2848#ifdef FEAT_GUI
2849 /*
2850 * For the GUI mode: If there are other than "normal" highlighting
2851 * attributes, need to allocate an attr number.
2852 */
2853 if (sgp->sg_gui_fg == INVALCOLOR
2854 && sgp->sg_gui_bg == INVALCOLOR
2855 && sgp->sg_gui_sp == INVALCOLOR
2856 && sgp->sg_font == NOFONT
2857# ifdef FEAT_XFONTSET
2858 && sgp->sg_fontset == NOFONTSET
2859# endif
2860 )
2861 {
2862 sgp->sg_gui_attr = sgp->sg_gui;
2863 }
2864 else
2865 {
2866 at_en.ae_attr = sgp->sg_gui;
2867 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
2868 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
2869 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
2870 at_en.ae_u.gui.font = sgp->sg_font;
2871# ifdef FEAT_XFONTSET
2872 at_en.ae_u.gui.fontset = sgp->sg_fontset;
2873# endif
2874 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
2875 }
2876#endif
2877 /*
2878 * For the term mode: If there are other than "normal" highlighting
2879 * attributes, need to allocate an attr number.
2880 */
2881 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
2882 sgp->sg_term_attr = sgp->sg_term;
2883 else
2884 {
2885 at_en.ae_attr = sgp->sg_term;
2886 at_en.ae_u.term.start = sgp->sg_start;
2887 at_en.ae_u.term.stop = sgp->sg_stop;
2888 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
2889 }
2890
2891 /*
2892 * For the color term mode: If there are other than "normal"
2893 * highlighting attributes, need to allocate an attr number.
2894 */
Bram Moolenaare023e882020-05-31 16:42:30 +02002895 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002896# ifdef FEAT_TERMGUICOLORS
2897 && sgp->sg_gui_fg == INVALCOLOR
2898 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02002899 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002900# endif
2901 )
2902 sgp->sg_cterm_attr = sgp->sg_cterm;
2903 else
2904 {
2905 at_en.ae_attr = sgp->sg_cterm;
2906 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
2907 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02002908 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002909# ifdef FEAT_TERMGUICOLORS
2910# ifdef MSWIN
2911# ifdef VIMDLL
2912 // Only when not using the GUI.
2913 if (!gui.in_use && !gui.starting)
2914# endif
2915 {
2916 int id;
2917 guicolor_T fg, bg;
2918
2919 id = syn_name2id((char_u *)"Normal");
2920 if (id > 0)
2921 {
2922 syn_id2colors(id, &fg, &bg);
2923 if (sgp->sg_gui_fg == INVALCOLOR)
2924 sgp->sg_gui_fg = fg;
2925 if (sgp->sg_gui_bg == INVALCOLOR)
2926 sgp->sg_gui_bg = bg;
2927 }
2928
2929 }
2930# endif
2931 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
2932 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02002933 // Only use the underline/undercurl color when used, it may clear the
2934 // background color if not supported.
2935 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
2936 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
2937 else
2938 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002939 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2940 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2941 {
2942 // If both fg and bg are invalid fall back to the cterm colors.
2943 // Helps when the GUI only uses an attribute, e.g. undercurl.
2944 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2945 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2946 }
2947# endif
2948 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2949 }
2950}
2951
2952/*
2953 * Lookup a highlight group name and return its ID.
2954 * If it is not found, 0 is returned.
2955 */
2956 int
2957syn_name2id(char_u *name)
2958{
2959 int i;
2960 char_u name_u[200];
2961
2962 // Avoid using stricmp() too much, it's slow on some systems
2963 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
2964 // don't deserve to be found!
2965 vim_strncpy(name_u, name, 199);
2966 vim_strup(name_u);
2967 for (i = highlight_ga.ga_len; --i >= 0; )
2968 if (HL_TABLE()[i].sg_name_u != NULL
2969 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2970 break;
2971 return i + 1;
2972}
2973
2974/*
2975 * Lookup a highlight group name and return its attributes.
2976 * Return zero if not found.
2977 */
2978 int
2979syn_name2attr(char_u *name)
2980{
2981 int id = syn_name2id(name);
2982
2983 if (id != 0)
2984 return syn_id2attr(id);
2985 return 0;
2986}
2987
2988#if defined(FEAT_EVAL) || defined(PROTO)
2989/*
2990 * Return TRUE if highlight group "name" exists.
2991 */
2992 int
2993highlight_exists(char_u *name)
2994{
2995 return (syn_name2id(name) > 0);
2996}
2997
2998# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
2999/*
3000 * Return the name of highlight group "id".
3001 * When not a valid ID return an empty string.
3002 */
3003 char_u *
3004syn_id2name(int id)
3005{
3006 if (id <= 0 || id > highlight_ga.ga_len)
3007 return (char_u *)"";
3008 return HL_TABLE()[id - 1].sg_name;
3009}
3010# endif
3011#endif
3012
3013/*
3014 * Like syn_name2id(), but take a pointer + length argument.
3015 */
3016 int
3017syn_namen2id(char_u *linep, int len)
3018{
3019 char_u *name;
3020 int id = 0;
3021
3022 name = vim_strnsave(linep, len);
3023 if (name != NULL)
3024 {
3025 id = syn_name2id(name);
3026 vim_free(name);
3027 }
3028 return id;
3029}
3030
3031/*
3032 * Find highlight group name in the table and return its ID.
3033 * The argument is a pointer to the name and the length of the name.
3034 * If it doesn't exist yet, a new entry is created.
3035 * Return 0 for failure.
3036 */
3037 int
3038syn_check_group(char_u *pp, int len)
3039{
3040 int id;
3041 char_u *name;
3042
3043 name = vim_strnsave(pp, len);
3044 if (name == NULL)
3045 return 0;
3046
3047 id = syn_name2id(name);
3048 if (id == 0) // doesn't exist yet
3049 id = syn_add_group(name);
3050 else
3051 vim_free(name);
3052 return id;
3053}
3054
3055/*
3056 * Add new highlight group and return its ID.
3057 * "name" must be an allocated string, it will be consumed.
3058 * Return 0 for failure.
3059 */
3060 static int
3061syn_add_group(char_u *name)
3062{
3063 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003064 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003065
3066 // Check that the name is ASCII letters, digits and underscore.
3067 for (p = name; *p != NUL; ++p)
3068 {
3069 if (!vim_isprintc(*p))
3070 {
3071 emsg(_("E669: Unprintable character in group name"));
3072 vim_free(name);
3073 return 0;
3074 }
3075 else if (!ASCII_ISALNUM(*p) && *p != '_')
3076 {
3077 // This is an error, but since there previously was no check only
3078 // give a warning.
3079 msg_source(HL_ATTR(HLF_W));
3080 msg(_("W18: Invalid character in group name"));
3081 break;
3082 }
3083 }
3084
3085 /*
3086 * First call for this growarray: init growing array.
3087 */
3088 if (highlight_ga.ga_data == NULL)
3089 {
3090 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3091 highlight_ga.ga_growsize = 10;
3092 }
3093
3094 if (highlight_ga.ga_len >= MAX_HL_ID)
3095 {
3096 emsg(_("E849: Too many highlight and syntax groups"));
3097 vim_free(name);
3098 return 0;
3099 }
3100
3101 /*
3102 * Make room for at least one other syntax_highlight entry.
3103 */
3104 if (ga_grow(&highlight_ga, 1) == FAIL)
3105 {
3106 vim_free(name);
3107 return 0;
3108 }
3109
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003110 name_up = vim_strsave_up(name);
3111 if (name_up == NULL)
3112 {
3113 vim_free(name);
3114 return 0;
3115 }
3116
Bram Moolenaara80faa82020-04-12 19:37:17 +02003117 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003118 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003119 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003120#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3121 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3122 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003123 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003124#endif
3125 ++highlight_ga.ga_len;
3126
3127 return highlight_ga.ga_len; // ID is index plus one
3128}
3129
3130/*
3131 * When, just after calling syn_add_group(), an error is discovered, this
3132 * function deletes the new name.
3133 */
3134 static void
3135syn_unadd_group(void)
3136{
3137 --highlight_ga.ga_len;
3138 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3139 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3140}
3141
3142/*
3143 * Translate a group ID to highlight attributes.
3144 */
3145 int
3146syn_id2attr(int hl_id)
3147{
3148 int attr;
3149 hl_group_T *sgp;
3150
3151 hl_id = syn_get_final_id(hl_id);
3152 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3153
3154#ifdef FEAT_GUI
3155 /*
3156 * Only use GUI attr when the GUI is being used.
3157 */
3158 if (gui.in_use)
3159 attr = sgp->sg_gui_attr;
3160 else
3161#endif
3162 if (IS_CTERM)
3163 attr = sgp->sg_cterm_attr;
3164 else
3165 attr = sgp->sg_term_attr;
3166
3167 return attr;
3168}
3169
3170#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3171/*
3172 * Get the GUI colors and attributes for a group ID.
3173 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3174 */
3175 int
3176syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3177{
3178 hl_group_T *sgp;
3179
3180 hl_id = syn_get_final_id(hl_id);
3181 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3182
3183 *fgp = sgp->sg_gui_fg;
3184 *bgp = sgp->sg_gui_bg;
3185 return sgp->sg_gui;
3186}
3187#endif
3188
3189#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003190 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3191 && defined(FEAT_TERMGUICOLORS)) \
3192 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003193 void
3194syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3195{
3196 hl_group_T *sgp;
3197
3198 hl_id = syn_get_final_id(hl_id);
3199 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3200 *fgp = sgp->sg_cterm_fg - 1;
3201 *bgp = sgp->sg_cterm_bg - 1;
3202}
3203#endif
3204
3205/*
3206 * Translate a group ID to the final group ID (following links).
3207 */
3208 int
3209syn_get_final_id(int hl_id)
3210{
3211 int count;
3212 hl_group_T *sgp;
3213
3214 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3215 return 0; // Can be called from eval!!
3216
3217 /*
3218 * Follow links until there is no more.
3219 * Look out for loops! Break after 100 links.
3220 */
3221 for (count = 100; --count >= 0; )
3222 {
3223 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3224 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3225 break;
3226 hl_id = sgp->sg_link;
3227 }
3228
3229 return hl_id;
3230}
3231
3232#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3233/*
3234 * Call this function just after the GUI has started.
3235 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3236 * It finds the font and color handles for the highlighting groups.
3237 */
3238 void
3239highlight_gui_started(void)
3240{
3241 int idx;
3242
3243 // First get the colors from the "Normal" and "Menu" group, if set
3244 if (USE_24BIT)
3245 set_normal_colors();
3246
3247 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3248 gui_do_one_color(idx, FALSE, FALSE);
3249
3250 highlight_changed();
3251}
3252
3253 static void
3254gui_do_one_color(
3255 int idx,
3256 int do_menu UNUSED, // TRUE: might set the menu font
3257 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3258{
3259 int didit = FALSE;
3260
3261# ifdef FEAT_GUI
3262# ifdef FEAT_TERMGUICOLORS
3263 if (gui.in_use)
3264# endif
3265 if (HL_TABLE()[idx].sg_font_name != NULL)
3266 {
3267 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3268 do_tooltip, TRUE);
3269 didit = TRUE;
3270 }
3271# endif
3272 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3273 {
3274 HL_TABLE()[idx].sg_gui_fg =
3275 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3276 didit = TRUE;
3277 }
3278 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3279 {
3280 HL_TABLE()[idx].sg_gui_bg =
3281 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3282 didit = TRUE;
3283 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003284 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3285 {
3286 HL_TABLE()[idx].sg_gui_sp =
3287 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3288 didit = TRUE;
3289 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003290 if (didit) // need to get a new attr number
3291 set_hl_attr(idx);
3292}
3293#endif
3294
3295#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3296/*
3297 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3298 */
3299 static void
3300combine_stl_hlt(
3301 int id,
3302 int id_S,
3303 int id_alt,
3304 int hlcnt,
3305 int i,
3306 int hlf,
3307 int *table)
3308{
3309 hl_group_T *hlt = HL_TABLE();
3310
3311 if (id_alt == 0)
3312 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003313 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003314 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3315 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3316# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3317 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3318# endif
3319 }
3320 else
3321 mch_memmove(&hlt[hlcnt + i],
3322 &hlt[id_alt - 1],
3323 sizeof(hl_group_T));
3324 hlt[hlcnt + i].sg_link = 0;
3325
3326 hlt[hlcnt + i].sg_term ^=
3327 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3328 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3329 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3330 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3331 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3332 hlt[hlcnt + i].sg_cterm ^=
3333 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3334 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3335 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3336 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3337 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3338# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3339 hlt[hlcnt + i].sg_gui ^=
3340 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3341# endif
3342# ifdef FEAT_GUI
3343 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3344 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3345 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3346 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3347 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3348 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3349 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3350 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3351# ifdef FEAT_XFONTSET
3352 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3353 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3354# endif
3355# endif
3356 highlight_ga.ga_len = hlcnt + i + 1;
3357 set_hl_attr(hlcnt + i); // At long last we can apply
3358 table[i] = syn_id2attr(hlcnt + i + 1);
3359}
3360#endif
3361
3362/*
3363 * Translate the 'highlight' option into attributes in highlight_attr[] and
3364 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3365 * corresponding highlights to use on top of HLF_SNC is computed.
3366 * Called only when the 'highlight' option has been changed and upon first
3367 * screen redraw after any :highlight command.
3368 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3369 */
3370 int
3371highlight_changed(void)
3372{
3373 int hlf;
3374 int i;
3375 char_u *p;
3376 int attr;
3377 char_u *end;
3378 int id;
3379#ifdef USER_HIGHLIGHT
3380 char_u userhl[30]; // use 30 to avoid compiler warning
3381# ifdef FEAT_STL_OPT
3382 int id_S = -1;
3383 int id_SNC = 0;
3384# ifdef FEAT_TERMINAL
3385 int id_ST = 0;
3386 int id_STNC = 0;
3387# endif
3388 int hlcnt;
3389# endif
3390#endif
3391 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3392
3393 need_highlight_changed = FALSE;
3394
3395 /*
3396 * Clear all attributes.
3397 */
3398 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3399 highlight_attr[hlf] = 0;
3400
3401 /*
3402 * First set all attributes to their default value.
3403 * Then use the attributes from the 'highlight' option.
3404 */
3405 for (i = 0; i < 2; ++i)
3406 {
3407 if (i)
3408 p = p_hl;
3409 else
3410 p = get_highlight_default();
3411 if (p == NULL) // just in case
3412 continue;
3413
3414 while (*p)
3415 {
3416 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3417 if (hl_flags[hlf] == *p)
3418 break;
3419 ++p;
3420 if (hlf == (int)HLF_COUNT || *p == NUL)
3421 return FAIL;
3422
3423 /*
3424 * Allow several hl_flags to be combined, like "bu" for
3425 * bold-underlined.
3426 */
3427 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003428 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003429 {
3430 if (VIM_ISWHITE(*p)) // ignore white space
3431 continue;
3432
3433 if (attr > HL_ALL) // Combination with ':' is not allowed.
3434 return FAIL;
3435
3436 switch (*p)
3437 {
3438 case 'b': attr |= HL_BOLD;
3439 break;
3440 case 'i': attr |= HL_ITALIC;
3441 break;
3442 case '-':
3443 case 'n': // no highlighting
3444 break;
3445 case 'r': attr |= HL_INVERSE;
3446 break;
3447 case 's': attr |= HL_STANDOUT;
3448 break;
3449 case 'u': attr |= HL_UNDERLINE;
3450 break;
3451 case 'c': attr |= HL_UNDERCURL;
3452 break;
3453 case 't': attr |= HL_STRIKETHROUGH;
3454 break;
3455 case ':': ++p; // highlight group name
3456 if (attr || *p == NUL) // no combinations
3457 return FAIL;
3458 end = vim_strchr(p, ',');
3459 if (end == NULL)
3460 end = p + STRLEN(p);
3461 id = syn_check_group(p, (int)(end - p));
3462 if (id == 0)
3463 return FAIL;
3464 attr = syn_id2attr(id);
3465 p = end - 1;
3466#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3467 if (hlf == (int)HLF_SNC)
3468 id_SNC = syn_get_final_id(id);
3469# ifdef FEAT_TERMINAL
3470 else if (hlf == (int)HLF_ST)
3471 id_ST = syn_get_final_id(id);
3472 else if (hlf == (int)HLF_STNC)
3473 id_STNC = syn_get_final_id(id);
3474# endif
3475 else if (hlf == (int)HLF_S)
3476 id_S = syn_get_final_id(id);
3477#endif
3478 break;
3479 default: return FAIL;
3480 }
3481 }
3482 highlight_attr[hlf] = attr;
3483
3484 p = skip_to_option_part(p); // skip comma and spaces
3485 }
3486 }
3487
3488#ifdef USER_HIGHLIGHT
3489 /*
3490 * Setup the user highlights
3491 *
3492 * Temporarily utilize 28 more hl entries:
3493 * 9 for User1-User9 combined with StatusLineNC
3494 * 9 for User1-User9 combined with StatusLineTerm
3495 * 9 for User1-User9 combined with StatusLineTermNC
3496 * 1 for StatusLine default
3497 * Have to be in there simultaneously in case of table overflows in
3498 * get_attr_entry()
3499 */
3500# ifdef FEAT_STL_OPT
3501 if (ga_grow(&highlight_ga, 28) == FAIL)
3502 return FAIL;
3503 hlcnt = highlight_ga.ga_len;
3504 if (id_S == -1)
3505 {
3506 // Make sure id_S is always valid to simplify code below. Use the last
3507 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003508 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003509 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3510 id_S = hlcnt + 19;
3511 }
3512# endif
3513 for (i = 0; i < 9; i++)
3514 {
3515 sprintf((char *)userhl, "User%d", i + 1);
3516 id = syn_name2id(userhl);
3517 if (id == 0)
3518 {
3519 highlight_user[i] = 0;
3520# ifdef FEAT_STL_OPT
3521 highlight_stlnc[i] = 0;
3522# ifdef FEAT_TERMINAL
3523 highlight_stlterm[i] = 0;
3524 highlight_stltermnc[i] = 0;
3525# endif
3526# endif
3527 }
3528 else
3529 {
3530 highlight_user[i] = syn_id2attr(id);
3531# ifdef FEAT_STL_OPT
3532 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3533 HLF_SNC, highlight_stlnc);
3534# ifdef FEAT_TERMINAL
3535 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3536 HLF_ST, highlight_stlterm);
3537 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3538 HLF_STNC, highlight_stltermnc);
3539# endif
3540# endif
3541 }
3542 }
3543# ifdef FEAT_STL_OPT
3544 highlight_ga.ga_len = hlcnt;
3545# endif
3546
3547#endif // USER_HIGHLIGHT
3548
3549 return OK;
3550}
3551
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003552static void highlight_list(void);
3553static void highlight_list_two(int cnt, int attr);
3554
3555/*
3556 * Handle command line completion for :highlight command.
3557 */
3558 void
3559set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3560{
3561 char_u *p;
3562
3563 // Default: expand group names
3564 xp->xp_context = EXPAND_HIGHLIGHT;
3565 xp->xp_pattern = arg;
3566 include_link = 2;
3567 include_default = 1;
3568
3569 // (part of) subcommand already typed
3570 if (*arg != NUL)
3571 {
3572 p = skiptowhite(arg);
3573 if (*p != NUL) // past "default" or group name
3574 {
3575 include_default = 0;
3576 if (STRNCMP("default", arg, p - arg) == 0)
3577 {
3578 arg = skipwhite(p);
3579 xp->xp_pattern = arg;
3580 p = skiptowhite(arg);
3581 }
3582 if (*p != NUL) // past group name
3583 {
3584 include_link = 0;
3585 if (arg[1] == 'i' && arg[0] == 'N')
3586 highlight_list();
3587 if (STRNCMP("link", arg, p - arg) == 0
3588 || STRNCMP("clear", arg, p - arg) == 0)
3589 {
3590 xp->xp_pattern = skipwhite(p);
3591 p = skiptowhite(xp->xp_pattern);
3592 if (*p != NUL) // past first group name
3593 {
3594 xp->xp_pattern = skipwhite(p);
3595 p = skiptowhite(xp->xp_pattern);
3596 }
3597 }
3598 if (*p != NUL) // past group name(s)
3599 xp->xp_context = EXPAND_NOTHING;
3600 }
3601 }
3602 }
3603}
3604
3605/*
3606 * List highlighting matches in a nice way.
3607 */
3608 static void
3609highlight_list(void)
3610{
3611 int i;
3612
3613 for (i = 10; --i >= 0; )
3614 highlight_list_two(i, HL_ATTR(HLF_D));
3615 for (i = 40; --i >= 0; )
3616 highlight_list_two(99, 0);
3617}
3618
3619 static void
3620highlight_list_two(int cnt, int attr)
3621{
3622 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3623 msg_clr_eos();
3624 out_flush();
3625 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3626}
3627
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003628/*
3629 * Function given to ExpandGeneric() to obtain the list of group names.
3630 */
3631 char_u *
3632get_highlight_name(expand_T *xp UNUSED, int idx)
3633{
3634 return get_highlight_name_ext(xp, idx, TRUE);
3635}
3636
3637/*
3638 * Obtain a highlight group name.
3639 * When "skip_cleared" is TRUE don't return a cleared entry.
3640 */
3641 char_u *
3642get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3643{
3644 if (idx < 0)
3645 return NULL;
3646
3647 // Items are never removed from the table, skip the ones that were
3648 // cleared.
3649 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3650 return (char_u *)"";
3651
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003652 if (idx == highlight_ga.ga_len && include_none != 0)
3653 return (char_u *)"none";
3654 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3655 return (char_u *)"default";
3656 if (idx == highlight_ga.ga_len + include_none + include_default
3657 && include_link != 0)
3658 return (char_u *)"link";
3659 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3660 && include_link != 0)
3661 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003662 if (idx >= highlight_ga.ga_len)
3663 return NULL;
3664 return HL_TABLE()[idx].sg_name;
3665}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003666
3667#if defined(FEAT_GUI) || defined(PROTO)
3668/*
3669 * Free all the highlight group fonts.
3670 * Used when quitting for systems which need it.
3671 */
3672 void
3673free_highlight_fonts(void)
3674{
3675 int idx;
3676
3677 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3678 {
3679 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3680 HL_TABLE()[idx].sg_font = NOFONT;
3681# ifdef FEAT_XFONTSET
3682 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3683 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3684# endif
3685 }
3686
3687 gui_mch_free_font(gui.norm_font);
3688# ifdef FEAT_XFONTSET
3689 gui_mch_free_fontset(gui.fontset);
3690# endif
3691# ifndef FEAT_GUI_GTK
3692 gui_mch_free_font(gui.bold_font);
3693 gui_mch_free_font(gui.ital_font);
3694 gui_mch_free_font(gui.boldital_font);
3695# endif
3696}
3697#endif