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