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