blob: 5dde08b8f3994d0257f94c19bbb57d1cea458bc3 [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
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100380 // Try finding the color scheme file. Used when a color file was loaded
381 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200382 p = get_var_value((char_u *)"g:colors_name");
383 if (p != NULL)
384 {
385 // The value of g:colors_name could be freed when sourcing the script,
386 // making "p" invalid, so copy it.
387 char_u *copy_p = vim_strsave(p);
388 int r;
389
390 if (copy_p != NULL)
391 {
392 r = load_colors(copy_p);
393 vim_free(copy_p);
394 if (r == OK)
395 return;
396 }
397 }
398
399#endif
400
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100401 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200402 if (both)
403 {
404 had_both = TRUE;
405 pp = highlight_init_both;
406 for (i = 0; pp[i] != NULL; ++i)
407 do_highlight((char_u *)pp[i], reset, TRUE);
408 }
409 else if (!had_both)
410 // Don't do anything before the call with both == TRUE from main().
411 // Not everything has been setup then, and that call will overrule
412 // everything anyway.
413 return;
414
415 if (*p_bg == 'l')
416 pp = highlight_init_light;
417 else
418 pp = highlight_init_dark;
419 for (i = 0; pp[i] != NULL; ++i)
420 do_highlight((char_u *)pp[i], reset, TRUE);
421
422 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
423 // depend on the number of colors available.
424 // With 8 colors brown is equal to yellow, need to use black for Search fg
425 // to avoid Statement highlighted text disappears.
426 // Clear the attributes, needed when changing the t_Co value.
427 if (t_colors > 8)
428 do_highlight((char_u *)(*p_bg == 'l'
429 ? "Visual cterm=NONE ctermbg=LightGrey"
430 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
431 else
432 {
433 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
434 FALSE, TRUE);
435 if (*p_bg == 'l')
436 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
437 }
438
439#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100440 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200441 if (get_var_value((char_u *)"g:syntax_on") != NULL)
442 {
443 static int recursive = 0;
444
445 if (recursive >= 5)
446 emsg(_("E679: recursive loop loading syncolor.vim"));
447 else
448 {
449 ++recursive;
450 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
451 --recursive;
452 }
453 }
454#endif
455}
456
457/*
458 * Load color file "name".
459 * Return OK for success, FAIL for failure.
460 */
461 int
462load_colors(char_u *name)
463{
464 char_u *buf;
465 int retval = FAIL;
466 static int recursive = FALSE;
467
468 // When being called recursively, this is probably because setting
469 // 'background' caused the highlighting to be reloaded. This means it is
470 // working, thus we should return OK.
471 if (recursive)
472 return OK;
473
474 recursive = TRUE;
475 buf = alloc(STRLEN(name) + 12);
476 if (buf != NULL)
477 {
Drew Vogele30d1022021-10-24 20:35:07 +0100478#ifdef FEAT_EVAL
479 load_default_colors_lists();
480#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200481 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
482 curbuf->b_fname, FALSE, curbuf);
483 sprintf((char *)buf, "colors/%s.vim", name);
484 retval = source_runtime(buf, DIP_START + DIP_OPT);
485 vim_free(buf);
486 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
487 }
488 recursive = FALSE;
489
490 return retval;
491}
492
493static char *(color_names[28]) = {
494 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
495 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
496 "Gray", "Grey", "LightGray", "LightGrey",
497 "DarkGray", "DarkGrey",
498 "Blue", "LightBlue", "Green", "LightGreen",
499 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
500 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
501 // indices:
502 // 0, 1, 2, 3,
503 // 4, 5, 6, 7,
504 // 8, 9, 10, 11,
505 // 12, 13,
506 // 14, 15, 16, 17,
507 // 18, 19, 20, 21, 22,
508 // 23, 24, 25, 26, 27
509static int color_numbers_16[28] = {0, 1, 2, 3,
510 4, 5, 6, 6,
511 7, 7, 7, 7,
512 8, 8,
513 9, 9, 10, 10,
514 11, 11, 12, 12, 13,
515 13, 14, 14, 15, -1};
516// for xterm with 88 colors...
517static int color_numbers_88[28] = {0, 4, 2, 6,
518 1, 5, 32, 72,
519 84, 84, 7, 7,
520 82, 82,
521 12, 43, 10, 61,
522 14, 63, 9, 74, 13,
523 75, 11, 78, 15, -1};
524// for xterm with 256 colors...
525static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200526 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200527 248, 248, 7, 7,
528 242, 242,
529 12, 81, 10, 121,
530 14, 159, 9, 224, 13,
531 225, 11, 229, 15, -1};
532// for terminals with less than 16 colors...
533static int color_numbers_8[28] = {0, 4, 2, 6,
534 1, 5, 3, 3,
535 7, 7, 7, 7,
536 0+8, 0+8,
537 4+8, 4+8, 2+8, 2+8,
538 6+8, 6+8, 1+8, 1+8, 5+8,
539 5+8, 3+8, 3+8, 7+8, -1};
540
541/*
542 * Lookup the "cterm" value to be used for color with index "idx" in
543 * color_names[].
544 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
545 * colors, otherwise it will be unchanged.
546 */
547 int
548lookup_color(int idx, int foreground, int *boldp)
549{
550 int color = color_numbers_16[idx];
551 char_u *p;
552
553 // Use the _16 table to check if it's a valid color name.
554 if (color < 0)
555 return -1;
556
557 if (t_colors == 8)
558 {
559 // t_Co is 8: use the 8 colors table
560#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100561 // On qnx, the 8 & 16 color arrays are the same
562 if (STRNCMP(T_NAME, "qansi", 5) == 0)
563 color = color_numbers_16[idx];
564 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200565#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100566 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200567 if (foreground)
568 {
569 // set/reset bold attribute to get light foreground
570 // colors (on some terminals, e.g. "linux")
571 if (color & 8)
572 *boldp = TRUE;
573 else
574 *boldp = FALSE;
575 }
576 color &= 7; // truncate to 8 colors
577 }
578 else if (t_colors == 16 || t_colors == 88
579 || t_colors >= 256)
580 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100581 // Guess: if the termcap entry ends in 'm', it is
582 // probably an xterm-like terminal. Use the changed
583 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200584 if (*T_CAF != NUL)
585 p = T_CAF;
586 else
587 p = T_CSF;
588 if (*p != NUL && (t_colors > 256
589 || *(p + STRLEN(p) - 1) == 'm'))
590 {
591 if (t_colors == 88)
592 color = color_numbers_88[idx];
593 else if (t_colors >= 256)
594 color = color_numbers_256[idx];
595 else
596 color = color_numbers_8[idx];
597 }
598#ifdef FEAT_TERMRESPONSE
599 if (t_colors >= 256 && color == 15 && is_mac_terminal)
600 // Terminal.app has a bug: 15 is light grey. Use white
601 // from the color cube instead.
602 color = 231;
603#endif
604 }
605 return color;
606}
607
608/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100609 * Link highlight group 'from_hg' to 'to_hg'.
610 * 'dodefault' is set to TRUE for ":highlight default link".
611 * 'forceit' is set to TRUE for ":higlight! link"
612 * 'init' is set to TRUE when initializing all the highlight groups.
613 */
614 static void
615highlight_group_link(
616 char_u *from_hg,
617 int from_len,
618 char_u *to_hg,
619 int to_len,
620 int dodefault,
621 int forceit,
622 int init)
623{
624 int from_id;
625 int to_id;
626 hl_group_T *hlgroup = NULL;
627
628 from_id = syn_check_group(from_hg, from_len);
629 if (STRNCMP(to_hg, "NONE", 4) == 0)
630 to_id = 0;
631 else
632 to_id = syn_check_group(to_hg, to_len);
633
634 if (from_id > 0)
635 {
636 hlgroup = &HL_TABLE()[from_id - 1];
637 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
638 {
639 hlgroup->sg_deflink = to_id;
640#ifdef FEAT_EVAL
641 hlgroup->sg_deflink_sctx = current_sctx;
642 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
643#endif
644 }
645 }
646
647 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
648 {
649 // Don't allow a link when there already is some highlighting
650 // for the group, unless '!' is used
651 if (to_id > 0 && !forceit && !init
652 && hl_has_settings(from_id - 1, dodefault))
653 {
654 if (SOURCING_NAME == NULL && !dodefault)
655 emsg(_("E414: group has settings, highlight link ignored"));
656 }
657 else if (hlgroup->sg_link != to_id
658#ifdef FEAT_EVAL
659 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
660#endif
661 || hlgroup->sg_cleared)
662 {
663 if (!init)
664 hlgroup->sg_set |= SG_LINK;
665 hlgroup->sg_link = to_id;
666#ifdef FEAT_EVAL
667 hlgroup->sg_script_ctx = current_sctx;
668 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
669#endif
670 hlgroup->sg_cleared = FALSE;
671 redraw_all_later(SOME_VALID);
672
673 // Only call highlight_changed() once after multiple changes.
674 need_highlight_changed = TRUE;
675 }
676 }
677
678}
679
680/*
681 * Reset all highlighting to the defaults. Removes all highlighting for the
682 * groups added by the user.
683 */
684 static void
685highlight_reset_all(void)
686{
687 int idx;
688
689#ifdef FEAT_GUI
690 // First, we do not destroy the old values, but allocate the new
691 // ones and update the display. THEN we destroy the old values.
692 // If we destroy the old values first, then the old values
693 // (such as GuiFont's or GuiFontset's) will still be displayed but
694 // invalid because they were free'd.
695 if (gui.in_use)
696 {
697# ifdef FEAT_BEVAL_TIP
698 gui_init_tooltip_font();
699# endif
700# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
701 gui_init_menu_font();
702# endif
703 }
704# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
705 gui_mch_def_colors();
706# endif
707# ifdef FEAT_GUI_X11
708# ifdef FEAT_MENU
709
710 // This only needs to be done when there is no Menu highlight
711 // group defined by default, which IS currently the case.
712 gui_mch_new_menu_colors();
713# endif
714 if (gui.in_use)
715 {
716 gui_new_scrollbar_colors();
717# ifdef FEAT_BEVAL_GUI
718 gui_mch_new_tooltip_colors();
719# endif
720# ifdef FEAT_MENU
721 gui_mch_new_menu_font();
722# endif
723 }
724# endif
725
726 // Ok, we're done allocating the new default graphics items.
727 // The screen should already be refreshed at this point.
728 // It is now Ok to clear out the old data.
729#endif
730#ifdef FEAT_EVAL
731 do_unlet((char_u *)"g:colors_name", TRUE);
732#endif
733 restore_cterm_colors();
734
735 // Clear all default highlight groups and load the defaults.
736 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
737 highlight_clear(idx);
738 init_highlight(TRUE, TRUE);
739#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
740 if (USE_24BIT)
741 highlight_gui_started();
742 else
743#endif
744 highlight_changed();
745 redraw_later_clear();
746}
747
748/*
749 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
750 * index 'idx'.
751 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
752 * 'arg' is the list of attribute names separated by comma.
753 * 'init' is set to TRUE when initializing all the highlight groups.
754 * Returns TRUE if the attributes are set.
755 */
756 static int
757highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
758{
759 int attr;
760 int off;
761 long i;
762 int len;
763
764 attr = 0;
765 off = 0;
766 while (arg[off] != NUL)
767 {
768 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
769 {
770 len = (int)STRLEN(hl_name_table[i]);
771 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
772 {
773 attr |= hl_attr_table[i];
774 off += len;
775 break;
776 }
777 }
778 if (i < 0)
779 {
780 semsg(_("E418: Illegal value: %s"), arg);
781 return FALSE;
782 }
783 if (arg[off] == ',') // another one follows
784 ++off;
785 }
786 if (*key == 'T')
787 {
788 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
789 {
790 if (!init)
791 HL_TABLE()[idx].sg_set |= SG_TERM;
792 HL_TABLE()[idx].sg_term = attr;
793 }
794 }
795 else if (*key == 'C')
796 {
797 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
798 {
799 if (!init)
800 HL_TABLE()[idx].sg_set |= SG_CTERM;
801 HL_TABLE()[idx].sg_cterm = attr;
802 HL_TABLE()[idx].sg_cterm_bold = FALSE;
803 }
804 }
805#if defined(FEAT_GUI) || defined(FEAT_EVAL)
806 else
807 {
808 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
809 {
810 if (!init)
811 HL_TABLE()[idx].sg_set |= SG_GUI;
812 HL_TABLE()[idx].sg_gui = attr;
813 }
814 }
815#endif
816
817 return TRUE;
818}
819
820#ifdef FEAT_GUI
821/*
822 * Set the font for the highlight group at 'idx'.
823 * 'arg' is the font name.
824 * Returns TRUE if the font is changed.
825 */
826 static int
827highlight_set_font(
828 int idx,
829 char_u *arg,
830 int is_normal_group,
831 int is_menu_group,
832 int is_tooltip_group)
833{
834 int did_change = FALSE;
835
836 // in non-GUI fonts are simply ignored
837 if (HL_TABLE()[idx].sg_font_name != NULL
838 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
839 {
840 // Font name didn't change, ignore.
841 }
842 else if (!gui.shell_created)
843 {
844 // GUI not started yet, always accept the name.
845 vim_free(HL_TABLE()[idx].sg_font_name);
846 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
847 did_change = TRUE;
848 }
849 else
850 {
851 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
852# ifdef FEAT_XFONTSET
853 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
854# endif
855 // First, save the current font/fontset.
856 // Then try to allocate the font/fontset.
857 // If the allocation fails, HL_TABLE()[idx].sg_font OR
858 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
859
860 HL_TABLE()[idx].sg_font = NOFONT;
861# ifdef FEAT_XFONTSET
862 HL_TABLE()[idx].sg_fontset = NOFONTSET;
863# endif
864 hl_do_font(idx, arg, is_normal_group, is_menu_group,
865 is_tooltip_group, FALSE);
866
867# ifdef FEAT_XFONTSET
868 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
869 {
870 // New fontset was accepted. Free the old one, if there
871 // was one.
872 gui_mch_free_fontset(temp_sg_fontset);
873 vim_free(HL_TABLE()[idx].sg_font_name);
874 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
875 did_change = TRUE;
876 }
877 else
878 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
879# endif
880 if (HL_TABLE()[idx].sg_font != NOFONT)
881 {
882 // New font was accepted. Free the old one, if there was
883 // one.
884 gui_mch_free_font(temp_sg_font);
885 vim_free(HL_TABLE()[idx].sg_font_name);
886 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
887 did_change = TRUE;
888 }
889 else
890 HL_TABLE()[idx].sg_font = temp_sg_font;
891 }
892
893 return did_change;
894}
895#endif
896
897/*
898 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
899 * Returns TRUE if the foreground color is set.
900 */
901 static void
902highlight_set_ctermfg(int idx, int color, int is_normal_group)
903{
904 HL_TABLE()[idx].sg_cterm_fg = color + 1;
905 if (is_normal_group)
906 {
907 cterm_normal_fg_color = color + 1;
908 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
909#ifdef FEAT_GUI
910 // Don't do this if the GUI is used.
911 if (!gui.in_use && !gui.starting)
912#endif
913 {
914 must_redraw = CLEAR;
915 if (termcap_active && color >= 0)
916 term_fg_color(color);
917 }
918 }
919}
920
921/*
922 * Set the cterm background color for the highlight group at 'idx' to 'color'.
923 * Returns TRUE if the background color is set.
924 */
925 static void
926highlight_set_ctermbg(int idx, int color, int is_normal_group)
927{
928 HL_TABLE()[idx].sg_cterm_bg = color + 1;
929 if (is_normal_group)
930 {
931 cterm_normal_bg_color = color + 1;
932#ifdef FEAT_GUI
933 // Don't mess with 'background' if the GUI is used.
934 if (!gui.in_use && !gui.starting)
935#endif
936 {
937 must_redraw = CLEAR;
938 if (color >= 0)
939 {
940 int dark = -1;
941
942 if (termcap_active)
943 term_bg_color(color);
944 if (t_colors < 16)
945 dark = (color == 0 || color == 4);
946 // Limit the heuristic to the standard 16 colors
947 else if (color < 16)
948 dark = (color < 7 || color == 8);
949 // Set the 'background' option if the value is
950 // wrong.
951 if (dark != -1
952 && dark != (*p_bg == 'd')
953 && !option_was_set((char_u *)"bg"))
954 {
955 set_option_value((char_u *)"bg", 0L,
956 (char_u *)(dark ? "dark" : "light"), 0);
957 reset_option_was_set((char_u *)"bg");
958 }
959 }
960 }
961 }
962}
963
964/*
965 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
966 * Returns TRUE if the underline color is set.
967 */
968 static void
969highlight_set_ctermul(int idx, int color, int is_normal_group)
970{
971 HL_TABLE()[idx].sg_cterm_ul = color + 1;
972 if (is_normal_group)
973 {
974 cterm_normal_ul_color = color + 1;
975#ifdef FEAT_GUI
976 // Don't do this if the GUI is used.
977 if (!gui.in_use && !gui.starting)
978#endif
979 {
980 must_redraw = CLEAR;
981 if (termcap_active && color >= 0)
982 term_ul_color(color);
983 }
984 }
985}
986
987/*
988 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
989 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
990 * 'keystart' is the color name/value.
991 * 'arg' is the color name or the numeric value as a string.
992 * 'is_normal_group' is set if the highlight group is 'NORMAL'
993 * 'init' is set to TRUE when initializing highlighting.
994 * Called for the ":highlight" command and the "hlset()" function.
995 *
996 * Returns TRUE if the color is set.
997 */
998 static int
999highlight_set_cterm_color(
1000 int idx,
1001 char_u *key,
1002 char_u *key_start,
1003 char_u *arg,
1004 int is_normal_group,
1005 int init)
1006{
1007 int color;
1008 long i;
1009 int off;
1010
1011 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1012 {
1013 if (!init)
1014 HL_TABLE()[idx].sg_set |= SG_CTERM;
1015
1016 // When setting the foreground color, and previously the "bold"
1017 // flag was set for a light color, reset it now
1018 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1019 {
1020 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1021 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1022 }
1023
1024 if (VIM_ISDIGIT(*arg))
1025 color = atoi((char *)arg);
1026 else if (STRICMP(arg, "fg") == 0)
1027 {
1028 if (cterm_normal_fg_color)
1029 color = cterm_normal_fg_color - 1;
1030 else
1031 {
1032 emsg(_("E419: FG color unknown"));
1033 return FALSE;
1034 }
1035 }
1036 else if (STRICMP(arg, "bg") == 0)
1037 {
1038 if (cterm_normal_bg_color > 0)
1039 color = cterm_normal_bg_color - 1;
1040 else
1041 {
1042 emsg(_("E420: BG color unknown"));
1043 return FALSE;
1044 }
1045 }
1046 else if (STRICMP(arg, "ul") == 0)
1047 {
1048 if (cterm_normal_ul_color > 0)
1049 color = cterm_normal_ul_color - 1;
1050 else
1051 {
1052 emsg(_("E453: UL color unknown"));
1053 return FALSE;
1054 }
1055 }
1056 else
1057 {
1058 int bold = MAYBE;
1059
1060 // reduce calls to STRICMP a bit, it can be slow
1061 off = TOUPPER_ASC(*arg);
1062 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1063 if (off == color_names[i][0]
1064 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1065 break;
1066 if (i < 0)
1067 {
1068 semsg(_("E421: Color name or number not recognized: %s"),
1069 key_start);
1070 return FALSE;
1071 }
1072
1073 color = lookup_color(i, key[5] == 'F', &bold);
1074
1075 // set/reset bold attribute to get light foreground
1076 // colors (on some terminals, e.g. "linux")
1077 if (bold == TRUE)
1078 {
1079 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1080 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1081 }
1082 else if (bold == FALSE)
1083 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1084 }
1085
1086 // Add one to the argument, to avoid zero. Zero is used for
1087 // "NONE", then "color" is -1.
1088 if (key[5] == 'F')
1089 highlight_set_ctermfg(idx, color, is_normal_group);
1090 else if (key[5] == 'B')
1091 highlight_set_ctermbg(idx, color, is_normal_group);
1092 else // ctermul
1093 highlight_set_ctermul(idx, color, is_normal_group);
1094 }
1095
1096 return TRUE;
1097}
1098
1099#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1100/*
1101 * Set the GUI foreground color for the highlight group at 'idx'.
1102 * Returns TRUE if the color is set.
1103 */
1104 static int
1105highlight_set_guifg(
1106 int idx,
1107 char_u *arg,
1108 int is_menu_group UNUSED,
1109 int is_scrollbar_group UNUSED,
1110 int is_tooltip_group UNUSED,
1111 int *do_colors UNUSED,
1112 int init)
1113{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001114# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001115 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001116# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001117 char_u **namep;
1118 int did_change = FALSE;
1119
1120 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1121 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1122 {
1123 if (!init)
1124 HL_TABLE()[idx].sg_set |= SG_GUI;
1125
1126# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1127 // In GUI guifg colors are only used when recognized
1128 i = color_name2handle(arg);
1129 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1130 {
1131 HL_TABLE()[idx].sg_gui_fg = i;
1132# endif
1133 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1134 {
1135 vim_free(*namep);
1136 if (STRCMP(arg, "NONE") != 0)
1137 *namep = vim_strsave(arg);
1138 else
1139 *namep = NULL;
1140 did_change = TRUE;
1141 }
1142# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1143# ifdef FEAT_GUI_X11
1144 if (is_menu_group && gui.menu_fg_pixel != i)
1145 {
1146 gui.menu_fg_pixel = i;
1147 *do_colors = TRUE;
1148 }
1149 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1150 {
1151 gui.scroll_fg_pixel = i;
1152 *do_colors = TRUE;
1153 }
1154# ifdef FEAT_BEVAL_GUI
1155 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1156 {
1157 gui.tooltip_fg_pixel = i;
1158 *do_colors = TRUE;
1159 }
1160# endif
1161# endif
1162 }
1163# endif
1164 }
1165
1166 return did_change;
1167}
1168
1169/*
1170 * Set the GUI background color for the highlight group at 'idx'.
1171 * Returns TRUE if the color is set.
1172 */
1173 static int
1174highlight_set_guibg(
1175 int idx,
1176 char_u *arg,
1177 int is_menu_group UNUSED,
1178 int is_scrollbar_group UNUSED,
1179 int is_tooltip_group UNUSED,
1180 int *do_colors UNUSED,
1181 int init)
1182{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001183# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001184 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001185# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001186 char_u **namep;
1187 int did_change = FALSE;
1188
1189 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1190 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1191 {
1192 if (!init)
1193 HL_TABLE()[idx].sg_set |= SG_GUI;
1194
1195# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Drew Vogele30d1022021-10-24 20:35:07 +01001196 // In GUI guibg colors are only used when recognized
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001197 i = color_name2handle(arg);
1198 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1199 {
1200 HL_TABLE()[idx].sg_gui_bg = i;
1201# endif
1202 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1203 {
1204 vim_free(*namep);
1205 if (STRCMP(arg, "NONE") != 0)
1206 *namep = vim_strsave(arg);
1207 else
1208 *namep = NULL;
1209 did_change = TRUE;
1210 }
1211# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1212# ifdef FEAT_GUI_X11
1213 if (is_menu_group && gui.menu_bg_pixel != i)
1214 {
1215 gui.menu_bg_pixel = i;
1216 *do_colors = TRUE;
1217 }
1218 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1219 {
1220 gui.scroll_bg_pixel = i;
1221 *do_colors = TRUE;
1222 }
1223# ifdef FEAT_BEVAL_GUI
1224 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1225 {
1226 gui.tooltip_bg_pixel = i;
1227 *do_colors = TRUE;
1228 }
1229# endif
1230# endif
1231 }
1232# endif
1233 }
1234
1235 return did_change;
1236}
1237
1238/*
1239 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1240 * Returns TRUE if the color is set.
1241 */
1242 static int
1243highlight_set_guisp(int idx, char_u *arg, int init)
1244{
1245# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1246 int i;
1247# endif
1248 int did_change = FALSE;
1249 char_u **namep;
1250
1251 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1252 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1253 {
1254 if (!init)
1255 HL_TABLE()[idx].sg_set |= SG_GUI;
1256
1257# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1258 // In GUI guisp colors are only used when recognized
1259 i = color_name2handle(arg);
1260 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1261 {
1262 HL_TABLE()[idx].sg_gui_sp = i;
1263# endif
1264 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1265 {
1266 vim_free(*namep);
1267 if (STRCMP(arg, "NONE") != 0)
1268 *namep = vim_strsave(arg);
1269 else
1270 *namep = NULL;
1271 did_change = TRUE;
1272 }
1273# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1274 }
1275# endif
1276 }
1277
1278 return did_change;
1279}
1280#endif
1281
1282/*
1283 * Set the start/stop terminal codes for a highlight group.
1284 * Returns TRUE if the terminal code is set.
1285 */
1286 static int
1287highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1288{
1289 int off;
1290 char_u buf[100];
1291 int len;
1292 char_u *tname;
1293 char_u *p;
1294
1295 if (!init)
1296 HL_TABLE()[idx].sg_set |= SG_TERM;
1297
1298 // The "start" and "stop" arguments can be a literal escape
1299 // sequence, or a comma separated list of terminal codes.
1300 if (STRNCMP(arg, "t_", 2) == 0)
1301 {
1302 off = 0;
1303 buf[0] = 0;
1304 while (arg[off] != NUL)
1305 {
1306 // Isolate one termcap name
1307 for (len = 0; arg[off + len] &&
1308 arg[off + len] != ','; ++len)
1309 ;
1310 tname = vim_strnsave(arg + off, len);
1311 if (tname == NULL) // out of memory
1312 return FALSE;
1313 // lookup the escape sequence for the item
1314 p = get_term_code(tname);
1315 vim_free(tname);
1316 if (p == NULL) // ignore non-existing things
1317 p = (char_u *)"";
1318
1319 // Append it to the already found stuff
1320 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1321 {
1322 semsg(_("E422: terminal code too long: %s"), arg);
1323 return FALSE;
1324 }
1325 STRCAT(buf, p);
1326
1327 // Advance to the next item
1328 off += len;
1329 if (arg[off] == ',') // another one follows
1330 ++off;
1331 }
1332 }
1333 else
1334 {
1335 // Copy characters from arg[] to buf[], translating <> codes.
1336 for (p = arg, off = 0; off < 100 - 6 && *p; )
1337 {
1338 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
1339 if (len > 0) // recognized special char
1340 off += len;
1341 else // copy as normal char
1342 buf[off++] = *p++;
1343 }
1344 buf[off] = NUL;
1345 }
1346
1347 if (STRCMP(buf, "NONE") == 0) // resetting the value
1348 p = NULL;
1349 else
1350 p = vim_strsave(buf);
1351 if (key[2] == 'A')
1352 {
1353 vim_free(HL_TABLE()[idx].sg_start);
1354 HL_TABLE()[idx].sg_start = p;
1355 }
1356 else
1357 {
1358 vim_free(HL_TABLE()[idx].sg_stop);
1359 HL_TABLE()[idx].sg_stop = p;
1360 }
1361 return TRUE;
1362}
1363
1364/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001365 * Handle the ":highlight .." command.
1366 * When using ":hi clear" this is called recursively for each group with
1367 * "forceit" and "init" both TRUE.
1368 */
1369 void
1370do_highlight(
1371 char_u *line,
1372 int forceit,
1373 int init) // TRUE when called for initializing
1374{
1375 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001376 char_u *linep;
1377 char_u *key_start;
1378 char_u *arg_start;
1379 char_u *key = NULL, *arg = NULL;
1380 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001381 int id;
1382 int idx;
1383 hl_group_T item_before;
1384 int did_change = FALSE;
1385 int dodefault = FALSE;
1386 int doclear = FALSE;
1387 int dolink = FALSE;
1388 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001389 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001390#ifdef FEAT_GUI_X11
1391 int is_menu_group = FALSE; // "Menu" group
1392 int is_scrollbar_group = FALSE; // "Scrollbar" group
1393 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001394#else
1395# define is_menu_group 0
1396# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001397# define is_scrollbar_group 0
1398#endif
1399#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1400 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001401#endif
1402#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1403 int did_highlight_changed = FALSE;
1404#endif
1405
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001406 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001407 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001408 {
1409 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1410 // TODO: only call when the group has attributes set
1411 highlight_list_one((int)i);
1412 return;
1413 }
1414
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001415 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001416 name_end = skiptowhite(line);
1417 linep = skipwhite(name_end);
1418
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001419 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001420 if (STRNCMP(line, "default", name_end - line) == 0)
1421 {
1422 dodefault = TRUE;
1423 line = linep;
1424 name_end = skiptowhite(line);
1425 linep = skipwhite(name_end);
1426 }
1427
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001428 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001429 if (STRNCMP(line, "clear", name_end - line) == 0)
1430 doclear = TRUE;
1431 if (STRNCMP(line, "link", name_end - line) == 0)
1432 dolink = TRUE;
1433
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001434 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001435 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001436 {
1437 id = syn_namen2id(line, (int)(name_end - line));
1438 if (id == 0)
1439 semsg(_("E411: highlight group not found: %s"), line);
1440 else
1441 highlight_list_one(id);
1442 return;
1443 }
1444
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001445 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001446 if (dolink)
1447 {
1448 char_u *from_start = linep;
1449 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001450 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001451 char_u *to_start;
1452 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001453 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001454
1455 from_end = skiptowhite(from_start);
1456 to_start = skipwhite(from_end);
1457 to_end = skiptowhite(to_start);
1458
Bram Moolenaar1966c242020-04-20 22:42:32 +02001459 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001460 {
1461 semsg(_("E412: Not enough arguments: \":highlight link %s\""),
1462 from_start);
1463 return;
1464 }
1465
Bram Moolenaar1966c242020-04-20 22:42:32 +02001466 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001467 {
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001468 semsg(_("E413: Too many arguments: \":highlight link %s\""),
1469 from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001470 return;
1471 }
1472
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001473 from_len = (int)(from_end - from_start);
1474 to_len = (int)(to_end - to_start);
1475 highlight_group_link(from_start, from_len, to_start, to_len,
1476 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001477 return;
1478 }
1479
1480 if (doclear)
1481 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001482 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001483 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001484 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001485 // ":highlight clear" without group name
1486 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001487 return;
1488 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001489 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001490 name_end = skiptowhite(line);
1491 linep = skipwhite(name_end);
1492 }
1493
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001494 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001495 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001496 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001497 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001498 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001499
1500 // Return if "default" was used and the group already has settings.
1501 if (dodefault && hl_has_settings(idx, TRUE))
1502 return;
1503
1504 // Make a copy so we can check if any attribute actually changed.
1505 item_before = HL_TABLE()[idx];
1506
1507 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1508 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001509#ifdef FEAT_GUI_X11
1510 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1511 is_menu_group = TRUE;
1512 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1513 is_scrollbar_group = TRUE;
1514 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1515 is_tooltip_group = TRUE;
1516#endif
1517
1518 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1519 if (doclear || (forceit && init))
1520 {
1521 highlight_clear(idx);
1522 if (!doclear)
1523 HL_TABLE()[idx].sg_set = 0;
1524 }
1525
1526 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001527 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001528 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001529 key_start = linep;
1530 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001531 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001532 semsg(_("E415: unexpected equal sign: %s"), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001533 error = TRUE;
1534 break;
1535 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001536
1537 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1538 // or "guibg").
1539 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1540 ++linep;
1541 vim_free(key);
1542 key = vim_strnsave_up(key_start, linep - key_start);
1543 if (key == NULL)
1544 {
1545 error = TRUE;
1546 break;
1547 }
1548 linep = skipwhite(linep);
1549
1550 if (STRCMP(key, "NONE") == 0)
1551 {
1552 if (!init || HL_TABLE()[idx].sg_set == 0)
1553 {
1554 if (!init)
1555 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1556 highlight_clear(idx);
1557 }
1558 continue;
1559 }
1560
1561 // Check for the equal sign.
1562 if (*linep != '=')
1563 {
1564 semsg(_("E416: missing equal sign: %s"), key_start);
1565 error = TRUE;
1566 break;
1567 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001568 ++linep;
1569
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001570 // Isolate the argument.
1571 linep = skipwhite(linep);
1572 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001573 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001574 arg_start = ++linep;
1575 linep = vim_strchr(linep, '\'');
1576 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001577 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001578 semsg(_(e_invarg2), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001579 error = TRUE;
1580 break;
1581 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001582 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001583 else
1584 {
1585 arg_start = linep;
1586 linep = skiptowhite(linep);
1587 }
1588 if (linep == arg_start)
1589 {
1590 semsg(_("E417: missing argument: %s"), key_start);
1591 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001592 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001593 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001594 vim_free(arg);
1595 arg = vim_strnsave(arg_start, linep - arg_start);
1596 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001597 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001598 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001599 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001600 }
1601 if (*linep == '\'')
1602 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001603
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001604 // Store the argument.
1605 if (STRCMP(key, "TERM") == 0
1606 || STRCMP(key, "CTERM") == 0
1607 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001608 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001609 if (!highlight_set_termgui_attr(idx, key, arg, init))
1610 {
1611 error = TRUE;
1612 break;
1613 }
1614 }
1615 else if (STRCMP(key, "FONT") == 0)
1616 {
1617 // in non-GUI fonts are simply ignored
1618#ifdef FEAT_GUI
1619 if (highlight_set_font(idx, arg, is_normal_group,
1620 is_menu_group, is_tooltip_group))
1621 did_change = TRUE;
1622#endif
1623 }
1624 else if (STRCMP(key, "CTERMFG") == 0
1625 || STRCMP(key, "CTERMBG") == 0
1626 || STRCMP(key, "CTERMUL") == 0)
1627 {
1628 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1629 is_normal_group, init))
1630 {
1631 error = TRUE;
1632 break;
1633 }
1634 }
1635 else if (STRCMP(key, "GUIFG") == 0)
1636 {
1637#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1638 if (highlight_set_guifg(idx, arg, is_menu_group,
1639 is_scrollbar_group, is_tooltip_group,
1640 &do_colors, init))
1641 did_change = TRUE;
1642#endif
1643 }
1644 else if (STRCMP(key, "GUIBG") == 0)
1645 {
1646#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1647 if (highlight_set_guibg(idx, arg, is_menu_group,
1648 is_scrollbar_group, is_tooltip_group,
1649 &do_colors, init))
1650 did_change = TRUE;
1651#endif
1652 }
1653 else if (STRCMP(key, "GUISP") == 0)
1654 {
1655#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1656 if (highlight_set_guisp(idx, arg, init))
1657 did_change = TRUE;
1658#endif
1659 }
1660 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1661 {
1662 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1663 {
1664 error = TRUE;
1665 break;
1666 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001667 }
1668 else
1669 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001670 semsg(_("E423: Illegal argument: %s"), key_start);
1671 error = TRUE;
1672 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001673 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001674 HL_TABLE()[idx].sg_cleared = FALSE;
1675
1676 // When highlighting has been given for a group, don't link it.
1677 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1678 HL_TABLE()[idx].sg_link = 0;
1679
1680 // Continue with next argument.
1681 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001682 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001683
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001684 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001685 if (error && idx == highlight_ga.ga_len)
1686 syn_unadd_group();
1687 else
1688 {
1689 if (is_normal_group)
1690 {
1691 HL_TABLE()[idx].sg_term_attr = 0;
1692 HL_TABLE()[idx].sg_cterm_attr = 0;
1693#ifdef FEAT_GUI
1694 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001695 // Need to update all groups, because they might be using "bg"
1696 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001697#endif
1698#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1699 if (USE_24BIT)
1700 {
1701 highlight_gui_started();
1702 did_highlight_changed = TRUE;
1703 redraw_all_later(NOT_VALID);
1704 }
1705#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001706#ifdef FEAT_VTP
1707 control_console_color_rgb();
1708#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001709 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001710#ifdef FEAT_GUI_X11
1711# ifdef FEAT_MENU
1712 else if (is_menu_group)
1713 {
1714 if (gui.in_use && do_colors)
1715 gui_mch_new_menu_colors();
1716 }
1717# endif
1718 else if (is_scrollbar_group)
1719 {
1720 if (gui.in_use && do_colors)
1721 gui_new_scrollbar_colors();
1722 else
1723 set_hl_attr(idx);
1724 }
1725# ifdef FEAT_BEVAL_GUI
1726 else if (is_tooltip_group)
1727 {
1728 if (gui.in_use && do_colors)
1729 gui_mch_new_tooltip_colors();
1730 }
1731# endif
1732#endif
1733 else
1734 set_hl_attr(idx);
1735#ifdef FEAT_EVAL
1736 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001737 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001738#endif
1739 }
1740
1741 vim_free(key);
1742 vim_free(arg);
1743
1744 // Only call highlight_changed() once, after a sequence of highlight
1745 // commands, and only if an attribute actually changed.
1746 if ((did_change
1747 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1748#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1749 && !did_highlight_changed
1750#endif
1751 )
1752 {
1753 // Do not trigger a redraw when highlighting is changed while
1754 // redrawing. This may happen when evaluating 'statusline' changes the
1755 // StatusLine group.
1756 if (!updating_screen)
1757 redraw_all_later(NOT_VALID);
1758 need_highlight_changed = TRUE;
1759 }
1760}
1761
1762#if defined(EXITFREE) || defined(PROTO)
1763 void
1764free_highlight(void)
1765{
1766 int i;
1767
1768 for (i = 0; i < highlight_ga.ga_len; ++i)
1769 {
1770 highlight_clear(i);
1771 vim_free(HL_TABLE()[i].sg_name);
1772 vim_free(HL_TABLE()[i].sg_name_u);
1773 }
1774 ga_clear(&highlight_ga);
1775}
1776#endif
1777
1778/*
1779 * Reset the cterm colors to what they were before Vim was started, if
1780 * possible. Otherwise reset them to zero.
1781 */
1782 void
1783restore_cterm_colors(void)
1784{
1785#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1786 // Since t_me has been set, this probably means that the user
1787 // wants to use this as default colors. Need to reset default
1788 // background/foreground colors.
1789 mch_set_normal_colors();
1790#else
1791# ifdef VIMDLL
1792 if (!gui.in_use)
1793 {
1794 mch_set_normal_colors();
1795 return;
1796 }
1797# endif
1798 cterm_normal_fg_color = 0;
1799 cterm_normal_fg_bold = 0;
1800 cterm_normal_bg_color = 0;
1801# ifdef FEAT_TERMGUICOLORS
1802 cterm_normal_fg_gui_color = INVALCOLOR;
1803 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001804 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001805# endif
1806#endif
1807}
1808
1809/*
1810 * Return TRUE if highlight group "idx" has any settings.
1811 * When "check_link" is TRUE also check for an existing link.
1812 */
1813 static int
1814hl_has_settings(int idx, int check_link)
1815{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001816 return HL_TABLE()[idx].sg_cleared == 0
1817 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001818 || HL_TABLE()[idx].sg_cterm_attr != 0
1819 || HL_TABLE()[idx].sg_cterm_fg != 0
1820 || HL_TABLE()[idx].sg_cterm_bg != 0
1821#ifdef FEAT_GUI
1822 || HL_TABLE()[idx].sg_gui_attr != 0
1823 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1824 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1825 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1826 || HL_TABLE()[idx].sg_font_name != NULL
1827#endif
1828 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1829}
1830
1831/*
1832 * Clear highlighting for one group.
1833 */
1834 static void
1835highlight_clear(int idx)
1836{
1837 HL_TABLE()[idx].sg_cleared = TRUE;
1838
1839 HL_TABLE()[idx].sg_term = 0;
1840 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1841 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1842 HL_TABLE()[idx].sg_term_attr = 0;
1843 HL_TABLE()[idx].sg_cterm = 0;
1844 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1845 HL_TABLE()[idx].sg_cterm_fg = 0;
1846 HL_TABLE()[idx].sg_cterm_bg = 0;
1847 HL_TABLE()[idx].sg_cterm_attr = 0;
1848#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1849 HL_TABLE()[idx].sg_gui = 0;
1850 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1851 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1852 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1853#endif
1854#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1855 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1856 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001857 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001858#endif
1859#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001860 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1861 HL_TABLE()[idx].sg_font = NOFONT;
1862# ifdef FEAT_XFONTSET
1863 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1864 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1865# endif
1866 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1867 HL_TABLE()[idx].sg_gui_attr = 0;
1868#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001869 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001870 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001871#ifdef FEAT_EVAL
1872 // Since we set the default link, set the location to where the default
1873 // link was set.
1874 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001875#endif
1876}
1877
1878#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1879/*
1880 * Set the normal foreground and background colors according to the "Normal"
1881 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1882 * "Tooltip" colors.
1883 */
1884 void
1885set_normal_colors(void)
1886{
1887# ifdef FEAT_GUI
1888# ifdef FEAT_TERMGUICOLORS
1889 if (gui.in_use)
1890# endif
1891 {
1892 if (set_group_colors((char_u *)"Normal",
1893 &gui.norm_pixel, &gui.back_pixel,
1894 FALSE, TRUE, FALSE))
1895 {
1896 gui_mch_new_colors();
1897 must_redraw = CLEAR;
1898 }
1899# ifdef FEAT_GUI_X11
1900 if (set_group_colors((char_u *)"Menu",
1901 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1902 TRUE, FALSE, FALSE))
1903 {
1904# ifdef FEAT_MENU
1905 gui_mch_new_menu_colors();
1906# endif
1907 must_redraw = CLEAR;
1908 }
1909# ifdef FEAT_BEVAL_GUI
1910 if (set_group_colors((char_u *)"Tooltip",
1911 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1912 FALSE, FALSE, TRUE))
1913 {
1914# ifdef FEAT_TOOLBAR
1915 gui_mch_new_tooltip_colors();
1916# endif
1917 must_redraw = CLEAR;
1918 }
1919# endif
1920 if (set_group_colors((char_u *)"Scrollbar",
1921 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1922 FALSE, FALSE, FALSE))
1923 {
1924 gui_new_scrollbar_colors();
1925 must_redraw = CLEAR;
1926 }
1927# endif
1928 }
1929# endif
1930# ifdef FEAT_TERMGUICOLORS
1931# ifdef FEAT_GUI
1932 else
1933# endif
1934 {
1935 int idx;
1936
1937 idx = syn_name2id((char_u *)"Normal") - 1;
1938 if (idx >= 0)
1939 {
1940 gui_do_one_color(idx, FALSE, FALSE);
1941
1942 // If the normal fg or bg color changed a complete redraw is
1943 // required.
1944 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1945 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1946 {
1947 // if the GUI color is INVALCOLOR then we use the default cterm
1948 // color
1949 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1950 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1951 must_redraw = CLEAR;
1952 }
1953 }
1954 }
1955# endif
1956}
1957#endif
1958
1959#if defined(FEAT_GUI) || defined(PROTO)
1960/*
1961 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1962 */
1963 static int
1964set_group_colors(
1965 char_u *name,
1966 guicolor_T *fgp,
1967 guicolor_T *bgp,
1968 int do_menu,
1969 int use_norm,
1970 int do_tooltip)
1971{
1972 int idx;
1973
1974 idx = syn_name2id(name) - 1;
1975 if (idx >= 0)
1976 {
1977 gui_do_one_color(idx, do_menu, do_tooltip);
1978
1979 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1980 *fgp = HL_TABLE()[idx].sg_gui_fg;
1981 else if (use_norm)
1982 *fgp = gui.def_norm_pixel;
1983 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1984 *bgp = HL_TABLE()[idx].sg_gui_bg;
1985 else if (use_norm)
1986 *bgp = gui.def_back_pixel;
1987 return TRUE;
1988 }
1989 return FALSE;
1990}
1991
1992/*
1993 * Get the font of the "Normal" group.
1994 * Returns "" when it's not found or not set.
1995 */
1996 char_u *
1997hl_get_font_name(void)
1998{
1999 int id;
2000 char_u *s;
2001
2002 id = syn_name2id((char_u *)"Normal");
2003 if (id > 0)
2004 {
2005 s = HL_TABLE()[id - 1].sg_font_name;
2006 if (s != NULL)
2007 return s;
2008 }
2009 return (char_u *)"";
2010}
2011
2012/*
2013 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2014 * actually chosen to be used.
2015 */
2016 void
2017hl_set_font_name(char_u *font_name)
2018{
2019 int id;
2020
2021 id = syn_name2id((char_u *)"Normal");
2022 if (id > 0)
2023 {
2024 vim_free(HL_TABLE()[id - 1].sg_font_name);
2025 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2026 }
2027}
2028
2029/*
2030 * Set background color for "Normal" group. Called by gui_set_bg_color()
2031 * when the color is known.
2032 */
2033 void
2034hl_set_bg_color_name(
2035 char_u *name) // must have been allocated
2036{
2037 int id;
2038
2039 if (name != NULL)
2040 {
2041 id = syn_name2id((char_u *)"Normal");
2042 if (id > 0)
2043 {
2044 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2045 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2046 }
2047 }
2048}
2049
2050/*
2051 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2052 * when the color is known.
2053 */
2054 void
2055hl_set_fg_color_name(
2056 char_u *name) // must have been allocated
2057{
2058 int id;
2059
2060 if (name != NULL)
2061 {
2062 id = syn_name2id((char_u *)"Normal");
2063 if (id > 0)
2064 {
2065 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2066 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2067 }
2068 }
2069}
2070
2071/*
2072 * Return the handle for a font name.
2073 * Returns NOFONT when failed.
2074 */
2075 static GuiFont
2076font_name2handle(char_u *name)
2077{
2078 if (STRCMP(name, "NONE") == 0)
2079 return NOFONT;
2080
2081 return gui_mch_get_font(name, TRUE);
2082}
2083
2084# ifdef FEAT_XFONTSET
2085/*
2086 * Return the handle for a fontset name.
2087 * Returns NOFONTSET when failed.
2088 */
2089 static GuiFontset
2090fontset_name2handle(char_u *name, int fixed_width)
2091{
2092 if (STRCMP(name, "NONE") == 0)
2093 return NOFONTSET;
2094
2095 return gui_mch_get_fontset(name, TRUE, fixed_width);
2096}
2097# endif
2098
2099/*
2100 * Get the font or fontset for one highlight group.
2101 */
2102 static void
2103hl_do_font(
2104 int idx,
2105 char_u *arg,
2106 int do_normal, // set normal font
2107 int do_menu UNUSED, // set menu font
2108 int do_tooltip UNUSED, // set tooltip font
2109 int free_font) // free current font/fontset
2110{
2111# ifdef FEAT_XFONTSET
2112 // If 'guifontset' is not empty, first try using the name as a
2113 // fontset. If that doesn't work, use it as a font name.
2114 if (*p_guifontset != NUL
2115# ifdef FONTSET_ALWAYS
2116 || do_menu
2117# endif
2118# ifdef FEAT_BEVAL_TIP
2119 // In Athena & Motif, the Tooltip highlight group is always a fontset
2120 || do_tooltip
2121# endif
2122 )
2123 {
2124 if (free_font)
2125 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2126 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2127# ifdef FONTSET_ALWAYS
2128 || do_menu
2129# endif
2130# ifdef FEAT_BEVAL_TIP
2131 || do_tooltip
2132# endif
2133 );
2134 }
2135 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2136 {
2137 // If it worked and it's the Normal group, use it as the normal
2138 // fontset. Same for the Menu group.
2139 if (do_normal)
2140 gui_init_font(arg, TRUE);
2141# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2142 if (do_menu)
2143 {
2144# ifdef FONTSET_ALWAYS
2145 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2146# else
2147 // YIKES! This is a bug waiting to crash the program
2148 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2149# endif
2150 gui_mch_new_menu_font();
2151 }
2152# ifdef FEAT_BEVAL_GUI
2153 if (do_tooltip)
2154 {
2155 // The Athena widget set cannot currently handle switching between
2156 // displaying a single font and a fontset.
2157 // If the XtNinternational resource is set to True at widget
2158 // creation, then a fontset is always used, otherwise an
2159 // XFontStruct is used.
2160 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2161 gui_mch_new_tooltip_font();
2162 }
2163# endif
2164# endif
2165 }
2166 else
2167# endif
2168 {
2169 if (free_font)
2170 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2171 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2172 // If it worked and it's the Normal group, use it as the
2173 // normal font. Same for the Menu group.
2174 if (HL_TABLE()[idx].sg_font != NOFONT)
2175 {
2176 if (do_normal)
2177 gui_init_font(arg, FALSE);
2178#ifndef FONTSET_ALWAYS
2179# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2180 if (do_menu)
2181 {
2182 gui.menu_font = HL_TABLE()[idx].sg_font;
2183 gui_mch_new_menu_font();
2184 }
2185# endif
2186#endif
2187 }
2188 }
2189}
2190
2191#endif // FEAT_GUI
2192
2193#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2194/*
2195 * Return the handle for a color name.
2196 * Returns INVALCOLOR when failed.
2197 */
2198 guicolor_T
2199color_name2handle(char_u *name)
2200{
2201 if (STRCMP(name, "NONE") == 0)
2202 return INVALCOLOR;
2203
2204 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2205 {
2206#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2207 if (gui.in_use)
2208#endif
2209#ifdef FEAT_GUI
2210 return gui.norm_pixel;
2211#endif
2212#ifdef FEAT_TERMGUICOLORS
2213 if (cterm_normal_fg_gui_color != INVALCOLOR)
2214 return cterm_normal_fg_gui_color;
2215 // Guess that the foreground is black or white.
2216 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2217#endif
2218 }
2219 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2220 {
2221#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2222 if (gui.in_use)
2223#endif
2224#ifdef FEAT_GUI
2225 return gui.back_pixel;
2226#endif
2227#ifdef FEAT_TERMGUICOLORS
2228 if (cterm_normal_bg_gui_color != INVALCOLOR)
2229 return cterm_normal_bg_gui_color;
2230 // Guess that the background is white or black.
2231 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2232#endif
2233 }
2234
2235 return GUI_GET_COLOR(name);
2236}
Drew Vogele30d1022021-10-24 20:35:07 +01002237
2238// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2239// values as used by the MS-Windows GDI api. It should be used only for
2240// MS-Windows GDI builds.
2241# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2242# undef RGB
2243# endif
2244# ifndef RGB
2245# define RGB(r, g, b) ((r<<16) | (g<<8) | (b))
2246# endif
2247
2248# ifdef VIMDLL
2249 static guicolor_T
2250gui_adjust_rgb(guicolor_T c)
2251{
2252 if (gui.in_use)
2253 return c;
2254 else
2255 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2256}
2257# else
2258# define gui_adjust_rgb(c) (c)
2259# endif
2260
2261 static int
2262hex_digit(int c)
2263{
2264 if (isdigit(c))
2265 return c - '0';
2266 c = TOLOWER_ASC(c);
2267 if (c >= 'a' && c <= 'f')
2268 return c - 'a' + 10;
2269 return 0x1ffffff;
2270}
2271
2272 guicolor_T
2273decode_hex_color(char_u *hex)
2274{
2275 guicolor_T color;
2276
2277 if (hex[0] != '#' || STRLEN(hex) != 7)
2278 return INVALCOLOR;
2279
2280 // Name is in "#rrggbb" format
2281 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2282 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2283 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2284 if (color > 0xffffff)
2285 return INVALCOLOR;
2286 return gui_adjust_rgb(color);
2287}
2288
2289#if defined(FEAT_EVAL)
2290// Returns the color currently mapped to the given name or INVALCOLOR if no
2291// such name exists in the color table. The convention is to use lowercase for
2292// all keys in the v:colornames dictionary. The value can be either a string in
2293// the form #rrggbb or a number, either of which is converted to a guicolor_T.
2294 guicolor_T
2295colorname2rgb(char_u *name)
2296{
2297 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2298 char_u *lc_name;
2299 dictitem_T *colentry;
2300 char_u *colstr;
2301 varnumber_T colnum;
2302
2303 lc_name = strlow_save(name);
2304 if (lc_name == NULL)
2305 return INVALCOLOR;
2306
2307 colentry = dict_find(colornames_table, lc_name, -1);
2308 vim_free(lc_name);
2309 if (colentry == NULL)
2310 return INVALCOLOR;
2311
2312 if (colentry->di_tv.v_type == VAR_STRING)
2313 {
2314 colstr = tv_get_string_strict(&colentry->di_tv);
2315 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2316 {
2317 return decode_hex_color(colstr);
2318 }
2319 else
2320 {
2321 semsg(_(e_bad_color_string_str), colstr);
2322 return INVALCOLOR;
2323 }
2324 }
2325
2326 if (colentry->di_tv.v_type == VAR_NUMBER)
2327 {
2328 colnum = tv_get_number(&colentry->di_tv);
2329 return (guicolor_T)colnum;
2330 }
2331
2332 return INVALCOLOR;
2333}
2334
2335// Maps the given name to the given color value, overwriting any current
2336// mapping. If allocation fails the named color will no longer exist in the
2337// table and the user will receive an error message.
2338 void
2339save_colorname_hexstr(int r, int g, int b, char_u *name)
2340{
2341 int result;
2342 dict_T *colornames_table;
2343 dictitem_T *existing;
2344 char_u hexstr[8];
2345
2346 if (vim_snprintf((char *)hexstr, sizeof(hexstr),
2347 "#%02x%02x%02x", r, g, b) < 0)
2348 {
2349 semsg(_(e_cannot_allocate_color_str), name);
2350 return;
2351 }
2352
2353 colornames_table = get_vim_var_dict(VV_COLORNAMES);
2354 // The colornames_table dict is safe to use here because it is allocated at
2355 // startup in evalvars.c
2356 existing = dict_find(colornames_table, name, -1);
2357 if (existing != NULL)
2358 {
2359 dictitem_remove(colornames_table, existing);
2360 existing = NULL; // dictitem_remove freed the item
2361 }
2362
2363 result = dict_add_string(colornames_table, (char *)name, hexstr);
2364 if (result == FAIL)
2365 semsg(_(e_cannot_allocate_color_str), name);
2366}
2367
2368/*
2369 * Load a default color list. Intended to support legacy color names but allows
2370 * the user to override the color values. Only loaded once.
2371 */
2372 void
2373load_default_colors_lists()
2374{
2375 // Lacking a default color list isn't the end of the world but it is likely
2376 // an inconvenience so users should know when it is missing.
2377 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
2378 msg("failed to load colors/lists/default.vim");
2379}
2380#endif
2381
2382 guicolor_T
2383gui_get_color_cmn(char_u *name)
2384{
2385 int i;
2386 guicolor_T color;
2387
2388 struct rgbcolor_table_S {
2389 char_u *color_name;
2390 guicolor_T color;
2391 };
2392
2393 // Only non X11 colors (not present in rgb.txt) and colors in
2394 // color_names[], useful when $VIMRUNTIME is not found,.
2395 static struct rgbcolor_table_S rgb_table[] = {
2396 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2397 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2398 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2399 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2400 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2401 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2402 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2403 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2404 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2405 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2406 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2407 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2408 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2409 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2410 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2411 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2412 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2413 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2414 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2415 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2416 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2417 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2418 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2419 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2420 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2421 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2422 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2423 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2424 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2425 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2426 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2427 };
2428
2429 color = decode_hex_color(name);
2430 if (color != INVALCOLOR)
2431 return color;
2432
2433 // Check if the name is one of the colors we know
2434 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2435 if (STRICMP(name, rgb_table[i].color_name) == 0)
2436 return gui_adjust_rgb(rgb_table[i].color);
2437
2438#if defined(FEAT_EVAL)
2439 /*
2440 * Not a traditional color. Load additional color aliases and then consult the alias table.
2441 */
2442
2443 color = colorname2rgb(name);
2444 if (color == INVALCOLOR)
2445 {
2446 load_default_colors_lists();
2447 color = colorname2rgb(name);
2448 }
2449
2450 return color;
2451#else
2452 return INVALCOLOR;
2453#endif
2454}
2455
2456 guicolor_T
2457gui_get_rgb_color_cmn(int r, int g, int b)
2458{
2459 guicolor_T color = RGB(r, g, b);
2460
2461 if (color > 0xffffff)
2462 return INVALCOLOR;
2463 return gui_adjust_rgb(color);
2464}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002465#endif
2466
2467/*
2468 * Table with the specifications for an attribute number.
2469 * Note that this table is used by ALL buffers. This is required because the
2470 * GUI can redraw at any time for any buffer.
2471 */
2472static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2473
2474#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2475
2476static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2477
2478#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2479
2480#ifdef FEAT_GUI
2481static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2482
2483#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2484#endif
2485
2486/*
2487 * Return the attr number for a set of colors and font.
2488 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2489 * if the combination is new.
2490 * Return 0 for error (no more room).
2491 */
2492 static int
2493get_attr_entry(garray_T *table, attrentry_T *aep)
2494{
2495 int i;
2496 attrentry_T *taep;
2497 static int recursive = FALSE;
2498
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002499 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002500 table->ga_itemsize = sizeof(attrentry_T);
2501 table->ga_growsize = 7;
2502
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002503 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002504 for (i = 0; i < table->ga_len; ++i)
2505 {
2506 taep = &(((attrentry_T *)table->ga_data)[i]);
2507 if ( aep->ae_attr == taep->ae_attr
2508 && (
2509#ifdef FEAT_GUI
2510 (table == &gui_attr_table
2511 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2512 && aep->ae_u.gui.bg_color
2513 == taep->ae_u.gui.bg_color
2514 && aep->ae_u.gui.sp_color
2515 == taep->ae_u.gui.sp_color
2516 && aep->ae_u.gui.font == taep->ae_u.gui.font
2517# ifdef FEAT_XFONTSET
2518 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2519# endif
2520 ))
2521 ||
2522#endif
2523 (table == &term_attr_table
2524 && (aep->ae_u.term.start == NULL)
2525 == (taep->ae_u.term.start == NULL)
2526 && (aep->ae_u.term.start == NULL
2527 || STRCMP(aep->ae_u.term.start,
2528 taep->ae_u.term.start) == 0)
2529 && (aep->ae_u.term.stop == NULL)
2530 == (taep->ae_u.term.stop == NULL)
2531 && (aep->ae_u.term.stop == NULL
2532 || STRCMP(aep->ae_u.term.stop,
2533 taep->ae_u.term.stop) == 0))
2534 || (table == &cterm_attr_table
2535 && aep->ae_u.cterm.fg_color
2536 == taep->ae_u.cterm.fg_color
2537 && aep->ae_u.cterm.bg_color
2538 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002539 && aep->ae_u.cterm.ul_color
2540 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002541#ifdef FEAT_TERMGUICOLORS
2542 && aep->ae_u.cterm.fg_rgb
2543 == taep->ae_u.cterm.fg_rgb
2544 && aep->ae_u.cterm.bg_rgb
2545 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002546 && aep->ae_u.cterm.ul_rgb
2547 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002548#endif
2549 )))
2550
2551 return i + ATTR_OFF;
2552 }
2553
2554 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2555 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002556 // Running out of attribute entries! remove all attributes, and
2557 // compute new ones for all groups.
2558 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002559 if (recursive)
2560 {
2561 emsg(_("E424: Too many different highlighting attributes in use"));
2562 return 0;
2563 }
2564 recursive = TRUE;
2565
2566 clear_hl_tables();
2567
2568 must_redraw = CLEAR;
2569
2570 for (i = 0; i < highlight_ga.ga_len; ++i)
2571 set_hl_attr(i);
2572
2573 recursive = FALSE;
2574 }
2575
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002576 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002577 if (ga_grow(table, 1) == FAIL)
2578 return 0;
2579
2580 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002581 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002582 taep->ae_attr = aep->ae_attr;
2583#ifdef FEAT_GUI
2584 if (table == &gui_attr_table)
2585 {
2586 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2587 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2588 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2589 taep->ae_u.gui.font = aep->ae_u.gui.font;
2590# ifdef FEAT_XFONTSET
2591 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2592# endif
2593 }
2594#endif
2595 if (table == &term_attr_table)
2596 {
2597 if (aep->ae_u.term.start == NULL)
2598 taep->ae_u.term.start = NULL;
2599 else
2600 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2601 if (aep->ae_u.term.stop == NULL)
2602 taep->ae_u.term.stop = NULL;
2603 else
2604 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2605 }
2606 else if (table == &cterm_attr_table)
2607 {
2608 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2609 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002610 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002611#ifdef FEAT_TERMGUICOLORS
2612 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2613 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002614 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002615#endif
2616 }
2617 ++table->ga_len;
2618 return (table->ga_len - 1 + ATTR_OFF);
2619}
2620
2621#if defined(FEAT_TERMINAL) || defined(PROTO)
2622/*
2623 * Get an attribute index for a cterm entry.
2624 * Uses an existing entry when possible or adds one when needed.
2625 */
2626 int
2627get_cterm_attr_idx(int attr, int fg, int bg)
2628{
2629 attrentry_T at_en;
2630
Bram Moolenaara80faa82020-04-12 19:37:17 +02002631 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002632#ifdef FEAT_TERMGUICOLORS
2633 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2634 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002635 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002636#endif
2637 at_en.ae_attr = attr;
2638 at_en.ae_u.cterm.fg_color = fg;
2639 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002640 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002641 return get_attr_entry(&cterm_attr_table, &at_en);
2642}
2643#endif
2644
2645#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2646/*
2647 * Get an attribute index for a 'termguicolors' entry.
2648 * Uses an existing entry when possible or adds one when needed.
2649 */
2650 int
2651get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2652{
2653 attrentry_T at_en;
2654
Bram Moolenaara80faa82020-04-12 19:37:17 +02002655 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002656 at_en.ae_attr = attr;
2657 if (fg == INVALCOLOR && bg == INVALCOLOR)
2658 {
2659 // If both GUI colors are not set fall back to the cterm colors. Helps
2660 // if the GUI only has an attribute, such as undercurl.
2661 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2662 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2663 }
2664 else
2665 {
2666 at_en.ae_u.cterm.fg_rgb = fg;
2667 at_en.ae_u.cterm.bg_rgb = bg;
2668 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002669 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002670 return get_attr_entry(&cterm_attr_table, &at_en);
2671}
2672#endif
2673
2674#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2675/*
2676 * Get an attribute index for a cterm entry.
2677 * Uses an existing entry when possible or adds one when needed.
2678 */
2679 int
2680get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2681{
2682 attrentry_T at_en;
2683
Bram Moolenaara80faa82020-04-12 19:37:17 +02002684 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002685 at_en.ae_attr = attr;
2686 at_en.ae_u.gui.fg_color = fg;
2687 at_en.ae_u.gui.bg_color = bg;
2688 return get_attr_entry(&gui_attr_table, &at_en);
2689}
2690#endif
2691
2692/*
2693 * Clear all highlight tables.
2694 */
2695 void
2696clear_hl_tables(void)
2697{
2698 int i;
2699 attrentry_T *taep;
2700
2701#ifdef FEAT_GUI
2702 ga_clear(&gui_attr_table);
2703#endif
2704 for (i = 0; i < term_attr_table.ga_len; ++i)
2705 {
2706 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2707 vim_free(taep->ae_u.term.start);
2708 vim_free(taep->ae_u.term.stop);
2709 }
2710 ga_clear(&term_attr_table);
2711 ga_clear(&cterm_attr_table);
2712}
2713
2714/*
2715 * Combine special attributes (e.g., for spelling) with other attributes
2716 * (e.g., for syntax highlighting).
2717 * "prim_attr" overrules "char_attr".
2718 * This creates a new group when required.
2719 * Since we expect there to be few spelling mistakes we don't cache the
2720 * result.
2721 * Return the resulting attributes.
2722 */
2723 int
2724hl_combine_attr(int char_attr, int prim_attr)
2725{
2726 attrentry_T *char_aep = NULL;
2727 attrentry_T *spell_aep;
2728 attrentry_T new_en;
2729
2730 if (char_attr == 0)
2731 return prim_attr;
2732 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2733 return ATTR_COMBINE(char_attr, prim_attr);
2734#ifdef FEAT_GUI
2735 if (gui.in_use)
2736 {
2737 if (char_attr > HL_ALL)
2738 char_aep = syn_gui_attr2entry(char_attr);
2739 if (char_aep != NULL)
2740 new_en = *char_aep;
2741 else
2742 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002743 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002744 new_en.ae_u.gui.fg_color = INVALCOLOR;
2745 new_en.ae_u.gui.bg_color = INVALCOLOR;
2746 new_en.ae_u.gui.sp_color = INVALCOLOR;
2747 if (char_attr <= HL_ALL)
2748 new_en.ae_attr = char_attr;
2749 }
2750
2751 if (prim_attr <= HL_ALL)
2752 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2753 else
2754 {
2755 spell_aep = syn_gui_attr2entry(prim_attr);
2756 if (spell_aep != NULL)
2757 {
2758 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2759 spell_aep->ae_attr);
2760 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2761 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2762 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2763 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2764 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2765 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2766 if (spell_aep->ae_u.gui.font != NOFONT)
2767 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2768# ifdef FEAT_XFONTSET
2769 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2770 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2771# endif
2772 }
2773 }
2774 return get_attr_entry(&gui_attr_table, &new_en);
2775 }
2776#endif
2777
2778 if (IS_CTERM)
2779 {
2780 if (char_attr > HL_ALL)
2781 char_aep = syn_cterm_attr2entry(char_attr);
2782 if (char_aep != NULL)
2783 new_en = *char_aep;
2784 else
2785 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002786 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002787#ifdef FEAT_TERMGUICOLORS
2788 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2789 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002790 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002791#endif
2792 if (char_attr <= HL_ALL)
2793 new_en.ae_attr = char_attr;
2794 }
2795
2796 if (prim_attr <= HL_ALL)
2797 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2798 else
2799 {
2800 spell_aep = syn_cterm_attr2entry(prim_attr);
2801 if (spell_aep != NULL)
2802 {
2803 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2804 spell_aep->ae_attr);
2805 if (spell_aep->ae_u.cterm.fg_color > 0)
2806 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2807 if (spell_aep->ae_u.cterm.bg_color > 0)
2808 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002809 if (spell_aep->ae_u.cterm.ul_color > 0)
2810 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002811#ifdef FEAT_TERMGUICOLORS
2812 // If both fg and bg are not set fall back to cterm colors.
2813 // Helps for SpellBad which uses undercurl in the GUI.
2814 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2815 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2816 {
2817 if (spell_aep->ae_u.cterm.fg_color > 0)
2818 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2819 if (spell_aep->ae_u.cterm.bg_color > 0)
2820 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2821 }
2822 else
2823 {
2824 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2825 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2826 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2827 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2828 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002829 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2830 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002831#endif
2832 }
2833 }
2834 return get_attr_entry(&cterm_attr_table, &new_en);
2835 }
2836
2837 if (char_attr > HL_ALL)
2838 char_aep = syn_term_attr2entry(char_attr);
2839 if (char_aep != NULL)
2840 new_en = *char_aep;
2841 else
2842 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002843 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002844 if (char_attr <= HL_ALL)
2845 new_en.ae_attr = char_attr;
2846 }
2847
2848 if (prim_attr <= HL_ALL)
2849 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2850 else
2851 {
2852 spell_aep = syn_term_attr2entry(prim_attr);
2853 if (spell_aep != NULL)
2854 {
2855 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2856 if (spell_aep->ae_u.term.start != NULL)
2857 {
2858 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2859 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2860 }
2861 }
2862 }
2863 return get_attr_entry(&term_attr_table, &new_en);
2864}
2865
2866#ifdef FEAT_GUI
2867 attrentry_T *
2868syn_gui_attr2entry(int attr)
2869{
2870 attr -= ATTR_OFF;
2871 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2872 return NULL;
2873 return &(GUI_ATTR_ENTRY(attr));
2874}
2875#endif
2876
2877/*
2878 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2879 * Only to be used when "attr" > HL_ALL.
2880 */
2881 int
2882syn_attr2attr(int attr)
2883{
2884 attrentry_T *aep;
2885
2886#ifdef FEAT_GUI
2887 if (gui.in_use)
2888 aep = syn_gui_attr2entry(attr);
2889 else
2890#endif
2891 if (IS_CTERM)
2892 aep = syn_cterm_attr2entry(attr);
2893 else
2894 aep = syn_term_attr2entry(attr);
2895
2896 if (aep == NULL) // highlighting not set
2897 return 0;
2898 return aep->ae_attr;
2899}
2900
2901
2902 attrentry_T *
2903syn_term_attr2entry(int attr)
2904{
2905 attr -= ATTR_OFF;
2906 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2907 return NULL;
2908 return &(TERM_ATTR_ENTRY(attr));
2909}
2910
2911 attrentry_T *
2912syn_cterm_attr2entry(int attr)
2913{
2914 attr -= ATTR_OFF;
2915 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2916 return NULL;
2917 return &(CTERM_ATTR_ENTRY(attr));
2918}
2919
2920#define LIST_ATTR 1
2921#define LIST_STRING 2
2922#define LIST_INT 3
2923
2924 static void
2925highlight_list_one(int id)
2926{
2927 hl_group_T *sgp;
2928 int didh = FALSE;
2929
2930 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2931
2932 if (message_filtered(sgp->sg_name))
2933 return;
2934
2935 didh = highlight_list_arg(id, didh, LIST_ATTR,
2936 sgp->sg_term, NULL, "term");
2937 didh = highlight_list_arg(id, didh, LIST_STRING,
2938 0, sgp->sg_start, "start");
2939 didh = highlight_list_arg(id, didh, LIST_STRING,
2940 0, sgp->sg_stop, "stop");
2941
2942 didh = highlight_list_arg(id, didh, LIST_ATTR,
2943 sgp->sg_cterm, NULL, "cterm");
2944 didh = highlight_list_arg(id, didh, LIST_INT,
2945 sgp->sg_cterm_fg, NULL, "ctermfg");
2946 didh = highlight_list_arg(id, didh, LIST_INT,
2947 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002948 didh = highlight_list_arg(id, didh, LIST_INT,
2949 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002950
2951#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2952 didh = highlight_list_arg(id, didh, LIST_ATTR,
2953 sgp->sg_gui, NULL, "gui");
2954 didh = highlight_list_arg(id, didh, LIST_STRING,
2955 0, sgp->sg_gui_fg_name, "guifg");
2956 didh = highlight_list_arg(id, didh, LIST_STRING,
2957 0, sgp->sg_gui_bg_name, "guibg");
2958 didh = highlight_list_arg(id, didh, LIST_STRING,
2959 0, sgp->sg_gui_sp_name, "guisp");
2960#endif
2961#ifdef FEAT_GUI
2962 didh = highlight_list_arg(id, didh, LIST_STRING,
2963 0, sgp->sg_font_name, "font");
2964#endif
2965
2966 if (sgp->sg_link && !got_int)
2967 {
2968 (void)syn_list_header(didh, 9999, id);
2969 didh = TRUE;
2970 msg_puts_attr("links to", HL_ATTR(HLF_D));
2971 msg_putchar(' ');
2972 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2973 }
2974
2975 if (!didh)
2976 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2977#ifdef FEAT_EVAL
2978 if (p_verbose > 0)
2979 last_set_msg(sgp->sg_script_ctx);
2980#endif
2981}
2982
2983 static int
2984highlight_list_arg(
2985 int id,
2986 int didh,
2987 int type,
2988 int iarg,
2989 char_u *sarg,
2990 char *name)
2991{
2992 char_u buf[100];
2993 char_u *ts;
2994 int i;
2995
2996 if (got_int)
2997 return FALSE;
2998 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2999 {
3000 ts = buf;
3001 if (type == LIST_INT)
3002 sprintf((char *)buf, "%d", iarg - 1);
3003 else if (type == LIST_STRING)
3004 ts = sarg;
3005 else // type == LIST_ATTR
3006 {
3007 buf[0] = NUL;
3008 for (i = 0; hl_attr_table[i] != 0; ++i)
3009 {
3010 if (iarg & hl_attr_table[i])
3011 {
3012 if (buf[0] != NUL)
3013 vim_strcat(buf, (char_u *)",", 100);
3014 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
3015 iarg &= ~hl_attr_table[i]; // don't want "inverse"
3016 }
3017 }
3018 }
3019
3020 (void)syn_list_header(didh,
3021 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3022 didh = TRUE;
3023 if (!got_int)
3024 {
3025 if (*name != NUL)
3026 {
3027 msg_puts_attr(name, HL_ATTR(HLF_D));
3028 msg_puts_attr("=", HL_ATTR(HLF_D));
3029 }
3030 msg_outtrans(ts);
3031 }
3032 }
3033 return didh;
3034}
3035
3036#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3037/*
3038 * Return "1" if highlight group "id" has attribute "flag".
3039 * Return NULL otherwise.
3040 */
3041 char_u *
3042highlight_has_attr(
3043 int id,
3044 int flag,
3045 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3046{
3047 int attr;
3048
3049 if (id <= 0 || id > highlight_ga.ga_len)
3050 return NULL;
3051
3052#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3053 if (modec == 'g')
3054 attr = HL_TABLE()[id - 1].sg_gui;
3055 else
3056#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003057 {
3058 if (modec == 'c')
3059 attr = HL_TABLE()[id - 1].sg_cterm;
3060 else
3061 attr = HL_TABLE()[id - 1].sg_term;
3062 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003063
3064 if (attr & flag)
3065 return (char_u *)"1";
3066 return NULL;
3067}
3068#endif
3069
3070#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3071/*
3072 * Return color name of highlight group "id".
3073 */
3074 char_u *
3075highlight_color(
3076 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003077 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003078 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3079{
3080 static char_u name[20];
3081 int n;
3082 int fg = FALSE;
3083 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003084 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003085 int font = FALSE;
3086
3087 if (id <= 0 || id > highlight_ga.ga_len)
3088 return NULL;
3089
3090 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3091 fg = TRUE;
3092 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3093 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3094 font = TRUE;
3095 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3096 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003097 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3098 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003099 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3100 return NULL;
3101 if (modec == 'g')
3102 {
3103# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3104# ifdef FEAT_GUI
3105 // return font name
3106 if (font)
3107 return HL_TABLE()[id - 1].sg_font_name;
3108# endif
3109
3110 // return #RRGGBB form (only possible when GUI is running)
3111 if ((USE_24BIT) && what[2] == '#')
3112 {
3113 guicolor_T color;
3114 long_u rgb;
3115 static char_u buf[10];
3116
3117 if (fg)
3118 color = HL_TABLE()[id - 1].sg_gui_fg;
3119 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003120 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003121 else
3122 color = HL_TABLE()[id - 1].sg_gui_bg;
3123 if (color == INVALCOLOR)
3124 return NULL;
3125 rgb = (long_u)GUI_MCH_GET_RGB(color);
3126 sprintf((char *)buf, "#%02x%02x%02x",
3127 (unsigned)(rgb >> 16),
3128 (unsigned)(rgb >> 8) & 255,
3129 (unsigned)rgb & 255);
3130 return buf;
3131 }
3132# endif
3133 if (fg)
3134 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3135 if (sp)
3136 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3137 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3138 }
3139 if (font || sp)
3140 return NULL;
3141 if (modec == 'c')
3142 {
3143 if (fg)
3144 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003145 else if (ul)
3146 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003147 else
3148 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3149 if (n < 0)
3150 return NULL;
3151 sprintf((char *)name, "%d", n);
3152 return name;
3153 }
3154 // term doesn't have color
3155 return NULL;
3156}
3157#endif
3158
3159#if (defined(FEAT_SYN_HL) \
3160 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3161 && defined(FEAT_PRINTER)) || defined(PROTO)
3162/*
3163 * Return color name of highlight group "id" as RGB value.
3164 */
3165 long_u
3166highlight_gui_color_rgb(
3167 int id,
3168 int fg) // TRUE = fg, FALSE = bg
3169{
3170 guicolor_T color;
3171
3172 if (id <= 0 || id > highlight_ga.ga_len)
3173 return 0L;
3174
3175 if (fg)
3176 color = HL_TABLE()[id - 1].sg_gui_fg;
3177 else
3178 color = HL_TABLE()[id - 1].sg_gui_bg;
3179
3180 if (color == INVALCOLOR)
3181 return 0L;
3182
3183 return GUI_MCH_GET_RGB(color);
3184}
3185#endif
3186
3187/*
3188 * Output the syntax list header.
3189 * Return TRUE when started a new line.
3190 */
3191 int
3192syn_list_header(
3193 int did_header, // did header already
3194 int outlen, // length of string that comes
3195 int id) // highlight group id
3196{
3197 int endcol = 19;
3198 int newline = TRUE;
3199 int name_col = 0;
3200
3201 if (!did_header)
3202 {
3203 msg_putchar('\n');
3204 if (got_int)
3205 return TRUE;
3206 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3207 name_col = msg_col;
3208 endcol = 15;
3209 }
3210 else if (msg_col + outlen + 1 >= Columns)
3211 {
3212 msg_putchar('\n');
3213 if (got_int)
3214 return TRUE;
3215 }
3216 else
3217 {
3218 if (msg_col >= endcol) // wrap around is like starting a new line
3219 newline = FALSE;
3220 }
3221
3222 if (msg_col >= endcol) // output at least one space
3223 endcol = msg_col + 1;
3224 if (Columns <= endcol) // avoid hang for tiny window
3225 endcol = Columns - 1;
3226
3227 msg_advance(endcol);
3228
3229 // Show "xxx" with the attributes.
3230 if (!did_header)
3231 {
3232 if (endcol == Columns - 1 && endcol <= name_col)
3233 msg_putchar(' ');
3234 msg_puts_attr("xxx", syn_id2attr(id));
3235 msg_putchar(' ');
3236 }
3237
3238 return newline;
3239}
3240
3241/*
3242 * Set the attribute numbers for a highlight group.
3243 * Called after one of the attributes has changed.
3244 */
3245 static void
3246set_hl_attr(
3247 int idx) // index in array
3248{
3249 attrentry_T at_en;
3250 hl_group_T *sgp = HL_TABLE() + idx;
3251
3252 // The "Normal" group doesn't need an attribute number
3253 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3254 return;
3255
3256#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003257 // For the GUI mode: If there are other than "normal" highlighting
3258 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003259 if (sgp->sg_gui_fg == INVALCOLOR
3260 && sgp->sg_gui_bg == INVALCOLOR
3261 && sgp->sg_gui_sp == INVALCOLOR
3262 && sgp->sg_font == NOFONT
3263# ifdef FEAT_XFONTSET
3264 && sgp->sg_fontset == NOFONTSET
3265# endif
3266 )
3267 {
3268 sgp->sg_gui_attr = sgp->sg_gui;
3269 }
3270 else
3271 {
3272 at_en.ae_attr = sgp->sg_gui;
3273 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3274 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3275 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3276 at_en.ae_u.gui.font = sgp->sg_font;
3277# ifdef FEAT_XFONTSET
3278 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3279# endif
3280 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3281 }
3282#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003283 // For the term mode: If there are other than "normal" highlighting
3284 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003285 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3286 sgp->sg_term_attr = sgp->sg_term;
3287 else
3288 {
3289 at_en.ae_attr = sgp->sg_term;
3290 at_en.ae_u.term.start = sgp->sg_start;
3291 at_en.ae_u.term.stop = sgp->sg_stop;
3292 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3293 }
3294
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003295 // For the color term mode: If there are other than "normal"
3296 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003297 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003298# ifdef FEAT_TERMGUICOLORS
3299 && sgp->sg_gui_fg == INVALCOLOR
3300 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003301 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003302# endif
3303 )
3304 sgp->sg_cterm_attr = sgp->sg_cterm;
3305 else
3306 {
3307 at_en.ae_attr = sgp->sg_cterm;
3308 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3309 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003310 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003311# ifdef FEAT_TERMGUICOLORS
3312# ifdef MSWIN
3313# ifdef VIMDLL
3314 // Only when not using the GUI.
3315 if (!gui.in_use && !gui.starting)
3316# endif
3317 {
3318 int id;
3319 guicolor_T fg, bg;
3320
3321 id = syn_name2id((char_u *)"Normal");
3322 if (id > 0)
3323 {
3324 syn_id2colors(id, &fg, &bg);
3325 if (sgp->sg_gui_fg == INVALCOLOR)
3326 sgp->sg_gui_fg = fg;
3327 if (sgp->sg_gui_bg == INVALCOLOR)
3328 sgp->sg_gui_bg = bg;
3329 }
3330
3331 }
3332# endif
3333 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3334 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003335 // Only use the underline/undercurl color when used, it may clear the
3336 // background color if not supported.
3337 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
3338 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3339 else
3340 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003341 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3342 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3343 {
3344 // If both fg and bg are invalid fall back to the cterm colors.
3345 // Helps when the GUI only uses an attribute, e.g. undercurl.
3346 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3347 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3348 }
3349# endif
3350 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3351 }
3352}
3353
3354/*
3355 * Lookup a highlight group name and return its ID.
3356 * If it is not found, 0 is returned.
3357 */
3358 int
3359syn_name2id(char_u *name)
3360{
3361 int i;
3362 char_u name_u[200];
3363
3364 // Avoid using stricmp() too much, it's slow on some systems
3365 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3366 // don't deserve to be found!
3367 vim_strncpy(name_u, name, 199);
3368 vim_strup(name_u);
3369 for (i = highlight_ga.ga_len; --i >= 0; )
3370 if (HL_TABLE()[i].sg_name_u != NULL
3371 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3372 break;
3373 return i + 1;
3374}
3375
3376/*
3377 * Lookup a highlight group name and return its attributes.
3378 * Return zero if not found.
3379 */
3380 int
3381syn_name2attr(char_u *name)
3382{
3383 int id = syn_name2id(name);
3384
3385 if (id != 0)
3386 return syn_id2attr(id);
3387 return 0;
3388}
3389
3390#if defined(FEAT_EVAL) || defined(PROTO)
3391/*
3392 * Return TRUE if highlight group "name" exists.
3393 */
3394 int
3395highlight_exists(char_u *name)
3396{
3397 return (syn_name2id(name) > 0);
3398}
3399
3400# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3401/*
3402 * Return the name of highlight group "id".
3403 * When not a valid ID return an empty string.
3404 */
3405 char_u *
3406syn_id2name(int id)
3407{
3408 if (id <= 0 || id > highlight_ga.ga_len)
3409 return (char_u *)"";
3410 return HL_TABLE()[id - 1].sg_name;
3411}
3412# endif
3413#endif
3414
3415/*
3416 * Like syn_name2id(), but take a pointer + length argument.
3417 */
3418 int
3419syn_namen2id(char_u *linep, int len)
3420{
3421 char_u *name;
3422 int id = 0;
3423
3424 name = vim_strnsave(linep, len);
3425 if (name != NULL)
3426 {
3427 id = syn_name2id(name);
3428 vim_free(name);
3429 }
3430 return id;
3431}
3432
3433/*
3434 * Find highlight group name in the table and return its ID.
3435 * The argument is a pointer to the name and the length of the name.
3436 * If it doesn't exist yet, a new entry is created.
3437 * Return 0 for failure.
3438 */
3439 int
3440syn_check_group(char_u *pp, int len)
3441{
3442 int id;
3443 char_u *name;
3444
3445 name = vim_strnsave(pp, len);
3446 if (name == NULL)
3447 return 0;
3448
3449 id = syn_name2id(name);
3450 if (id == 0) // doesn't exist yet
3451 id = syn_add_group(name);
3452 else
3453 vim_free(name);
3454 return id;
3455}
3456
3457/*
3458 * Add new highlight group and return its ID.
3459 * "name" must be an allocated string, it will be consumed.
3460 * Return 0 for failure.
3461 */
3462 static int
3463syn_add_group(char_u *name)
3464{
3465 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003466 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003467
3468 // Check that the name is ASCII letters, digits and underscore.
3469 for (p = name; *p != NUL; ++p)
3470 {
3471 if (!vim_isprintc(*p))
3472 {
3473 emsg(_("E669: Unprintable character in group name"));
3474 vim_free(name);
3475 return 0;
3476 }
3477 else if (!ASCII_ISALNUM(*p) && *p != '_')
3478 {
3479 // This is an error, but since there previously was no check only
3480 // give a warning.
3481 msg_source(HL_ATTR(HLF_W));
3482 msg(_("W18: Invalid character in group name"));
3483 break;
3484 }
3485 }
3486
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003487 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003488 if (highlight_ga.ga_data == NULL)
3489 {
3490 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3491 highlight_ga.ga_growsize = 10;
3492 }
3493
3494 if (highlight_ga.ga_len >= MAX_HL_ID)
3495 {
3496 emsg(_("E849: Too many highlight and syntax groups"));
3497 vim_free(name);
3498 return 0;
3499 }
3500
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003501 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003502 if (ga_grow(&highlight_ga, 1) == FAIL)
3503 {
3504 vim_free(name);
3505 return 0;
3506 }
3507
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003508 name_up = vim_strsave_up(name);
3509 if (name_up == NULL)
3510 {
3511 vim_free(name);
3512 return 0;
3513 }
3514
Bram Moolenaara80faa82020-04-12 19:37:17 +02003515 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003516 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003517 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003518#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3519 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3520 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003521 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003522#endif
3523 ++highlight_ga.ga_len;
3524
3525 return highlight_ga.ga_len; // ID is index plus one
3526}
3527
3528/*
3529 * When, just after calling syn_add_group(), an error is discovered, this
3530 * function deletes the new name.
3531 */
3532 static void
3533syn_unadd_group(void)
3534{
3535 --highlight_ga.ga_len;
3536 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3537 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3538}
3539
3540/*
3541 * Translate a group ID to highlight attributes.
3542 */
3543 int
3544syn_id2attr(int hl_id)
3545{
3546 int attr;
3547 hl_group_T *sgp;
3548
3549 hl_id = syn_get_final_id(hl_id);
3550 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3551
3552#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003553 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003554 if (gui.in_use)
3555 attr = sgp->sg_gui_attr;
3556 else
3557#endif
3558 if (IS_CTERM)
3559 attr = sgp->sg_cterm_attr;
3560 else
3561 attr = sgp->sg_term_attr;
3562
3563 return attr;
3564}
3565
3566#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3567/*
3568 * Get the GUI colors and attributes for a group ID.
3569 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3570 */
3571 int
3572syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3573{
3574 hl_group_T *sgp;
3575
3576 hl_id = syn_get_final_id(hl_id);
3577 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3578
3579 *fgp = sgp->sg_gui_fg;
3580 *bgp = sgp->sg_gui_bg;
3581 return sgp->sg_gui;
3582}
3583#endif
3584
3585#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003586 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3587 && defined(FEAT_TERMGUICOLORS)) \
3588 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003589 void
3590syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3591{
3592 hl_group_T *sgp;
3593
3594 hl_id = syn_get_final_id(hl_id);
3595 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3596 *fgp = sgp->sg_cterm_fg - 1;
3597 *bgp = sgp->sg_cterm_bg - 1;
3598}
3599#endif
3600
3601/*
3602 * Translate a group ID to the final group ID (following links).
3603 */
3604 int
3605syn_get_final_id(int hl_id)
3606{
3607 int count;
3608 hl_group_T *sgp;
3609
3610 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3611 return 0; // Can be called from eval!!
3612
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003613 // Follow links until there is no more.
3614 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003615 for (count = 100; --count >= 0; )
3616 {
3617 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3618 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3619 break;
3620 hl_id = sgp->sg_link;
3621 }
3622
3623 return hl_id;
3624}
3625
3626#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3627/*
3628 * Call this function just after the GUI has started.
3629 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3630 * It finds the font and color handles for the highlighting groups.
3631 */
3632 void
3633highlight_gui_started(void)
3634{
3635 int idx;
3636
3637 // First get the colors from the "Normal" and "Menu" group, if set
3638 if (USE_24BIT)
3639 set_normal_colors();
3640
3641 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3642 gui_do_one_color(idx, FALSE, FALSE);
3643
3644 highlight_changed();
3645}
3646
3647 static void
3648gui_do_one_color(
3649 int idx,
3650 int do_menu UNUSED, // TRUE: might set the menu font
3651 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3652{
3653 int didit = FALSE;
3654
3655# ifdef FEAT_GUI
3656# ifdef FEAT_TERMGUICOLORS
3657 if (gui.in_use)
3658# endif
3659 if (HL_TABLE()[idx].sg_font_name != NULL)
3660 {
3661 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3662 do_tooltip, TRUE);
3663 didit = TRUE;
3664 }
3665# endif
3666 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3667 {
3668 HL_TABLE()[idx].sg_gui_fg =
3669 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3670 didit = TRUE;
3671 }
3672 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3673 {
3674 HL_TABLE()[idx].sg_gui_bg =
3675 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3676 didit = TRUE;
3677 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003678 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3679 {
3680 HL_TABLE()[idx].sg_gui_sp =
3681 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3682 didit = TRUE;
3683 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003684 if (didit) // need to get a new attr number
3685 set_hl_attr(idx);
3686}
3687#endif
3688
3689#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3690/*
3691 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3692 */
3693 static void
3694combine_stl_hlt(
3695 int id,
3696 int id_S,
3697 int id_alt,
3698 int hlcnt,
3699 int i,
3700 int hlf,
3701 int *table)
3702{
3703 hl_group_T *hlt = HL_TABLE();
3704
3705 if (id_alt == 0)
3706 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003707 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003708 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3709 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3710# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3711 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3712# endif
3713 }
3714 else
3715 mch_memmove(&hlt[hlcnt + i],
3716 &hlt[id_alt - 1],
3717 sizeof(hl_group_T));
3718 hlt[hlcnt + i].sg_link = 0;
3719
3720 hlt[hlcnt + i].sg_term ^=
3721 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3722 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3723 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3724 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3725 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3726 hlt[hlcnt + i].sg_cterm ^=
3727 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3728 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3729 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3730 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3731 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3732# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3733 hlt[hlcnt + i].sg_gui ^=
3734 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3735# endif
3736# ifdef FEAT_GUI
3737 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3738 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3739 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3740 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3741 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3742 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3743 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3744 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3745# ifdef FEAT_XFONTSET
3746 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3747 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3748# endif
3749# endif
3750 highlight_ga.ga_len = hlcnt + i + 1;
3751 set_hl_attr(hlcnt + i); // At long last we can apply
3752 table[i] = syn_id2attr(hlcnt + i + 1);
3753}
3754#endif
3755
3756/*
3757 * Translate the 'highlight' option into attributes in highlight_attr[] and
3758 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3759 * corresponding highlights to use on top of HLF_SNC is computed.
3760 * Called only when the 'highlight' option has been changed and upon first
3761 * screen redraw after any :highlight command.
3762 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3763 */
3764 int
3765highlight_changed(void)
3766{
3767 int hlf;
3768 int i;
3769 char_u *p;
3770 int attr;
3771 char_u *end;
3772 int id;
3773#ifdef USER_HIGHLIGHT
3774 char_u userhl[30]; // use 30 to avoid compiler warning
3775# ifdef FEAT_STL_OPT
3776 int id_S = -1;
3777 int id_SNC = 0;
3778# ifdef FEAT_TERMINAL
3779 int id_ST = 0;
3780 int id_STNC = 0;
3781# endif
3782 int hlcnt;
3783# endif
3784#endif
3785 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3786
3787 need_highlight_changed = FALSE;
3788
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003789 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003790 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3791 highlight_attr[hlf] = 0;
3792
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003793 // First set all attributes to their default value.
3794 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003795 for (i = 0; i < 2; ++i)
3796 {
3797 if (i)
3798 p = p_hl;
3799 else
3800 p = get_highlight_default();
3801 if (p == NULL) // just in case
3802 continue;
3803
3804 while (*p)
3805 {
3806 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3807 if (hl_flags[hlf] == *p)
3808 break;
3809 ++p;
3810 if (hlf == (int)HLF_COUNT || *p == NUL)
3811 return FAIL;
3812
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003813 // Allow several hl_flags to be combined, like "bu" for
3814 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003815 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003816 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003817 {
3818 if (VIM_ISWHITE(*p)) // ignore white space
3819 continue;
3820
3821 if (attr > HL_ALL) // Combination with ':' is not allowed.
3822 return FAIL;
3823
3824 switch (*p)
3825 {
3826 case 'b': attr |= HL_BOLD;
3827 break;
3828 case 'i': attr |= HL_ITALIC;
3829 break;
3830 case '-':
3831 case 'n': // no highlighting
3832 break;
3833 case 'r': attr |= HL_INVERSE;
3834 break;
3835 case 's': attr |= HL_STANDOUT;
3836 break;
3837 case 'u': attr |= HL_UNDERLINE;
3838 break;
3839 case 'c': attr |= HL_UNDERCURL;
3840 break;
3841 case 't': attr |= HL_STRIKETHROUGH;
3842 break;
3843 case ':': ++p; // highlight group name
3844 if (attr || *p == NUL) // no combinations
3845 return FAIL;
3846 end = vim_strchr(p, ',');
3847 if (end == NULL)
3848 end = p + STRLEN(p);
3849 id = syn_check_group(p, (int)(end - p));
3850 if (id == 0)
3851 return FAIL;
3852 attr = syn_id2attr(id);
3853 p = end - 1;
3854#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3855 if (hlf == (int)HLF_SNC)
3856 id_SNC = syn_get_final_id(id);
3857# ifdef FEAT_TERMINAL
3858 else if (hlf == (int)HLF_ST)
3859 id_ST = syn_get_final_id(id);
3860 else if (hlf == (int)HLF_STNC)
3861 id_STNC = syn_get_final_id(id);
3862# endif
3863 else if (hlf == (int)HLF_S)
3864 id_S = syn_get_final_id(id);
3865#endif
3866 break;
3867 default: return FAIL;
3868 }
3869 }
3870 highlight_attr[hlf] = attr;
3871
3872 p = skip_to_option_part(p); // skip comma and spaces
3873 }
3874 }
3875
3876#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003877 // Setup the user highlights
3878 //
3879 // Temporarily utilize 28 more hl entries:
3880 // 9 for User1-User9 combined with StatusLineNC
3881 // 9 for User1-User9 combined with StatusLineTerm
3882 // 9 for User1-User9 combined with StatusLineTermNC
3883 // 1 for StatusLine default
3884 // Have to be in there simultaneously in case of table overflows in
3885 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003886# ifdef FEAT_STL_OPT
3887 if (ga_grow(&highlight_ga, 28) == FAIL)
3888 return FAIL;
3889 hlcnt = highlight_ga.ga_len;
3890 if (id_S == -1)
3891 {
3892 // Make sure id_S is always valid to simplify code below. Use the last
3893 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003894 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003895 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3896 id_S = hlcnt + 19;
3897 }
3898# endif
3899 for (i = 0; i < 9; i++)
3900 {
3901 sprintf((char *)userhl, "User%d", i + 1);
3902 id = syn_name2id(userhl);
3903 if (id == 0)
3904 {
3905 highlight_user[i] = 0;
3906# ifdef FEAT_STL_OPT
3907 highlight_stlnc[i] = 0;
3908# ifdef FEAT_TERMINAL
3909 highlight_stlterm[i] = 0;
3910 highlight_stltermnc[i] = 0;
3911# endif
3912# endif
3913 }
3914 else
3915 {
3916 highlight_user[i] = syn_id2attr(id);
3917# ifdef FEAT_STL_OPT
3918 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3919 HLF_SNC, highlight_stlnc);
3920# ifdef FEAT_TERMINAL
3921 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3922 HLF_ST, highlight_stlterm);
3923 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3924 HLF_STNC, highlight_stltermnc);
3925# endif
3926# endif
3927 }
3928 }
3929# ifdef FEAT_STL_OPT
3930 highlight_ga.ga_len = hlcnt;
3931# endif
3932
3933#endif // USER_HIGHLIGHT
3934
3935 return OK;
3936}
3937
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003938static void highlight_list(void);
3939static void highlight_list_two(int cnt, int attr);
3940
3941/*
3942 * Handle command line completion for :highlight command.
3943 */
3944 void
3945set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3946{
3947 char_u *p;
3948
3949 // Default: expand group names
3950 xp->xp_context = EXPAND_HIGHLIGHT;
3951 xp->xp_pattern = arg;
3952 include_link = 2;
3953 include_default = 1;
3954
3955 // (part of) subcommand already typed
3956 if (*arg != NUL)
3957 {
3958 p = skiptowhite(arg);
3959 if (*p != NUL) // past "default" or group name
3960 {
3961 include_default = 0;
3962 if (STRNCMP("default", arg, p - arg) == 0)
3963 {
3964 arg = skipwhite(p);
3965 xp->xp_pattern = arg;
3966 p = skiptowhite(arg);
3967 }
3968 if (*p != NUL) // past group name
3969 {
3970 include_link = 0;
3971 if (arg[1] == 'i' && arg[0] == 'N')
3972 highlight_list();
3973 if (STRNCMP("link", arg, p - arg) == 0
3974 || STRNCMP("clear", arg, p - arg) == 0)
3975 {
3976 xp->xp_pattern = skipwhite(p);
3977 p = skiptowhite(xp->xp_pattern);
3978 if (*p != NUL) // past first group name
3979 {
3980 xp->xp_pattern = skipwhite(p);
3981 p = skiptowhite(xp->xp_pattern);
3982 }
3983 }
3984 if (*p != NUL) // past group name(s)
3985 xp->xp_context = EXPAND_NOTHING;
3986 }
3987 }
3988 }
3989}
3990
3991/*
3992 * List highlighting matches in a nice way.
3993 */
3994 static void
3995highlight_list(void)
3996{
3997 int i;
3998
3999 for (i = 10; --i >= 0; )
4000 highlight_list_two(i, HL_ATTR(HLF_D));
4001 for (i = 40; --i >= 0; )
4002 highlight_list_two(99, 0);
4003}
4004
4005 static void
4006highlight_list_two(int cnt, int attr)
4007{
4008 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4009 msg_clr_eos();
4010 out_flush();
4011 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4012}
4013
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004014/*
4015 * Function given to ExpandGeneric() to obtain the list of group names.
4016 */
4017 char_u *
4018get_highlight_name(expand_T *xp UNUSED, int idx)
4019{
4020 return get_highlight_name_ext(xp, idx, TRUE);
4021}
4022
4023/*
4024 * Obtain a highlight group name.
4025 * When "skip_cleared" is TRUE don't return a cleared entry.
4026 */
4027 char_u *
4028get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4029{
4030 if (idx < 0)
4031 return NULL;
4032
4033 // Items are never removed from the table, skip the ones that were
4034 // cleared.
4035 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4036 return (char_u *)"";
4037
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004038 if (idx == highlight_ga.ga_len && include_none != 0)
4039 return (char_u *)"none";
4040 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4041 return (char_u *)"default";
4042 if (idx == highlight_ga.ga_len + include_none + include_default
4043 && include_link != 0)
4044 return (char_u *)"link";
4045 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4046 && include_link != 0)
4047 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004048 if (idx >= highlight_ga.ga_len)
4049 return NULL;
4050 return HL_TABLE()[idx].sg_name;
4051}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004052
4053#if defined(FEAT_GUI) || defined(PROTO)
4054/*
4055 * Free all the highlight group fonts.
4056 * Used when quitting for systems which need it.
4057 */
4058 void
4059free_highlight_fonts(void)
4060{
4061 int idx;
4062
4063 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4064 {
4065 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4066 HL_TABLE()[idx].sg_font = NOFONT;
4067# ifdef FEAT_XFONTSET
4068 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4069 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4070# endif
4071 }
4072
4073 gui_mch_free_font(gui.norm_font);
4074# ifdef FEAT_XFONTSET
4075 gui_mch_free_fontset(gui.fontset);
4076# endif
4077# ifndef FEAT_GUI_GTK
4078 gui_mch_free_font(gui.bold_font);
4079 gui_mch_free_font(gui.ital_font);
4080 gui_mch_free_font(gui.boldital_font);
4081# endif
4082}
4083#endif