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