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