blob: 3f46a072761c0ed2efb61a441abba298a567c88b [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
erw7f7f7aaf2021-12-07 21:29:20 +000021#define MAX_SYN_NAME 200
22
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020023/*
24 * The "term", "cterm" and "gui" arguments can be any combination of the
25 * following names, separated by commas (but no spaces!).
26 */
27static char *(hl_name_table[]) =
28 {"bold", "standout", "underline", "undercurl",
29 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
30static int hl_attr_table[] =
31 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
kylo252ae6f1d82022-02-16 19:24:07 +000032#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? (attr_b) : (attr_a)) | (attr_b))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020033
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020034/*
35 * Structure that stores information about a highlight group.
36 * The ID of a highlight group is also called group ID. It is the index in
37 * the highlight_ga array PLUS ONE.
38 */
39typedef struct
40{
41 char_u *sg_name; // highlight group name
42 char_u *sg_name_u; // uppercase of sg_name
43 int sg_cleared; // "hi clear" was used
44// for normal terminals
45 int sg_term; // "term=" highlighting attributes
46 char_u *sg_start; // terminal string for start highl
47 char_u *sg_stop; // terminal string for stop highl
48 int sg_term_attr; // Screen attr for term mode
49// for color terminals
50 int sg_cterm; // "cterm=" highlighting attr
51 int sg_cterm_bold; // bold attr was set for light color
52 int sg_cterm_fg; // terminal fg color number + 1
53 int sg_cterm_bg; // terminal bg color number + 1
Bram Moolenaare023e882020-05-31 16:42:30 +020054 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020055 int sg_cterm_attr; // Screen attr for color term mode
56// for when using the GUI
57#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
58 guicolor_T sg_gui_fg; // GUI foreground color handle
59 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020060 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020061#endif
62#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020063 GuiFont sg_font; // GUI font handle
64#ifdef FEAT_XFONTSET
65 GuiFontset sg_fontset; // GUI fontset handle
66#endif
67 char_u *sg_font_name; // GUI font or fontset name
68 int sg_gui_attr; // Screen attr for GUI mode
69#endif
70#if defined(FEAT_GUI) || defined(FEAT_EVAL)
71// Store the sp color name for the GUI or synIDattr()
72 int sg_gui; // "gui=" highlighting attributes
73 char_u *sg_gui_fg_name;// GUI foreground color name
74 char_u *sg_gui_bg_name;// GUI background color name
75 char_u *sg_gui_sp_name;// GUI special color name
76#endif
77 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +020078 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020079 int sg_set; // combination of SG_* flags
80#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +020081 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020082 sctx_T sg_script_ctx; // script in which the group was last set
83#endif
84} hl_group_T;
85
86// highlight groups for 'highlight' option
87static garray_T highlight_ga;
88#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
89
90/*
91 * An attribute number is the index in attr_table plus ATTR_OFF.
92 */
93#define ATTR_OFF (HL_ALL + 1)
94
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020095static void syn_unadd_group(void);
96static void set_hl_attr(int idx);
97static void highlight_list_one(int id);
98static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
99static int syn_add_group(char_u *name);
100static int hl_has_settings(int idx, int check_link);
101static void highlight_clear(int idx);
102
103#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
104static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
105#endif
106#ifdef FEAT_GUI
107static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
108static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
109#endif
110
111/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200112 * The default highlight groups. These are compiled-in for fast startup and
113 * they still work when the runtime files can't be found.
114 * When making changes here, also change runtime/colors/default.vim!
115 * The #ifdefs are needed to reduce the amount of static data. Helps to make
116 * the 16 bit DOS (museum) version compile.
117 */
118#if defined(FEAT_GUI) || defined(FEAT_EVAL)
119# define CENT(a, b) b
120#else
121# define CENT(a, b) a
122#endif
123static char *(highlight_init_both[]) = {
124 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
125 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
126 CENT("IncSearch term=reverse cterm=reverse",
127 "IncSearch term=reverse cterm=reverse gui=reverse"),
128 CENT("ModeMsg term=bold cterm=bold",
129 "ModeMsg term=bold cterm=bold gui=bold"),
130 CENT("NonText term=bold ctermfg=Blue",
131 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
132 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
133 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
134 CENT("StatusLineNC term=reverse cterm=reverse",
135 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
136 "default link EndOfBuffer NonText",
137 CENT("VertSplit term=reverse cterm=reverse",
138 "VertSplit term=reverse cterm=reverse gui=reverse"),
139#ifdef FEAT_CLIPBOARD
140 CENT("VisualNOS term=underline,bold cterm=underline,bold",
141 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
142#endif
143#ifdef FEAT_DIFF
144 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
145 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
146#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200147 CENT("PmenuSbar ctermbg=Grey",
148 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200149 CENT("TabLineSel term=bold cterm=bold",
150 "TabLineSel term=bold cterm=bold gui=bold"),
151 CENT("TabLineFill term=reverse cterm=reverse",
152 "TabLineFill term=reverse cterm=reverse gui=reverse"),
153#ifdef FEAT_GUI
154 "Cursor guibg=fg guifg=bg",
155 "lCursor guibg=fg guifg=bg", // should be different, but what?
156#endif
157 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000158 "default link CursorLineSign SignColumn",
159 "default link CursorLineFold FoldColumn",
LemonBoya4399382022-04-09 21:04:08 +0100160 "default link CurSearch Search",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200161 CENT("Normal cterm=NONE", "Normal gui=NONE"),
162 NULL
163};
164
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200165// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200166static char *(highlight_init_light[]) = {
167 CENT("Directory term=bold ctermfg=DarkBlue",
168 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
169 CENT("LineNr term=underline ctermfg=Brown",
170 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200171 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
172 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200173 CENT("MoreMsg term=bold ctermfg=DarkGreen",
174 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
175 CENT("Question term=standout ctermfg=DarkGreen",
176 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
177 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
178 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
179#ifdef FEAT_SPELL
180 CENT("SpellBad term=reverse ctermbg=LightRed",
181 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
182 CENT("SpellCap term=reverse ctermbg=LightBlue",
183 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
184 CENT("SpellRare term=reverse ctermbg=LightMagenta",
185 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
186 CENT("SpellLocal term=underline ctermbg=Cyan",
187 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
188#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200189 CENT("PmenuThumb ctermbg=Black",
190 "PmenuThumb ctermbg=Black guibg=Black"),
191 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
192 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
193 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
194 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200195 CENT("SpecialKey term=bold ctermfg=DarkBlue",
196 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
197 CENT("Title term=bold ctermfg=DarkMagenta",
198 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
199 CENT("WarningMsg term=standout ctermfg=DarkRed",
200 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
201#ifdef FEAT_WILDMENU
202 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
203 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
204#endif
205#ifdef FEAT_FOLDING
206 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
207 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
208 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
209 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
210#endif
211#ifdef FEAT_SIGNS
212 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
213 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
214#endif
215 CENT("Visual term=reverse",
216 "Visual term=reverse guibg=LightGrey"),
217#ifdef FEAT_DIFF
218 CENT("DiffAdd term=bold ctermbg=LightBlue",
219 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
220 CENT("DiffChange term=bold ctermbg=LightMagenta",
221 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
222 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
223 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
224#endif
225 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
226 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
227#ifdef FEAT_SYN_HL
228 CENT("CursorColumn term=reverse ctermbg=LightGrey",
229 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
230 CENT("CursorLine term=underline cterm=underline",
231 "CursorLine term=underline cterm=underline guibg=Grey90"),
232 CENT("ColorColumn term=reverse ctermbg=LightRed",
233 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
234#endif
235#ifdef FEAT_CONCEAL
236 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
237 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
238#endif
239 CENT("MatchParen term=reverse ctermbg=Cyan",
240 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
241#ifdef FEAT_TERMINAL
242 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
243 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
244 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
245 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
246#endif
247#ifdef FEAT_MENU
248 CENT("ToolbarLine term=underline ctermbg=LightGrey",
249 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
250 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
251 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
252#endif
253 NULL
254};
255
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200256// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200257static char *(highlight_init_dark[]) = {
258 CENT("Directory term=bold ctermfg=LightCyan",
259 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
260 CENT("LineNr term=underline ctermfg=Yellow",
261 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200262 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
263 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200264 CENT("MoreMsg term=bold ctermfg=LightGreen",
265 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
266 CENT("Question term=standout ctermfg=LightGreen",
267 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
268 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
269 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
270 CENT("SpecialKey term=bold ctermfg=LightBlue",
271 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
272#ifdef FEAT_SPELL
273 CENT("SpellBad term=reverse ctermbg=Red",
274 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
275 CENT("SpellCap term=reverse ctermbg=Blue",
276 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
277 CENT("SpellRare term=reverse ctermbg=Magenta",
278 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
279 CENT("SpellLocal term=underline ctermbg=Cyan",
280 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
281#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200282 CENT("PmenuThumb ctermbg=White",
283 "PmenuThumb ctermbg=White guibg=White"),
284 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
285 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
286 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
287 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200288 CENT("Title term=bold ctermfg=LightMagenta",
289 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
290 CENT("WarningMsg term=standout ctermfg=LightRed",
291 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
292#ifdef FEAT_WILDMENU
293 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
294 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
295#endif
296#ifdef FEAT_FOLDING
297 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
298 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
299 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
300 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
301#endif
302#ifdef FEAT_SIGNS
303 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
304 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
305#endif
306 CENT("Visual term=reverse",
307 "Visual term=reverse guibg=DarkGrey"),
308#ifdef FEAT_DIFF
309 CENT("DiffAdd term=bold ctermbg=DarkBlue",
310 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
311 CENT("DiffChange term=bold ctermbg=DarkMagenta",
312 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
313 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
314 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
315#endif
316 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
317 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
318#ifdef FEAT_SYN_HL
319 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
320 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
321 CENT("CursorLine term=underline cterm=underline",
322 "CursorLine term=underline cterm=underline guibg=Grey40"),
323 CENT("ColorColumn term=reverse ctermbg=DarkRed",
324 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
325#endif
326 CENT("MatchParen term=reverse ctermbg=DarkCyan",
327 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
328#ifdef FEAT_CONCEAL
329 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
330 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
331#endif
332#ifdef FEAT_TERMINAL
333 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
334 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
335 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
336 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
337#endif
338#ifdef FEAT_MENU
339 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
340 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
341 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
342 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
343#endif
344 NULL
345};
346
Dominique Pelle748b3082022-01-08 12:41:16 +0000347#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200348/*
349 * Returns the number of highlight groups.
350 */
351 int
352highlight_num_groups(void)
353{
354 return highlight_ga.ga_len;
355}
356
357/*
358 * Returns the name of a highlight group.
359 */
360 char_u *
361highlight_group_name(int id)
362{
363 return HL_TABLE()[id].sg_name;
364}
365
366/*
367 * Returns the ID of the link to a highlight group.
368 */
369 int
370highlight_link_id(int id)
371{
372 return HL_TABLE()[id].sg_link;
373}
Dominique Pelle748b3082022-01-08 12:41:16 +0000374#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200375
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200376 void
377init_highlight(
378 int both, // include groups where 'bg' doesn't matter
379 int reset) // clear group first
380{
381 int i;
382 char **pp;
383 static int had_both = FALSE;
384#ifdef FEAT_EVAL
385 char_u *p;
386
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100387 // Try finding the color scheme file. Used when a color file was loaded
388 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200389 p = get_var_value((char_u *)"g:colors_name");
390 if (p != NULL)
391 {
392 // The value of g:colors_name could be freed when sourcing the script,
393 // making "p" invalid, so copy it.
394 char_u *copy_p = vim_strsave(p);
395 int r;
396
397 if (copy_p != NULL)
398 {
399 r = load_colors(copy_p);
400 vim_free(copy_p);
401 if (r == OK)
402 return;
403 }
404 }
405
406#endif
407
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100408 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200409 if (both)
410 {
411 had_both = TRUE;
412 pp = highlight_init_both;
413 for (i = 0; pp[i] != NULL; ++i)
414 do_highlight((char_u *)pp[i], reset, TRUE);
415 }
416 else if (!had_both)
417 // Don't do anything before the call with both == TRUE from main().
418 // Not everything has been setup then, and that call will overrule
419 // everything anyway.
420 return;
421
422 if (*p_bg == 'l')
423 pp = highlight_init_light;
424 else
425 pp = highlight_init_dark;
426 for (i = 0; pp[i] != NULL; ++i)
427 do_highlight((char_u *)pp[i], reset, TRUE);
428
429 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
430 // depend on the number of colors available.
431 // With 8 colors brown is equal to yellow, need to use black for Search fg
432 // to avoid Statement highlighted text disappears.
433 // Clear the attributes, needed when changing the t_Co value.
434 if (t_colors > 8)
435 do_highlight((char_u *)(*p_bg == 'l'
436 ? "Visual cterm=NONE ctermbg=LightGrey"
437 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
438 else
439 {
440 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
441 FALSE, TRUE);
442 if (*p_bg == 'l')
443 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
444 }
445
446#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100447 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200448 if (get_var_value((char_u *)"g:syntax_on") != NULL)
449 {
450 static int recursive = 0;
451
452 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000453 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200454 else
455 {
456 ++recursive;
457 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
458 --recursive;
459 }
460 }
461#endif
462}
463
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000464#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
465/*
466 * Load a default color list. Intended to support legacy color names but allows
467 * the user to override the color values. Only loaded once.
468 */
469 static void
470load_default_colors_lists()
471{
472 // Lacking a default color list isn't the end of the world but it is likely
473 // an inconvenience so users should know when it is missing.
474 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
475 msg("failed to load colors/lists/default.vim");
476}
477#endif
478
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200479/*
480 * Load color file "name".
481 * Return OK for success, FAIL for failure.
482 */
483 int
484load_colors(char_u *name)
485{
486 char_u *buf;
487 int retval = FAIL;
488 static int recursive = FALSE;
489
490 // When being called recursively, this is probably because setting
491 // 'background' caused the highlighting to be reloaded. This means it is
492 // working, thus we should return OK.
493 if (recursive)
494 return OK;
495
496 recursive = TRUE;
497 buf = alloc(STRLEN(name) + 12);
498 if (buf != NULL)
499 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100500#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100501 load_default_colors_lists();
502#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200503 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
504 curbuf->b_fname, FALSE, curbuf);
505 sprintf((char *)buf, "colors/%s.vim", name);
506 retval = source_runtime(buf, DIP_START + DIP_OPT);
507 vim_free(buf);
508 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
509 }
510 recursive = FALSE;
511
512 return retval;
513}
514
515static char *(color_names[28]) = {
516 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
517 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
518 "Gray", "Grey", "LightGray", "LightGrey",
519 "DarkGray", "DarkGrey",
520 "Blue", "LightBlue", "Green", "LightGreen",
521 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
522 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
523 // indices:
524 // 0, 1, 2, 3,
525 // 4, 5, 6, 7,
526 // 8, 9, 10, 11,
527 // 12, 13,
528 // 14, 15, 16, 17,
529 // 18, 19, 20, 21, 22,
530 // 23, 24, 25, 26, 27
531static int color_numbers_16[28] = {0, 1, 2, 3,
532 4, 5, 6, 6,
533 7, 7, 7, 7,
534 8, 8,
535 9, 9, 10, 10,
536 11, 11, 12, 12, 13,
537 13, 14, 14, 15, -1};
538// for xterm with 88 colors...
539static int color_numbers_88[28] = {0, 4, 2, 6,
540 1, 5, 32, 72,
541 84, 84, 7, 7,
542 82, 82,
543 12, 43, 10, 61,
544 14, 63, 9, 74, 13,
545 75, 11, 78, 15, -1};
546// for xterm with 256 colors...
547static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200548 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200549 248, 248, 7, 7,
550 242, 242,
551 12, 81, 10, 121,
552 14, 159, 9, 224, 13,
553 225, 11, 229, 15, -1};
554// for terminals with less than 16 colors...
555static int color_numbers_8[28] = {0, 4, 2, 6,
556 1, 5, 3, 3,
557 7, 7, 7, 7,
558 0+8, 0+8,
559 4+8, 4+8, 2+8, 2+8,
560 6+8, 6+8, 1+8, 1+8, 5+8,
561 5+8, 3+8, 3+8, 7+8, -1};
562
563/*
564 * Lookup the "cterm" value to be used for color with index "idx" in
565 * color_names[].
566 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
567 * colors, otherwise it will be unchanged.
568 */
569 int
570lookup_color(int idx, int foreground, int *boldp)
571{
572 int color = color_numbers_16[idx];
573 char_u *p;
574
575 // Use the _16 table to check if it's a valid color name.
576 if (color < 0)
577 return -1;
578
579 if (t_colors == 8)
580 {
581 // t_Co is 8: use the 8 colors table
582#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100583 // On qnx, the 8 & 16 color arrays are the same
584 if (STRNCMP(T_NAME, "qansi", 5) == 0)
585 color = color_numbers_16[idx];
586 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200587#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100588 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200589 if (foreground)
590 {
591 // set/reset bold attribute to get light foreground
592 // colors (on some terminals, e.g. "linux")
593 if (color & 8)
594 *boldp = TRUE;
595 else
596 *boldp = FALSE;
597 }
598 color &= 7; // truncate to 8 colors
599 }
600 else if (t_colors == 16 || t_colors == 88
601 || t_colors >= 256)
602 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100603 // Guess: if the termcap entry ends in 'm', it is
604 // probably an xterm-like terminal. Use the changed
605 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200606 if (*T_CAF != NUL)
607 p = T_CAF;
608 else
609 p = T_CSF;
610 if (*p != NUL && (t_colors > 256
611 || *(p + STRLEN(p) - 1) == 'm'))
612 {
613 if (t_colors == 88)
614 color = color_numbers_88[idx];
615 else if (t_colors >= 256)
616 color = color_numbers_256[idx];
617 else
618 color = color_numbers_8[idx];
619 }
620#ifdef FEAT_TERMRESPONSE
621 if (t_colors >= 256 && color == 15 && is_mac_terminal)
622 // Terminal.app has a bug: 15 is light grey. Use white
623 // from the color cube instead.
624 color = 231;
625#endif
626 }
627 return color;
628}
629
630/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100631 * Link highlight group 'from_hg' to 'to_hg'.
632 * 'dodefault' is set to TRUE for ":highlight default link".
633 * 'forceit' is set to TRUE for ":higlight! link"
634 * 'init' is set to TRUE when initializing all the highlight groups.
635 */
636 static void
637highlight_group_link(
638 char_u *from_hg,
639 int from_len,
640 char_u *to_hg,
641 int to_len,
642 int dodefault,
643 int forceit,
644 int init)
645{
646 int from_id;
647 int to_id;
648 hl_group_T *hlgroup = NULL;
649
650 from_id = syn_check_group(from_hg, from_len);
651 if (STRNCMP(to_hg, "NONE", 4) == 0)
652 to_id = 0;
653 else
654 to_id = syn_check_group(to_hg, to_len);
655
656 if (from_id > 0)
657 {
658 hlgroup = &HL_TABLE()[from_id - 1];
659 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
660 {
661 hlgroup->sg_deflink = to_id;
662#ifdef FEAT_EVAL
663 hlgroup->sg_deflink_sctx = current_sctx;
664 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
665#endif
666 }
667 }
668
669 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
670 {
671 // Don't allow a link when there already is some highlighting
672 // for the group, unless '!' is used
673 if (to_id > 0 && !forceit && !init
674 && hl_has_settings(from_id - 1, dodefault))
675 {
676 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000677 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100678 }
679 else if (hlgroup->sg_link != to_id
680#ifdef FEAT_EVAL
681 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
682#endif
683 || hlgroup->sg_cleared)
684 {
685 if (!init)
686 hlgroup->sg_set |= SG_LINK;
687 hlgroup->sg_link = to_id;
688#ifdef FEAT_EVAL
689 hlgroup->sg_script_ctx = current_sctx;
690 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
691#endif
692 hlgroup->sg_cleared = FALSE;
693 redraw_all_later(SOME_VALID);
694
695 // Only call highlight_changed() once after multiple changes.
696 need_highlight_changed = TRUE;
697 }
698 }
699
700}
701
702/*
703 * Reset all highlighting to the defaults. Removes all highlighting for the
704 * groups added by the user.
705 */
706 static void
707highlight_reset_all(void)
708{
709 int idx;
710
711#ifdef FEAT_GUI
712 // First, we do not destroy the old values, but allocate the new
713 // ones and update the display. THEN we destroy the old values.
714 // If we destroy the old values first, then the old values
715 // (such as GuiFont's or GuiFontset's) will still be displayed but
716 // invalid because they were free'd.
717 if (gui.in_use)
718 {
719# ifdef FEAT_BEVAL_TIP
720 gui_init_tooltip_font();
721# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100722# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100723 gui_init_menu_font();
724# endif
725 }
726# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
727 gui_mch_def_colors();
728# endif
729# ifdef FEAT_GUI_X11
730# ifdef FEAT_MENU
731
732 // This only needs to be done when there is no Menu highlight
733 // group defined by default, which IS currently the case.
734 gui_mch_new_menu_colors();
735# endif
736 if (gui.in_use)
737 {
738 gui_new_scrollbar_colors();
739# ifdef FEAT_BEVAL_GUI
740 gui_mch_new_tooltip_colors();
741# endif
742# ifdef FEAT_MENU
743 gui_mch_new_menu_font();
744# endif
745 }
746# endif
747
748 // Ok, we're done allocating the new default graphics items.
749 // The screen should already be refreshed at this point.
750 // It is now Ok to clear out the old data.
751#endif
752#ifdef FEAT_EVAL
753 do_unlet((char_u *)"g:colors_name", TRUE);
754#endif
755 restore_cterm_colors();
756
757 // Clear all default highlight groups and load the defaults.
758 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
759 highlight_clear(idx);
760 init_highlight(TRUE, TRUE);
761#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
762 if (USE_24BIT)
763 highlight_gui_started();
764 else
765#endif
766 highlight_changed();
767 redraw_later_clear();
768}
769
770/*
771 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
772 * index 'idx'.
773 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
774 * 'arg' is the list of attribute names separated by comma.
775 * 'init' is set to TRUE when initializing all the highlight groups.
776 * Returns TRUE if the attributes are set.
777 */
778 static int
779highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
780{
781 int attr;
782 int off;
783 long i;
784 int len;
785
786 attr = 0;
787 off = 0;
788 while (arg[off] != NUL)
789 {
790 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
791 {
792 len = (int)STRLEN(hl_name_table[i]);
793 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
794 {
795 attr |= hl_attr_table[i];
796 off += len;
797 break;
798 }
799 }
800 if (i < 0)
801 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000802 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100803 return FALSE;
804 }
805 if (arg[off] == ',') // another one follows
806 ++off;
807 }
808 if (*key == 'T')
809 {
810 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
811 {
812 if (!init)
813 HL_TABLE()[idx].sg_set |= SG_TERM;
814 HL_TABLE()[idx].sg_term = attr;
815 }
816 }
817 else if (*key == 'C')
818 {
819 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
820 {
821 if (!init)
822 HL_TABLE()[idx].sg_set |= SG_CTERM;
823 HL_TABLE()[idx].sg_cterm = attr;
824 HL_TABLE()[idx].sg_cterm_bold = FALSE;
825 }
826 }
827#if defined(FEAT_GUI) || defined(FEAT_EVAL)
828 else
829 {
830 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
831 {
832 if (!init)
833 HL_TABLE()[idx].sg_set |= SG_GUI;
834 HL_TABLE()[idx].sg_gui = attr;
835 }
836 }
837#endif
838
839 return TRUE;
840}
841
842#ifdef FEAT_GUI
843/*
844 * Set the font for the highlight group at 'idx'.
845 * 'arg' is the font name.
846 * Returns TRUE if the font is changed.
847 */
848 static int
849highlight_set_font(
850 int idx,
851 char_u *arg,
852 int is_normal_group,
853 int is_menu_group,
854 int is_tooltip_group)
855{
856 int did_change = FALSE;
857
858 // in non-GUI fonts are simply ignored
859 if (HL_TABLE()[idx].sg_font_name != NULL
860 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
861 {
862 // Font name didn't change, ignore.
863 }
864 else if (!gui.shell_created)
865 {
866 // GUI not started yet, always accept the name.
867 vim_free(HL_TABLE()[idx].sg_font_name);
868 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
869 did_change = TRUE;
870 }
871 else
872 {
873 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
874# ifdef FEAT_XFONTSET
875 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
876# endif
877 // First, save the current font/fontset.
878 // Then try to allocate the font/fontset.
879 // If the allocation fails, HL_TABLE()[idx].sg_font OR
880 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
881
882 HL_TABLE()[idx].sg_font = NOFONT;
883# ifdef FEAT_XFONTSET
884 HL_TABLE()[idx].sg_fontset = NOFONTSET;
885# endif
886 hl_do_font(idx, arg, is_normal_group, is_menu_group,
887 is_tooltip_group, FALSE);
888
889# ifdef FEAT_XFONTSET
890 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
891 {
892 // New fontset was accepted. Free the old one, if there
893 // was one.
894 gui_mch_free_fontset(temp_sg_fontset);
895 vim_free(HL_TABLE()[idx].sg_font_name);
896 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
897 did_change = TRUE;
898 }
899 else
900 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
901# endif
902 if (HL_TABLE()[idx].sg_font != NOFONT)
903 {
904 // New font was accepted. Free the old one, if there was
905 // one.
906 gui_mch_free_font(temp_sg_font);
907 vim_free(HL_TABLE()[idx].sg_font_name);
908 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
909 did_change = TRUE;
910 }
911 else
912 HL_TABLE()[idx].sg_font = temp_sg_font;
913 }
914
915 return did_change;
916}
917#endif
918
919/*
920 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
921 * Returns TRUE if the foreground color is set.
922 */
923 static void
924highlight_set_ctermfg(int idx, int color, int is_normal_group)
925{
926 HL_TABLE()[idx].sg_cterm_fg = color + 1;
927 if (is_normal_group)
928 {
929 cterm_normal_fg_color = color + 1;
930 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
931#ifdef FEAT_GUI
932 // Don't do this if the GUI is used.
933 if (!gui.in_use && !gui.starting)
934#endif
935 {
936 must_redraw = CLEAR;
937 if (termcap_active && color >= 0)
938 term_fg_color(color);
939 }
940 }
941}
942
943/*
944 * Set the cterm background color for the highlight group at 'idx' to 'color'.
945 * Returns TRUE if the background color is set.
946 */
947 static void
948highlight_set_ctermbg(int idx, int color, int is_normal_group)
949{
950 HL_TABLE()[idx].sg_cterm_bg = color + 1;
951 if (is_normal_group)
952 {
953 cterm_normal_bg_color = color + 1;
954#ifdef FEAT_GUI
955 // Don't mess with 'background' if the GUI is used.
956 if (!gui.in_use && !gui.starting)
957#endif
958 {
959 must_redraw = CLEAR;
960 if (color >= 0)
961 {
962 int dark = -1;
963
964 if (termcap_active)
965 term_bg_color(color);
966 if (t_colors < 16)
967 dark = (color == 0 || color == 4);
968 // Limit the heuristic to the standard 16 colors
969 else if (color < 16)
970 dark = (color < 7 || color == 8);
971 // Set the 'background' option if the value is
972 // wrong.
973 if (dark != -1
974 && dark != (*p_bg == 'd')
975 && !option_was_set((char_u *)"bg"))
976 {
Bram Moolenaar31e5c602022-04-15 13:53:33 +0100977 set_option_value_give_err((char_u *)"bg",
978 0L, (char_u *)(dark ? "dark" : "light"), 0);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100979 reset_option_was_set((char_u *)"bg");
980 }
981 }
982 }
983 }
984}
985
986/*
987 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
988 * Returns TRUE if the underline color is set.
989 */
990 static void
991highlight_set_ctermul(int idx, int color, int is_normal_group)
992{
993 HL_TABLE()[idx].sg_cterm_ul = color + 1;
994 if (is_normal_group)
995 {
996 cterm_normal_ul_color = color + 1;
997#ifdef FEAT_GUI
998 // Don't do this if the GUI is used.
999 if (!gui.in_use && !gui.starting)
1000#endif
1001 {
1002 must_redraw = CLEAR;
1003 if (termcap_active && color >= 0)
1004 term_ul_color(color);
1005 }
1006 }
1007}
1008
1009/*
1010 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1011 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1012 * 'keystart' is the color name/value.
1013 * 'arg' is the color name or the numeric value as a string.
1014 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1015 * 'init' is set to TRUE when initializing highlighting.
1016 * Called for the ":highlight" command and the "hlset()" function.
1017 *
1018 * Returns TRUE if the color is set.
1019 */
1020 static int
1021highlight_set_cterm_color(
1022 int idx,
1023 char_u *key,
1024 char_u *key_start,
1025 char_u *arg,
1026 int is_normal_group,
1027 int init)
1028{
1029 int color;
1030 long i;
1031 int off;
1032
1033 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1034 {
1035 if (!init)
1036 HL_TABLE()[idx].sg_set |= SG_CTERM;
1037
1038 // When setting the foreground color, and previously the "bold"
1039 // flag was set for a light color, reset it now
1040 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1041 {
1042 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1043 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1044 }
1045
1046 if (VIM_ISDIGIT(*arg))
1047 color = atoi((char *)arg);
1048 else if (STRICMP(arg, "fg") == 0)
1049 {
1050 if (cterm_normal_fg_color)
1051 color = cterm_normal_fg_color - 1;
1052 else
1053 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001054 emsg(_(e_fg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001055 return FALSE;
1056 }
1057 }
1058 else if (STRICMP(arg, "bg") == 0)
1059 {
1060 if (cterm_normal_bg_color > 0)
1061 color = cterm_normal_bg_color - 1;
1062 else
1063 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001064 emsg(_(e_bg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001065 return FALSE;
1066 }
1067 }
1068 else if (STRICMP(arg, "ul") == 0)
1069 {
1070 if (cterm_normal_ul_color > 0)
1071 color = cterm_normal_ul_color - 1;
1072 else
1073 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00001074 emsg(_(e_ul_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001075 return FALSE;
1076 }
1077 }
1078 else
1079 {
1080 int bold = MAYBE;
1081
1082 // reduce calls to STRICMP a bit, it can be slow
1083 off = TOUPPER_ASC(*arg);
1084 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1085 if (off == color_names[i][0]
1086 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1087 break;
1088 if (i < 0)
1089 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001090 semsg(_(e_color_name_or_number_not_recognized), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001091 return FALSE;
1092 }
1093
1094 color = lookup_color(i, key[5] == 'F', &bold);
1095
1096 // set/reset bold attribute to get light foreground
1097 // colors (on some terminals, e.g. "linux")
1098 if (bold == TRUE)
1099 {
1100 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1101 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1102 }
1103 else if (bold == FALSE)
1104 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1105 }
1106
1107 // Add one to the argument, to avoid zero. Zero is used for
1108 // "NONE", then "color" is -1.
1109 if (key[5] == 'F')
1110 highlight_set_ctermfg(idx, color, is_normal_group);
1111 else if (key[5] == 'B')
1112 highlight_set_ctermbg(idx, color, is_normal_group);
1113 else // ctermul
1114 highlight_set_ctermul(idx, color, is_normal_group);
1115 }
1116
1117 return TRUE;
1118}
1119
1120#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1121/*
1122 * Set the GUI foreground color for the highlight group at 'idx'.
1123 * Returns TRUE if the color is set.
1124 */
1125 static int
1126highlight_set_guifg(
1127 int idx,
1128 char_u *arg,
1129 int is_menu_group UNUSED,
1130 int is_scrollbar_group UNUSED,
1131 int is_tooltip_group UNUSED,
1132 int *do_colors UNUSED,
1133 int init)
1134{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001135# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001136 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001137# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001138 char_u **namep;
1139 int did_change = FALSE;
1140
1141 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1142 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1143 {
1144 if (!init)
1145 HL_TABLE()[idx].sg_set |= SG_GUI;
1146
1147# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1148 // In GUI guifg colors are only used when recognized
1149 i = color_name2handle(arg);
1150 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1151 {
1152 HL_TABLE()[idx].sg_gui_fg = i;
1153# endif
1154 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1155 {
1156 vim_free(*namep);
1157 if (STRCMP(arg, "NONE") != 0)
1158 *namep = vim_strsave(arg);
1159 else
1160 *namep = NULL;
1161 did_change = TRUE;
1162 }
1163# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1164# ifdef FEAT_GUI_X11
1165 if (is_menu_group && gui.menu_fg_pixel != i)
1166 {
1167 gui.menu_fg_pixel = i;
1168 *do_colors = TRUE;
1169 }
1170 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1171 {
1172 gui.scroll_fg_pixel = i;
1173 *do_colors = TRUE;
1174 }
1175# ifdef FEAT_BEVAL_GUI
1176 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1177 {
1178 gui.tooltip_fg_pixel = i;
1179 *do_colors = TRUE;
1180 }
1181# endif
1182# endif
1183 }
1184# endif
1185 }
1186
1187 return did_change;
1188}
1189
1190/*
1191 * Set the GUI background color for the highlight group at 'idx'.
1192 * Returns TRUE if the color is set.
1193 */
1194 static int
1195highlight_set_guibg(
1196 int idx,
1197 char_u *arg,
1198 int is_menu_group UNUSED,
1199 int is_scrollbar_group UNUSED,
1200 int is_tooltip_group UNUSED,
1201 int *do_colors UNUSED,
1202 int init)
1203{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001204# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001205 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001206# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001207 char_u **namep;
1208 int did_change = FALSE;
1209
1210 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1211 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1212 {
1213 if (!init)
1214 HL_TABLE()[idx].sg_set |= SG_GUI;
1215
1216# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Drew Vogele30d1022021-10-24 20:35:07 +01001217 // In GUI guibg colors are only used when recognized
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001218 i = color_name2handle(arg);
1219 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1220 {
1221 HL_TABLE()[idx].sg_gui_bg = i;
1222# endif
1223 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1224 {
1225 vim_free(*namep);
1226 if (STRCMP(arg, "NONE") != 0)
1227 *namep = vim_strsave(arg);
1228 else
1229 *namep = NULL;
1230 did_change = TRUE;
1231 }
1232# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1233# ifdef FEAT_GUI_X11
1234 if (is_menu_group && gui.menu_bg_pixel != i)
1235 {
1236 gui.menu_bg_pixel = i;
1237 *do_colors = TRUE;
1238 }
1239 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1240 {
1241 gui.scroll_bg_pixel = i;
1242 *do_colors = TRUE;
1243 }
1244# ifdef FEAT_BEVAL_GUI
1245 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1246 {
1247 gui.tooltip_bg_pixel = i;
1248 *do_colors = TRUE;
1249 }
1250# endif
1251# endif
1252 }
1253# endif
1254 }
1255
1256 return did_change;
1257}
1258
1259/*
1260 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1261 * Returns TRUE if the color is set.
1262 */
1263 static int
1264highlight_set_guisp(int idx, char_u *arg, int init)
1265{
1266# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1267 int i;
1268# endif
1269 int did_change = FALSE;
1270 char_u **namep;
1271
1272 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1273 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1274 {
1275 if (!init)
1276 HL_TABLE()[idx].sg_set |= SG_GUI;
1277
1278# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1279 // In GUI guisp colors are only used when recognized
1280 i = color_name2handle(arg);
1281 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1282 {
1283 HL_TABLE()[idx].sg_gui_sp = i;
1284# endif
1285 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1286 {
1287 vim_free(*namep);
1288 if (STRCMP(arg, "NONE") != 0)
1289 *namep = vim_strsave(arg);
1290 else
1291 *namep = NULL;
1292 did_change = TRUE;
1293 }
1294# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1295 }
1296# endif
1297 }
1298
1299 return did_change;
1300}
1301#endif
1302
1303/*
1304 * Set the start/stop terminal codes for a highlight group.
1305 * Returns TRUE if the terminal code is set.
1306 */
1307 static int
1308highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1309{
1310 int off;
1311 char_u buf[100];
1312 int len;
1313 char_u *tname;
1314 char_u *p;
1315
1316 if (!init)
1317 HL_TABLE()[idx].sg_set |= SG_TERM;
1318
1319 // The "start" and "stop" arguments can be a literal escape
1320 // sequence, or a comma separated list of terminal codes.
1321 if (STRNCMP(arg, "t_", 2) == 0)
1322 {
1323 off = 0;
1324 buf[0] = 0;
1325 while (arg[off] != NUL)
1326 {
1327 // Isolate one termcap name
1328 for (len = 0; arg[off + len] &&
1329 arg[off + len] != ','; ++len)
1330 ;
1331 tname = vim_strnsave(arg + off, len);
1332 if (tname == NULL) // out of memory
1333 return FALSE;
1334 // lookup the escape sequence for the item
1335 p = get_term_code(tname);
1336 vim_free(tname);
1337 if (p == NULL) // ignore non-existing things
1338 p = (char_u *)"";
1339
1340 // Append it to the already found stuff
1341 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1342 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001343 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001344 return FALSE;
1345 }
1346 STRCAT(buf, p);
1347
1348 // Advance to the next item
1349 off += len;
1350 if (arg[off] == ',') // another one follows
1351 ++off;
1352 }
1353 }
1354 else
1355 {
1356 // Copy characters from arg[] to buf[], translating <> codes.
1357 for (p = arg, off = 0; off < 100 - 6 && *p; )
1358 {
zeertzjqdb088872022-05-02 22:53:45 +01001359 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001360 if (len > 0) // recognized special char
1361 off += len;
1362 else // copy as normal char
1363 buf[off++] = *p++;
1364 }
1365 buf[off] = NUL;
1366 }
1367
1368 if (STRCMP(buf, "NONE") == 0) // resetting the value
1369 p = NULL;
1370 else
1371 p = vim_strsave(buf);
1372 if (key[2] == 'A')
1373 {
1374 vim_free(HL_TABLE()[idx].sg_start);
1375 HL_TABLE()[idx].sg_start = p;
1376 }
1377 else
1378 {
1379 vim_free(HL_TABLE()[idx].sg_stop);
1380 HL_TABLE()[idx].sg_stop = p;
1381 }
1382 return TRUE;
1383}
1384
1385/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001386 * Handle the ":highlight .." command.
1387 * When using ":hi clear" this is called recursively for each group with
1388 * "forceit" and "init" both TRUE.
1389 */
1390 void
1391do_highlight(
1392 char_u *line,
1393 int forceit,
1394 int init) // TRUE when called for initializing
1395{
1396 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001397 char_u *linep;
1398 char_u *key_start;
1399 char_u *arg_start;
1400 char_u *key = NULL, *arg = NULL;
1401 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001402 int id;
1403 int idx;
1404 hl_group_T item_before;
1405 int did_change = FALSE;
1406 int dodefault = FALSE;
1407 int doclear = FALSE;
1408 int dolink = FALSE;
1409 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001410 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001411#ifdef FEAT_GUI_X11
1412 int is_menu_group = FALSE; // "Menu" group
1413 int is_scrollbar_group = FALSE; // "Scrollbar" group
1414 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001415#else
1416# define is_menu_group 0
1417# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001418# define is_scrollbar_group 0
1419#endif
1420#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1421 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001422#endif
1423#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1424 int did_highlight_changed = FALSE;
1425#endif
1426
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001427 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001428 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001429 {
1430 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1431 // TODO: only call when the group has attributes set
1432 highlight_list_one((int)i);
1433 return;
1434 }
1435
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001436 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001437 name_end = skiptowhite(line);
1438 linep = skipwhite(name_end);
1439
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001440 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001441 if (STRNCMP(line, "default", name_end - line) == 0)
1442 {
1443 dodefault = TRUE;
1444 line = linep;
1445 name_end = skiptowhite(line);
1446 linep = skipwhite(name_end);
1447 }
1448
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001449 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001450 if (STRNCMP(line, "clear", name_end - line) == 0)
1451 doclear = TRUE;
1452 if (STRNCMP(line, "link", name_end - line) == 0)
1453 dolink = TRUE;
1454
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001455 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001456 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001457 {
1458 id = syn_namen2id(line, (int)(name_end - line));
1459 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001460 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001461 else
1462 highlight_list_one(id);
1463 return;
1464 }
1465
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001466 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001467 if (dolink)
1468 {
1469 char_u *from_start = linep;
1470 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001471 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001472 char_u *to_start;
1473 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001474 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001475
1476 from_end = skiptowhite(from_start);
1477 to_start = skipwhite(from_end);
1478 to_end = skiptowhite(to_start);
1479
Bram Moolenaar1966c242020-04-20 22:42:32 +02001480 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001481 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001482 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001483 return;
1484 }
1485
Bram Moolenaar1966c242020-04-20 22:42:32 +02001486 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001487 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001488 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001489 return;
1490 }
1491
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001492 from_len = (int)(from_end - from_start);
1493 to_len = (int)(to_end - to_start);
1494 highlight_group_link(from_start, from_len, to_start, to_len,
1495 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001496 return;
1497 }
1498
1499 if (doclear)
1500 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001501 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001502 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001503 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001504 // ":highlight clear" without group name
1505 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001506 return;
1507 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001508 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001509 name_end = skiptowhite(line);
1510 linep = skipwhite(name_end);
1511 }
1512
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001513 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001514 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001515 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001516 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001517 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001518
1519 // Return if "default" was used and the group already has settings.
1520 if (dodefault && hl_has_settings(idx, TRUE))
1521 return;
1522
1523 // Make a copy so we can check if any attribute actually changed.
1524 item_before = HL_TABLE()[idx];
1525
1526 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1527 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001528#ifdef FEAT_GUI_X11
1529 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1530 is_menu_group = TRUE;
1531 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1532 is_scrollbar_group = TRUE;
1533 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1534 is_tooltip_group = TRUE;
1535#endif
1536
1537 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1538 if (doclear || (forceit && init))
1539 {
1540 highlight_clear(idx);
1541 if (!doclear)
1542 HL_TABLE()[idx].sg_set = 0;
1543 }
1544
1545 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001546 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001547 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001548 key_start = linep;
1549 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001550 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001551 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001552 error = TRUE;
1553 break;
1554 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001555
1556 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1557 // or "guibg").
1558 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1559 ++linep;
1560 vim_free(key);
1561 key = vim_strnsave_up(key_start, linep - key_start);
1562 if (key == NULL)
1563 {
1564 error = TRUE;
1565 break;
1566 }
1567 linep = skipwhite(linep);
1568
1569 if (STRCMP(key, "NONE") == 0)
1570 {
1571 if (!init || HL_TABLE()[idx].sg_set == 0)
1572 {
1573 if (!init)
1574 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1575 highlight_clear(idx);
1576 }
1577 continue;
1578 }
1579
1580 // Check for the equal sign.
1581 if (*linep != '=')
1582 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001583 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001584 error = TRUE;
1585 break;
1586 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001587 ++linep;
1588
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001589 // Isolate the argument.
1590 linep = skipwhite(linep);
1591 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001592 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001593 arg_start = ++linep;
1594 linep = vim_strchr(linep, '\'');
1595 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001596 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001597 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001598 error = TRUE;
1599 break;
1600 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001601 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001602 else
1603 {
1604 arg_start = linep;
1605 linep = skiptowhite(linep);
1606 }
1607 if (linep == arg_start)
1608 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001609 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001610 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001611 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001612 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001613 vim_free(arg);
1614 arg = vim_strnsave(arg_start, linep - arg_start);
1615 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001616 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001617 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001618 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001619 }
1620 if (*linep == '\'')
1621 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001622
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001623 // Store the argument.
1624 if (STRCMP(key, "TERM") == 0
1625 || STRCMP(key, "CTERM") == 0
1626 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001627 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001628 if (!highlight_set_termgui_attr(idx, key, arg, init))
1629 {
1630 error = TRUE;
1631 break;
1632 }
1633 }
1634 else if (STRCMP(key, "FONT") == 0)
1635 {
1636 // in non-GUI fonts are simply ignored
1637#ifdef FEAT_GUI
1638 if (highlight_set_font(idx, arg, is_normal_group,
1639 is_menu_group, is_tooltip_group))
1640 did_change = TRUE;
1641#endif
1642 }
1643 else if (STRCMP(key, "CTERMFG") == 0
1644 || STRCMP(key, "CTERMBG") == 0
1645 || STRCMP(key, "CTERMUL") == 0)
1646 {
1647 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1648 is_normal_group, init))
1649 {
1650 error = TRUE;
1651 break;
1652 }
1653 }
1654 else if (STRCMP(key, "GUIFG") == 0)
1655 {
1656#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1657 if (highlight_set_guifg(idx, arg, is_menu_group,
1658 is_scrollbar_group, is_tooltip_group,
1659 &do_colors, init))
1660 did_change = TRUE;
1661#endif
1662 }
1663 else if (STRCMP(key, "GUIBG") == 0)
1664 {
1665#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1666 if (highlight_set_guibg(idx, arg, is_menu_group,
1667 is_scrollbar_group, is_tooltip_group,
1668 &do_colors, init))
1669 did_change = TRUE;
1670#endif
1671 }
1672 else if (STRCMP(key, "GUISP") == 0)
1673 {
1674#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1675 if (highlight_set_guisp(idx, arg, init))
1676 did_change = TRUE;
1677#endif
1678 }
1679 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1680 {
1681 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1682 {
1683 error = TRUE;
1684 break;
1685 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001686 }
1687 else
1688 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001689 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001690 error = TRUE;
1691 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001692 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001693 HL_TABLE()[idx].sg_cleared = FALSE;
1694
1695 // When highlighting has been given for a group, don't link it.
1696 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1697 HL_TABLE()[idx].sg_link = 0;
1698
1699 // Continue with next argument.
1700 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001701 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001702
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001703 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001704 if (error && idx == highlight_ga.ga_len)
1705 syn_unadd_group();
1706 else
1707 {
1708 if (is_normal_group)
1709 {
1710 HL_TABLE()[idx].sg_term_attr = 0;
1711 HL_TABLE()[idx].sg_cterm_attr = 0;
1712#ifdef FEAT_GUI
1713 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001714 // Need to update all groups, because they might be using "bg"
1715 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001716#endif
1717#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1718 if (USE_24BIT)
1719 {
1720 highlight_gui_started();
1721 did_highlight_changed = TRUE;
1722 redraw_all_later(NOT_VALID);
1723 }
1724#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001725#ifdef FEAT_VTP
1726 control_console_color_rgb();
1727#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001728 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001729#ifdef FEAT_GUI_X11
1730# ifdef FEAT_MENU
1731 else if (is_menu_group)
1732 {
1733 if (gui.in_use && do_colors)
1734 gui_mch_new_menu_colors();
1735 }
1736# endif
1737 else if (is_scrollbar_group)
1738 {
1739 if (gui.in_use && do_colors)
1740 gui_new_scrollbar_colors();
1741 else
1742 set_hl_attr(idx);
1743 }
1744# ifdef FEAT_BEVAL_GUI
1745 else if (is_tooltip_group)
1746 {
1747 if (gui.in_use && do_colors)
1748 gui_mch_new_tooltip_colors();
1749 }
1750# endif
1751#endif
1752 else
1753 set_hl_attr(idx);
1754#ifdef FEAT_EVAL
1755 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001756 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001757#endif
1758 }
1759
1760 vim_free(key);
1761 vim_free(arg);
1762
1763 // Only call highlight_changed() once, after a sequence of highlight
1764 // commands, and only if an attribute actually changed.
1765 if ((did_change
1766 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1767#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1768 && !did_highlight_changed
1769#endif
1770 )
1771 {
1772 // Do not trigger a redraw when highlighting is changed while
1773 // redrawing. This may happen when evaluating 'statusline' changes the
1774 // StatusLine group.
1775 if (!updating_screen)
1776 redraw_all_later(NOT_VALID);
1777 need_highlight_changed = TRUE;
1778 }
1779}
1780
1781#if defined(EXITFREE) || defined(PROTO)
1782 void
1783free_highlight(void)
1784{
1785 int i;
1786
1787 for (i = 0; i < highlight_ga.ga_len; ++i)
1788 {
1789 highlight_clear(i);
1790 vim_free(HL_TABLE()[i].sg_name);
1791 vim_free(HL_TABLE()[i].sg_name_u);
1792 }
1793 ga_clear(&highlight_ga);
1794}
1795#endif
1796
1797/*
1798 * Reset the cterm colors to what they were before Vim was started, if
1799 * possible. Otherwise reset them to zero.
1800 */
1801 void
1802restore_cterm_colors(void)
1803{
1804#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1805 // Since t_me has been set, this probably means that the user
1806 // wants to use this as default colors. Need to reset default
1807 // background/foreground colors.
1808 mch_set_normal_colors();
1809#else
1810# ifdef VIMDLL
1811 if (!gui.in_use)
1812 {
1813 mch_set_normal_colors();
1814 return;
1815 }
1816# endif
1817 cterm_normal_fg_color = 0;
1818 cterm_normal_fg_bold = 0;
1819 cterm_normal_bg_color = 0;
1820# ifdef FEAT_TERMGUICOLORS
1821 cterm_normal_fg_gui_color = INVALCOLOR;
1822 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001823 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001824# endif
1825#endif
1826}
1827
1828/*
1829 * Return TRUE if highlight group "idx" has any settings.
1830 * When "check_link" is TRUE also check for an existing link.
1831 */
1832 static int
1833hl_has_settings(int idx, int check_link)
1834{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001835 return HL_TABLE()[idx].sg_cleared == 0
1836 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001837 || HL_TABLE()[idx].sg_cterm_attr != 0
1838 || HL_TABLE()[idx].sg_cterm_fg != 0
1839 || HL_TABLE()[idx].sg_cterm_bg != 0
1840#ifdef FEAT_GUI
1841 || HL_TABLE()[idx].sg_gui_attr != 0
1842 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1843 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1844 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1845 || HL_TABLE()[idx].sg_font_name != NULL
1846#endif
1847 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1848}
1849
1850/*
1851 * Clear highlighting for one group.
1852 */
1853 static void
1854highlight_clear(int idx)
1855{
1856 HL_TABLE()[idx].sg_cleared = TRUE;
1857
1858 HL_TABLE()[idx].sg_term = 0;
1859 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1860 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1861 HL_TABLE()[idx].sg_term_attr = 0;
1862 HL_TABLE()[idx].sg_cterm = 0;
1863 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1864 HL_TABLE()[idx].sg_cterm_fg = 0;
1865 HL_TABLE()[idx].sg_cterm_bg = 0;
1866 HL_TABLE()[idx].sg_cterm_attr = 0;
1867#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1868 HL_TABLE()[idx].sg_gui = 0;
1869 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1870 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1871 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1872#endif
1873#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1874 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1875 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001876 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001877#endif
1878#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001879 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1880 HL_TABLE()[idx].sg_font = NOFONT;
1881# ifdef FEAT_XFONTSET
1882 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1883 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1884# endif
1885 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1886 HL_TABLE()[idx].sg_gui_attr = 0;
1887#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001888 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001889 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001890#ifdef FEAT_EVAL
1891 // Since we set the default link, set the location to where the default
1892 // link was set.
1893 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001894#endif
1895}
1896
1897#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1898/*
1899 * Set the normal foreground and background colors according to the "Normal"
1900 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1901 * "Tooltip" colors.
1902 */
1903 void
1904set_normal_colors(void)
1905{
1906# ifdef FEAT_GUI
1907# ifdef FEAT_TERMGUICOLORS
1908 if (gui.in_use)
1909# endif
1910 {
1911 if (set_group_colors((char_u *)"Normal",
1912 &gui.norm_pixel, &gui.back_pixel,
1913 FALSE, TRUE, FALSE))
1914 {
1915 gui_mch_new_colors();
1916 must_redraw = CLEAR;
1917 }
1918# ifdef FEAT_GUI_X11
1919 if (set_group_colors((char_u *)"Menu",
1920 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1921 TRUE, FALSE, FALSE))
1922 {
1923# ifdef FEAT_MENU
1924 gui_mch_new_menu_colors();
1925# endif
1926 must_redraw = CLEAR;
1927 }
1928# ifdef FEAT_BEVAL_GUI
1929 if (set_group_colors((char_u *)"Tooltip",
1930 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1931 FALSE, FALSE, TRUE))
1932 {
1933# ifdef FEAT_TOOLBAR
1934 gui_mch_new_tooltip_colors();
1935# endif
1936 must_redraw = CLEAR;
1937 }
1938# endif
1939 if (set_group_colors((char_u *)"Scrollbar",
1940 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1941 FALSE, FALSE, FALSE))
1942 {
1943 gui_new_scrollbar_colors();
1944 must_redraw = CLEAR;
1945 }
1946# endif
1947 }
1948# endif
1949# ifdef FEAT_TERMGUICOLORS
1950# ifdef FEAT_GUI
1951 else
1952# endif
1953 {
1954 int idx;
1955
1956 idx = syn_name2id((char_u *)"Normal") - 1;
1957 if (idx >= 0)
1958 {
1959 gui_do_one_color(idx, FALSE, FALSE);
1960
1961 // If the normal fg or bg color changed a complete redraw is
1962 // required.
1963 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1964 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1965 {
1966 // if the GUI color is INVALCOLOR then we use the default cterm
1967 // color
1968 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1969 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1970 must_redraw = CLEAR;
1971 }
1972 }
1973 }
1974# endif
1975}
1976#endif
1977
1978#if defined(FEAT_GUI) || defined(PROTO)
1979/*
1980 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1981 */
1982 static int
1983set_group_colors(
1984 char_u *name,
1985 guicolor_T *fgp,
1986 guicolor_T *bgp,
1987 int do_menu,
1988 int use_norm,
1989 int do_tooltip)
1990{
1991 int idx;
1992
1993 idx = syn_name2id(name) - 1;
1994 if (idx >= 0)
1995 {
1996 gui_do_one_color(idx, do_menu, do_tooltip);
1997
1998 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1999 *fgp = HL_TABLE()[idx].sg_gui_fg;
2000 else if (use_norm)
2001 *fgp = gui.def_norm_pixel;
2002 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2003 *bgp = HL_TABLE()[idx].sg_gui_bg;
2004 else if (use_norm)
2005 *bgp = gui.def_back_pixel;
2006 return TRUE;
2007 }
2008 return FALSE;
2009}
2010
2011/*
2012 * Get the font of the "Normal" group.
2013 * Returns "" when it's not found or not set.
2014 */
2015 char_u *
2016hl_get_font_name(void)
2017{
2018 int id;
2019 char_u *s;
2020
2021 id = syn_name2id((char_u *)"Normal");
2022 if (id > 0)
2023 {
2024 s = HL_TABLE()[id - 1].sg_font_name;
2025 if (s != NULL)
2026 return s;
2027 }
2028 return (char_u *)"";
2029}
2030
2031/*
2032 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2033 * actually chosen to be used.
2034 */
2035 void
2036hl_set_font_name(char_u *font_name)
2037{
2038 int id;
2039
2040 id = syn_name2id((char_u *)"Normal");
2041 if (id > 0)
2042 {
2043 vim_free(HL_TABLE()[id - 1].sg_font_name);
2044 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2045 }
2046}
2047
2048/*
2049 * Set background color for "Normal" group. Called by gui_set_bg_color()
2050 * when the color is known.
2051 */
2052 void
2053hl_set_bg_color_name(
2054 char_u *name) // must have been allocated
2055{
2056 int id;
2057
2058 if (name != NULL)
2059 {
2060 id = syn_name2id((char_u *)"Normal");
2061 if (id > 0)
2062 {
2063 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2064 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2065 }
2066 }
2067}
2068
2069/*
2070 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2071 * when the color is known.
2072 */
2073 void
2074hl_set_fg_color_name(
2075 char_u *name) // must have been allocated
2076{
2077 int id;
2078
2079 if (name != NULL)
2080 {
2081 id = syn_name2id((char_u *)"Normal");
2082 if (id > 0)
2083 {
2084 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2085 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2086 }
2087 }
2088}
2089
2090/*
2091 * Return the handle for a font name.
2092 * Returns NOFONT when failed.
2093 */
2094 static GuiFont
2095font_name2handle(char_u *name)
2096{
2097 if (STRCMP(name, "NONE") == 0)
2098 return NOFONT;
2099
2100 return gui_mch_get_font(name, TRUE);
2101}
2102
2103# ifdef FEAT_XFONTSET
2104/*
2105 * Return the handle for a fontset name.
2106 * Returns NOFONTSET when failed.
2107 */
2108 static GuiFontset
2109fontset_name2handle(char_u *name, int fixed_width)
2110{
2111 if (STRCMP(name, "NONE") == 0)
2112 return NOFONTSET;
2113
2114 return gui_mch_get_fontset(name, TRUE, fixed_width);
2115}
2116# endif
2117
2118/*
2119 * Get the font or fontset for one highlight group.
2120 */
2121 static void
2122hl_do_font(
2123 int idx,
2124 char_u *arg,
2125 int do_normal, // set normal font
2126 int do_menu UNUSED, // set menu font
2127 int do_tooltip UNUSED, // set tooltip font
2128 int free_font) // free current font/fontset
2129{
2130# ifdef FEAT_XFONTSET
2131 // If 'guifontset' is not empty, first try using the name as a
2132 // fontset. If that doesn't work, use it as a font name.
2133 if (*p_guifontset != NUL
2134# ifdef FONTSET_ALWAYS
2135 || do_menu
2136# endif
2137# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002138 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002139 || do_tooltip
2140# endif
2141 )
2142 {
2143 if (free_font)
2144 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2145 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2146# ifdef FONTSET_ALWAYS
2147 || do_menu
2148# endif
2149# ifdef FEAT_BEVAL_TIP
2150 || do_tooltip
2151# endif
2152 );
2153 }
2154 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2155 {
2156 // If it worked and it's the Normal group, use it as the normal
2157 // fontset. Same for the Menu group.
2158 if (do_normal)
2159 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002160# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002161 if (do_menu)
2162 {
2163# ifdef FONTSET_ALWAYS
2164 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2165# else
2166 // YIKES! This is a bug waiting to crash the program
2167 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2168# endif
2169 gui_mch_new_menu_font();
2170 }
2171# ifdef FEAT_BEVAL_GUI
2172 if (do_tooltip)
2173 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002174 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002175 // displaying a single font and a fontset.
2176 // If the XtNinternational resource is set to True at widget
2177 // creation, then a fontset is always used, otherwise an
2178 // XFontStruct is used.
2179 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2180 gui_mch_new_tooltip_font();
2181 }
2182# endif
2183# endif
2184 }
2185 else
2186# endif
2187 {
2188 if (free_font)
2189 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2190 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2191 // If it worked and it's the Normal group, use it as the
2192 // normal font. Same for the Menu group.
2193 if (HL_TABLE()[idx].sg_font != NOFONT)
2194 {
2195 if (do_normal)
2196 gui_init_font(arg, FALSE);
2197#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002198# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002199 if (do_menu)
2200 {
2201 gui.menu_font = HL_TABLE()[idx].sg_font;
2202 gui_mch_new_menu_font();
2203 }
2204# endif
2205#endif
2206 }
2207 }
2208}
2209
2210#endif // FEAT_GUI
2211
2212#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2213/*
2214 * Return the handle for a color name.
2215 * Returns INVALCOLOR when failed.
2216 */
2217 guicolor_T
2218color_name2handle(char_u *name)
2219{
2220 if (STRCMP(name, "NONE") == 0)
2221 return INVALCOLOR;
2222
2223 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2224 {
2225#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2226 if (gui.in_use)
2227#endif
2228#ifdef FEAT_GUI
2229 return gui.norm_pixel;
2230#endif
2231#ifdef FEAT_TERMGUICOLORS
2232 if (cterm_normal_fg_gui_color != INVALCOLOR)
2233 return cterm_normal_fg_gui_color;
2234 // Guess that the foreground is black or white.
2235 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2236#endif
2237 }
2238 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2239 {
2240#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2241 if (gui.in_use)
2242#endif
2243#ifdef FEAT_GUI
2244 return gui.back_pixel;
2245#endif
2246#ifdef FEAT_TERMGUICOLORS
2247 if (cterm_normal_bg_gui_color != INVALCOLOR)
2248 return cterm_normal_bg_gui_color;
2249 // Guess that the background is white or black.
2250 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2251#endif
2252 }
2253
2254 return GUI_GET_COLOR(name);
2255}
Drew Vogele30d1022021-10-24 20:35:07 +01002256
2257// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2258// values as used by the MS-Windows GDI api. It should be used only for
2259// MS-Windows GDI builds.
2260# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2261# undef RGB
2262# endif
2263# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002264# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002265# endif
2266
2267# ifdef VIMDLL
2268 static guicolor_T
2269gui_adjust_rgb(guicolor_T c)
2270{
2271 if (gui.in_use)
2272 return c;
2273 else
2274 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2275}
2276# else
2277# define gui_adjust_rgb(c) (c)
2278# endif
2279
2280 static int
2281hex_digit(int c)
2282{
2283 if (isdigit(c))
2284 return c - '0';
2285 c = TOLOWER_ASC(c);
2286 if (c >= 'a' && c <= 'f')
2287 return c - 'a' + 10;
2288 return 0x1ffffff;
2289}
2290
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002291 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002292decode_hex_color(char_u *hex)
2293{
2294 guicolor_T color;
2295
2296 if (hex[0] != '#' || STRLEN(hex) != 7)
2297 return INVALCOLOR;
2298
2299 // Name is in "#rrggbb" format
2300 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2301 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2302 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2303 if (color > 0xffffff)
2304 return INVALCOLOR;
2305 return gui_adjust_rgb(color);
2306}
2307
Bram Moolenaar2a521962021-10-25 10:30:14 +01002308#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002309// Returns the color currently mapped to the given name or INVALCOLOR if no
2310// such name exists in the color table. The convention is to use lowercase for
2311// all keys in the v:colornames dictionary. The value can be either a string in
2312// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002313 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002314colorname2rgb(char_u *name)
2315{
2316 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2317 char_u *lc_name;
2318 dictitem_T *colentry;
2319 char_u *colstr;
2320 varnumber_T colnum;
2321
2322 lc_name = strlow_save(name);
2323 if (lc_name == NULL)
2324 return INVALCOLOR;
2325
2326 colentry = dict_find(colornames_table, lc_name, -1);
2327 vim_free(lc_name);
2328 if (colentry == NULL)
2329 return INVALCOLOR;
2330
2331 if (colentry->di_tv.v_type == VAR_STRING)
2332 {
2333 colstr = tv_get_string_strict(&colentry->di_tv);
2334 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2335 {
2336 return decode_hex_color(colstr);
2337 }
2338 else
2339 {
2340 semsg(_(e_bad_color_string_str), colstr);
2341 return INVALCOLOR;
2342 }
2343 }
2344
2345 if (colentry->di_tv.v_type == VAR_NUMBER)
2346 {
2347 colnum = tv_get_number(&colentry->di_tv);
2348 return (guicolor_T)colnum;
2349 }
2350
2351 return INVALCOLOR;
2352}
2353
Drew Vogele30d1022021-10-24 20:35:07 +01002354#endif
2355
2356 guicolor_T
2357gui_get_color_cmn(char_u *name)
2358{
2359 int i;
2360 guicolor_T color;
2361
2362 struct rgbcolor_table_S {
2363 char_u *color_name;
2364 guicolor_T color;
2365 };
2366
2367 // Only non X11 colors (not present in rgb.txt) and colors in
2368 // color_names[], useful when $VIMRUNTIME is not found,.
2369 static struct rgbcolor_table_S rgb_table[] = {
2370 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2371 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2372 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2373 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2374 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2375 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2376 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2377 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2378 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2379 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2380 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2381 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2382 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2383 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2384 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2385 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2386 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2387 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2388 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2389 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2390 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2391 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2392 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2393 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2394 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2395 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2396 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2397 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2398 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2399 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2400 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2401 };
2402
2403 color = decode_hex_color(name);
2404 if (color != INVALCOLOR)
2405 return color;
2406
2407 // Check if the name is one of the colors we know
2408 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2409 if (STRICMP(name, rgb_table[i].color_name) == 0)
2410 return gui_adjust_rgb(rgb_table[i].color);
2411
2412#if defined(FEAT_EVAL)
2413 /*
2414 * Not a traditional color. Load additional color aliases and then consult the alias table.
2415 */
2416
2417 color = colorname2rgb(name);
2418 if (color == INVALCOLOR)
2419 {
2420 load_default_colors_lists();
2421 color = colorname2rgb(name);
2422 }
2423
2424 return color;
2425#else
2426 return INVALCOLOR;
2427#endif
2428}
2429
2430 guicolor_T
2431gui_get_rgb_color_cmn(int r, int g, int b)
2432{
2433 guicolor_T color = RGB(r, g, b);
2434
2435 if (color > 0xffffff)
2436 return INVALCOLOR;
2437 return gui_adjust_rgb(color);
2438}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002439#endif
2440
2441/*
2442 * Table with the specifications for an attribute number.
2443 * Note that this table is used by ALL buffers. This is required because the
2444 * GUI can redraw at any time for any buffer.
2445 */
2446static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2447
2448#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2449
2450static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2451
2452#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2453
2454#ifdef FEAT_GUI
2455static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2456
2457#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2458#endif
2459
2460/*
2461 * Return the attr number for a set of colors and font.
2462 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2463 * if the combination is new.
2464 * Return 0 for error (no more room).
2465 */
2466 static int
2467get_attr_entry(garray_T *table, attrentry_T *aep)
2468{
2469 int i;
2470 attrentry_T *taep;
2471 static int recursive = FALSE;
2472
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002473 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002474 table->ga_itemsize = sizeof(attrentry_T);
2475 table->ga_growsize = 7;
2476
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002477 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002478 for (i = 0; i < table->ga_len; ++i)
2479 {
2480 taep = &(((attrentry_T *)table->ga_data)[i]);
2481 if ( aep->ae_attr == taep->ae_attr
2482 && (
2483#ifdef FEAT_GUI
2484 (table == &gui_attr_table
2485 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2486 && aep->ae_u.gui.bg_color
2487 == taep->ae_u.gui.bg_color
2488 && aep->ae_u.gui.sp_color
2489 == taep->ae_u.gui.sp_color
2490 && aep->ae_u.gui.font == taep->ae_u.gui.font
2491# ifdef FEAT_XFONTSET
2492 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2493# endif
2494 ))
2495 ||
2496#endif
2497 (table == &term_attr_table
2498 && (aep->ae_u.term.start == NULL)
2499 == (taep->ae_u.term.start == NULL)
2500 && (aep->ae_u.term.start == NULL
2501 || STRCMP(aep->ae_u.term.start,
2502 taep->ae_u.term.start) == 0)
2503 && (aep->ae_u.term.stop == NULL)
2504 == (taep->ae_u.term.stop == NULL)
2505 && (aep->ae_u.term.stop == NULL
2506 || STRCMP(aep->ae_u.term.stop,
2507 taep->ae_u.term.stop) == 0))
2508 || (table == &cterm_attr_table
2509 && aep->ae_u.cterm.fg_color
2510 == taep->ae_u.cterm.fg_color
2511 && aep->ae_u.cterm.bg_color
2512 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002513 && aep->ae_u.cterm.ul_color
2514 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002515#ifdef FEAT_TERMGUICOLORS
2516 && aep->ae_u.cterm.fg_rgb
2517 == taep->ae_u.cterm.fg_rgb
2518 && aep->ae_u.cterm.bg_rgb
2519 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002520 && aep->ae_u.cterm.ul_rgb
2521 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002522#endif
2523 )))
2524
2525 return i + ATTR_OFF;
2526 }
2527
2528 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2529 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002530 // Running out of attribute entries! remove all attributes, and
2531 // compute new ones for all groups.
2532 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002533 if (recursive)
2534 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002535 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002536 return 0;
2537 }
2538 recursive = TRUE;
2539
2540 clear_hl_tables();
2541
2542 must_redraw = CLEAR;
2543
2544 for (i = 0; i < highlight_ga.ga_len; ++i)
2545 set_hl_attr(i);
2546
2547 recursive = FALSE;
2548 }
2549
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002550 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002551 if (ga_grow(table, 1) == FAIL)
2552 return 0;
2553
2554 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002555 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002556 taep->ae_attr = aep->ae_attr;
2557#ifdef FEAT_GUI
2558 if (table == &gui_attr_table)
2559 {
2560 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2561 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2562 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2563 taep->ae_u.gui.font = aep->ae_u.gui.font;
2564# ifdef FEAT_XFONTSET
2565 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2566# endif
2567 }
2568#endif
2569 if (table == &term_attr_table)
2570 {
2571 if (aep->ae_u.term.start == NULL)
2572 taep->ae_u.term.start = NULL;
2573 else
2574 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2575 if (aep->ae_u.term.stop == NULL)
2576 taep->ae_u.term.stop = NULL;
2577 else
2578 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2579 }
2580 else if (table == &cterm_attr_table)
2581 {
2582 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2583 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002584 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002585#ifdef FEAT_TERMGUICOLORS
2586 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2587 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002588 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002589#endif
2590 }
2591 ++table->ga_len;
2592 return (table->ga_len - 1 + ATTR_OFF);
2593}
2594
2595#if defined(FEAT_TERMINAL) || defined(PROTO)
2596/*
2597 * Get an attribute index for a cterm entry.
2598 * Uses an existing entry when possible or adds one when needed.
2599 */
2600 int
2601get_cterm_attr_idx(int attr, int fg, int bg)
2602{
2603 attrentry_T at_en;
2604
Bram Moolenaara80faa82020-04-12 19:37:17 +02002605 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002606#ifdef FEAT_TERMGUICOLORS
2607 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2608 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002609 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002610#endif
2611 at_en.ae_attr = attr;
2612 at_en.ae_u.cterm.fg_color = fg;
2613 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002614 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002615 return get_attr_entry(&cterm_attr_table, &at_en);
2616}
2617#endif
2618
2619#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2620/*
2621 * Get an attribute index for a 'termguicolors' entry.
2622 * Uses an existing entry when possible or adds one when needed.
2623 */
2624 int
2625get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2626{
2627 attrentry_T at_en;
2628
Bram Moolenaara80faa82020-04-12 19:37:17 +02002629 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002630 at_en.ae_attr = attr;
2631 if (fg == INVALCOLOR && bg == INVALCOLOR)
2632 {
2633 // If both GUI colors are not set fall back to the cterm colors. Helps
2634 // if the GUI only has an attribute, such as undercurl.
2635 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2636 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2637 }
2638 else
2639 {
2640 at_en.ae_u.cterm.fg_rgb = fg;
2641 at_en.ae_u.cterm.bg_rgb = bg;
2642 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002643 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002644 return get_attr_entry(&cterm_attr_table, &at_en);
2645}
2646#endif
2647
2648#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2649/*
2650 * Get an attribute index for a cterm entry.
2651 * Uses an existing entry when possible or adds one when needed.
2652 */
2653 int
2654get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2655{
2656 attrentry_T at_en;
2657
Bram Moolenaara80faa82020-04-12 19:37:17 +02002658 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002659 at_en.ae_attr = attr;
2660 at_en.ae_u.gui.fg_color = fg;
2661 at_en.ae_u.gui.bg_color = bg;
2662 return get_attr_entry(&gui_attr_table, &at_en);
2663}
2664#endif
2665
2666/*
2667 * Clear all highlight tables.
2668 */
2669 void
2670clear_hl_tables(void)
2671{
2672 int i;
2673 attrentry_T *taep;
2674
2675#ifdef FEAT_GUI
2676 ga_clear(&gui_attr_table);
2677#endif
2678 for (i = 0; i < term_attr_table.ga_len; ++i)
2679 {
2680 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2681 vim_free(taep->ae_u.term.start);
2682 vim_free(taep->ae_u.term.stop);
2683 }
2684 ga_clear(&term_attr_table);
2685 ga_clear(&cterm_attr_table);
2686}
2687
2688/*
2689 * Combine special attributes (e.g., for spelling) with other attributes
2690 * (e.g., for syntax highlighting).
2691 * "prim_attr" overrules "char_attr".
2692 * This creates a new group when required.
2693 * Since we expect there to be few spelling mistakes we don't cache the
2694 * result.
2695 * Return the resulting attributes.
2696 */
2697 int
2698hl_combine_attr(int char_attr, int prim_attr)
2699{
2700 attrentry_T *char_aep = NULL;
2701 attrentry_T *spell_aep;
2702 attrentry_T new_en;
2703
2704 if (char_attr == 0)
2705 return prim_attr;
2706 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2707 return ATTR_COMBINE(char_attr, prim_attr);
2708#ifdef FEAT_GUI
2709 if (gui.in_use)
2710 {
2711 if (char_attr > HL_ALL)
2712 char_aep = syn_gui_attr2entry(char_attr);
2713 if (char_aep != NULL)
2714 new_en = *char_aep;
2715 else
2716 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002717 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002718 new_en.ae_u.gui.fg_color = INVALCOLOR;
2719 new_en.ae_u.gui.bg_color = INVALCOLOR;
2720 new_en.ae_u.gui.sp_color = INVALCOLOR;
2721 if (char_attr <= HL_ALL)
2722 new_en.ae_attr = char_attr;
2723 }
2724
2725 if (prim_attr <= HL_ALL)
2726 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2727 else
2728 {
2729 spell_aep = syn_gui_attr2entry(prim_attr);
2730 if (spell_aep != NULL)
2731 {
2732 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2733 spell_aep->ae_attr);
2734 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2735 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2736 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2737 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2738 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2739 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2740 if (spell_aep->ae_u.gui.font != NOFONT)
2741 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2742# ifdef FEAT_XFONTSET
2743 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2744 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2745# endif
2746 }
2747 }
2748 return get_attr_entry(&gui_attr_table, &new_en);
2749 }
2750#endif
2751
2752 if (IS_CTERM)
2753 {
2754 if (char_attr > HL_ALL)
2755 char_aep = syn_cterm_attr2entry(char_attr);
2756 if (char_aep != NULL)
2757 new_en = *char_aep;
2758 else
2759 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002760 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002761#ifdef FEAT_TERMGUICOLORS
2762 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2763 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002764 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002765#endif
2766 if (char_attr <= HL_ALL)
2767 new_en.ae_attr = char_attr;
2768 }
2769
2770 if (prim_attr <= HL_ALL)
2771 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2772 else
2773 {
2774 spell_aep = syn_cterm_attr2entry(prim_attr);
2775 if (spell_aep != NULL)
2776 {
2777 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2778 spell_aep->ae_attr);
2779 if (spell_aep->ae_u.cterm.fg_color > 0)
2780 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2781 if (spell_aep->ae_u.cterm.bg_color > 0)
2782 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002783 if (spell_aep->ae_u.cterm.ul_color > 0)
2784 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002785#ifdef FEAT_TERMGUICOLORS
2786 // If both fg and bg are not set fall back to cterm colors.
2787 // Helps for SpellBad which uses undercurl in the GUI.
2788 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2789 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2790 {
2791 if (spell_aep->ae_u.cterm.fg_color > 0)
2792 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2793 if (spell_aep->ae_u.cterm.bg_color > 0)
2794 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2795 }
2796 else
2797 {
2798 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2799 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2800 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2801 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2802 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002803 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2804 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002805#endif
2806 }
2807 }
2808 return get_attr_entry(&cterm_attr_table, &new_en);
2809 }
2810
2811 if (char_attr > HL_ALL)
2812 char_aep = syn_term_attr2entry(char_attr);
2813 if (char_aep != NULL)
2814 new_en = *char_aep;
2815 else
2816 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002817 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002818 if (char_attr <= HL_ALL)
2819 new_en.ae_attr = char_attr;
2820 }
2821
2822 if (prim_attr <= HL_ALL)
2823 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2824 else
2825 {
2826 spell_aep = syn_term_attr2entry(prim_attr);
2827 if (spell_aep != NULL)
2828 {
2829 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2830 if (spell_aep->ae_u.term.start != NULL)
2831 {
2832 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2833 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2834 }
2835 }
2836 }
2837 return get_attr_entry(&term_attr_table, &new_en);
2838}
2839
2840#ifdef FEAT_GUI
2841 attrentry_T *
2842syn_gui_attr2entry(int attr)
2843{
2844 attr -= ATTR_OFF;
2845 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2846 return NULL;
2847 return &(GUI_ATTR_ENTRY(attr));
2848}
2849#endif
2850
2851/*
2852 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2853 * Only to be used when "attr" > HL_ALL.
2854 */
2855 int
2856syn_attr2attr(int attr)
2857{
2858 attrentry_T *aep;
2859
2860#ifdef FEAT_GUI
2861 if (gui.in_use)
2862 aep = syn_gui_attr2entry(attr);
2863 else
2864#endif
2865 if (IS_CTERM)
2866 aep = syn_cterm_attr2entry(attr);
2867 else
2868 aep = syn_term_attr2entry(attr);
2869
2870 if (aep == NULL) // highlighting not set
2871 return 0;
2872 return aep->ae_attr;
2873}
2874
2875
2876 attrentry_T *
2877syn_term_attr2entry(int attr)
2878{
2879 attr -= ATTR_OFF;
2880 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2881 return NULL;
2882 return &(TERM_ATTR_ENTRY(attr));
2883}
2884
2885 attrentry_T *
2886syn_cterm_attr2entry(int attr)
2887{
2888 attr -= ATTR_OFF;
2889 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2890 return NULL;
2891 return &(CTERM_ATTR_ENTRY(attr));
2892}
2893
2894#define LIST_ATTR 1
2895#define LIST_STRING 2
2896#define LIST_INT 3
2897
2898 static void
2899highlight_list_one(int id)
2900{
2901 hl_group_T *sgp;
2902 int didh = FALSE;
2903
2904 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2905
2906 if (message_filtered(sgp->sg_name))
2907 return;
2908
2909 didh = highlight_list_arg(id, didh, LIST_ATTR,
2910 sgp->sg_term, NULL, "term");
2911 didh = highlight_list_arg(id, didh, LIST_STRING,
2912 0, sgp->sg_start, "start");
2913 didh = highlight_list_arg(id, didh, LIST_STRING,
2914 0, sgp->sg_stop, "stop");
2915
2916 didh = highlight_list_arg(id, didh, LIST_ATTR,
2917 sgp->sg_cterm, NULL, "cterm");
2918 didh = highlight_list_arg(id, didh, LIST_INT,
2919 sgp->sg_cterm_fg, NULL, "ctermfg");
2920 didh = highlight_list_arg(id, didh, LIST_INT,
2921 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002922 didh = highlight_list_arg(id, didh, LIST_INT,
2923 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002924
2925#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2926 didh = highlight_list_arg(id, didh, LIST_ATTR,
2927 sgp->sg_gui, NULL, "gui");
2928 didh = highlight_list_arg(id, didh, LIST_STRING,
2929 0, sgp->sg_gui_fg_name, "guifg");
2930 didh = highlight_list_arg(id, didh, LIST_STRING,
2931 0, sgp->sg_gui_bg_name, "guibg");
2932 didh = highlight_list_arg(id, didh, LIST_STRING,
2933 0, sgp->sg_gui_sp_name, "guisp");
2934#endif
2935#ifdef FEAT_GUI
2936 didh = highlight_list_arg(id, didh, LIST_STRING,
2937 0, sgp->sg_font_name, "font");
2938#endif
2939
2940 if (sgp->sg_link && !got_int)
2941 {
2942 (void)syn_list_header(didh, 9999, id);
2943 didh = TRUE;
2944 msg_puts_attr("links to", HL_ATTR(HLF_D));
2945 msg_putchar(' ');
2946 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2947 }
2948
2949 if (!didh)
2950 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2951#ifdef FEAT_EVAL
2952 if (p_verbose > 0)
2953 last_set_msg(sgp->sg_script_ctx);
2954#endif
2955}
2956
2957 static int
2958highlight_list_arg(
2959 int id,
2960 int didh,
2961 int type,
2962 int iarg,
2963 char_u *sarg,
2964 char *name)
2965{
2966 char_u buf[100];
2967 char_u *ts;
2968 int i;
2969
2970 if (got_int)
2971 return FALSE;
2972 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2973 {
2974 ts = buf;
2975 if (type == LIST_INT)
2976 sprintf((char *)buf, "%d", iarg - 1);
2977 else if (type == LIST_STRING)
2978 ts = sarg;
2979 else // type == LIST_ATTR
2980 {
2981 buf[0] = NUL;
2982 for (i = 0; hl_attr_table[i] != 0; ++i)
2983 {
2984 if (iarg & hl_attr_table[i])
2985 {
2986 if (buf[0] != NUL)
2987 vim_strcat(buf, (char_u *)",", 100);
2988 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2989 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2990 }
2991 }
2992 }
2993
2994 (void)syn_list_header(didh,
2995 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2996 didh = TRUE;
2997 if (!got_int)
2998 {
2999 if (*name != NUL)
3000 {
3001 msg_puts_attr(name, HL_ATTR(HLF_D));
3002 msg_puts_attr("=", HL_ATTR(HLF_D));
3003 }
3004 msg_outtrans(ts);
3005 }
3006 }
3007 return didh;
3008}
3009
3010#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3011/*
3012 * Return "1" if highlight group "id" has attribute "flag".
3013 * Return NULL otherwise.
3014 */
3015 char_u *
3016highlight_has_attr(
3017 int id,
3018 int flag,
3019 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3020{
3021 int attr;
3022
3023 if (id <= 0 || id > highlight_ga.ga_len)
3024 return NULL;
3025
3026#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3027 if (modec == 'g')
3028 attr = HL_TABLE()[id - 1].sg_gui;
3029 else
3030#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003031 {
3032 if (modec == 'c')
3033 attr = HL_TABLE()[id - 1].sg_cterm;
3034 else
3035 attr = HL_TABLE()[id - 1].sg_term;
3036 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003037
3038 if (attr & flag)
3039 return (char_u *)"1";
3040 return NULL;
3041}
3042#endif
3043
3044#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3045/*
3046 * Return color name of highlight group "id".
3047 */
3048 char_u *
3049highlight_color(
3050 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003051 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003052 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3053{
3054 static char_u name[20];
3055 int n;
3056 int fg = FALSE;
3057 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003058 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003059 int font = FALSE;
3060
3061 if (id <= 0 || id > highlight_ga.ga_len)
3062 return NULL;
3063
3064 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3065 fg = TRUE;
3066 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3067 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3068 font = TRUE;
3069 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3070 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003071 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3072 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003073 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3074 return NULL;
3075 if (modec == 'g')
3076 {
3077# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3078# ifdef FEAT_GUI
3079 // return font name
3080 if (font)
3081 return HL_TABLE()[id - 1].sg_font_name;
3082# endif
3083
3084 // return #RRGGBB form (only possible when GUI is running)
3085 if ((USE_24BIT) && what[2] == '#')
3086 {
3087 guicolor_T color;
3088 long_u rgb;
3089 static char_u buf[10];
3090
3091 if (fg)
3092 color = HL_TABLE()[id - 1].sg_gui_fg;
3093 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003094 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003095 else
3096 color = HL_TABLE()[id - 1].sg_gui_bg;
3097 if (color == INVALCOLOR)
3098 return NULL;
3099 rgb = (long_u)GUI_MCH_GET_RGB(color);
3100 sprintf((char *)buf, "#%02x%02x%02x",
3101 (unsigned)(rgb >> 16),
3102 (unsigned)(rgb >> 8) & 255,
3103 (unsigned)rgb & 255);
3104 return buf;
3105 }
3106# endif
3107 if (fg)
3108 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3109 if (sp)
3110 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3111 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3112 }
3113 if (font || sp)
3114 return NULL;
3115 if (modec == 'c')
3116 {
3117 if (fg)
3118 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003119 else if (ul)
3120 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003121 else
3122 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3123 if (n < 0)
3124 return NULL;
3125 sprintf((char *)name, "%d", n);
3126 return name;
3127 }
3128 // term doesn't have color
3129 return NULL;
3130}
3131#endif
3132
3133#if (defined(FEAT_SYN_HL) \
3134 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3135 && defined(FEAT_PRINTER)) || defined(PROTO)
3136/*
3137 * Return color name of highlight group "id" as RGB value.
3138 */
3139 long_u
3140highlight_gui_color_rgb(
3141 int id,
3142 int fg) // TRUE = fg, FALSE = bg
3143{
3144 guicolor_T color;
3145
3146 if (id <= 0 || id > highlight_ga.ga_len)
3147 return 0L;
3148
3149 if (fg)
3150 color = HL_TABLE()[id - 1].sg_gui_fg;
3151 else
3152 color = HL_TABLE()[id - 1].sg_gui_bg;
3153
3154 if (color == INVALCOLOR)
3155 return 0L;
3156
3157 return GUI_MCH_GET_RGB(color);
3158}
3159#endif
3160
3161/*
3162 * Output the syntax list header.
3163 * Return TRUE when started a new line.
3164 */
3165 int
3166syn_list_header(
3167 int did_header, // did header already
3168 int outlen, // length of string that comes
3169 int id) // highlight group id
3170{
3171 int endcol = 19;
3172 int newline = TRUE;
3173 int name_col = 0;
3174
3175 if (!did_header)
3176 {
3177 msg_putchar('\n');
3178 if (got_int)
3179 return TRUE;
3180 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3181 name_col = msg_col;
3182 endcol = 15;
3183 }
3184 else if (msg_col + outlen + 1 >= Columns)
3185 {
3186 msg_putchar('\n');
3187 if (got_int)
3188 return TRUE;
3189 }
3190 else
3191 {
3192 if (msg_col >= endcol) // wrap around is like starting a new line
3193 newline = FALSE;
3194 }
3195
3196 if (msg_col >= endcol) // output at least one space
3197 endcol = msg_col + 1;
3198 if (Columns <= endcol) // avoid hang for tiny window
3199 endcol = Columns - 1;
3200
3201 msg_advance(endcol);
3202
3203 // Show "xxx" with the attributes.
3204 if (!did_header)
3205 {
3206 if (endcol == Columns - 1 && endcol <= name_col)
3207 msg_putchar(' ');
3208 msg_puts_attr("xxx", syn_id2attr(id));
3209 msg_putchar(' ');
3210 }
3211
3212 return newline;
3213}
3214
3215/*
3216 * Set the attribute numbers for a highlight group.
3217 * Called after one of the attributes has changed.
3218 */
3219 static void
3220set_hl_attr(
3221 int idx) // index in array
3222{
3223 attrentry_T at_en;
3224 hl_group_T *sgp = HL_TABLE() + idx;
3225
3226 // The "Normal" group doesn't need an attribute number
3227 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3228 return;
3229
3230#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003231 // For the GUI mode: If there are other than "normal" highlighting
3232 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003233 if (sgp->sg_gui_fg == INVALCOLOR
3234 && sgp->sg_gui_bg == INVALCOLOR
3235 && sgp->sg_gui_sp == INVALCOLOR
3236 && sgp->sg_font == NOFONT
3237# ifdef FEAT_XFONTSET
3238 && sgp->sg_fontset == NOFONTSET
3239# endif
3240 )
3241 {
3242 sgp->sg_gui_attr = sgp->sg_gui;
3243 }
3244 else
3245 {
3246 at_en.ae_attr = sgp->sg_gui;
3247 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3248 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3249 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3250 at_en.ae_u.gui.font = sgp->sg_font;
3251# ifdef FEAT_XFONTSET
3252 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3253# endif
3254 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3255 }
3256#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003257 // For the term mode: If there are other than "normal" highlighting
3258 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003259 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3260 sgp->sg_term_attr = sgp->sg_term;
3261 else
3262 {
3263 at_en.ae_attr = sgp->sg_term;
3264 at_en.ae_u.term.start = sgp->sg_start;
3265 at_en.ae_u.term.stop = sgp->sg_stop;
3266 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3267 }
3268
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003269 // For the color term mode: If there are other than "normal"
3270 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003271 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003272# ifdef FEAT_TERMGUICOLORS
3273 && sgp->sg_gui_fg == INVALCOLOR
3274 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003275 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003276# endif
3277 )
3278 sgp->sg_cterm_attr = sgp->sg_cterm;
3279 else
3280 {
3281 at_en.ae_attr = sgp->sg_cterm;
3282 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3283 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003284 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003285# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003286 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3287 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003288 // Only use the underline/undercurl color when used, it may clear the
3289 // background color if not supported.
3290 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
3291 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3292 else
3293 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003294 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3295 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3296 {
3297 // If both fg and bg are invalid fall back to the cterm colors.
3298 // Helps when the GUI only uses an attribute, e.g. undercurl.
3299 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3300 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3301 }
3302# endif
3303 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3304 }
3305}
3306
3307/*
3308 * Lookup a highlight group name and return its ID.
3309 * If it is not found, 0 is returned.
3310 */
3311 int
3312syn_name2id(char_u *name)
3313{
3314 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003315 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003316
3317 // Avoid using stricmp() too much, it's slow on some systems
3318 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3319 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003320 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003321 vim_strup(name_u);
3322 for (i = highlight_ga.ga_len; --i >= 0; )
3323 if (HL_TABLE()[i].sg_name_u != NULL
3324 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3325 break;
3326 return i + 1;
3327}
3328
3329/*
3330 * Lookup a highlight group name and return its attributes.
3331 * Return zero if not found.
3332 */
3333 int
3334syn_name2attr(char_u *name)
3335{
3336 int id = syn_name2id(name);
3337
3338 if (id != 0)
3339 return syn_id2attr(id);
3340 return 0;
3341}
3342
3343#if defined(FEAT_EVAL) || defined(PROTO)
3344/*
3345 * Return TRUE if highlight group "name" exists.
3346 */
3347 int
3348highlight_exists(char_u *name)
3349{
3350 return (syn_name2id(name) > 0);
3351}
3352
3353# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3354/*
3355 * Return the name of highlight group "id".
3356 * When not a valid ID return an empty string.
3357 */
3358 char_u *
3359syn_id2name(int id)
3360{
3361 if (id <= 0 || id > highlight_ga.ga_len)
3362 return (char_u *)"";
3363 return HL_TABLE()[id - 1].sg_name;
3364}
3365# endif
3366#endif
3367
3368/*
3369 * Like syn_name2id(), but take a pointer + length argument.
3370 */
3371 int
3372syn_namen2id(char_u *linep, int len)
3373{
3374 char_u *name;
3375 int id = 0;
3376
3377 name = vim_strnsave(linep, len);
3378 if (name != NULL)
3379 {
3380 id = syn_name2id(name);
3381 vim_free(name);
3382 }
3383 return id;
3384}
3385
3386/*
3387 * Find highlight group name in the table and return its ID.
3388 * The argument is a pointer to the name and the length of the name.
3389 * If it doesn't exist yet, a new entry is created.
3390 * Return 0 for failure.
3391 */
3392 int
3393syn_check_group(char_u *pp, int len)
3394{
3395 int id;
3396 char_u *name;
3397
erw7f7f7aaf2021-12-07 21:29:20 +00003398 if (len > MAX_SYN_NAME)
3399 {
3400 emsg(_(e_highlight_group_name_too_long));
3401 return 0;
3402 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003403 name = vim_strnsave(pp, len);
3404 if (name == NULL)
3405 return 0;
3406
3407 id = syn_name2id(name);
3408 if (id == 0) // doesn't exist yet
3409 id = syn_add_group(name);
3410 else
3411 vim_free(name);
3412 return id;
3413}
3414
3415/*
3416 * Add new highlight group and return its ID.
3417 * "name" must be an allocated string, it will be consumed.
3418 * Return 0 for failure.
3419 */
3420 static int
3421syn_add_group(char_u *name)
3422{
3423 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003424 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003425
3426 // Check that the name is ASCII letters, digits and underscore.
3427 for (p = name; *p != NUL; ++p)
3428 {
3429 if (!vim_isprintc(*p))
3430 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003431 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003432 vim_free(name);
3433 return 0;
3434 }
3435 else if (!ASCII_ISALNUM(*p) && *p != '_')
3436 {
3437 // This is an error, but since there previously was no check only
3438 // give a warning.
3439 msg_source(HL_ATTR(HLF_W));
3440 msg(_("W18: Invalid character in group name"));
3441 break;
3442 }
3443 }
3444
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003445 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003446 if (highlight_ga.ga_data == NULL)
3447 {
3448 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3449 highlight_ga.ga_growsize = 10;
3450 }
3451
3452 if (highlight_ga.ga_len >= MAX_HL_ID)
3453 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003454 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003455 vim_free(name);
3456 return 0;
3457 }
3458
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003459 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003460 if (ga_grow(&highlight_ga, 1) == FAIL)
3461 {
3462 vim_free(name);
3463 return 0;
3464 }
3465
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003466 name_up = vim_strsave_up(name);
3467 if (name_up == NULL)
3468 {
3469 vim_free(name);
3470 return 0;
3471 }
3472
Bram Moolenaara80faa82020-04-12 19:37:17 +02003473 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003474 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003475 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003476#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3477 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3478 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003479 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003480#endif
3481 ++highlight_ga.ga_len;
3482
3483 return highlight_ga.ga_len; // ID is index plus one
3484}
3485
3486/*
3487 * When, just after calling syn_add_group(), an error is discovered, this
3488 * function deletes the new name.
3489 */
3490 static void
3491syn_unadd_group(void)
3492{
3493 --highlight_ga.ga_len;
3494 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3495 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3496}
3497
3498/*
3499 * Translate a group ID to highlight attributes.
3500 */
3501 int
3502syn_id2attr(int hl_id)
3503{
3504 int attr;
3505 hl_group_T *sgp;
3506
3507 hl_id = syn_get_final_id(hl_id);
3508 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3509
3510#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003511 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003512 if (gui.in_use)
3513 attr = sgp->sg_gui_attr;
3514 else
3515#endif
3516 if (IS_CTERM)
3517 attr = sgp->sg_cterm_attr;
3518 else
3519 attr = sgp->sg_term_attr;
3520
3521 return attr;
3522}
3523
3524#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3525/*
3526 * Get the GUI colors and attributes for a group ID.
3527 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3528 */
3529 int
3530syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3531{
3532 hl_group_T *sgp;
3533
3534 hl_id = syn_get_final_id(hl_id);
3535 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3536
3537 *fgp = sgp->sg_gui_fg;
3538 *bgp = sgp->sg_gui_bg;
3539 return sgp->sg_gui;
3540}
3541#endif
3542
3543#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003544 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3545 && defined(FEAT_TERMGUICOLORS)) \
3546 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003547 void
3548syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3549{
3550 hl_group_T *sgp;
3551
3552 hl_id = syn_get_final_id(hl_id);
3553 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3554 *fgp = sgp->sg_cterm_fg - 1;
3555 *bgp = sgp->sg_cterm_bg - 1;
3556}
3557#endif
3558
3559/*
3560 * Translate a group ID to the final group ID (following links).
3561 */
3562 int
3563syn_get_final_id(int hl_id)
3564{
3565 int count;
3566 hl_group_T *sgp;
3567
3568 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3569 return 0; // Can be called from eval!!
3570
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003571 // Follow links until there is no more.
3572 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003573 for (count = 100; --count >= 0; )
3574 {
3575 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3576 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3577 break;
3578 hl_id = sgp->sg_link;
3579 }
3580
3581 return hl_id;
3582}
3583
3584#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3585/*
3586 * Call this function just after the GUI has started.
3587 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3588 * It finds the font and color handles for the highlighting groups.
3589 */
3590 void
3591highlight_gui_started(void)
3592{
3593 int idx;
3594
3595 // First get the colors from the "Normal" and "Menu" group, if set
3596 if (USE_24BIT)
3597 set_normal_colors();
3598
3599 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3600 gui_do_one_color(idx, FALSE, FALSE);
3601
3602 highlight_changed();
3603}
3604
3605 static void
3606gui_do_one_color(
3607 int idx,
3608 int do_menu UNUSED, // TRUE: might set the menu font
3609 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3610{
3611 int didit = FALSE;
3612
3613# ifdef FEAT_GUI
3614# ifdef FEAT_TERMGUICOLORS
3615 if (gui.in_use)
3616# endif
3617 if (HL_TABLE()[idx].sg_font_name != NULL)
3618 {
3619 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3620 do_tooltip, TRUE);
3621 didit = TRUE;
3622 }
3623# endif
3624 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3625 {
3626 HL_TABLE()[idx].sg_gui_fg =
3627 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3628 didit = TRUE;
3629 }
3630 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3631 {
3632 HL_TABLE()[idx].sg_gui_bg =
3633 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3634 didit = TRUE;
3635 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003636 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3637 {
3638 HL_TABLE()[idx].sg_gui_sp =
3639 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3640 didit = TRUE;
3641 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003642 if (didit) // need to get a new attr number
3643 set_hl_attr(idx);
3644}
3645#endif
3646
3647#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3648/*
3649 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3650 */
3651 static void
3652combine_stl_hlt(
3653 int id,
3654 int id_S,
3655 int id_alt,
3656 int hlcnt,
3657 int i,
3658 int hlf,
3659 int *table)
3660{
3661 hl_group_T *hlt = HL_TABLE();
3662
3663 if (id_alt == 0)
3664 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003665 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003666 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3667 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3668# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3669 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3670# endif
3671 }
3672 else
3673 mch_memmove(&hlt[hlcnt + i],
3674 &hlt[id_alt - 1],
3675 sizeof(hl_group_T));
3676 hlt[hlcnt + i].sg_link = 0;
3677
3678 hlt[hlcnt + i].sg_term ^=
3679 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3680 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3681 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3682 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3683 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3684 hlt[hlcnt + i].sg_cterm ^=
3685 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3686 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3687 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3688 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3689 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3690# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3691 hlt[hlcnt + i].sg_gui ^=
3692 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3693# endif
3694# ifdef FEAT_GUI
3695 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3696 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3697 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3698 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3699 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3700 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3701 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3702 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3703# ifdef FEAT_XFONTSET
3704 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3705 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3706# endif
3707# endif
3708 highlight_ga.ga_len = hlcnt + i + 1;
3709 set_hl_attr(hlcnt + i); // At long last we can apply
3710 table[i] = syn_id2attr(hlcnt + i + 1);
3711}
3712#endif
3713
3714/*
3715 * Translate the 'highlight' option into attributes in highlight_attr[] and
3716 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3717 * corresponding highlights to use on top of HLF_SNC is computed.
3718 * Called only when the 'highlight' option has been changed and upon first
3719 * screen redraw after any :highlight command.
3720 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3721 */
3722 int
3723highlight_changed(void)
3724{
3725 int hlf;
3726 int i;
3727 char_u *p;
3728 int attr;
3729 char_u *end;
3730 int id;
3731#ifdef USER_HIGHLIGHT
3732 char_u userhl[30]; // use 30 to avoid compiler warning
3733# ifdef FEAT_STL_OPT
3734 int id_S = -1;
3735 int id_SNC = 0;
3736# ifdef FEAT_TERMINAL
3737 int id_ST = 0;
3738 int id_STNC = 0;
3739# endif
3740 int hlcnt;
3741# endif
3742#endif
3743 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3744
3745 need_highlight_changed = FALSE;
3746
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003747#ifdef FEAT_TERMINAL
3748 term_update_colors_all();
3749 term_update_wincolor_all();
3750#endif
3751
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003752 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003753 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3754 highlight_attr[hlf] = 0;
3755
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003756 // First set all attributes to their default value.
3757 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003758 for (i = 0; i < 2; ++i)
3759 {
3760 if (i)
3761 p = p_hl;
3762 else
3763 p = get_highlight_default();
3764 if (p == NULL) // just in case
3765 continue;
3766
3767 while (*p)
3768 {
3769 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3770 if (hl_flags[hlf] == *p)
3771 break;
3772 ++p;
3773 if (hlf == (int)HLF_COUNT || *p == NUL)
3774 return FAIL;
3775
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003776 // Allow several hl_flags to be combined, like "bu" for
3777 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003778 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003779 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003780 {
3781 if (VIM_ISWHITE(*p)) // ignore white space
3782 continue;
3783
3784 if (attr > HL_ALL) // Combination with ':' is not allowed.
3785 return FAIL;
3786
3787 switch (*p)
3788 {
3789 case 'b': attr |= HL_BOLD;
3790 break;
3791 case 'i': attr |= HL_ITALIC;
3792 break;
3793 case '-':
3794 case 'n': // no highlighting
3795 break;
3796 case 'r': attr |= HL_INVERSE;
3797 break;
3798 case 's': attr |= HL_STANDOUT;
3799 break;
3800 case 'u': attr |= HL_UNDERLINE;
3801 break;
3802 case 'c': attr |= HL_UNDERCURL;
3803 break;
3804 case 't': attr |= HL_STRIKETHROUGH;
3805 break;
3806 case ':': ++p; // highlight group name
3807 if (attr || *p == NUL) // no combinations
3808 return FAIL;
3809 end = vim_strchr(p, ',');
3810 if (end == NULL)
3811 end = p + STRLEN(p);
3812 id = syn_check_group(p, (int)(end - p));
3813 if (id == 0)
3814 return FAIL;
3815 attr = syn_id2attr(id);
3816 p = end - 1;
3817#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3818 if (hlf == (int)HLF_SNC)
3819 id_SNC = syn_get_final_id(id);
3820# ifdef FEAT_TERMINAL
3821 else if (hlf == (int)HLF_ST)
3822 id_ST = syn_get_final_id(id);
3823 else if (hlf == (int)HLF_STNC)
3824 id_STNC = syn_get_final_id(id);
3825# endif
3826 else if (hlf == (int)HLF_S)
3827 id_S = syn_get_final_id(id);
3828#endif
3829 break;
3830 default: return FAIL;
3831 }
3832 }
3833 highlight_attr[hlf] = attr;
3834
3835 p = skip_to_option_part(p); // skip comma and spaces
3836 }
3837 }
3838
3839#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003840 // Setup the user highlights
3841 //
3842 // Temporarily utilize 28 more hl entries:
3843 // 9 for User1-User9 combined with StatusLineNC
3844 // 9 for User1-User9 combined with StatusLineTerm
3845 // 9 for User1-User9 combined with StatusLineTermNC
3846 // 1 for StatusLine default
3847 // Have to be in there simultaneously in case of table overflows in
3848 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003849# ifdef FEAT_STL_OPT
3850 if (ga_grow(&highlight_ga, 28) == FAIL)
3851 return FAIL;
3852 hlcnt = highlight_ga.ga_len;
3853 if (id_S == -1)
3854 {
3855 // Make sure id_S is always valid to simplify code below. Use the last
3856 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003857 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003858 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3859 id_S = hlcnt + 19;
3860 }
3861# endif
3862 for (i = 0; i < 9; i++)
3863 {
3864 sprintf((char *)userhl, "User%d", i + 1);
3865 id = syn_name2id(userhl);
3866 if (id == 0)
3867 {
3868 highlight_user[i] = 0;
3869# ifdef FEAT_STL_OPT
3870 highlight_stlnc[i] = 0;
3871# ifdef FEAT_TERMINAL
3872 highlight_stlterm[i] = 0;
3873 highlight_stltermnc[i] = 0;
3874# endif
3875# endif
3876 }
3877 else
3878 {
3879 highlight_user[i] = syn_id2attr(id);
3880# ifdef FEAT_STL_OPT
3881 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3882 HLF_SNC, highlight_stlnc);
3883# ifdef FEAT_TERMINAL
3884 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3885 HLF_ST, highlight_stlterm);
3886 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3887 HLF_STNC, highlight_stltermnc);
3888# endif
3889# endif
3890 }
3891 }
3892# ifdef FEAT_STL_OPT
3893 highlight_ga.ga_len = hlcnt;
3894# endif
3895
3896#endif // USER_HIGHLIGHT
3897
3898 return OK;
3899}
3900
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003901static void highlight_list(void);
3902static void highlight_list_two(int cnt, int attr);
3903
3904/*
3905 * Handle command line completion for :highlight command.
3906 */
3907 void
3908set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3909{
3910 char_u *p;
3911
3912 // Default: expand group names
3913 xp->xp_context = EXPAND_HIGHLIGHT;
3914 xp->xp_pattern = arg;
3915 include_link = 2;
3916 include_default = 1;
3917
3918 // (part of) subcommand already typed
3919 if (*arg != NUL)
3920 {
3921 p = skiptowhite(arg);
3922 if (*p != NUL) // past "default" or group name
3923 {
3924 include_default = 0;
3925 if (STRNCMP("default", arg, p - arg) == 0)
3926 {
3927 arg = skipwhite(p);
3928 xp->xp_pattern = arg;
3929 p = skiptowhite(arg);
3930 }
3931 if (*p != NUL) // past group name
3932 {
3933 include_link = 0;
3934 if (arg[1] == 'i' && arg[0] == 'N')
3935 highlight_list();
3936 if (STRNCMP("link", arg, p - arg) == 0
3937 || STRNCMP("clear", arg, p - arg) == 0)
3938 {
3939 xp->xp_pattern = skipwhite(p);
3940 p = skiptowhite(xp->xp_pattern);
3941 if (*p != NUL) // past first group name
3942 {
3943 xp->xp_pattern = skipwhite(p);
3944 p = skiptowhite(xp->xp_pattern);
3945 }
3946 }
3947 if (*p != NUL) // past group name(s)
3948 xp->xp_context = EXPAND_NOTHING;
3949 }
3950 }
3951 }
3952}
3953
3954/*
3955 * List highlighting matches in a nice way.
3956 */
3957 static void
3958highlight_list(void)
3959{
3960 int i;
3961
3962 for (i = 10; --i >= 0; )
3963 highlight_list_two(i, HL_ATTR(HLF_D));
3964 for (i = 40; --i >= 0; )
3965 highlight_list_two(99, 0);
3966}
3967
3968 static void
3969highlight_list_two(int cnt, int attr)
3970{
3971 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3972 msg_clr_eos();
3973 out_flush();
3974 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3975}
3976
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003977/*
3978 * Function given to ExpandGeneric() to obtain the list of group names.
3979 */
3980 char_u *
3981get_highlight_name(expand_T *xp UNUSED, int idx)
3982{
3983 return get_highlight_name_ext(xp, idx, TRUE);
3984}
3985
3986/*
3987 * Obtain a highlight group name.
3988 * When "skip_cleared" is TRUE don't return a cleared entry.
3989 */
3990 char_u *
3991get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3992{
3993 if (idx < 0)
3994 return NULL;
3995
3996 // Items are never removed from the table, skip the ones that were
3997 // cleared.
3998 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3999 return (char_u *)"";
4000
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004001 if (idx == highlight_ga.ga_len && include_none != 0)
4002 return (char_u *)"none";
4003 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4004 return (char_u *)"default";
4005 if (idx == highlight_ga.ga_len + include_none + include_default
4006 && include_link != 0)
4007 return (char_u *)"link";
4008 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4009 && include_link != 0)
4010 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004011 if (idx >= highlight_ga.ga_len)
4012 return NULL;
4013 return HL_TABLE()[idx].sg_name;
4014}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004015
4016#if defined(FEAT_GUI) || defined(PROTO)
4017/*
4018 * Free all the highlight group fonts.
4019 * Used when quitting for systems which need it.
4020 */
4021 void
4022free_highlight_fonts(void)
4023{
4024 int idx;
4025
4026 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4027 {
4028 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4029 HL_TABLE()[idx].sg_font = NOFONT;
4030# ifdef FEAT_XFONTSET
4031 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4032 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4033# endif
4034 }
4035
4036 gui_mch_free_font(gui.norm_font);
4037# ifdef FEAT_XFONTSET
4038 gui_mch_free_fontset(gui.fontset);
4039# endif
4040# ifndef FEAT_GUI_GTK
4041 gui_mch_free_font(gui.bold_font);
4042 gui_mch_free_font(gui.ital_font);
4043 gui_mch_free_font(gui.boldital_font);
4044# endif
4045}
4046#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004047
4048#if defined(FEAT_EVAL) || defined(PROTO)
4049/*
4050 * Convert each of the highlight attribute bits (bold, standout, underline,
4051 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4052 * the attribute name as the key.
4053 */
4054 static dict_T *
4055highlight_get_attr_dict(int hlattr)
4056{
4057 dict_T *dict;
4058 int i;
4059
4060 dict = dict_alloc();
4061 if (dict == NULL)
4062 return NULL;
4063
4064 for (i = 0; hl_attr_table[i] != 0; ++i)
4065 {
4066 if (hlattr & hl_attr_table[i])
4067 {
4068 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4069 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4070 }
4071 }
4072
4073 return dict;
4074}
4075
4076/*
4077 * Return the attributes of the highlight group at index 'hl_idx' as a
4078 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4079 * links recursively.
4080 */
4081 static dict_T *
4082highlight_get_info(int hl_idx, int resolve_link)
4083{
4084 dict_T *dict;
4085 hl_group_T *sgp;
4086 dict_T *attr_dict;
4087 int hlgid;
4088
4089 dict = dict_alloc();
4090 if (dict == NULL)
4091 return dict;
4092
4093 sgp = &HL_TABLE()[hl_idx];
4094 // highlight group id is 1-based
4095 hlgid = hl_idx + 1;
4096
4097 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4098 goto error;
4099 if (dict_add_number(dict, "id", hlgid) == FAIL)
4100 goto error;
4101
4102 if (sgp->sg_link && resolve_link)
4103 {
4104 // resolve the highlight group link recursively
4105 while (sgp->sg_link)
4106 {
4107 hlgid = sgp->sg_link;
4108 sgp = &HL_TABLE()[sgp->sg_link - 1];
4109 }
4110 }
4111
4112 if (sgp->sg_term != 0)
4113 {
4114 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4115 if (attr_dict != NULL)
4116 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4117 goto error;
4118 }
4119 if (sgp->sg_start != NULL)
4120 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4121 goto error;
4122 if (sgp->sg_stop != NULL)
4123 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4124 goto error;
4125 if (sgp->sg_cterm != 0)
4126 {
4127 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4128 if (attr_dict != NULL)
4129 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4130 goto error;
4131 }
4132 if (sgp->sg_cterm_fg != 0)
4133 if (dict_add_string(dict, "ctermfg",
4134 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4135 goto error;
4136 if (sgp->sg_cterm_bg != 0)
4137 if (dict_add_string(dict, "ctermbg",
4138 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4139 goto error;
4140 if (sgp->sg_cterm_ul != 0)
4141 if (dict_add_string(dict, "ctermul",
4142 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4143 goto error;
4144 if (sgp->sg_gui != 0)
4145 {
4146 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4147 if (attr_dict != NULL)
4148 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4149 goto error;
4150 }
4151 if (sgp->sg_gui_fg_name != NULL)
4152 if (dict_add_string(dict, "guifg",
4153 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4154 goto error;
4155 if (sgp->sg_gui_bg_name != NULL)
4156 if (dict_add_string(dict, "guibg",
4157 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4158 goto error;
4159 if (sgp->sg_gui_sp_name != NULL)
4160 if (dict_add_string(dict, "guisp",
4161 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4162 goto error;
4163# ifdef FEAT_GUI
4164 if (sgp->sg_font_name != NULL)
4165 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4166 goto error;
4167# endif
4168 if (sgp->sg_link)
4169 {
4170 char_u *link;
4171
4172 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4173 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4174 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004175
4176 if (sgp->sg_deflink)
4177 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004178 }
4179 if (dict_len(dict) == 2)
4180 // If only 'name' is present, then the highlight group is cleared.
4181 dict_add_bool(dict, "cleared", VVAL_TRUE);
4182
4183 return dict;
4184
4185error:
4186 vim_free(dict);
4187 return NULL;
4188}
4189
4190/*
4191 * "hlget([name])" function
4192 * Return the attributes of a specific highlight group (if specified) or all
4193 * the highlight groups.
4194 */
4195 void
4196f_hlget(typval_T *argvars, typval_T *rettv)
4197{
4198 list_T *list;
4199 dict_T *dict;
4200 int i;
4201 char_u *hlarg = NULL;
4202 int resolve_link = FALSE;
4203
4204 if (rettv_list_alloc(rettv) == FAIL)
4205 return;
4206
4207 if (check_for_opt_string_arg(argvars, 0) == FAIL
4208 || (argvars[0].v_type != VAR_UNKNOWN
4209 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4210 return;
4211
4212 if (argvars[0].v_type != VAR_UNKNOWN)
4213 {
4214 // highlight group name supplied
4215 hlarg = tv_get_string_chk(&argvars[0]);
4216 if (hlarg == NULL)
4217 return;
4218
4219 if (argvars[1].v_type != VAR_UNKNOWN)
4220 {
4221 int error = FALSE;
4222
4223 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4224 if (error)
4225 return;
4226 }
4227 }
4228
4229 list = rettv->vval.v_list;
4230 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4231 {
4232 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4233 {
4234 dict = highlight_get_info(i, resolve_link);
4235 if (dict != NULL)
4236 list_append_dict(list, dict);
4237 }
4238 }
4239}
4240
4241/*
4242 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4243 * 'dict' or the value is not a string type. If the value is not a string type
4244 * or is NULL, then 'error' is set to TRUE.
4245 */
4246 static char_u *
4247hldict_get_string(dict_T *dict, char_u *key, int *error)
4248{
4249 dictitem_T *di;
4250
4251 *error = FALSE;
4252 di = dict_find(dict, key, -1);
4253 if (di == NULL)
4254 return NULL;
4255
4256 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4257 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004258 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004259 *error = TRUE;
4260 return NULL;
4261 }
4262
4263 return di->di_tv.vval.v_string;
4264}
4265
4266/*
4267 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4268 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4269 * Dictionary or is NULL.
4270 */
4271 static int
4272hldict_attr_to_str(
4273 dict_T *dict,
4274 char_u *key,
4275 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004276 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004277{
4278 dictitem_T *di;
4279 dict_T *attrdict;
4280 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004281 char_u *p;
4282 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004283
4284 attr_str[0] = NUL;
4285 di = dict_find(dict, key, -1);
4286 if (di == NULL)
4287 return TRUE;
4288
4289 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4290 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004291 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004292 return FALSE;
4293 }
4294
4295 attrdict = di->di_tv.vval.v_dict;
4296
4297 // If the attribute dict is empty, then return NONE to clear the attributes
4298 if (dict_len(attrdict) == 0)
4299 {
4300 vim_strcat(attr_str, (char_u *)"NONE", len);
4301 return TRUE;
4302 }
4303
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004304 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004305 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4306 {
4307 if (dict_get_bool(attrdict, (char_u *)hl_name_table[i],
4308 VVAL_FALSE) == VVAL_TRUE)
4309 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004310 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4311 STRCPY(p, (char_u *)",");
4312 sz = STRLEN(hl_name_table[i]);
4313 if (p - attr_str + sz + 1 < len)
4314 {
4315 STRCPY(p, (char_u *)hl_name_table[i]);
4316 p += sz;
4317 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004318 }
4319 }
4320
4321 return TRUE;
4322}
4323
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004324// Temporary buffer used to store the command string produced by hlset().
4325// IObuff cannot be used for this as the error messages produced by hlset()
4326// internally use IObuff.
4327#define HLSETBUFSZ 512
4328static char_u hlsetBuf[HLSETBUFSZ + 1];
4329
4330/*
4331 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4332 * "dptr", which points into "hlsetBuf".
4333 * Returns the updated pointer.
4334 */
4335 static char_u *
4336add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4337{
4338 size_t vallen;
4339
4340 // Do nothing if the value is not specified or is empty
4341 if (value == NULL || *value == NUL)
4342 return dptr;
4343
4344 vallen = STRLEN(value);
4345 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4346 {
4347 STRCPY(dptr, attr);
4348 dptr += attrlen;
4349 STRCPY(dptr, value);
4350 dptr += vallen;
4351 }
4352
4353 return dptr;
4354}
4355
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004356/*
4357 * Add or update a highlight group using 'dict' items. Returns TRUE if
4358 * successfully updated the highlight group.
4359 */
4360 static int
4361hlg_add_or_update(dict_T *dict)
4362{
4363 char_u *name;
4364 int error;
4365 char_u term_attr[80];
4366 char_u cterm_attr[80];
4367 char_u gui_attr[80];
4368 char_u *start;
4369 char_u *stop;
4370 char_u *ctermfg;
4371 char_u *ctermbg;
4372 char_u *ctermul;
4373 char_u *guifg;
4374 char_u *guibg;
4375 char_u *guisp;
4376# ifdef FEAT_GUI
4377 char_u *font;
4378# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004379 int forceit = FALSE;
4380 int dodefault = FALSE;
4381 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004382 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004383
4384 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004385 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004386 return FALSE;
4387
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004388 if (dict_get_bool(dict, (char_u *)"force", VVAL_FALSE) == VVAL_TRUE)
4389 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004390
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004391 if (dict_get_bool(dict, (char_u *)"default", VVAL_FALSE) == VVAL_TRUE)
4392 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004393
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004394 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004395 {
4396 varnumber_T cleared;
4397
4398 // clear a highlight group
4399 cleared = dict_get_bool(dict, (char_u *)"cleared", FALSE);
4400 if (cleared == TRUE)
4401 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004402 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4403 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004404 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004405 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004406 }
4407
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004408 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004409 {
4410 char_u *linksto;
4411
4412 // link highlight groups
4413 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004414 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004415 return FALSE;
4416
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004417 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004418 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004419 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004420
4421 done = TRUE;
4422 }
4423
4424 // If 'cleared' or 'linksto' are specified, then don't process the other
4425 // attributes.
4426 if (done)
4427 return TRUE;
4428
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004429 start = hldict_get_string(dict, (char_u *)"start", &error);
4430 if (error)
4431 return FALSE;
4432
4433 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4434 if (error)
4435 return FALSE;
4436
4437 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004438 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004439 return FALSE;
4440
4441 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004442 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004443 return FALSE;
4444
4445 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4446 if (error)
4447 return FALSE;
4448
4449 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4450 if (error)
4451 return FALSE;
4452
4453 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4454 if (error)
4455 return FALSE;
4456
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004457 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004458 return FALSE;
4459
4460 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4461 if (error)
4462 return FALSE;
4463
4464 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4465 if (error)
4466 return FALSE;
4467
4468 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4469 if (error)
4470 return FALSE;
4471
4472# ifdef FEAT_GUI
4473 font = hldict_get_string(dict, (char_u *)"font", &error);
4474 if (error)
4475 return FALSE;
4476# endif
4477
4478 // If none of the attributes are specified, then do nothing.
4479 if (term_attr[0] == NUL && start == NULL && stop == NULL
4480 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4481 && ctermul == NULL && gui_attr[0] == NUL
4482# ifdef FEAT_GUI
4483 && font == NULL
4484# endif
4485 && guifg == NULL && guibg == NULL && guisp == NULL
4486 )
4487 return TRUE;
4488
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004489 hlsetBuf[0] = NUL;
4490 p = hlsetBuf;
4491 if (dodefault)
4492 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4493 p = add_attr_and_value(p, (char_u *)"", 0, name);
4494 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4495 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4496 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4497 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4498 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4499 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4500 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4501 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004502# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004503 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004504# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004505 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4506 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
4507 p = add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004508
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004509 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004510
4511 return TRUE;
4512}
4513
4514/*
4515 * "hlset([{highlight_attr}])" function
4516 * Add or modify highlight groups
4517 */
4518 void
4519f_hlset(typval_T *argvars, typval_T *rettv)
4520{
4521 listitem_T *li;
4522 dict_T *dict;
4523
4524 rettv->vval.v_number = -1;
4525
4526 if (check_for_list_arg(argvars, 0) == FAIL)
4527 return;
4528
4529 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4530 {
4531 if (li->li_tv.v_type != VAR_DICT)
4532 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004533 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004534 return;
4535 }
4536
4537 dict = li->li_tv.vval.v_dict;
4538 if (!hlg_add_or_update(dict))
4539 return;
4540 }
4541
4542 rettv->vval.v_number = 0;
4543}
4544#endif