blob: 77ad0bc78c1466822f4dfb1577a37ca3fdb1ae6e [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 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100478#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100479 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
Bram Moolenaar2a521962021-10-25 10:30:14 +01002289#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002290// 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
Drew Vogele30d1022021-10-24 20:35:07 +01002335/*
2336 * Load a default color list. Intended to support legacy color names but allows
2337 * the user to override the color values. Only loaded once.
2338 */
2339 void
2340load_default_colors_lists()
2341{
2342 // Lacking a default color list isn't the end of the world but it is likely
2343 // an inconvenience so users should know when it is missing.
2344 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
2345 msg("failed to load colors/lists/default.vim");
2346}
2347#endif
2348
2349 guicolor_T
2350gui_get_color_cmn(char_u *name)
2351{
2352 int i;
2353 guicolor_T color;
2354
2355 struct rgbcolor_table_S {
2356 char_u *color_name;
2357 guicolor_T color;
2358 };
2359
2360 // Only non X11 colors (not present in rgb.txt) and colors in
2361 // color_names[], useful when $VIMRUNTIME is not found,.
2362 static struct rgbcolor_table_S rgb_table[] = {
2363 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2364 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2365 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2366 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2367 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2368 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2369 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2370 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2371 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2372 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2373 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2374 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2375 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2376 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2377 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2378 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2379 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2380 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2381 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2382 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2383 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2384 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2385 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2386 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2387 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2388 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2389 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2390 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2391 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2392 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2393 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2394 };
2395
2396 color = decode_hex_color(name);
2397 if (color != INVALCOLOR)
2398 return color;
2399
2400 // Check if the name is one of the colors we know
2401 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2402 if (STRICMP(name, rgb_table[i].color_name) == 0)
2403 return gui_adjust_rgb(rgb_table[i].color);
2404
2405#if defined(FEAT_EVAL)
2406 /*
2407 * Not a traditional color. Load additional color aliases and then consult the alias table.
2408 */
2409
2410 color = colorname2rgb(name);
2411 if (color == INVALCOLOR)
2412 {
2413 load_default_colors_lists();
2414 color = colorname2rgb(name);
2415 }
2416
2417 return color;
2418#else
2419 return INVALCOLOR;
2420#endif
2421}
2422
2423 guicolor_T
2424gui_get_rgb_color_cmn(int r, int g, int b)
2425{
2426 guicolor_T color = RGB(r, g, b);
2427
2428 if (color > 0xffffff)
2429 return INVALCOLOR;
2430 return gui_adjust_rgb(color);
2431}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002432#endif
2433
2434/*
2435 * Table with the specifications for an attribute number.
2436 * Note that this table is used by ALL buffers. This is required because the
2437 * GUI can redraw at any time for any buffer.
2438 */
2439static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2440
2441#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2442
2443static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2444
2445#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2446
2447#ifdef FEAT_GUI
2448static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2449
2450#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2451#endif
2452
2453/*
2454 * Return the attr number for a set of colors and font.
2455 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2456 * if the combination is new.
2457 * Return 0 for error (no more room).
2458 */
2459 static int
2460get_attr_entry(garray_T *table, attrentry_T *aep)
2461{
2462 int i;
2463 attrentry_T *taep;
2464 static int recursive = FALSE;
2465
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002466 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002467 table->ga_itemsize = sizeof(attrentry_T);
2468 table->ga_growsize = 7;
2469
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002470 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002471 for (i = 0; i < table->ga_len; ++i)
2472 {
2473 taep = &(((attrentry_T *)table->ga_data)[i]);
2474 if ( aep->ae_attr == taep->ae_attr
2475 && (
2476#ifdef FEAT_GUI
2477 (table == &gui_attr_table
2478 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2479 && aep->ae_u.gui.bg_color
2480 == taep->ae_u.gui.bg_color
2481 && aep->ae_u.gui.sp_color
2482 == taep->ae_u.gui.sp_color
2483 && aep->ae_u.gui.font == taep->ae_u.gui.font
2484# ifdef FEAT_XFONTSET
2485 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2486# endif
2487 ))
2488 ||
2489#endif
2490 (table == &term_attr_table
2491 && (aep->ae_u.term.start == NULL)
2492 == (taep->ae_u.term.start == NULL)
2493 && (aep->ae_u.term.start == NULL
2494 || STRCMP(aep->ae_u.term.start,
2495 taep->ae_u.term.start) == 0)
2496 && (aep->ae_u.term.stop == NULL)
2497 == (taep->ae_u.term.stop == NULL)
2498 && (aep->ae_u.term.stop == NULL
2499 || STRCMP(aep->ae_u.term.stop,
2500 taep->ae_u.term.stop) == 0))
2501 || (table == &cterm_attr_table
2502 && aep->ae_u.cterm.fg_color
2503 == taep->ae_u.cterm.fg_color
2504 && aep->ae_u.cterm.bg_color
2505 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002506 && aep->ae_u.cterm.ul_color
2507 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002508#ifdef FEAT_TERMGUICOLORS
2509 && aep->ae_u.cterm.fg_rgb
2510 == taep->ae_u.cterm.fg_rgb
2511 && aep->ae_u.cterm.bg_rgb
2512 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002513 && aep->ae_u.cterm.ul_rgb
2514 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002515#endif
2516 )))
2517
2518 return i + ATTR_OFF;
2519 }
2520
2521 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2522 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002523 // Running out of attribute entries! remove all attributes, and
2524 // compute new ones for all groups.
2525 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002526 if (recursive)
2527 {
2528 emsg(_("E424: Too many different highlighting attributes in use"));
2529 return 0;
2530 }
2531 recursive = TRUE;
2532
2533 clear_hl_tables();
2534
2535 must_redraw = CLEAR;
2536
2537 for (i = 0; i < highlight_ga.ga_len; ++i)
2538 set_hl_attr(i);
2539
2540 recursive = FALSE;
2541 }
2542
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002543 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002544 if (ga_grow(table, 1) == FAIL)
2545 return 0;
2546
2547 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002548 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002549 taep->ae_attr = aep->ae_attr;
2550#ifdef FEAT_GUI
2551 if (table == &gui_attr_table)
2552 {
2553 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2554 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2555 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2556 taep->ae_u.gui.font = aep->ae_u.gui.font;
2557# ifdef FEAT_XFONTSET
2558 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2559# endif
2560 }
2561#endif
2562 if (table == &term_attr_table)
2563 {
2564 if (aep->ae_u.term.start == NULL)
2565 taep->ae_u.term.start = NULL;
2566 else
2567 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2568 if (aep->ae_u.term.stop == NULL)
2569 taep->ae_u.term.stop = NULL;
2570 else
2571 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2572 }
2573 else if (table == &cterm_attr_table)
2574 {
2575 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2576 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002577 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002578#ifdef FEAT_TERMGUICOLORS
2579 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2580 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002581 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002582#endif
2583 }
2584 ++table->ga_len;
2585 return (table->ga_len - 1 + ATTR_OFF);
2586}
2587
2588#if defined(FEAT_TERMINAL) || defined(PROTO)
2589/*
2590 * Get an attribute index for a cterm entry.
2591 * Uses an existing entry when possible or adds one when needed.
2592 */
2593 int
2594get_cterm_attr_idx(int attr, int fg, int bg)
2595{
2596 attrentry_T at_en;
2597
Bram Moolenaara80faa82020-04-12 19:37:17 +02002598 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002599#ifdef FEAT_TERMGUICOLORS
2600 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2601 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002602 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002603#endif
2604 at_en.ae_attr = attr;
2605 at_en.ae_u.cterm.fg_color = fg;
2606 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002607 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002608 return get_attr_entry(&cterm_attr_table, &at_en);
2609}
2610#endif
2611
2612#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2613/*
2614 * Get an attribute index for a 'termguicolors' entry.
2615 * Uses an existing entry when possible or adds one when needed.
2616 */
2617 int
2618get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2619{
2620 attrentry_T at_en;
2621
Bram Moolenaara80faa82020-04-12 19:37:17 +02002622 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002623 at_en.ae_attr = attr;
2624 if (fg == INVALCOLOR && bg == INVALCOLOR)
2625 {
2626 // If both GUI colors are not set fall back to the cterm colors. Helps
2627 // if the GUI only has an attribute, such as undercurl.
2628 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2629 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2630 }
2631 else
2632 {
2633 at_en.ae_u.cterm.fg_rgb = fg;
2634 at_en.ae_u.cterm.bg_rgb = bg;
2635 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002636 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002637 return get_attr_entry(&cterm_attr_table, &at_en);
2638}
2639#endif
2640
2641#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2642/*
2643 * Get an attribute index for a cterm entry.
2644 * Uses an existing entry when possible or adds one when needed.
2645 */
2646 int
2647get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2648{
2649 attrentry_T at_en;
2650
Bram Moolenaara80faa82020-04-12 19:37:17 +02002651 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002652 at_en.ae_attr = attr;
2653 at_en.ae_u.gui.fg_color = fg;
2654 at_en.ae_u.gui.bg_color = bg;
2655 return get_attr_entry(&gui_attr_table, &at_en);
2656}
2657#endif
2658
2659/*
2660 * Clear all highlight tables.
2661 */
2662 void
2663clear_hl_tables(void)
2664{
2665 int i;
2666 attrentry_T *taep;
2667
2668#ifdef FEAT_GUI
2669 ga_clear(&gui_attr_table);
2670#endif
2671 for (i = 0; i < term_attr_table.ga_len; ++i)
2672 {
2673 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2674 vim_free(taep->ae_u.term.start);
2675 vim_free(taep->ae_u.term.stop);
2676 }
2677 ga_clear(&term_attr_table);
2678 ga_clear(&cterm_attr_table);
2679}
2680
2681/*
2682 * Combine special attributes (e.g., for spelling) with other attributes
2683 * (e.g., for syntax highlighting).
2684 * "prim_attr" overrules "char_attr".
2685 * This creates a new group when required.
2686 * Since we expect there to be few spelling mistakes we don't cache the
2687 * result.
2688 * Return the resulting attributes.
2689 */
2690 int
2691hl_combine_attr(int char_attr, int prim_attr)
2692{
2693 attrentry_T *char_aep = NULL;
2694 attrentry_T *spell_aep;
2695 attrentry_T new_en;
2696
2697 if (char_attr == 0)
2698 return prim_attr;
2699 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2700 return ATTR_COMBINE(char_attr, prim_attr);
2701#ifdef FEAT_GUI
2702 if (gui.in_use)
2703 {
2704 if (char_attr > HL_ALL)
2705 char_aep = syn_gui_attr2entry(char_attr);
2706 if (char_aep != NULL)
2707 new_en = *char_aep;
2708 else
2709 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002710 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002711 new_en.ae_u.gui.fg_color = INVALCOLOR;
2712 new_en.ae_u.gui.bg_color = INVALCOLOR;
2713 new_en.ae_u.gui.sp_color = INVALCOLOR;
2714 if (char_attr <= HL_ALL)
2715 new_en.ae_attr = char_attr;
2716 }
2717
2718 if (prim_attr <= HL_ALL)
2719 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2720 else
2721 {
2722 spell_aep = syn_gui_attr2entry(prim_attr);
2723 if (spell_aep != NULL)
2724 {
2725 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2726 spell_aep->ae_attr);
2727 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2728 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2729 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2730 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2731 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2732 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2733 if (spell_aep->ae_u.gui.font != NOFONT)
2734 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2735# ifdef FEAT_XFONTSET
2736 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2737 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2738# endif
2739 }
2740 }
2741 return get_attr_entry(&gui_attr_table, &new_en);
2742 }
2743#endif
2744
2745 if (IS_CTERM)
2746 {
2747 if (char_attr > HL_ALL)
2748 char_aep = syn_cterm_attr2entry(char_attr);
2749 if (char_aep != NULL)
2750 new_en = *char_aep;
2751 else
2752 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002753 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002754#ifdef FEAT_TERMGUICOLORS
2755 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2756 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002757 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002758#endif
2759 if (char_attr <= HL_ALL)
2760 new_en.ae_attr = char_attr;
2761 }
2762
2763 if (prim_attr <= HL_ALL)
2764 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2765 else
2766 {
2767 spell_aep = syn_cterm_attr2entry(prim_attr);
2768 if (spell_aep != NULL)
2769 {
2770 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2771 spell_aep->ae_attr);
2772 if (spell_aep->ae_u.cterm.fg_color > 0)
2773 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2774 if (spell_aep->ae_u.cterm.bg_color > 0)
2775 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002776 if (spell_aep->ae_u.cterm.ul_color > 0)
2777 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002778#ifdef FEAT_TERMGUICOLORS
2779 // If both fg and bg are not set fall back to cterm colors.
2780 // Helps for SpellBad which uses undercurl in the GUI.
2781 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2782 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2783 {
2784 if (spell_aep->ae_u.cterm.fg_color > 0)
2785 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2786 if (spell_aep->ae_u.cterm.bg_color > 0)
2787 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2788 }
2789 else
2790 {
2791 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2792 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2793 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2794 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2795 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002796 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2797 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002798#endif
2799 }
2800 }
2801 return get_attr_entry(&cterm_attr_table, &new_en);
2802 }
2803
2804 if (char_attr > HL_ALL)
2805 char_aep = syn_term_attr2entry(char_attr);
2806 if (char_aep != NULL)
2807 new_en = *char_aep;
2808 else
2809 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002810 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002811 if (char_attr <= HL_ALL)
2812 new_en.ae_attr = char_attr;
2813 }
2814
2815 if (prim_attr <= HL_ALL)
2816 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2817 else
2818 {
2819 spell_aep = syn_term_attr2entry(prim_attr);
2820 if (spell_aep != NULL)
2821 {
2822 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2823 if (spell_aep->ae_u.term.start != NULL)
2824 {
2825 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2826 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2827 }
2828 }
2829 }
2830 return get_attr_entry(&term_attr_table, &new_en);
2831}
2832
2833#ifdef FEAT_GUI
2834 attrentry_T *
2835syn_gui_attr2entry(int attr)
2836{
2837 attr -= ATTR_OFF;
2838 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2839 return NULL;
2840 return &(GUI_ATTR_ENTRY(attr));
2841}
2842#endif
2843
2844/*
2845 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2846 * Only to be used when "attr" > HL_ALL.
2847 */
2848 int
2849syn_attr2attr(int attr)
2850{
2851 attrentry_T *aep;
2852
2853#ifdef FEAT_GUI
2854 if (gui.in_use)
2855 aep = syn_gui_attr2entry(attr);
2856 else
2857#endif
2858 if (IS_CTERM)
2859 aep = syn_cterm_attr2entry(attr);
2860 else
2861 aep = syn_term_attr2entry(attr);
2862
2863 if (aep == NULL) // highlighting not set
2864 return 0;
2865 return aep->ae_attr;
2866}
2867
2868
2869 attrentry_T *
2870syn_term_attr2entry(int attr)
2871{
2872 attr -= ATTR_OFF;
2873 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2874 return NULL;
2875 return &(TERM_ATTR_ENTRY(attr));
2876}
2877
2878 attrentry_T *
2879syn_cterm_attr2entry(int attr)
2880{
2881 attr -= ATTR_OFF;
2882 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2883 return NULL;
2884 return &(CTERM_ATTR_ENTRY(attr));
2885}
2886
2887#define LIST_ATTR 1
2888#define LIST_STRING 2
2889#define LIST_INT 3
2890
2891 static void
2892highlight_list_one(int id)
2893{
2894 hl_group_T *sgp;
2895 int didh = FALSE;
2896
2897 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2898
2899 if (message_filtered(sgp->sg_name))
2900 return;
2901
2902 didh = highlight_list_arg(id, didh, LIST_ATTR,
2903 sgp->sg_term, NULL, "term");
2904 didh = highlight_list_arg(id, didh, LIST_STRING,
2905 0, sgp->sg_start, "start");
2906 didh = highlight_list_arg(id, didh, LIST_STRING,
2907 0, sgp->sg_stop, "stop");
2908
2909 didh = highlight_list_arg(id, didh, LIST_ATTR,
2910 sgp->sg_cterm, NULL, "cterm");
2911 didh = highlight_list_arg(id, didh, LIST_INT,
2912 sgp->sg_cterm_fg, NULL, "ctermfg");
2913 didh = highlight_list_arg(id, didh, LIST_INT,
2914 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002915 didh = highlight_list_arg(id, didh, LIST_INT,
2916 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002917
2918#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2919 didh = highlight_list_arg(id, didh, LIST_ATTR,
2920 sgp->sg_gui, NULL, "gui");
2921 didh = highlight_list_arg(id, didh, LIST_STRING,
2922 0, sgp->sg_gui_fg_name, "guifg");
2923 didh = highlight_list_arg(id, didh, LIST_STRING,
2924 0, sgp->sg_gui_bg_name, "guibg");
2925 didh = highlight_list_arg(id, didh, LIST_STRING,
2926 0, sgp->sg_gui_sp_name, "guisp");
2927#endif
2928#ifdef FEAT_GUI
2929 didh = highlight_list_arg(id, didh, LIST_STRING,
2930 0, sgp->sg_font_name, "font");
2931#endif
2932
2933 if (sgp->sg_link && !got_int)
2934 {
2935 (void)syn_list_header(didh, 9999, id);
2936 didh = TRUE;
2937 msg_puts_attr("links to", HL_ATTR(HLF_D));
2938 msg_putchar(' ');
2939 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2940 }
2941
2942 if (!didh)
2943 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2944#ifdef FEAT_EVAL
2945 if (p_verbose > 0)
2946 last_set_msg(sgp->sg_script_ctx);
2947#endif
2948}
2949
2950 static int
2951highlight_list_arg(
2952 int id,
2953 int didh,
2954 int type,
2955 int iarg,
2956 char_u *sarg,
2957 char *name)
2958{
2959 char_u buf[100];
2960 char_u *ts;
2961 int i;
2962
2963 if (got_int)
2964 return FALSE;
2965 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2966 {
2967 ts = buf;
2968 if (type == LIST_INT)
2969 sprintf((char *)buf, "%d", iarg - 1);
2970 else if (type == LIST_STRING)
2971 ts = sarg;
2972 else // type == LIST_ATTR
2973 {
2974 buf[0] = NUL;
2975 for (i = 0; hl_attr_table[i] != 0; ++i)
2976 {
2977 if (iarg & hl_attr_table[i])
2978 {
2979 if (buf[0] != NUL)
2980 vim_strcat(buf, (char_u *)",", 100);
2981 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2982 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2983 }
2984 }
2985 }
2986
2987 (void)syn_list_header(didh,
2988 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2989 didh = TRUE;
2990 if (!got_int)
2991 {
2992 if (*name != NUL)
2993 {
2994 msg_puts_attr(name, HL_ATTR(HLF_D));
2995 msg_puts_attr("=", HL_ATTR(HLF_D));
2996 }
2997 msg_outtrans(ts);
2998 }
2999 }
3000 return didh;
3001}
3002
3003#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3004/*
3005 * Return "1" if highlight group "id" has attribute "flag".
3006 * Return NULL otherwise.
3007 */
3008 char_u *
3009highlight_has_attr(
3010 int id,
3011 int flag,
3012 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3013{
3014 int attr;
3015
3016 if (id <= 0 || id > highlight_ga.ga_len)
3017 return NULL;
3018
3019#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3020 if (modec == 'g')
3021 attr = HL_TABLE()[id - 1].sg_gui;
3022 else
3023#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003024 {
3025 if (modec == 'c')
3026 attr = HL_TABLE()[id - 1].sg_cterm;
3027 else
3028 attr = HL_TABLE()[id - 1].sg_term;
3029 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003030
3031 if (attr & flag)
3032 return (char_u *)"1";
3033 return NULL;
3034}
3035#endif
3036
3037#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3038/*
3039 * Return color name of highlight group "id".
3040 */
3041 char_u *
3042highlight_color(
3043 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003044 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003045 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3046{
3047 static char_u name[20];
3048 int n;
3049 int fg = FALSE;
3050 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003051 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003052 int font = FALSE;
3053
3054 if (id <= 0 || id > highlight_ga.ga_len)
3055 return NULL;
3056
3057 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3058 fg = TRUE;
3059 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3060 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3061 font = TRUE;
3062 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3063 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003064 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3065 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003066 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3067 return NULL;
3068 if (modec == 'g')
3069 {
3070# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3071# ifdef FEAT_GUI
3072 // return font name
3073 if (font)
3074 return HL_TABLE()[id - 1].sg_font_name;
3075# endif
3076
3077 // return #RRGGBB form (only possible when GUI is running)
3078 if ((USE_24BIT) && what[2] == '#')
3079 {
3080 guicolor_T color;
3081 long_u rgb;
3082 static char_u buf[10];
3083
3084 if (fg)
3085 color = HL_TABLE()[id - 1].sg_gui_fg;
3086 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003087 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003088 else
3089 color = HL_TABLE()[id - 1].sg_gui_bg;
3090 if (color == INVALCOLOR)
3091 return NULL;
3092 rgb = (long_u)GUI_MCH_GET_RGB(color);
3093 sprintf((char *)buf, "#%02x%02x%02x",
3094 (unsigned)(rgb >> 16),
3095 (unsigned)(rgb >> 8) & 255,
3096 (unsigned)rgb & 255);
3097 return buf;
3098 }
3099# endif
3100 if (fg)
3101 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3102 if (sp)
3103 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3104 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3105 }
3106 if (font || sp)
3107 return NULL;
3108 if (modec == 'c')
3109 {
3110 if (fg)
3111 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003112 else if (ul)
3113 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003114 else
3115 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3116 if (n < 0)
3117 return NULL;
3118 sprintf((char *)name, "%d", n);
3119 return name;
3120 }
3121 // term doesn't have color
3122 return NULL;
3123}
3124#endif
3125
3126#if (defined(FEAT_SYN_HL) \
3127 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3128 && defined(FEAT_PRINTER)) || defined(PROTO)
3129/*
3130 * Return color name of highlight group "id" as RGB value.
3131 */
3132 long_u
3133highlight_gui_color_rgb(
3134 int id,
3135 int fg) // TRUE = fg, FALSE = bg
3136{
3137 guicolor_T color;
3138
3139 if (id <= 0 || id > highlight_ga.ga_len)
3140 return 0L;
3141
3142 if (fg)
3143 color = HL_TABLE()[id - 1].sg_gui_fg;
3144 else
3145 color = HL_TABLE()[id - 1].sg_gui_bg;
3146
3147 if (color == INVALCOLOR)
3148 return 0L;
3149
3150 return GUI_MCH_GET_RGB(color);
3151}
3152#endif
3153
3154/*
3155 * Output the syntax list header.
3156 * Return TRUE when started a new line.
3157 */
3158 int
3159syn_list_header(
3160 int did_header, // did header already
3161 int outlen, // length of string that comes
3162 int id) // highlight group id
3163{
3164 int endcol = 19;
3165 int newline = TRUE;
3166 int name_col = 0;
3167
3168 if (!did_header)
3169 {
3170 msg_putchar('\n');
3171 if (got_int)
3172 return TRUE;
3173 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3174 name_col = msg_col;
3175 endcol = 15;
3176 }
3177 else if (msg_col + outlen + 1 >= Columns)
3178 {
3179 msg_putchar('\n');
3180 if (got_int)
3181 return TRUE;
3182 }
3183 else
3184 {
3185 if (msg_col >= endcol) // wrap around is like starting a new line
3186 newline = FALSE;
3187 }
3188
3189 if (msg_col >= endcol) // output at least one space
3190 endcol = msg_col + 1;
3191 if (Columns <= endcol) // avoid hang for tiny window
3192 endcol = Columns - 1;
3193
3194 msg_advance(endcol);
3195
3196 // Show "xxx" with the attributes.
3197 if (!did_header)
3198 {
3199 if (endcol == Columns - 1 && endcol <= name_col)
3200 msg_putchar(' ');
3201 msg_puts_attr("xxx", syn_id2attr(id));
3202 msg_putchar(' ');
3203 }
3204
3205 return newline;
3206}
3207
3208/*
3209 * Set the attribute numbers for a highlight group.
3210 * Called after one of the attributes has changed.
3211 */
3212 static void
3213set_hl_attr(
3214 int idx) // index in array
3215{
3216 attrentry_T at_en;
3217 hl_group_T *sgp = HL_TABLE() + idx;
3218
3219 // The "Normal" group doesn't need an attribute number
3220 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3221 return;
3222
3223#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003224 // For the GUI mode: If there are other than "normal" highlighting
3225 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003226 if (sgp->sg_gui_fg == INVALCOLOR
3227 && sgp->sg_gui_bg == INVALCOLOR
3228 && sgp->sg_gui_sp == INVALCOLOR
3229 && sgp->sg_font == NOFONT
3230# ifdef FEAT_XFONTSET
3231 && sgp->sg_fontset == NOFONTSET
3232# endif
3233 )
3234 {
3235 sgp->sg_gui_attr = sgp->sg_gui;
3236 }
3237 else
3238 {
3239 at_en.ae_attr = sgp->sg_gui;
3240 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3241 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3242 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3243 at_en.ae_u.gui.font = sgp->sg_font;
3244# ifdef FEAT_XFONTSET
3245 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3246# endif
3247 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3248 }
3249#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003250 // For the term mode: If there are other than "normal" highlighting
3251 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003252 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3253 sgp->sg_term_attr = sgp->sg_term;
3254 else
3255 {
3256 at_en.ae_attr = sgp->sg_term;
3257 at_en.ae_u.term.start = sgp->sg_start;
3258 at_en.ae_u.term.stop = sgp->sg_stop;
3259 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3260 }
3261
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003262 // For the color term mode: If there are other than "normal"
3263 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003264 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003265# ifdef FEAT_TERMGUICOLORS
3266 && sgp->sg_gui_fg == INVALCOLOR
3267 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003268 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003269# endif
3270 )
3271 sgp->sg_cterm_attr = sgp->sg_cterm;
3272 else
3273 {
3274 at_en.ae_attr = sgp->sg_cterm;
3275 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3276 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003277 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003278# ifdef FEAT_TERMGUICOLORS
3279# ifdef MSWIN
3280# ifdef VIMDLL
3281 // Only when not using the GUI.
3282 if (!gui.in_use && !gui.starting)
3283# endif
3284 {
3285 int id;
3286 guicolor_T fg, bg;
3287
3288 id = syn_name2id((char_u *)"Normal");
3289 if (id > 0)
3290 {
3291 syn_id2colors(id, &fg, &bg);
3292 if (sgp->sg_gui_fg == INVALCOLOR)
3293 sgp->sg_gui_fg = fg;
3294 if (sgp->sg_gui_bg == INVALCOLOR)
3295 sgp->sg_gui_bg = bg;
3296 }
3297
3298 }
3299# endif
3300 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3301 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003302 // Only use the underline/undercurl color when used, it may clear the
3303 // background color if not supported.
3304 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
3305 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3306 else
3307 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003308 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3309 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3310 {
3311 // If both fg and bg are invalid fall back to the cterm colors.
3312 // Helps when the GUI only uses an attribute, e.g. undercurl.
3313 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3314 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3315 }
3316# endif
3317 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3318 }
3319}
3320
3321/*
3322 * Lookup a highlight group name and return its ID.
3323 * If it is not found, 0 is returned.
3324 */
3325 int
3326syn_name2id(char_u *name)
3327{
3328 int i;
3329 char_u name_u[200];
3330
3331 // Avoid using stricmp() too much, it's slow on some systems
3332 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3333 // don't deserve to be found!
3334 vim_strncpy(name_u, name, 199);
3335 vim_strup(name_u);
3336 for (i = highlight_ga.ga_len; --i >= 0; )
3337 if (HL_TABLE()[i].sg_name_u != NULL
3338 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3339 break;
3340 return i + 1;
3341}
3342
3343/*
3344 * Lookup a highlight group name and return its attributes.
3345 * Return zero if not found.
3346 */
3347 int
3348syn_name2attr(char_u *name)
3349{
3350 int id = syn_name2id(name);
3351
3352 if (id != 0)
3353 return syn_id2attr(id);
3354 return 0;
3355}
3356
3357#if defined(FEAT_EVAL) || defined(PROTO)
3358/*
3359 * Return TRUE if highlight group "name" exists.
3360 */
3361 int
3362highlight_exists(char_u *name)
3363{
3364 return (syn_name2id(name) > 0);
3365}
3366
3367# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3368/*
3369 * Return the name of highlight group "id".
3370 * When not a valid ID return an empty string.
3371 */
3372 char_u *
3373syn_id2name(int id)
3374{
3375 if (id <= 0 || id > highlight_ga.ga_len)
3376 return (char_u *)"";
3377 return HL_TABLE()[id - 1].sg_name;
3378}
3379# endif
3380#endif
3381
3382/*
3383 * Like syn_name2id(), but take a pointer + length argument.
3384 */
3385 int
3386syn_namen2id(char_u *linep, int len)
3387{
3388 char_u *name;
3389 int id = 0;
3390
3391 name = vim_strnsave(linep, len);
3392 if (name != NULL)
3393 {
3394 id = syn_name2id(name);
3395 vim_free(name);
3396 }
3397 return id;
3398}
3399
3400/*
3401 * Find highlight group name in the table and return its ID.
3402 * The argument is a pointer to the name and the length of the name.
3403 * If it doesn't exist yet, a new entry is created.
3404 * Return 0 for failure.
3405 */
3406 int
3407syn_check_group(char_u *pp, int len)
3408{
3409 int id;
3410 char_u *name;
3411
3412 name = vim_strnsave(pp, len);
3413 if (name == NULL)
3414 return 0;
3415
3416 id = syn_name2id(name);
3417 if (id == 0) // doesn't exist yet
3418 id = syn_add_group(name);
3419 else
3420 vim_free(name);
3421 return id;
3422}
3423
3424/*
3425 * Add new highlight group and return its ID.
3426 * "name" must be an allocated string, it will be consumed.
3427 * Return 0 for failure.
3428 */
3429 static int
3430syn_add_group(char_u *name)
3431{
3432 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003433 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003434
3435 // Check that the name is ASCII letters, digits and underscore.
3436 for (p = name; *p != NUL; ++p)
3437 {
3438 if (!vim_isprintc(*p))
3439 {
3440 emsg(_("E669: Unprintable character in group name"));
3441 vim_free(name);
3442 return 0;
3443 }
3444 else if (!ASCII_ISALNUM(*p) && *p != '_')
3445 {
3446 // This is an error, but since there previously was no check only
3447 // give a warning.
3448 msg_source(HL_ATTR(HLF_W));
3449 msg(_("W18: Invalid character in group name"));
3450 break;
3451 }
3452 }
3453
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003454 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003455 if (highlight_ga.ga_data == NULL)
3456 {
3457 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3458 highlight_ga.ga_growsize = 10;
3459 }
3460
3461 if (highlight_ga.ga_len >= MAX_HL_ID)
3462 {
3463 emsg(_("E849: Too many highlight and syntax groups"));
3464 vim_free(name);
3465 return 0;
3466 }
3467
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003468 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003469 if (ga_grow(&highlight_ga, 1) == FAIL)
3470 {
3471 vim_free(name);
3472 return 0;
3473 }
3474
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003475 name_up = vim_strsave_up(name);
3476 if (name_up == NULL)
3477 {
3478 vim_free(name);
3479 return 0;
3480 }
3481
Bram Moolenaara80faa82020-04-12 19:37:17 +02003482 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003483 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003484 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003485#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3486 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3487 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003488 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003489#endif
3490 ++highlight_ga.ga_len;
3491
3492 return highlight_ga.ga_len; // ID is index plus one
3493}
3494
3495/*
3496 * When, just after calling syn_add_group(), an error is discovered, this
3497 * function deletes the new name.
3498 */
3499 static void
3500syn_unadd_group(void)
3501{
3502 --highlight_ga.ga_len;
3503 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3504 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3505}
3506
3507/*
3508 * Translate a group ID to highlight attributes.
3509 */
3510 int
3511syn_id2attr(int hl_id)
3512{
3513 int attr;
3514 hl_group_T *sgp;
3515
3516 hl_id = syn_get_final_id(hl_id);
3517 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3518
3519#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003520 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003521 if (gui.in_use)
3522 attr = sgp->sg_gui_attr;
3523 else
3524#endif
3525 if (IS_CTERM)
3526 attr = sgp->sg_cterm_attr;
3527 else
3528 attr = sgp->sg_term_attr;
3529
3530 return attr;
3531}
3532
3533#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3534/*
3535 * Get the GUI colors and attributes for a group ID.
3536 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3537 */
3538 int
3539syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3540{
3541 hl_group_T *sgp;
3542
3543 hl_id = syn_get_final_id(hl_id);
3544 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3545
3546 *fgp = sgp->sg_gui_fg;
3547 *bgp = sgp->sg_gui_bg;
3548 return sgp->sg_gui;
3549}
3550#endif
3551
3552#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003553 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3554 && defined(FEAT_TERMGUICOLORS)) \
3555 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003556 void
3557syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3558{
3559 hl_group_T *sgp;
3560
3561 hl_id = syn_get_final_id(hl_id);
3562 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3563 *fgp = sgp->sg_cterm_fg - 1;
3564 *bgp = sgp->sg_cterm_bg - 1;
3565}
3566#endif
3567
3568/*
3569 * Translate a group ID to the final group ID (following links).
3570 */
3571 int
3572syn_get_final_id(int hl_id)
3573{
3574 int count;
3575 hl_group_T *sgp;
3576
3577 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3578 return 0; // Can be called from eval!!
3579
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003580 // Follow links until there is no more.
3581 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003582 for (count = 100; --count >= 0; )
3583 {
3584 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3585 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3586 break;
3587 hl_id = sgp->sg_link;
3588 }
3589
3590 return hl_id;
3591}
3592
3593#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3594/*
3595 * Call this function just after the GUI has started.
3596 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3597 * It finds the font and color handles for the highlighting groups.
3598 */
3599 void
3600highlight_gui_started(void)
3601{
3602 int idx;
3603
3604 // First get the colors from the "Normal" and "Menu" group, if set
3605 if (USE_24BIT)
3606 set_normal_colors();
3607
3608 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3609 gui_do_one_color(idx, FALSE, FALSE);
3610
3611 highlight_changed();
3612}
3613
3614 static void
3615gui_do_one_color(
3616 int idx,
3617 int do_menu UNUSED, // TRUE: might set the menu font
3618 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3619{
3620 int didit = FALSE;
3621
3622# ifdef FEAT_GUI
3623# ifdef FEAT_TERMGUICOLORS
3624 if (gui.in_use)
3625# endif
3626 if (HL_TABLE()[idx].sg_font_name != NULL)
3627 {
3628 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3629 do_tooltip, TRUE);
3630 didit = TRUE;
3631 }
3632# endif
3633 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3634 {
3635 HL_TABLE()[idx].sg_gui_fg =
3636 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3637 didit = TRUE;
3638 }
3639 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3640 {
3641 HL_TABLE()[idx].sg_gui_bg =
3642 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3643 didit = TRUE;
3644 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003645 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3646 {
3647 HL_TABLE()[idx].sg_gui_sp =
3648 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3649 didit = TRUE;
3650 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003651 if (didit) // need to get a new attr number
3652 set_hl_attr(idx);
3653}
3654#endif
3655
3656#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3657/*
3658 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3659 */
3660 static void
3661combine_stl_hlt(
3662 int id,
3663 int id_S,
3664 int id_alt,
3665 int hlcnt,
3666 int i,
3667 int hlf,
3668 int *table)
3669{
3670 hl_group_T *hlt = HL_TABLE();
3671
3672 if (id_alt == 0)
3673 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003674 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003675 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3676 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3677# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3678 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3679# endif
3680 }
3681 else
3682 mch_memmove(&hlt[hlcnt + i],
3683 &hlt[id_alt - 1],
3684 sizeof(hl_group_T));
3685 hlt[hlcnt + i].sg_link = 0;
3686
3687 hlt[hlcnt + i].sg_term ^=
3688 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3689 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3690 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3691 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3692 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3693 hlt[hlcnt + i].sg_cterm ^=
3694 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3695 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3696 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3697 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3698 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3699# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3700 hlt[hlcnt + i].sg_gui ^=
3701 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3702# endif
3703# ifdef FEAT_GUI
3704 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3705 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3706 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3707 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3708 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3709 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3710 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3711 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3712# ifdef FEAT_XFONTSET
3713 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3714 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3715# endif
3716# endif
3717 highlight_ga.ga_len = hlcnt + i + 1;
3718 set_hl_attr(hlcnt + i); // At long last we can apply
3719 table[i] = syn_id2attr(hlcnt + i + 1);
3720}
3721#endif
3722
3723/*
3724 * Translate the 'highlight' option into attributes in highlight_attr[] and
3725 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3726 * corresponding highlights to use on top of HLF_SNC is computed.
3727 * Called only when the 'highlight' option has been changed and upon first
3728 * screen redraw after any :highlight command.
3729 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3730 */
3731 int
3732highlight_changed(void)
3733{
3734 int hlf;
3735 int i;
3736 char_u *p;
3737 int attr;
3738 char_u *end;
3739 int id;
3740#ifdef USER_HIGHLIGHT
3741 char_u userhl[30]; // use 30 to avoid compiler warning
3742# ifdef FEAT_STL_OPT
3743 int id_S = -1;
3744 int id_SNC = 0;
3745# ifdef FEAT_TERMINAL
3746 int id_ST = 0;
3747 int id_STNC = 0;
3748# endif
3749 int hlcnt;
3750# endif
3751#endif
3752 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3753
3754 need_highlight_changed = FALSE;
3755
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003756#ifdef FEAT_TERMINAL
3757 term_update_colors_all();
3758 term_update_wincolor_all();
3759#endif
3760
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003761 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003762 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3763 highlight_attr[hlf] = 0;
3764
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003765 // First set all attributes to their default value.
3766 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003767 for (i = 0; i < 2; ++i)
3768 {
3769 if (i)
3770 p = p_hl;
3771 else
3772 p = get_highlight_default();
3773 if (p == NULL) // just in case
3774 continue;
3775
3776 while (*p)
3777 {
3778 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3779 if (hl_flags[hlf] == *p)
3780 break;
3781 ++p;
3782 if (hlf == (int)HLF_COUNT || *p == NUL)
3783 return FAIL;
3784
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003785 // Allow several hl_flags to be combined, like "bu" for
3786 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003787 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003788 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003789 {
3790 if (VIM_ISWHITE(*p)) // ignore white space
3791 continue;
3792
3793 if (attr > HL_ALL) // Combination with ':' is not allowed.
3794 return FAIL;
3795
3796 switch (*p)
3797 {
3798 case 'b': attr |= HL_BOLD;
3799 break;
3800 case 'i': attr |= HL_ITALIC;
3801 break;
3802 case '-':
3803 case 'n': // no highlighting
3804 break;
3805 case 'r': attr |= HL_INVERSE;
3806 break;
3807 case 's': attr |= HL_STANDOUT;
3808 break;
3809 case 'u': attr |= HL_UNDERLINE;
3810 break;
3811 case 'c': attr |= HL_UNDERCURL;
3812 break;
3813 case 't': attr |= HL_STRIKETHROUGH;
3814 break;
3815 case ':': ++p; // highlight group name
3816 if (attr || *p == NUL) // no combinations
3817 return FAIL;
3818 end = vim_strchr(p, ',');
3819 if (end == NULL)
3820 end = p + STRLEN(p);
3821 id = syn_check_group(p, (int)(end - p));
3822 if (id == 0)
3823 return FAIL;
3824 attr = syn_id2attr(id);
3825 p = end - 1;
3826#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3827 if (hlf == (int)HLF_SNC)
3828 id_SNC = syn_get_final_id(id);
3829# ifdef FEAT_TERMINAL
3830 else if (hlf == (int)HLF_ST)
3831 id_ST = syn_get_final_id(id);
3832 else if (hlf == (int)HLF_STNC)
3833 id_STNC = syn_get_final_id(id);
3834# endif
3835 else if (hlf == (int)HLF_S)
3836 id_S = syn_get_final_id(id);
3837#endif
3838 break;
3839 default: return FAIL;
3840 }
3841 }
3842 highlight_attr[hlf] = attr;
3843
3844 p = skip_to_option_part(p); // skip comma and spaces
3845 }
3846 }
3847
3848#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003849 // Setup the user highlights
3850 //
3851 // Temporarily utilize 28 more hl entries:
3852 // 9 for User1-User9 combined with StatusLineNC
3853 // 9 for User1-User9 combined with StatusLineTerm
3854 // 9 for User1-User9 combined with StatusLineTermNC
3855 // 1 for StatusLine default
3856 // Have to be in there simultaneously in case of table overflows in
3857 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003858# ifdef FEAT_STL_OPT
3859 if (ga_grow(&highlight_ga, 28) == FAIL)
3860 return FAIL;
3861 hlcnt = highlight_ga.ga_len;
3862 if (id_S == -1)
3863 {
3864 // Make sure id_S is always valid to simplify code below. Use the last
3865 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003866 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003867 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3868 id_S = hlcnt + 19;
3869 }
3870# endif
3871 for (i = 0; i < 9; i++)
3872 {
3873 sprintf((char *)userhl, "User%d", i + 1);
3874 id = syn_name2id(userhl);
3875 if (id == 0)
3876 {
3877 highlight_user[i] = 0;
3878# ifdef FEAT_STL_OPT
3879 highlight_stlnc[i] = 0;
3880# ifdef FEAT_TERMINAL
3881 highlight_stlterm[i] = 0;
3882 highlight_stltermnc[i] = 0;
3883# endif
3884# endif
3885 }
3886 else
3887 {
3888 highlight_user[i] = syn_id2attr(id);
3889# ifdef FEAT_STL_OPT
3890 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3891 HLF_SNC, highlight_stlnc);
3892# ifdef FEAT_TERMINAL
3893 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3894 HLF_ST, highlight_stlterm);
3895 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3896 HLF_STNC, highlight_stltermnc);
3897# endif
3898# endif
3899 }
3900 }
3901# ifdef FEAT_STL_OPT
3902 highlight_ga.ga_len = hlcnt;
3903# endif
3904
3905#endif // USER_HIGHLIGHT
3906
3907 return OK;
3908}
3909
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003910static void highlight_list(void);
3911static void highlight_list_two(int cnt, int attr);
3912
3913/*
3914 * Handle command line completion for :highlight command.
3915 */
3916 void
3917set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3918{
3919 char_u *p;
3920
3921 // Default: expand group names
3922 xp->xp_context = EXPAND_HIGHLIGHT;
3923 xp->xp_pattern = arg;
3924 include_link = 2;
3925 include_default = 1;
3926
3927 // (part of) subcommand already typed
3928 if (*arg != NUL)
3929 {
3930 p = skiptowhite(arg);
3931 if (*p != NUL) // past "default" or group name
3932 {
3933 include_default = 0;
3934 if (STRNCMP("default", arg, p - arg) == 0)
3935 {
3936 arg = skipwhite(p);
3937 xp->xp_pattern = arg;
3938 p = skiptowhite(arg);
3939 }
3940 if (*p != NUL) // past group name
3941 {
3942 include_link = 0;
3943 if (arg[1] == 'i' && arg[0] == 'N')
3944 highlight_list();
3945 if (STRNCMP("link", arg, p - arg) == 0
3946 || STRNCMP("clear", arg, p - arg) == 0)
3947 {
3948 xp->xp_pattern = skipwhite(p);
3949 p = skiptowhite(xp->xp_pattern);
3950 if (*p != NUL) // past first group name
3951 {
3952 xp->xp_pattern = skipwhite(p);
3953 p = skiptowhite(xp->xp_pattern);
3954 }
3955 }
3956 if (*p != NUL) // past group name(s)
3957 xp->xp_context = EXPAND_NOTHING;
3958 }
3959 }
3960 }
3961}
3962
3963/*
3964 * List highlighting matches in a nice way.
3965 */
3966 static void
3967highlight_list(void)
3968{
3969 int i;
3970
3971 for (i = 10; --i >= 0; )
3972 highlight_list_two(i, HL_ATTR(HLF_D));
3973 for (i = 40; --i >= 0; )
3974 highlight_list_two(99, 0);
3975}
3976
3977 static void
3978highlight_list_two(int cnt, int attr)
3979{
3980 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3981 msg_clr_eos();
3982 out_flush();
3983 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3984}
3985
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003986/*
3987 * Function given to ExpandGeneric() to obtain the list of group names.
3988 */
3989 char_u *
3990get_highlight_name(expand_T *xp UNUSED, int idx)
3991{
3992 return get_highlight_name_ext(xp, idx, TRUE);
3993}
3994
3995/*
3996 * Obtain a highlight group name.
3997 * When "skip_cleared" is TRUE don't return a cleared entry.
3998 */
3999 char_u *
4000get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4001{
4002 if (idx < 0)
4003 return NULL;
4004
4005 // Items are never removed from the table, skip the ones that were
4006 // cleared.
4007 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4008 return (char_u *)"";
4009
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004010 if (idx == highlight_ga.ga_len && include_none != 0)
4011 return (char_u *)"none";
4012 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4013 return (char_u *)"default";
4014 if (idx == highlight_ga.ga_len + include_none + include_default
4015 && include_link != 0)
4016 return (char_u *)"link";
4017 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4018 && include_link != 0)
4019 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004020 if (idx >= highlight_ga.ga_len)
4021 return NULL;
4022 return HL_TABLE()[idx].sg_name;
4023}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004024
4025#if defined(FEAT_GUI) || defined(PROTO)
4026/*
4027 * Free all the highlight group fonts.
4028 * Used when quitting for systems which need it.
4029 */
4030 void
4031free_highlight_fonts(void)
4032{
4033 int idx;
4034
4035 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4036 {
4037 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4038 HL_TABLE()[idx].sg_font = NOFONT;
4039# ifdef FEAT_XFONTSET
4040 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4041 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4042# endif
4043 }
4044
4045 gui_mch_free_font(gui.norm_font);
4046# ifdef FEAT_XFONTSET
4047 gui_mch_free_fontset(gui.fontset);
4048# endif
4049# ifndef FEAT_GUI_GTK
4050 gui_mch_free_font(gui.bold_font);
4051 gui_mch_free_font(gui.ital_font);
4052 gui_mch_free_font(gui.boldital_font);
4053# endif
4054}
4055#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004056
4057#if defined(FEAT_EVAL) || defined(PROTO)
4058/*
4059 * Convert each of the highlight attribute bits (bold, standout, underline,
4060 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4061 * the attribute name as the key.
4062 */
4063 static dict_T *
4064highlight_get_attr_dict(int hlattr)
4065{
4066 dict_T *dict;
4067 int i;
4068
4069 dict = dict_alloc();
4070 if (dict == NULL)
4071 return NULL;
4072
4073 for (i = 0; hl_attr_table[i] != 0; ++i)
4074 {
4075 if (hlattr & hl_attr_table[i])
4076 {
4077 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4078 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4079 }
4080 }
4081
4082 return dict;
4083}
4084
4085/*
4086 * Return the attributes of the highlight group at index 'hl_idx' as a
4087 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4088 * links recursively.
4089 */
4090 static dict_T *
4091highlight_get_info(int hl_idx, int resolve_link)
4092{
4093 dict_T *dict;
4094 hl_group_T *sgp;
4095 dict_T *attr_dict;
4096 int hlgid;
4097
4098 dict = dict_alloc();
4099 if (dict == NULL)
4100 return dict;
4101
4102 sgp = &HL_TABLE()[hl_idx];
4103 // highlight group id is 1-based
4104 hlgid = hl_idx + 1;
4105
4106 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4107 goto error;
4108 if (dict_add_number(dict, "id", hlgid) == FAIL)
4109 goto error;
4110
4111 if (sgp->sg_link && resolve_link)
4112 {
4113 // resolve the highlight group link recursively
4114 while (sgp->sg_link)
4115 {
4116 hlgid = sgp->sg_link;
4117 sgp = &HL_TABLE()[sgp->sg_link - 1];
4118 }
4119 }
4120
4121 if (sgp->sg_term != 0)
4122 {
4123 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4124 if (attr_dict != NULL)
4125 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4126 goto error;
4127 }
4128 if (sgp->sg_start != NULL)
4129 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4130 goto error;
4131 if (sgp->sg_stop != NULL)
4132 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4133 goto error;
4134 if (sgp->sg_cterm != 0)
4135 {
4136 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4137 if (attr_dict != NULL)
4138 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4139 goto error;
4140 }
4141 if (sgp->sg_cterm_fg != 0)
4142 if (dict_add_string(dict, "ctermfg",
4143 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4144 goto error;
4145 if (sgp->sg_cterm_bg != 0)
4146 if (dict_add_string(dict, "ctermbg",
4147 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4148 goto error;
4149 if (sgp->sg_cterm_ul != 0)
4150 if (dict_add_string(dict, "ctermul",
4151 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4152 goto error;
4153 if (sgp->sg_gui != 0)
4154 {
4155 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4156 if (attr_dict != NULL)
4157 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4158 goto error;
4159 }
4160 if (sgp->sg_gui_fg_name != NULL)
4161 if (dict_add_string(dict, "guifg",
4162 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4163 goto error;
4164 if (sgp->sg_gui_bg_name != NULL)
4165 if (dict_add_string(dict, "guibg",
4166 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4167 goto error;
4168 if (sgp->sg_gui_sp_name != NULL)
4169 if (dict_add_string(dict, "guisp",
4170 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4171 goto error;
4172# ifdef FEAT_GUI
4173 if (sgp->sg_font_name != NULL)
4174 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4175 goto error;
4176# endif
4177 if (sgp->sg_link)
4178 {
4179 char_u *link;
4180
4181 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4182 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4183 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004184
4185 if (sgp->sg_deflink)
4186 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004187 }
4188 if (dict_len(dict) == 2)
4189 // If only 'name' is present, then the highlight group is cleared.
4190 dict_add_bool(dict, "cleared", VVAL_TRUE);
4191
4192 return dict;
4193
4194error:
4195 vim_free(dict);
4196 return NULL;
4197}
4198
4199/*
4200 * "hlget([name])" function
4201 * Return the attributes of a specific highlight group (if specified) or all
4202 * the highlight groups.
4203 */
4204 void
4205f_hlget(typval_T *argvars, typval_T *rettv)
4206{
4207 list_T *list;
4208 dict_T *dict;
4209 int i;
4210 char_u *hlarg = NULL;
4211 int resolve_link = FALSE;
4212
4213 if (rettv_list_alloc(rettv) == FAIL)
4214 return;
4215
4216 if (check_for_opt_string_arg(argvars, 0) == FAIL
4217 || (argvars[0].v_type != VAR_UNKNOWN
4218 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4219 return;
4220
4221 if (argvars[0].v_type != VAR_UNKNOWN)
4222 {
4223 // highlight group name supplied
4224 hlarg = tv_get_string_chk(&argvars[0]);
4225 if (hlarg == NULL)
4226 return;
4227
4228 if (argvars[1].v_type != VAR_UNKNOWN)
4229 {
4230 int error = FALSE;
4231
4232 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4233 if (error)
4234 return;
4235 }
4236 }
4237
4238 list = rettv->vval.v_list;
4239 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4240 {
4241 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4242 {
4243 dict = highlight_get_info(i, resolve_link);
4244 if (dict != NULL)
4245 list_append_dict(list, dict);
4246 }
4247 }
4248}
4249
4250/*
4251 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4252 * 'dict' or the value is not a string type. If the value is not a string type
4253 * or is NULL, then 'error' is set to TRUE.
4254 */
4255 static char_u *
4256hldict_get_string(dict_T *dict, char_u *key, int *error)
4257{
4258 dictitem_T *di;
4259
4260 *error = FALSE;
4261 di = dict_find(dict, key, -1);
4262 if (di == NULL)
4263 return NULL;
4264
4265 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4266 {
4267 emsg(_(e_stringreq));
4268 *error = TRUE;
4269 return NULL;
4270 }
4271
4272 return di->di_tv.vval.v_string;
4273}
4274
4275/*
4276 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4277 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4278 * Dictionary or is NULL.
4279 */
4280 static int
4281hldict_attr_to_str(
4282 dict_T *dict,
4283 char_u *key,
4284 char_u *attr_str,
4285 int len)
4286{
4287 dictitem_T *di;
4288 dict_T *attrdict;
4289 int i;
4290
4291 attr_str[0] = NUL;
4292 di = dict_find(dict, key, -1);
4293 if (di == NULL)
4294 return TRUE;
4295
4296 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4297 {
4298 emsg(_(e_dictreq));
4299 return FALSE;
4300 }
4301
4302 attrdict = di->di_tv.vval.v_dict;
4303
4304 // If the attribute dict is empty, then return NONE to clear the attributes
4305 if (dict_len(attrdict) == 0)
4306 {
4307 vim_strcat(attr_str, (char_u *)"NONE", len);
4308 return TRUE;
4309 }
4310
4311 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4312 {
4313 if (dict_get_bool(attrdict, (char_u *)hl_name_table[i],
4314 VVAL_FALSE) == VVAL_TRUE)
4315 {
4316 if (attr_str[0] != NUL)
4317 vim_strcat(attr_str, (char_u *)",", len);
4318 vim_strcat(attr_str, (char_u *)hl_name_table[i], len);
4319 }
4320 }
4321
4322 return TRUE;
4323}
4324
4325/*
4326 * Add or update a highlight group using 'dict' items. Returns TRUE if
4327 * successfully updated the highlight group.
4328 */
4329 static int
4330hlg_add_or_update(dict_T *dict)
4331{
4332 char_u *name;
4333 int error;
4334 char_u term_attr[80];
4335 char_u cterm_attr[80];
4336 char_u gui_attr[80];
4337 char_u *start;
4338 char_u *stop;
4339 char_u *ctermfg;
4340 char_u *ctermbg;
4341 char_u *ctermul;
4342 char_u *guifg;
4343 char_u *guibg;
4344 char_u *guisp;
4345# ifdef FEAT_GUI
4346 char_u *font;
4347# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004348 int forceit = FALSE;
4349 int dodefault = FALSE;
4350 int done = FALSE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004351
4352 name = hldict_get_string(dict, (char_u *)"name", &error);
4353 if (name == NULL || error)
4354 return FALSE;
4355
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004356 if (dict_get_bool(dict, (char_u *)"force", VVAL_FALSE) == VVAL_TRUE)
4357 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004358
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004359 if (dict_get_bool(dict, (char_u *)"default", VVAL_FALSE) == VVAL_TRUE)
4360 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004361
4362 if (dict_find(dict, (char_u *)"cleared", -1) != NULL)
4363 {
4364 varnumber_T cleared;
4365
4366 // clear a highlight group
4367 cleared = dict_get_bool(dict, (char_u *)"cleared", FALSE);
4368 if (cleared == TRUE)
4369 {
4370 vim_snprintf((char *)IObuff, IOSIZE, "clear %s", name);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004371 do_highlight(IObuff, forceit, FALSE);
4372 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004373 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004374 }
4375
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004376 if (dict_find(dict, (char_u *)"linksto", -1) != NULL)
4377 {
4378 char_u *linksto;
4379
4380 // link highlight groups
4381 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
4382 if (linksto == NULL || error)
4383 return FALSE;
4384
4385 vim_snprintf((char *)IObuff, IOSIZE, "%slink %s %s",
4386 dodefault ? "default " : "", name, linksto);
4387 do_highlight(IObuff, forceit, FALSE);
4388
4389 done = TRUE;
4390 }
4391
4392 // If 'cleared' or 'linksto' are specified, then don't process the other
4393 // attributes.
4394 if (done)
4395 return TRUE;
4396
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004397 start = hldict_get_string(dict, (char_u *)"start", &error);
4398 if (error)
4399 return FALSE;
4400
4401 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4402 if (error)
4403 return FALSE;
4404
4405 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
4406 sizeof(term_attr)))
4407 return FALSE;
4408
4409 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
4410 sizeof(cterm_attr)))
4411 return FALSE;
4412
4413 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4414 if (error)
4415 return FALSE;
4416
4417 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4418 if (error)
4419 return FALSE;
4420
4421 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4422 if (error)
4423 return FALSE;
4424
4425 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr,
4426 sizeof(gui_attr)))
4427 return FALSE;
4428
4429 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4430 if (error)
4431 return FALSE;
4432
4433 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4434 if (error)
4435 return FALSE;
4436
4437 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4438 if (error)
4439 return FALSE;
4440
4441# ifdef FEAT_GUI
4442 font = hldict_get_string(dict, (char_u *)"font", &error);
4443 if (error)
4444 return FALSE;
4445# endif
4446
4447 // If none of the attributes are specified, then do nothing.
4448 if (term_attr[0] == NUL && start == NULL && stop == NULL
4449 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4450 && ctermul == NULL && gui_attr[0] == NUL
4451# ifdef FEAT_GUI
4452 && font == NULL
4453# endif
4454 && guifg == NULL && guibg == NULL && guisp == NULL
4455 )
4456 return TRUE;
4457
4458 vim_snprintf((char *)IObuff, IOSIZE,
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004459 "%s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s %s%s",
4460 dodefault ? "default " : "",
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004461 name,
4462 term_attr[0] != NUL ? "term=" : "",
4463 term_attr[0] != NUL ? term_attr : (char_u *)"",
4464 start != NULL ? "start=" : "",
4465 start != NULL ? start : (char_u *)"",
4466 stop != NULL ? "stop=" : "",
4467 stop != NULL ? stop : (char_u *)"",
4468 cterm_attr[0] != NUL ? "cterm=" : "",
4469 cterm_attr[0] != NUL ? cterm_attr : (char_u *)"",
4470 ctermfg != NULL ? "ctermfg=" : "",
4471 ctermfg != NULL ? ctermfg : (char_u *)"",
4472 ctermbg != NULL ? "ctermbg=" : "",
4473 ctermbg != NULL ? ctermbg : (char_u *)"",
4474 ctermul != NULL ? "ctermul=" : "",
4475 ctermul != NULL ? ctermul : (char_u *)"",
4476 gui_attr[0] != NUL ? "gui=" : "",
4477 gui_attr[0] != NUL ? gui_attr : (char_u *)"",
4478# ifdef FEAT_GUI
4479 font != NULL ? "font=" : "",
4480 font != NULL ? font : (char_u *)"",
4481# else
4482 "", "",
4483# endif
4484 guifg != NULL ? "guifg=" : "",
4485 guifg != NULL ? guifg : (char_u *)"",
4486 guibg != NULL ? "guibg=" : "",
4487 guibg != NULL ? guibg : (char_u *)"",
4488 guisp != NULL ? "guisp=" : "",
4489 guisp != NULL ? guisp : (char_u *)""
4490 );
4491
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004492 do_highlight(IObuff, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004493
4494 return TRUE;
4495}
4496
4497/*
4498 * "hlset([{highlight_attr}])" function
4499 * Add or modify highlight groups
4500 */
4501 void
4502f_hlset(typval_T *argvars, typval_T *rettv)
4503{
4504 listitem_T *li;
4505 dict_T *dict;
4506
4507 rettv->vval.v_number = -1;
4508
4509 if (check_for_list_arg(argvars, 0) == FAIL)
4510 return;
4511
4512 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4513 {
4514 if (li->li_tv.v_type != VAR_DICT)
4515 {
4516 emsg(_(e_dictreq));
4517 return;
4518 }
4519
4520 dict = li->li_tv.vval.v_dict;
4521 if (!hlg_add_or_update(dict))
4522 return;
4523 }
4524
4525 rettv->vval.v_number = 0;
4526}
4527#endif