blob: 7349f4b4ab61619d81de7b7be8999b1d4d231042 [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};
32#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
33
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",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200160 CENT("Normal cterm=NONE", "Normal gui=NONE"),
161 NULL
162};
163
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200164// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200165static char *(highlight_init_light[]) = {
166 CENT("Directory term=bold ctermfg=DarkBlue",
167 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
168 CENT("LineNr term=underline ctermfg=Brown",
169 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200170 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
171 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200172 CENT("MoreMsg term=bold ctermfg=DarkGreen",
173 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
174 CENT("Question term=standout ctermfg=DarkGreen",
175 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
176 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
177 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
178#ifdef FEAT_SPELL
179 CENT("SpellBad term=reverse ctermbg=LightRed",
180 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
181 CENT("SpellCap term=reverse ctermbg=LightBlue",
182 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
183 CENT("SpellRare term=reverse ctermbg=LightMagenta",
184 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
185 CENT("SpellLocal term=underline ctermbg=Cyan",
186 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
187#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200188 CENT("PmenuThumb ctermbg=Black",
189 "PmenuThumb ctermbg=Black guibg=Black"),
190 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
191 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
192 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
193 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200194 CENT("SpecialKey term=bold ctermfg=DarkBlue",
195 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
196 CENT("Title term=bold ctermfg=DarkMagenta",
197 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
198 CENT("WarningMsg term=standout ctermfg=DarkRed",
199 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
200#ifdef FEAT_WILDMENU
201 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
202 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
203#endif
204#ifdef FEAT_FOLDING
205 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
206 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
207 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
208 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
209#endif
210#ifdef FEAT_SIGNS
211 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
212 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
213#endif
214 CENT("Visual term=reverse",
215 "Visual term=reverse guibg=LightGrey"),
216#ifdef FEAT_DIFF
217 CENT("DiffAdd term=bold ctermbg=LightBlue",
218 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
219 CENT("DiffChange term=bold ctermbg=LightMagenta",
220 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
221 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
222 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
223#endif
224 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
225 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
226#ifdef FEAT_SYN_HL
227 CENT("CursorColumn term=reverse ctermbg=LightGrey",
228 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
229 CENT("CursorLine term=underline cterm=underline",
230 "CursorLine term=underline cterm=underline guibg=Grey90"),
231 CENT("ColorColumn term=reverse ctermbg=LightRed",
232 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
233#endif
234#ifdef FEAT_CONCEAL
235 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
236 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
237#endif
238 CENT("MatchParen term=reverse ctermbg=Cyan",
239 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
240#ifdef FEAT_TERMINAL
241 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
242 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
243 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
244 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
245#endif
246#ifdef FEAT_MENU
247 CENT("ToolbarLine term=underline ctermbg=LightGrey",
248 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
249 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
250 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
251#endif
252 NULL
253};
254
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200255// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200256static char *(highlight_init_dark[]) = {
257 CENT("Directory term=bold ctermfg=LightCyan",
258 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
259 CENT("LineNr term=underline ctermfg=Yellow",
260 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200261 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
262 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200263 CENT("MoreMsg term=bold ctermfg=LightGreen",
264 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
265 CENT("Question term=standout ctermfg=LightGreen",
266 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
267 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
268 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
269 CENT("SpecialKey term=bold ctermfg=LightBlue",
270 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
271#ifdef FEAT_SPELL
272 CENT("SpellBad term=reverse ctermbg=Red",
273 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
274 CENT("SpellCap term=reverse ctermbg=Blue",
275 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
276 CENT("SpellRare term=reverse ctermbg=Magenta",
277 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
278 CENT("SpellLocal term=underline ctermbg=Cyan",
279 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
280#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200281 CENT("PmenuThumb ctermbg=White",
282 "PmenuThumb ctermbg=White guibg=White"),
283 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
284 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
285 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
286 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200287 CENT("Title term=bold ctermfg=LightMagenta",
288 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
289 CENT("WarningMsg term=standout ctermfg=LightRed",
290 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
291#ifdef FEAT_WILDMENU
292 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
293 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
294#endif
295#ifdef FEAT_FOLDING
296 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
297 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
298 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
299 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
300#endif
301#ifdef FEAT_SIGNS
302 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
303 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
304#endif
305 CENT("Visual term=reverse",
306 "Visual term=reverse guibg=DarkGrey"),
307#ifdef FEAT_DIFF
308 CENT("DiffAdd term=bold ctermbg=DarkBlue",
309 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
310 CENT("DiffChange term=bold ctermbg=DarkMagenta",
311 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
312 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
313 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
314#endif
315 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
316 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
317#ifdef FEAT_SYN_HL
318 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
319 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
320 CENT("CursorLine term=underline cterm=underline",
321 "CursorLine term=underline cterm=underline guibg=Grey40"),
322 CENT("ColorColumn term=reverse ctermbg=DarkRed",
323 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
324#endif
325 CENT("MatchParen term=reverse ctermbg=DarkCyan",
326 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
327#ifdef FEAT_CONCEAL
328 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
329 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
330#endif
331#ifdef FEAT_TERMINAL
332 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
333 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
334 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
335 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
336#endif
337#ifdef FEAT_MENU
338 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
339 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
340 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
341 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
342#endif
343 NULL
344};
345
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200346/*
347 * Returns the number of highlight groups.
348 */
349 int
350highlight_num_groups(void)
351{
352 return highlight_ga.ga_len;
353}
354
355/*
356 * Returns the name of a highlight group.
357 */
358 char_u *
359highlight_group_name(int id)
360{
361 return HL_TABLE()[id].sg_name;
362}
363
364/*
365 * Returns the ID of the link to a highlight group.
366 */
367 int
368highlight_link_id(int id)
369{
370 return HL_TABLE()[id].sg_link;
371}
372
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200373 void
374init_highlight(
375 int both, // include groups where 'bg' doesn't matter
376 int reset) // clear group first
377{
378 int i;
379 char **pp;
380 static int had_both = FALSE;
381#ifdef FEAT_EVAL
382 char_u *p;
383
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100384 // Try finding the color scheme file. Used when a color file was loaded
385 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200386 p = get_var_value((char_u *)"g:colors_name");
387 if (p != NULL)
388 {
389 // The value of g:colors_name could be freed when sourcing the script,
390 // making "p" invalid, so copy it.
391 char_u *copy_p = vim_strsave(p);
392 int r;
393
394 if (copy_p != NULL)
395 {
396 r = load_colors(copy_p);
397 vim_free(copy_p);
398 if (r == OK)
399 return;
400 }
401 }
402
403#endif
404
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100405 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200406 if (both)
407 {
408 had_both = TRUE;
409 pp = highlight_init_both;
410 for (i = 0; pp[i] != NULL; ++i)
411 do_highlight((char_u *)pp[i], reset, TRUE);
412 }
413 else if (!had_both)
414 // Don't do anything before the call with both == TRUE from main().
415 // Not everything has been setup then, and that call will overrule
416 // everything anyway.
417 return;
418
419 if (*p_bg == 'l')
420 pp = highlight_init_light;
421 else
422 pp = highlight_init_dark;
423 for (i = 0; pp[i] != NULL; ++i)
424 do_highlight((char_u *)pp[i], reset, TRUE);
425
426 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
427 // depend on the number of colors available.
428 // With 8 colors brown is equal to yellow, need to use black for Search fg
429 // to avoid Statement highlighted text disappears.
430 // Clear the attributes, needed when changing the t_Co value.
431 if (t_colors > 8)
432 do_highlight((char_u *)(*p_bg == 'l'
433 ? "Visual cterm=NONE ctermbg=LightGrey"
434 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
435 else
436 {
437 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
438 FALSE, TRUE);
439 if (*p_bg == 'l')
440 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
441 }
442
443#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100444 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200445 if (get_var_value((char_u *)"g:syntax_on") != NULL)
446 {
447 static int recursive = 0;
448
449 if (recursive >= 5)
450 emsg(_("E679: recursive loop loading syncolor.vim"));
451 else
452 {
453 ++recursive;
454 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
455 --recursive;
456 }
457 }
458#endif
459}
460
461/*
462 * Load color file "name".
463 * Return OK for success, FAIL for failure.
464 */
465 int
466load_colors(char_u *name)
467{
468 char_u *buf;
469 int retval = FAIL;
470 static int recursive = FALSE;
471
472 // When being called recursively, this is probably because setting
473 // 'background' caused the highlighting to be reloaded. This means it is
474 // working, thus we should return OK.
475 if (recursive)
476 return OK;
477
478 recursive = TRUE;
479 buf = alloc(STRLEN(name) + 12);
480 if (buf != NULL)
481 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100482#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100483 load_default_colors_lists();
484#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200485 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
486 curbuf->b_fname, FALSE, curbuf);
487 sprintf((char *)buf, "colors/%s.vim", name);
488 retval = source_runtime(buf, DIP_START + DIP_OPT);
489 vim_free(buf);
490 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
491 }
492 recursive = FALSE;
493
494 return retval;
495}
496
497static char *(color_names[28]) = {
498 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
499 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
500 "Gray", "Grey", "LightGray", "LightGrey",
501 "DarkGray", "DarkGrey",
502 "Blue", "LightBlue", "Green", "LightGreen",
503 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
504 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
505 // indices:
506 // 0, 1, 2, 3,
507 // 4, 5, 6, 7,
508 // 8, 9, 10, 11,
509 // 12, 13,
510 // 14, 15, 16, 17,
511 // 18, 19, 20, 21, 22,
512 // 23, 24, 25, 26, 27
513static int color_numbers_16[28] = {0, 1, 2, 3,
514 4, 5, 6, 6,
515 7, 7, 7, 7,
516 8, 8,
517 9, 9, 10, 10,
518 11, 11, 12, 12, 13,
519 13, 14, 14, 15, -1};
520// for xterm with 88 colors...
521static int color_numbers_88[28] = {0, 4, 2, 6,
522 1, 5, 32, 72,
523 84, 84, 7, 7,
524 82, 82,
525 12, 43, 10, 61,
526 14, 63, 9, 74, 13,
527 75, 11, 78, 15, -1};
528// for xterm with 256 colors...
529static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200530 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200531 248, 248, 7, 7,
532 242, 242,
533 12, 81, 10, 121,
534 14, 159, 9, 224, 13,
535 225, 11, 229, 15, -1};
536// for terminals with less than 16 colors...
537static int color_numbers_8[28] = {0, 4, 2, 6,
538 1, 5, 3, 3,
539 7, 7, 7, 7,
540 0+8, 0+8,
541 4+8, 4+8, 2+8, 2+8,
542 6+8, 6+8, 1+8, 1+8, 5+8,
543 5+8, 3+8, 3+8, 7+8, -1};
544
545/*
546 * Lookup the "cterm" value to be used for color with index "idx" in
547 * color_names[].
548 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
549 * colors, otherwise it will be unchanged.
550 */
551 int
552lookup_color(int idx, int foreground, int *boldp)
553{
554 int color = color_numbers_16[idx];
555 char_u *p;
556
557 // Use the _16 table to check if it's a valid color name.
558 if (color < 0)
559 return -1;
560
561 if (t_colors == 8)
562 {
563 // t_Co is 8: use the 8 colors table
564#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100565 // On qnx, the 8 & 16 color arrays are the same
566 if (STRNCMP(T_NAME, "qansi", 5) == 0)
567 color = color_numbers_16[idx];
568 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200569#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100570 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200571 if (foreground)
572 {
573 // set/reset bold attribute to get light foreground
574 // colors (on some terminals, e.g. "linux")
575 if (color & 8)
576 *boldp = TRUE;
577 else
578 *boldp = FALSE;
579 }
580 color &= 7; // truncate to 8 colors
581 }
582 else if (t_colors == 16 || t_colors == 88
583 || t_colors >= 256)
584 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100585 // Guess: if the termcap entry ends in 'm', it is
586 // probably an xterm-like terminal. Use the changed
587 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200588 if (*T_CAF != NUL)
589 p = T_CAF;
590 else
591 p = T_CSF;
592 if (*p != NUL && (t_colors > 256
593 || *(p + STRLEN(p) - 1) == 'm'))
594 {
595 if (t_colors == 88)
596 color = color_numbers_88[idx];
597 else if (t_colors >= 256)
598 color = color_numbers_256[idx];
599 else
600 color = color_numbers_8[idx];
601 }
602#ifdef FEAT_TERMRESPONSE
603 if (t_colors >= 256 && color == 15 && is_mac_terminal)
604 // Terminal.app has a bug: 15 is light grey. Use white
605 // from the color cube instead.
606 color = 231;
607#endif
608 }
609 return color;
610}
611
612/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100613 * Link highlight group 'from_hg' to 'to_hg'.
614 * 'dodefault' is set to TRUE for ":highlight default link".
615 * 'forceit' is set to TRUE for ":higlight! link"
616 * 'init' is set to TRUE when initializing all the highlight groups.
617 */
618 static void
619highlight_group_link(
620 char_u *from_hg,
621 int from_len,
622 char_u *to_hg,
623 int to_len,
624 int dodefault,
625 int forceit,
626 int init)
627{
628 int from_id;
629 int to_id;
630 hl_group_T *hlgroup = NULL;
631
632 from_id = syn_check_group(from_hg, from_len);
633 if (STRNCMP(to_hg, "NONE", 4) == 0)
634 to_id = 0;
635 else
636 to_id = syn_check_group(to_hg, to_len);
637
638 if (from_id > 0)
639 {
640 hlgroup = &HL_TABLE()[from_id - 1];
641 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
642 {
643 hlgroup->sg_deflink = to_id;
644#ifdef FEAT_EVAL
645 hlgroup->sg_deflink_sctx = current_sctx;
646 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
647#endif
648 }
649 }
650
651 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
652 {
653 // Don't allow a link when there already is some highlighting
654 // for the group, unless '!' is used
655 if (to_id > 0 && !forceit && !init
656 && hl_has_settings(from_id - 1, dodefault))
657 {
658 if (SOURCING_NAME == NULL && !dodefault)
659 emsg(_("E414: group has settings, highlight link ignored"));
660 }
661 else if (hlgroup->sg_link != to_id
662#ifdef FEAT_EVAL
663 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
664#endif
665 || hlgroup->sg_cleared)
666 {
667 if (!init)
668 hlgroup->sg_set |= SG_LINK;
669 hlgroup->sg_link = to_id;
670#ifdef FEAT_EVAL
671 hlgroup->sg_script_ctx = current_sctx;
672 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
673#endif
674 hlgroup->sg_cleared = FALSE;
675 redraw_all_later(SOME_VALID);
676
677 // Only call highlight_changed() once after multiple changes.
678 need_highlight_changed = TRUE;
679 }
680 }
681
682}
683
684/*
685 * Reset all highlighting to the defaults. Removes all highlighting for the
686 * groups added by the user.
687 */
688 static void
689highlight_reset_all(void)
690{
691 int idx;
692
693#ifdef FEAT_GUI
694 // First, we do not destroy the old values, but allocate the new
695 // ones and update the display. THEN we destroy the old values.
696 // If we destroy the old values first, then the old values
697 // (such as GuiFont's or GuiFontset's) will still be displayed but
698 // invalid because they were free'd.
699 if (gui.in_use)
700 {
701# ifdef FEAT_BEVAL_TIP
702 gui_init_tooltip_font();
703# endif
704# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
705 gui_init_menu_font();
706# endif
707 }
708# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
709 gui_mch_def_colors();
710# endif
711# ifdef FEAT_GUI_X11
712# ifdef FEAT_MENU
713
714 // This only needs to be done when there is no Menu highlight
715 // group defined by default, which IS currently the case.
716 gui_mch_new_menu_colors();
717# endif
718 if (gui.in_use)
719 {
720 gui_new_scrollbar_colors();
721# ifdef FEAT_BEVAL_GUI
722 gui_mch_new_tooltip_colors();
723# endif
724# ifdef FEAT_MENU
725 gui_mch_new_menu_font();
726# endif
727 }
728# endif
729
730 // Ok, we're done allocating the new default graphics items.
731 // The screen should already be refreshed at this point.
732 // It is now Ok to clear out the old data.
733#endif
734#ifdef FEAT_EVAL
735 do_unlet((char_u *)"g:colors_name", TRUE);
736#endif
737 restore_cterm_colors();
738
739 // Clear all default highlight groups and load the defaults.
740 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
741 highlight_clear(idx);
742 init_highlight(TRUE, TRUE);
743#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
744 if (USE_24BIT)
745 highlight_gui_started();
746 else
747#endif
748 highlight_changed();
749 redraw_later_clear();
750}
751
752/*
753 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
754 * index 'idx'.
755 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
756 * 'arg' is the list of attribute names separated by comma.
757 * 'init' is set to TRUE when initializing all the highlight groups.
758 * Returns TRUE if the attributes are set.
759 */
760 static int
761highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
762{
763 int attr;
764 int off;
765 long i;
766 int len;
767
768 attr = 0;
769 off = 0;
770 while (arg[off] != NUL)
771 {
772 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
773 {
774 len = (int)STRLEN(hl_name_table[i]);
775 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
776 {
777 attr |= hl_attr_table[i];
778 off += len;
779 break;
780 }
781 }
782 if (i < 0)
783 {
784 semsg(_("E418: Illegal value: %s"), arg);
785 return FALSE;
786 }
787 if (arg[off] == ',') // another one follows
788 ++off;
789 }
790 if (*key == 'T')
791 {
792 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
793 {
794 if (!init)
795 HL_TABLE()[idx].sg_set |= SG_TERM;
796 HL_TABLE()[idx].sg_term = attr;
797 }
798 }
799 else if (*key == 'C')
800 {
801 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
802 {
803 if (!init)
804 HL_TABLE()[idx].sg_set |= SG_CTERM;
805 HL_TABLE()[idx].sg_cterm = attr;
806 HL_TABLE()[idx].sg_cterm_bold = FALSE;
807 }
808 }
809#if defined(FEAT_GUI) || defined(FEAT_EVAL)
810 else
811 {
812 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
813 {
814 if (!init)
815 HL_TABLE()[idx].sg_set |= SG_GUI;
816 HL_TABLE()[idx].sg_gui = attr;
817 }
818 }
819#endif
820
821 return TRUE;
822}
823
824#ifdef FEAT_GUI
825/*
826 * Set the font for the highlight group at 'idx'.
827 * 'arg' is the font name.
828 * Returns TRUE if the font is changed.
829 */
830 static int
831highlight_set_font(
832 int idx,
833 char_u *arg,
834 int is_normal_group,
835 int is_menu_group,
836 int is_tooltip_group)
837{
838 int did_change = FALSE;
839
840 // in non-GUI fonts are simply ignored
841 if (HL_TABLE()[idx].sg_font_name != NULL
842 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
843 {
844 // Font name didn't change, ignore.
845 }
846 else if (!gui.shell_created)
847 {
848 // GUI not started yet, always accept the name.
849 vim_free(HL_TABLE()[idx].sg_font_name);
850 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
851 did_change = TRUE;
852 }
853 else
854 {
855 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
856# ifdef FEAT_XFONTSET
857 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
858# endif
859 // First, save the current font/fontset.
860 // Then try to allocate the font/fontset.
861 // If the allocation fails, HL_TABLE()[idx].sg_font OR
862 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
863
864 HL_TABLE()[idx].sg_font = NOFONT;
865# ifdef FEAT_XFONTSET
866 HL_TABLE()[idx].sg_fontset = NOFONTSET;
867# endif
868 hl_do_font(idx, arg, is_normal_group, is_menu_group,
869 is_tooltip_group, FALSE);
870
871# ifdef FEAT_XFONTSET
872 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
873 {
874 // New fontset was accepted. Free the old one, if there
875 // was one.
876 gui_mch_free_fontset(temp_sg_fontset);
877 vim_free(HL_TABLE()[idx].sg_font_name);
878 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
879 did_change = TRUE;
880 }
881 else
882 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
883# endif
884 if (HL_TABLE()[idx].sg_font != NOFONT)
885 {
886 // New font was accepted. Free the old one, if there was
887 // one.
888 gui_mch_free_font(temp_sg_font);
889 vim_free(HL_TABLE()[idx].sg_font_name);
890 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
891 did_change = TRUE;
892 }
893 else
894 HL_TABLE()[idx].sg_font = temp_sg_font;
895 }
896
897 return did_change;
898}
899#endif
900
901/*
902 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
903 * Returns TRUE if the foreground color is set.
904 */
905 static void
906highlight_set_ctermfg(int idx, int color, int is_normal_group)
907{
908 HL_TABLE()[idx].sg_cterm_fg = color + 1;
909 if (is_normal_group)
910 {
911 cterm_normal_fg_color = color + 1;
912 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
913#ifdef FEAT_GUI
914 // Don't do this if the GUI is used.
915 if (!gui.in_use && !gui.starting)
916#endif
917 {
918 must_redraw = CLEAR;
919 if (termcap_active && color >= 0)
920 term_fg_color(color);
921 }
922 }
923}
924
925/*
926 * Set the cterm background color for the highlight group at 'idx' to 'color'.
927 * Returns TRUE if the background color is set.
928 */
929 static void
930highlight_set_ctermbg(int idx, int color, int is_normal_group)
931{
932 HL_TABLE()[idx].sg_cterm_bg = color + 1;
933 if (is_normal_group)
934 {
935 cterm_normal_bg_color = color + 1;
936#ifdef FEAT_GUI
937 // Don't mess with 'background' if the GUI is used.
938 if (!gui.in_use && !gui.starting)
939#endif
940 {
941 must_redraw = CLEAR;
942 if (color >= 0)
943 {
944 int dark = -1;
945
946 if (termcap_active)
947 term_bg_color(color);
948 if (t_colors < 16)
949 dark = (color == 0 || color == 4);
950 // Limit the heuristic to the standard 16 colors
951 else if (color < 16)
952 dark = (color < 7 || color == 8);
953 // Set the 'background' option if the value is
954 // wrong.
955 if (dark != -1
956 && dark != (*p_bg == 'd')
957 && !option_was_set((char_u *)"bg"))
958 {
959 set_option_value((char_u *)"bg", 0L,
960 (char_u *)(dark ? "dark" : "light"), 0);
961 reset_option_was_set((char_u *)"bg");
962 }
963 }
964 }
965 }
966}
967
968/*
969 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
970 * Returns TRUE if the underline color is set.
971 */
972 static void
973highlight_set_ctermul(int idx, int color, int is_normal_group)
974{
975 HL_TABLE()[idx].sg_cterm_ul = color + 1;
976 if (is_normal_group)
977 {
978 cterm_normal_ul_color = color + 1;
979#ifdef FEAT_GUI
980 // Don't do this if the GUI is used.
981 if (!gui.in_use && !gui.starting)
982#endif
983 {
984 must_redraw = CLEAR;
985 if (termcap_active && color >= 0)
986 term_ul_color(color);
987 }
988 }
989}
990
991/*
992 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
993 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
994 * 'keystart' is the color name/value.
995 * 'arg' is the color name or the numeric value as a string.
996 * 'is_normal_group' is set if the highlight group is 'NORMAL'
997 * 'init' is set to TRUE when initializing highlighting.
998 * Called for the ":highlight" command and the "hlset()" function.
999 *
1000 * Returns TRUE if the color is set.
1001 */
1002 static int
1003highlight_set_cterm_color(
1004 int idx,
1005 char_u *key,
1006 char_u *key_start,
1007 char_u *arg,
1008 int is_normal_group,
1009 int init)
1010{
1011 int color;
1012 long i;
1013 int off;
1014
1015 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1016 {
1017 if (!init)
1018 HL_TABLE()[idx].sg_set |= SG_CTERM;
1019
1020 // When setting the foreground color, and previously the "bold"
1021 // flag was set for a light color, reset it now
1022 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1023 {
1024 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1025 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1026 }
1027
1028 if (VIM_ISDIGIT(*arg))
1029 color = atoi((char *)arg);
1030 else if (STRICMP(arg, "fg") == 0)
1031 {
1032 if (cterm_normal_fg_color)
1033 color = cterm_normal_fg_color - 1;
1034 else
1035 {
1036 emsg(_("E419: FG color unknown"));
1037 return FALSE;
1038 }
1039 }
1040 else if (STRICMP(arg, "bg") == 0)
1041 {
1042 if (cterm_normal_bg_color > 0)
1043 color = cterm_normal_bg_color - 1;
1044 else
1045 {
1046 emsg(_("E420: BG color unknown"));
1047 return FALSE;
1048 }
1049 }
1050 else if (STRICMP(arg, "ul") == 0)
1051 {
1052 if (cterm_normal_ul_color > 0)
1053 color = cterm_normal_ul_color - 1;
1054 else
1055 {
1056 emsg(_("E453: UL color unknown"));
1057 return FALSE;
1058 }
1059 }
1060 else
1061 {
1062 int bold = MAYBE;
1063
1064 // reduce calls to STRICMP a bit, it can be slow
1065 off = TOUPPER_ASC(*arg);
1066 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1067 if (off == color_names[i][0]
1068 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1069 break;
1070 if (i < 0)
1071 {
1072 semsg(_("E421: Color name or number not recognized: %s"),
1073 key_start);
1074 return FALSE;
1075 }
1076
1077 color = lookup_color(i, key[5] == 'F', &bold);
1078
1079 // set/reset bold attribute to get light foreground
1080 // colors (on some terminals, e.g. "linux")
1081 if (bold == TRUE)
1082 {
1083 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1084 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1085 }
1086 else if (bold == FALSE)
1087 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1088 }
1089
1090 // Add one to the argument, to avoid zero. Zero is used for
1091 // "NONE", then "color" is -1.
1092 if (key[5] == 'F')
1093 highlight_set_ctermfg(idx, color, is_normal_group);
1094 else if (key[5] == 'B')
1095 highlight_set_ctermbg(idx, color, is_normal_group);
1096 else // ctermul
1097 highlight_set_ctermul(idx, color, is_normal_group);
1098 }
1099
1100 return TRUE;
1101}
1102
1103#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1104/*
1105 * Set the GUI foreground color for the highlight group at 'idx'.
1106 * Returns TRUE if the color is set.
1107 */
1108 static int
1109highlight_set_guifg(
1110 int idx,
1111 char_u *arg,
1112 int is_menu_group UNUSED,
1113 int is_scrollbar_group UNUSED,
1114 int is_tooltip_group UNUSED,
1115 int *do_colors UNUSED,
1116 int init)
1117{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001118# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001119 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001120# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001121 char_u **namep;
1122 int did_change = FALSE;
1123
1124 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1125 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1126 {
1127 if (!init)
1128 HL_TABLE()[idx].sg_set |= SG_GUI;
1129
1130# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1131 // In GUI guifg colors are only used when recognized
1132 i = color_name2handle(arg);
1133 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1134 {
1135 HL_TABLE()[idx].sg_gui_fg = i;
1136# endif
1137 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1138 {
1139 vim_free(*namep);
1140 if (STRCMP(arg, "NONE") != 0)
1141 *namep = vim_strsave(arg);
1142 else
1143 *namep = NULL;
1144 did_change = TRUE;
1145 }
1146# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1147# ifdef FEAT_GUI_X11
1148 if (is_menu_group && gui.menu_fg_pixel != i)
1149 {
1150 gui.menu_fg_pixel = i;
1151 *do_colors = TRUE;
1152 }
1153 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1154 {
1155 gui.scroll_fg_pixel = i;
1156 *do_colors = TRUE;
1157 }
1158# ifdef FEAT_BEVAL_GUI
1159 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1160 {
1161 gui.tooltip_fg_pixel = i;
1162 *do_colors = TRUE;
1163 }
1164# endif
1165# endif
1166 }
1167# endif
1168 }
1169
1170 return did_change;
1171}
1172
1173/*
1174 * Set the GUI background color for the highlight group at 'idx'.
1175 * Returns TRUE if the color is set.
1176 */
1177 static int
1178highlight_set_guibg(
1179 int idx,
1180 char_u *arg,
1181 int is_menu_group UNUSED,
1182 int is_scrollbar_group UNUSED,
1183 int is_tooltip_group UNUSED,
1184 int *do_colors UNUSED,
1185 int init)
1186{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001187# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001188 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001189# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001190 char_u **namep;
1191 int did_change = FALSE;
1192
1193 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1194 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1195 {
1196 if (!init)
1197 HL_TABLE()[idx].sg_set |= SG_GUI;
1198
1199# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Drew Vogele30d1022021-10-24 20:35:07 +01001200 // In GUI guibg colors are only used when recognized
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001201 i = color_name2handle(arg);
1202 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1203 {
1204 HL_TABLE()[idx].sg_gui_bg = i;
1205# endif
1206 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1207 {
1208 vim_free(*namep);
1209 if (STRCMP(arg, "NONE") != 0)
1210 *namep = vim_strsave(arg);
1211 else
1212 *namep = NULL;
1213 did_change = TRUE;
1214 }
1215# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1216# ifdef FEAT_GUI_X11
1217 if (is_menu_group && gui.menu_bg_pixel != i)
1218 {
1219 gui.menu_bg_pixel = i;
1220 *do_colors = TRUE;
1221 }
1222 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1223 {
1224 gui.scroll_bg_pixel = i;
1225 *do_colors = TRUE;
1226 }
1227# ifdef FEAT_BEVAL_GUI
1228 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1229 {
1230 gui.tooltip_bg_pixel = i;
1231 *do_colors = TRUE;
1232 }
1233# endif
1234# endif
1235 }
1236# endif
1237 }
1238
1239 return did_change;
1240}
1241
1242/*
1243 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1244 * Returns TRUE if the color is set.
1245 */
1246 static int
1247highlight_set_guisp(int idx, char_u *arg, int init)
1248{
1249# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1250 int i;
1251# endif
1252 int did_change = FALSE;
1253 char_u **namep;
1254
1255 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1256 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1257 {
1258 if (!init)
1259 HL_TABLE()[idx].sg_set |= SG_GUI;
1260
1261# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1262 // In GUI guisp colors are only used when recognized
1263 i = color_name2handle(arg);
1264 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1265 {
1266 HL_TABLE()[idx].sg_gui_sp = i;
1267# endif
1268 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1269 {
1270 vim_free(*namep);
1271 if (STRCMP(arg, "NONE") != 0)
1272 *namep = vim_strsave(arg);
1273 else
1274 *namep = NULL;
1275 did_change = TRUE;
1276 }
1277# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1278 }
1279# endif
1280 }
1281
1282 return did_change;
1283}
1284#endif
1285
1286/*
1287 * Set the start/stop terminal codes for a highlight group.
1288 * Returns TRUE if the terminal code is set.
1289 */
1290 static int
1291highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1292{
1293 int off;
1294 char_u buf[100];
1295 int len;
1296 char_u *tname;
1297 char_u *p;
1298
1299 if (!init)
1300 HL_TABLE()[idx].sg_set |= SG_TERM;
1301
1302 // The "start" and "stop" arguments can be a literal escape
1303 // sequence, or a comma separated list of terminal codes.
1304 if (STRNCMP(arg, "t_", 2) == 0)
1305 {
1306 off = 0;
1307 buf[0] = 0;
1308 while (arg[off] != NUL)
1309 {
1310 // Isolate one termcap name
1311 for (len = 0; arg[off + len] &&
1312 arg[off + len] != ','; ++len)
1313 ;
1314 tname = vim_strnsave(arg + off, len);
1315 if (tname == NULL) // out of memory
1316 return FALSE;
1317 // lookup the escape sequence for the item
1318 p = get_term_code(tname);
1319 vim_free(tname);
1320 if (p == NULL) // ignore non-existing things
1321 p = (char_u *)"";
1322
1323 // Append it to the already found stuff
1324 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1325 {
1326 semsg(_("E422: terminal code too long: %s"), arg);
1327 return FALSE;
1328 }
1329 STRCAT(buf, p);
1330
1331 // Advance to the next item
1332 off += len;
1333 if (arg[off] == ',') // another one follows
1334 ++off;
1335 }
1336 }
1337 else
1338 {
1339 // Copy characters from arg[] to buf[], translating <> codes.
1340 for (p = arg, off = 0; off < 100 - 6 && *p; )
1341 {
1342 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
1343 if (len > 0) // recognized special char
1344 off += len;
1345 else // copy as normal char
1346 buf[off++] = *p++;
1347 }
1348 buf[off] = NUL;
1349 }
1350
1351 if (STRCMP(buf, "NONE") == 0) // resetting the value
1352 p = NULL;
1353 else
1354 p = vim_strsave(buf);
1355 if (key[2] == 'A')
1356 {
1357 vim_free(HL_TABLE()[idx].sg_start);
1358 HL_TABLE()[idx].sg_start = p;
1359 }
1360 else
1361 {
1362 vim_free(HL_TABLE()[idx].sg_stop);
1363 HL_TABLE()[idx].sg_stop = p;
1364 }
1365 return TRUE;
1366}
1367
1368/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001369 * Handle the ":highlight .." command.
1370 * When using ":hi clear" this is called recursively for each group with
1371 * "forceit" and "init" both TRUE.
1372 */
1373 void
1374do_highlight(
1375 char_u *line,
1376 int forceit,
1377 int init) // TRUE when called for initializing
1378{
1379 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001380 char_u *linep;
1381 char_u *key_start;
1382 char_u *arg_start;
1383 char_u *key = NULL, *arg = NULL;
1384 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001385 int id;
1386 int idx;
1387 hl_group_T item_before;
1388 int did_change = FALSE;
1389 int dodefault = FALSE;
1390 int doclear = FALSE;
1391 int dolink = FALSE;
1392 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001393 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001394#ifdef FEAT_GUI_X11
1395 int is_menu_group = FALSE; // "Menu" group
1396 int is_scrollbar_group = FALSE; // "Scrollbar" group
1397 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001398#else
1399# define is_menu_group 0
1400# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001401# define is_scrollbar_group 0
1402#endif
1403#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1404 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001405#endif
1406#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1407 int did_highlight_changed = FALSE;
1408#endif
1409
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001410 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001411 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001412 {
1413 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1414 // TODO: only call when the group has attributes set
1415 highlight_list_one((int)i);
1416 return;
1417 }
1418
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001419 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001420 name_end = skiptowhite(line);
1421 linep = skipwhite(name_end);
1422
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001423 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001424 if (STRNCMP(line, "default", name_end - line) == 0)
1425 {
1426 dodefault = TRUE;
1427 line = linep;
1428 name_end = skiptowhite(line);
1429 linep = skipwhite(name_end);
1430 }
1431
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001432 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001433 if (STRNCMP(line, "clear", name_end - line) == 0)
1434 doclear = TRUE;
1435 if (STRNCMP(line, "link", name_end - line) == 0)
1436 dolink = TRUE;
1437
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001438 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001439 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001440 {
1441 id = syn_namen2id(line, (int)(name_end - line));
1442 if (id == 0)
1443 semsg(_("E411: highlight group not found: %s"), line);
1444 else
1445 highlight_list_one(id);
1446 return;
1447 }
1448
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001449 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001450 if (dolink)
1451 {
1452 char_u *from_start = linep;
1453 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001454 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001455 char_u *to_start;
1456 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001457 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001458
1459 from_end = skiptowhite(from_start);
1460 to_start = skipwhite(from_end);
1461 to_end = skiptowhite(to_start);
1462
Bram Moolenaar1966c242020-04-20 22:42:32 +02001463 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001464 {
1465 semsg(_("E412: Not enough arguments: \":highlight link %s\""),
1466 from_start);
1467 return;
1468 }
1469
Bram Moolenaar1966c242020-04-20 22:42:32 +02001470 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001471 {
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001472 semsg(_("E413: Too many arguments: \":highlight link %s\""),
1473 from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001474 return;
1475 }
1476
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001477 from_len = (int)(from_end - from_start);
1478 to_len = (int)(to_end - to_start);
1479 highlight_group_link(from_start, from_len, to_start, to_len,
1480 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001481 return;
1482 }
1483
1484 if (doclear)
1485 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001486 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001487 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001488 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001489 // ":highlight clear" without group name
1490 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001491 return;
1492 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001493 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001494 name_end = skiptowhite(line);
1495 linep = skipwhite(name_end);
1496 }
1497
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001498 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001499 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001500 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001501 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001502 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001503
1504 // Return if "default" was used and the group already has settings.
1505 if (dodefault && hl_has_settings(idx, TRUE))
1506 return;
1507
1508 // Make a copy so we can check if any attribute actually changed.
1509 item_before = HL_TABLE()[idx];
1510
1511 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1512 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001513#ifdef FEAT_GUI_X11
1514 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1515 is_menu_group = TRUE;
1516 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1517 is_scrollbar_group = TRUE;
1518 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1519 is_tooltip_group = TRUE;
1520#endif
1521
1522 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1523 if (doclear || (forceit && init))
1524 {
1525 highlight_clear(idx);
1526 if (!doclear)
1527 HL_TABLE()[idx].sg_set = 0;
1528 }
1529
1530 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001531 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001532 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001533 key_start = linep;
1534 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001535 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001536 semsg(_("E415: unexpected equal sign: %s"), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001537 error = TRUE;
1538 break;
1539 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001540
1541 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1542 // or "guibg").
1543 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1544 ++linep;
1545 vim_free(key);
1546 key = vim_strnsave_up(key_start, linep - key_start);
1547 if (key == NULL)
1548 {
1549 error = TRUE;
1550 break;
1551 }
1552 linep = skipwhite(linep);
1553
1554 if (STRCMP(key, "NONE") == 0)
1555 {
1556 if (!init || HL_TABLE()[idx].sg_set == 0)
1557 {
1558 if (!init)
1559 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1560 highlight_clear(idx);
1561 }
1562 continue;
1563 }
1564
1565 // Check for the equal sign.
1566 if (*linep != '=')
1567 {
1568 semsg(_("E416: missing equal sign: %s"), key_start);
1569 error = TRUE;
1570 break;
1571 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001572 ++linep;
1573
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001574 // Isolate the argument.
1575 linep = skipwhite(linep);
1576 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001577 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001578 arg_start = ++linep;
1579 linep = vim_strchr(linep, '\'');
1580 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001581 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001582 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001583 error = TRUE;
1584 break;
1585 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001586 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001587 else
1588 {
1589 arg_start = linep;
1590 linep = skiptowhite(linep);
1591 }
1592 if (linep == arg_start)
1593 {
1594 semsg(_("E417: missing argument: %s"), key_start);
1595 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001596 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001597 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001598 vim_free(arg);
1599 arg = vim_strnsave(arg_start, linep - arg_start);
1600 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001601 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001602 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001603 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001604 }
1605 if (*linep == '\'')
1606 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001607
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001608 // Store the argument.
1609 if (STRCMP(key, "TERM") == 0
1610 || STRCMP(key, "CTERM") == 0
1611 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001612 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001613 if (!highlight_set_termgui_attr(idx, key, arg, init))
1614 {
1615 error = TRUE;
1616 break;
1617 }
1618 }
1619 else if (STRCMP(key, "FONT") == 0)
1620 {
1621 // in non-GUI fonts are simply ignored
1622#ifdef FEAT_GUI
1623 if (highlight_set_font(idx, arg, is_normal_group,
1624 is_menu_group, is_tooltip_group))
1625 did_change = TRUE;
1626#endif
1627 }
1628 else if (STRCMP(key, "CTERMFG") == 0
1629 || STRCMP(key, "CTERMBG") == 0
1630 || STRCMP(key, "CTERMUL") == 0)
1631 {
1632 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1633 is_normal_group, init))
1634 {
1635 error = TRUE;
1636 break;
1637 }
1638 }
1639 else if (STRCMP(key, "GUIFG") == 0)
1640 {
1641#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1642 if (highlight_set_guifg(idx, arg, is_menu_group,
1643 is_scrollbar_group, is_tooltip_group,
1644 &do_colors, init))
1645 did_change = TRUE;
1646#endif
1647 }
1648 else if (STRCMP(key, "GUIBG") == 0)
1649 {
1650#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1651 if (highlight_set_guibg(idx, arg, is_menu_group,
1652 is_scrollbar_group, is_tooltip_group,
1653 &do_colors, init))
1654 did_change = TRUE;
1655#endif
1656 }
1657 else if (STRCMP(key, "GUISP") == 0)
1658 {
1659#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1660 if (highlight_set_guisp(idx, arg, init))
1661 did_change = TRUE;
1662#endif
1663 }
1664 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1665 {
1666 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1667 {
1668 error = TRUE;
1669 break;
1670 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001671 }
1672 else
1673 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001674 semsg(_("E423: Illegal argument: %s"), key_start);
1675 error = TRUE;
1676 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001677 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001678 HL_TABLE()[idx].sg_cleared = FALSE;
1679
1680 // When highlighting has been given for a group, don't link it.
1681 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1682 HL_TABLE()[idx].sg_link = 0;
1683
1684 // Continue with next argument.
1685 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001686 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001687
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001688 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001689 if (error && idx == highlight_ga.ga_len)
1690 syn_unadd_group();
1691 else
1692 {
1693 if (is_normal_group)
1694 {
1695 HL_TABLE()[idx].sg_term_attr = 0;
1696 HL_TABLE()[idx].sg_cterm_attr = 0;
1697#ifdef FEAT_GUI
1698 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001699 // Need to update all groups, because they might be using "bg"
1700 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001701#endif
1702#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1703 if (USE_24BIT)
1704 {
1705 highlight_gui_started();
1706 did_highlight_changed = TRUE;
1707 redraw_all_later(NOT_VALID);
1708 }
1709#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001710#ifdef FEAT_VTP
1711 control_console_color_rgb();
1712#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001713 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001714#ifdef FEAT_GUI_X11
1715# ifdef FEAT_MENU
1716 else if (is_menu_group)
1717 {
1718 if (gui.in_use && do_colors)
1719 gui_mch_new_menu_colors();
1720 }
1721# endif
1722 else if (is_scrollbar_group)
1723 {
1724 if (gui.in_use && do_colors)
1725 gui_new_scrollbar_colors();
1726 else
1727 set_hl_attr(idx);
1728 }
1729# ifdef FEAT_BEVAL_GUI
1730 else if (is_tooltip_group)
1731 {
1732 if (gui.in_use && do_colors)
1733 gui_mch_new_tooltip_colors();
1734 }
1735# endif
1736#endif
1737 else
1738 set_hl_attr(idx);
1739#ifdef FEAT_EVAL
1740 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001741 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001742#endif
1743 }
1744
1745 vim_free(key);
1746 vim_free(arg);
1747
1748 // Only call highlight_changed() once, after a sequence of highlight
1749 // commands, and only if an attribute actually changed.
1750 if ((did_change
1751 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1752#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1753 && !did_highlight_changed
1754#endif
1755 )
1756 {
1757 // Do not trigger a redraw when highlighting is changed while
1758 // redrawing. This may happen when evaluating 'statusline' changes the
1759 // StatusLine group.
1760 if (!updating_screen)
1761 redraw_all_later(NOT_VALID);
1762 need_highlight_changed = TRUE;
1763 }
1764}
1765
1766#if defined(EXITFREE) || defined(PROTO)
1767 void
1768free_highlight(void)
1769{
1770 int i;
1771
1772 for (i = 0; i < highlight_ga.ga_len; ++i)
1773 {
1774 highlight_clear(i);
1775 vim_free(HL_TABLE()[i].sg_name);
1776 vim_free(HL_TABLE()[i].sg_name_u);
1777 }
1778 ga_clear(&highlight_ga);
1779}
1780#endif
1781
1782/*
1783 * Reset the cterm colors to what they were before Vim was started, if
1784 * possible. Otherwise reset them to zero.
1785 */
1786 void
1787restore_cterm_colors(void)
1788{
1789#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1790 // Since t_me has been set, this probably means that the user
1791 // wants to use this as default colors. Need to reset default
1792 // background/foreground colors.
1793 mch_set_normal_colors();
1794#else
1795# ifdef VIMDLL
1796 if (!gui.in_use)
1797 {
1798 mch_set_normal_colors();
1799 return;
1800 }
1801# endif
1802 cterm_normal_fg_color = 0;
1803 cterm_normal_fg_bold = 0;
1804 cterm_normal_bg_color = 0;
1805# ifdef FEAT_TERMGUICOLORS
1806 cterm_normal_fg_gui_color = INVALCOLOR;
1807 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001808 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001809# endif
1810#endif
1811}
1812
1813/*
1814 * Return TRUE if highlight group "idx" has any settings.
1815 * When "check_link" is TRUE also check for an existing link.
1816 */
1817 static int
1818hl_has_settings(int idx, int check_link)
1819{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001820 return HL_TABLE()[idx].sg_cleared == 0
1821 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001822 || HL_TABLE()[idx].sg_cterm_attr != 0
1823 || HL_TABLE()[idx].sg_cterm_fg != 0
1824 || HL_TABLE()[idx].sg_cterm_bg != 0
1825#ifdef FEAT_GUI
1826 || HL_TABLE()[idx].sg_gui_attr != 0
1827 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1828 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1829 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1830 || HL_TABLE()[idx].sg_font_name != NULL
1831#endif
1832 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1833}
1834
1835/*
1836 * Clear highlighting for one group.
1837 */
1838 static void
1839highlight_clear(int idx)
1840{
1841 HL_TABLE()[idx].sg_cleared = TRUE;
1842
1843 HL_TABLE()[idx].sg_term = 0;
1844 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1845 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1846 HL_TABLE()[idx].sg_term_attr = 0;
1847 HL_TABLE()[idx].sg_cterm = 0;
1848 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1849 HL_TABLE()[idx].sg_cterm_fg = 0;
1850 HL_TABLE()[idx].sg_cterm_bg = 0;
1851 HL_TABLE()[idx].sg_cterm_attr = 0;
1852#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1853 HL_TABLE()[idx].sg_gui = 0;
1854 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1855 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1856 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1857#endif
1858#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1859 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1860 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001861 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001862#endif
1863#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001864 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1865 HL_TABLE()[idx].sg_font = NOFONT;
1866# ifdef FEAT_XFONTSET
1867 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1868 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1869# endif
1870 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1871 HL_TABLE()[idx].sg_gui_attr = 0;
1872#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001873 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001874 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001875#ifdef FEAT_EVAL
1876 // Since we set the default link, set the location to where the default
1877 // link was set.
1878 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001879#endif
1880}
1881
1882#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1883/*
1884 * Set the normal foreground and background colors according to the "Normal"
1885 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1886 * "Tooltip" colors.
1887 */
1888 void
1889set_normal_colors(void)
1890{
1891# ifdef FEAT_GUI
1892# ifdef FEAT_TERMGUICOLORS
1893 if (gui.in_use)
1894# endif
1895 {
1896 if (set_group_colors((char_u *)"Normal",
1897 &gui.norm_pixel, &gui.back_pixel,
1898 FALSE, TRUE, FALSE))
1899 {
1900 gui_mch_new_colors();
1901 must_redraw = CLEAR;
1902 }
1903# ifdef FEAT_GUI_X11
1904 if (set_group_colors((char_u *)"Menu",
1905 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1906 TRUE, FALSE, FALSE))
1907 {
1908# ifdef FEAT_MENU
1909 gui_mch_new_menu_colors();
1910# endif
1911 must_redraw = CLEAR;
1912 }
1913# ifdef FEAT_BEVAL_GUI
1914 if (set_group_colors((char_u *)"Tooltip",
1915 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1916 FALSE, FALSE, TRUE))
1917 {
1918# ifdef FEAT_TOOLBAR
1919 gui_mch_new_tooltip_colors();
1920# endif
1921 must_redraw = CLEAR;
1922 }
1923# endif
1924 if (set_group_colors((char_u *)"Scrollbar",
1925 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1926 FALSE, FALSE, FALSE))
1927 {
1928 gui_new_scrollbar_colors();
1929 must_redraw = CLEAR;
1930 }
1931# endif
1932 }
1933# endif
1934# ifdef FEAT_TERMGUICOLORS
1935# ifdef FEAT_GUI
1936 else
1937# endif
1938 {
1939 int idx;
1940
1941 idx = syn_name2id((char_u *)"Normal") - 1;
1942 if (idx >= 0)
1943 {
1944 gui_do_one_color(idx, FALSE, FALSE);
1945
1946 // If the normal fg or bg color changed a complete redraw is
1947 // required.
1948 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1949 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1950 {
1951 // if the GUI color is INVALCOLOR then we use the default cterm
1952 // color
1953 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1954 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1955 must_redraw = CLEAR;
1956 }
1957 }
1958 }
1959# endif
1960}
1961#endif
1962
1963#if defined(FEAT_GUI) || defined(PROTO)
1964/*
1965 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1966 */
1967 static int
1968set_group_colors(
1969 char_u *name,
1970 guicolor_T *fgp,
1971 guicolor_T *bgp,
1972 int do_menu,
1973 int use_norm,
1974 int do_tooltip)
1975{
1976 int idx;
1977
1978 idx = syn_name2id(name) - 1;
1979 if (idx >= 0)
1980 {
1981 gui_do_one_color(idx, do_menu, do_tooltip);
1982
1983 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1984 *fgp = HL_TABLE()[idx].sg_gui_fg;
1985 else if (use_norm)
1986 *fgp = gui.def_norm_pixel;
1987 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1988 *bgp = HL_TABLE()[idx].sg_gui_bg;
1989 else if (use_norm)
1990 *bgp = gui.def_back_pixel;
1991 return TRUE;
1992 }
1993 return FALSE;
1994}
1995
1996/*
1997 * Get the font of the "Normal" group.
1998 * Returns "" when it's not found or not set.
1999 */
2000 char_u *
2001hl_get_font_name(void)
2002{
2003 int id;
2004 char_u *s;
2005
2006 id = syn_name2id((char_u *)"Normal");
2007 if (id > 0)
2008 {
2009 s = HL_TABLE()[id - 1].sg_font_name;
2010 if (s != NULL)
2011 return s;
2012 }
2013 return (char_u *)"";
2014}
2015
2016/*
2017 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2018 * actually chosen to be used.
2019 */
2020 void
2021hl_set_font_name(char_u *font_name)
2022{
2023 int id;
2024
2025 id = syn_name2id((char_u *)"Normal");
2026 if (id > 0)
2027 {
2028 vim_free(HL_TABLE()[id - 1].sg_font_name);
2029 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2030 }
2031}
2032
2033/*
2034 * Set background color for "Normal" group. Called by gui_set_bg_color()
2035 * when the color is known.
2036 */
2037 void
2038hl_set_bg_color_name(
2039 char_u *name) // must have been allocated
2040{
2041 int id;
2042
2043 if (name != NULL)
2044 {
2045 id = syn_name2id((char_u *)"Normal");
2046 if (id > 0)
2047 {
2048 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2049 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2050 }
2051 }
2052}
2053
2054/*
2055 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2056 * when the color is known.
2057 */
2058 void
2059hl_set_fg_color_name(
2060 char_u *name) // must have been allocated
2061{
2062 int id;
2063
2064 if (name != NULL)
2065 {
2066 id = syn_name2id((char_u *)"Normal");
2067 if (id > 0)
2068 {
2069 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2070 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2071 }
2072 }
2073}
2074
2075/*
2076 * Return the handle for a font name.
2077 * Returns NOFONT when failed.
2078 */
2079 static GuiFont
2080font_name2handle(char_u *name)
2081{
2082 if (STRCMP(name, "NONE") == 0)
2083 return NOFONT;
2084
2085 return gui_mch_get_font(name, TRUE);
2086}
2087
2088# ifdef FEAT_XFONTSET
2089/*
2090 * Return the handle for a fontset name.
2091 * Returns NOFONTSET when failed.
2092 */
2093 static GuiFontset
2094fontset_name2handle(char_u *name, int fixed_width)
2095{
2096 if (STRCMP(name, "NONE") == 0)
2097 return NOFONTSET;
2098
2099 return gui_mch_get_fontset(name, TRUE, fixed_width);
2100}
2101# endif
2102
2103/*
2104 * Get the font or fontset for one highlight group.
2105 */
2106 static void
2107hl_do_font(
2108 int idx,
2109 char_u *arg,
2110 int do_normal, // set normal font
2111 int do_menu UNUSED, // set menu font
2112 int do_tooltip UNUSED, // set tooltip font
2113 int free_font) // free current font/fontset
2114{
2115# ifdef FEAT_XFONTSET
2116 // If 'guifontset' is not empty, first try using the name as a
2117 // fontset. If that doesn't work, use it as a font name.
2118 if (*p_guifontset != NUL
2119# ifdef FONTSET_ALWAYS
2120 || do_menu
2121# endif
2122# ifdef FEAT_BEVAL_TIP
2123 // In Athena & Motif, the Tooltip highlight group is always a fontset
2124 || do_tooltip
2125# endif
2126 )
2127 {
2128 if (free_font)
2129 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2130 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2131# ifdef FONTSET_ALWAYS
2132 || do_menu
2133# endif
2134# ifdef FEAT_BEVAL_TIP
2135 || do_tooltip
2136# endif
2137 );
2138 }
2139 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2140 {
2141 // If it worked and it's the Normal group, use it as the normal
2142 // fontset. Same for the Menu group.
2143 if (do_normal)
2144 gui_init_font(arg, TRUE);
2145# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2146 if (do_menu)
2147 {
2148# ifdef FONTSET_ALWAYS
2149 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2150# else
2151 // YIKES! This is a bug waiting to crash the program
2152 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2153# endif
2154 gui_mch_new_menu_font();
2155 }
2156# ifdef FEAT_BEVAL_GUI
2157 if (do_tooltip)
2158 {
2159 // The Athena widget set cannot currently handle switching between
2160 // displaying a single font and a fontset.
2161 // If the XtNinternational resource is set to True at widget
2162 // creation, then a fontset is always used, otherwise an
2163 // XFontStruct is used.
2164 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2165 gui_mch_new_tooltip_font();
2166 }
2167# endif
2168# endif
2169 }
2170 else
2171# endif
2172 {
2173 if (free_font)
2174 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2175 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2176 // If it worked and it's the Normal group, use it as the
2177 // normal font. Same for the Menu group.
2178 if (HL_TABLE()[idx].sg_font != NOFONT)
2179 {
2180 if (do_normal)
2181 gui_init_font(arg, FALSE);
2182#ifndef FONTSET_ALWAYS
2183# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2184 if (do_menu)
2185 {
2186 gui.menu_font = HL_TABLE()[idx].sg_font;
2187 gui_mch_new_menu_font();
2188 }
2189# endif
2190#endif
2191 }
2192 }
2193}
2194
2195#endif // FEAT_GUI
2196
2197#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2198/*
2199 * Return the handle for a color name.
2200 * Returns INVALCOLOR when failed.
2201 */
2202 guicolor_T
2203color_name2handle(char_u *name)
2204{
2205 if (STRCMP(name, "NONE") == 0)
2206 return INVALCOLOR;
2207
2208 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2209 {
2210#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2211 if (gui.in_use)
2212#endif
2213#ifdef FEAT_GUI
2214 return gui.norm_pixel;
2215#endif
2216#ifdef FEAT_TERMGUICOLORS
2217 if (cterm_normal_fg_gui_color != INVALCOLOR)
2218 return cterm_normal_fg_gui_color;
2219 // Guess that the foreground is black or white.
2220 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2221#endif
2222 }
2223 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2224 {
2225#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2226 if (gui.in_use)
2227#endif
2228#ifdef FEAT_GUI
2229 return gui.back_pixel;
2230#endif
2231#ifdef FEAT_TERMGUICOLORS
2232 if (cterm_normal_bg_gui_color != INVALCOLOR)
2233 return cterm_normal_bg_gui_color;
2234 // Guess that the background is white or black.
2235 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2236#endif
2237 }
2238
2239 return GUI_GET_COLOR(name);
2240}
Drew Vogele30d1022021-10-24 20:35:07 +01002241
2242// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2243// values as used by the MS-Windows GDI api. It should be used only for
2244// MS-Windows GDI builds.
2245# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2246# undef RGB
2247# endif
2248# ifndef RGB
2249# define RGB(r, g, b) ((r<<16) | (g<<8) | (b))
2250# endif
2251
2252# ifdef VIMDLL
2253 static guicolor_T
2254gui_adjust_rgb(guicolor_T c)
2255{
2256 if (gui.in_use)
2257 return c;
2258 else
2259 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2260}
2261# else
2262# define gui_adjust_rgb(c) (c)
2263# endif
2264
2265 static int
2266hex_digit(int c)
2267{
2268 if (isdigit(c))
2269 return c - '0';
2270 c = TOLOWER_ASC(c);
2271 if (c >= 'a' && c <= 'f')
2272 return c - 'a' + 10;
2273 return 0x1ffffff;
2274}
2275
2276 guicolor_T
2277decode_hex_color(char_u *hex)
2278{
2279 guicolor_T color;
2280
2281 if (hex[0] != '#' || STRLEN(hex) != 7)
2282 return INVALCOLOR;
2283
2284 // Name is in "#rrggbb" format
2285 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2286 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2287 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2288 if (color > 0xffffff)
2289 return INVALCOLOR;
2290 return gui_adjust_rgb(color);
2291}
2292
Bram Moolenaar2a521962021-10-25 10:30:14 +01002293#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002294// Returns the color currently mapped to the given name or INVALCOLOR if no
2295// such name exists in the color table. The convention is to use lowercase for
2296// all keys in the v:colornames dictionary. The value can be either a string in
2297// the form #rrggbb or a number, either of which is converted to a guicolor_T.
2298 guicolor_T
2299colorname2rgb(char_u *name)
2300{
2301 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2302 char_u *lc_name;
2303 dictitem_T *colentry;
2304 char_u *colstr;
2305 varnumber_T colnum;
2306
2307 lc_name = strlow_save(name);
2308 if (lc_name == NULL)
2309 return INVALCOLOR;
2310
2311 colentry = dict_find(colornames_table, lc_name, -1);
2312 vim_free(lc_name);
2313 if (colentry == NULL)
2314 return INVALCOLOR;
2315
2316 if (colentry->di_tv.v_type == VAR_STRING)
2317 {
2318 colstr = tv_get_string_strict(&colentry->di_tv);
2319 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2320 {
2321 return decode_hex_color(colstr);
2322 }
2323 else
2324 {
2325 semsg(_(e_bad_color_string_str), colstr);
2326 return INVALCOLOR;
2327 }
2328 }
2329
2330 if (colentry->di_tv.v_type == VAR_NUMBER)
2331 {
2332 colnum = tv_get_number(&colentry->di_tv);
2333 return (guicolor_T)colnum;
2334 }
2335
2336 return INVALCOLOR;
2337}
2338
Drew Vogele30d1022021-10-24 20:35:07 +01002339/*
2340 * Load a default color list. Intended to support legacy color names but allows
2341 * the user to override the color values. Only loaded once.
2342 */
2343 void
2344load_default_colors_lists()
2345{
2346 // Lacking a default color list isn't the end of the world but it is likely
2347 // an inconvenience so users should know when it is missing.
2348 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
2349 msg("failed to load colors/lists/default.vim");
2350}
2351#endif
2352
2353 guicolor_T
2354gui_get_color_cmn(char_u *name)
2355{
2356 int i;
2357 guicolor_T color;
2358
2359 struct rgbcolor_table_S {
2360 char_u *color_name;
2361 guicolor_T color;
2362 };
2363
2364 // Only non X11 colors (not present in rgb.txt) and colors in
2365 // color_names[], useful when $VIMRUNTIME is not found,.
2366 static struct rgbcolor_table_S rgb_table[] = {
2367 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2368 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2369 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2370 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2371 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2372 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2373 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2374 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2375 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2376 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2377 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2378 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2379 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2380 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2381 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2382 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2383 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2384 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2385 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2386 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2387 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2388 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2389 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2390 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2391 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2392 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2393 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2394 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2395 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2396 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2397 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2398 };
2399
2400 color = decode_hex_color(name);
2401 if (color != INVALCOLOR)
2402 return color;
2403
2404 // Check if the name is one of the colors we know
2405 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2406 if (STRICMP(name, rgb_table[i].color_name) == 0)
2407 return gui_adjust_rgb(rgb_table[i].color);
2408
2409#if defined(FEAT_EVAL)
2410 /*
2411 * Not a traditional color. Load additional color aliases and then consult the alias table.
2412 */
2413
2414 color = colorname2rgb(name);
2415 if (color == INVALCOLOR)
2416 {
2417 load_default_colors_lists();
2418 color = colorname2rgb(name);
2419 }
2420
2421 return color;
2422#else
2423 return INVALCOLOR;
2424#endif
2425}
2426
2427 guicolor_T
2428gui_get_rgb_color_cmn(int r, int g, int b)
2429{
2430 guicolor_T color = RGB(r, g, b);
2431
2432 if (color > 0xffffff)
2433 return INVALCOLOR;
2434 return gui_adjust_rgb(color);
2435}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002436#endif
2437
2438/*
2439 * Table with the specifications for an attribute number.
2440 * Note that this table is used by ALL buffers. This is required because the
2441 * GUI can redraw at any time for any buffer.
2442 */
2443static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2444
2445#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2446
2447static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2448
2449#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2450
2451#ifdef FEAT_GUI
2452static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2453
2454#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2455#endif
2456
2457/*
2458 * Return the attr number for a set of colors and font.
2459 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2460 * if the combination is new.
2461 * Return 0 for error (no more room).
2462 */
2463 static int
2464get_attr_entry(garray_T *table, attrentry_T *aep)
2465{
2466 int i;
2467 attrentry_T *taep;
2468 static int recursive = FALSE;
2469
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002470 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002471 table->ga_itemsize = sizeof(attrentry_T);
2472 table->ga_growsize = 7;
2473
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002474 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002475 for (i = 0; i < table->ga_len; ++i)
2476 {
2477 taep = &(((attrentry_T *)table->ga_data)[i]);
2478 if ( aep->ae_attr == taep->ae_attr
2479 && (
2480#ifdef FEAT_GUI
2481 (table == &gui_attr_table
2482 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2483 && aep->ae_u.gui.bg_color
2484 == taep->ae_u.gui.bg_color
2485 && aep->ae_u.gui.sp_color
2486 == taep->ae_u.gui.sp_color
2487 && aep->ae_u.gui.font == taep->ae_u.gui.font
2488# ifdef FEAT_XFONTSET
2489 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2490# endif
2491 ))
2492 ||
2493#endif
2494 (table == &term_attr_table
2495 && (aep->ae_u.term.start == NULL)
2496 == (taep->ae_u.term.start == NULL)
2497 && (aep->ae_u.term.start == NULL
2498 || STRCMP(aep->ae_u.term.start,
2499 taep->ae_u.term.start) == 0)
2500 && (aep->ae_u.term.stop == NULL)
2501 == (taep->ae_u.term.stop == NULL)
2502 && (aep->ae_u.term.stop == NULL
2503 || STRCMP(aep->ae_u.term.stop,
2504 taep->ae_u.term.stop) == 0))
2505 || (table == &cterm_attr_table
2506 && aep->ae_u.cterm.fg_color
2507 == taep->ae_u.cterm.fg_color
2508 && aep->ae_u.cterm.bg_color
2509 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002510 && aep->ae_u.cterm.ul_color
2511 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002512#ifdef FEAT_TERMGUICOLORS
2513 && aep->ae_u.cterm.fg_rgb
2514 == taep->ae_u.cterm.fg_rgb
2515 && aep->ae_u.cterm.bg_rgb
2516 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002517 && aep->ae_u.cterm.ul_rgb
2518 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002519#endif
2520 )))
2521
2522 return i + ATTR_OFF;
2523 }
2524
2525 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2526 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002527 // Running out of attribute entries! remove all attributes, and
2528 // compute new ones for all groups.
2529 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002530 if (recursive)
2531 {
2532 emsg(_("E424: Too many different highlighting attributes in use"));
2533 return 0;
2534 }
2535 recursive = TRUE;
2536
2537 clear_hl_tables();
2538
2539 must_redraw = CLEAR;
2540
2541 for (i = 0; i < highlight_ga.ga_len; ++i)
2542 set_hl_attr(i);
2543
2544 recursive = FALSE;
2545 }
2546
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002547 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002548 if (ga_grow(table, 1) == FAIL)
2549 return 0;
2550
2551 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002552 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002553 taep->ae_attr = aep->ae_attr;
2554#ifdef FEAT_GUI
2555 if (table == &gui_attr_table)
2556 {
2557 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2558 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2559 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2560 taep->ae_u.gui.font = aep->ae_u.gui.font;
2561# ifdef FEAT_XFONTSET
2562 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2563# endif
2564 }
2565#endif
2566 if (table == &term_attr_table)
2567 {
2568 if (aep->ae_u.term.start == NULL)
2569 taep->ae_u.term.start = NULL;
2570 else
2571 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2572 if (aep->ae_u.term.stop == NULL)
2573 taep->ae_u.term.stop = NULL;
2574 else
2575 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2576 }
2577 else if (table == &cterm_attr_table)
2578 {
2579 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2580 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002581 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002582#ifdef FEAT_TERMGUICOLORS
2583 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2584 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002585 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002586#endif
2587 }
2588 ++table->ga_len;
2589 return (table->ga_len - 1 + ATTR_OFF);
2590}
2591
2592#if defined(FEAT_TERMINAL) || defined(PROTO)
2593/*
2594 * Get an attribute index for a cterm entry.
2595 * Uses an existing entry when possible or adds one when needed.
2596 */
2597 int
2598get_cterm_attr_idx(int attr, int fg, int bg)
2599{
2600 attrentry_T at_en;
2601
Bram Moolenaara80faa82020-04-12 19:37:17 +02002602 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002603#ifdef FEAT_TERMGUICOLORS
2604 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2605 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002606 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002607#endif
2608 at_en.ae_attr = attr;
2609 at_en.ae_u.cterm.fg_color = fg;
2610 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002611 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002612 return get_attr_entry(&cterm_attr_table, &at_en);
2613}
2614#endif
2615
2616#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2617/*
2618 * Get an attribute index for a 'termguicolors' entry.
2619 * Uses an existing entry when possible or adds one when needed.
2620 */
2621 int
2622get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2623{
2624 attrentry_T at_en;
2625
Bram Moolenaara80faa82020-04-12 19:37:17 +02002626 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002627 at_en.ae_attr = attr;
2628 if (fg == INVALCOLOR && bg == INVALCOLOR)
2629 {
2630 // If both GUI colors are not set fall back to the cterm colors. Helps
2631 // if the GUI only has an attribute, such as undercurl.
2632 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2633 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2634 }
2635 else
2636 {
2637 at_en.ae_u.cterm.fg_rgb = fg;
2638 at_en.ae_u.cterm.bg_rgb = bg;
2639 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002640 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002641 return get_attr_entry(&cterm_attr_table, &at_en);
2642}
2643#endif
2644
2645#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2646/*
2647 * Get an attribute index for a cterm entry.
2648 * Uses an existing entry when possible or adds one when needed.
2649 */
2650 int
2651get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2652{
2653 attrentry_T at_en;
2654
Bram Moolenaara80faa82020-04-12 19:37:17 +02002655 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002656 at_en.ae_attr = attr;
2657 at_en.ae_u.gui.fg_color = fg;
2658 at_en.ae_u.gui.bg_color = bg;
2659 return get_attr_entry(&gui_attr_table, &at_en);
2660}
2661#endif
2662
2663/*
2664 * Clear all highlight tables.
2665 */
2666 void
2667clear_hl_tables(void)
2668{
2669 int i;
2670 attrentry_T *taep;
2671
2672#ifdef FEAT_GUI
2673 ga_clear(&gui_attr_table);
2674#endif
2675 for (i = 0; i < term_attr_table.ga_len; ++i)
2676 {
2677 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2678 vim_free(taep->ae_u.term.start);
2679 vim_free(taep->ae_u.term.stop);
2680 }
2681 ga_clear(&term_attr_table);
2682 ga_clear(&cterm_attr_table);
2683}
2684
2685/*
2686 * Combine special attributes (e.g., for spelling) with other attributes
2687 * (e.g., for syntax highlighting).
2688 * "prim_attr" overrules "char_attr".
2689 * This creates a new group when required.
2690 * Since we expect there to be few spelling mistakes we don't cache the
2691 * result.
2692 * Return the resulting attributes.
2693 */
2694 int
2695hl_combine_attr(int char_attr, int prim_attr)
2696{
2697 attrentry_T *char_aep = NULL;
2698 attrentry_T *spell_aep;
2699 attrentry_T new_en;
2700
2701 if (char_attr == 0)
2702 return prim_attr;
2703 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2704 return ATTR_COMBINE(char_attr, prim_attr);
2705#ifdef FEAT_GUI
2706 if (gui.in_use)
2707 {
2708 if (char_attr > HL_ALL)
2709 char_aep = syn_gui_attr2entry(char_attr);
2710 if (char_aep != NULL)
2711 new_en = *char_aep;
2712 else
2713 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002714 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002715 new_en.ae_u.gui.fg_color = INVALCOLOR;
2716 new_en.ae_u.gui.bg_color = INVALCOLOR;
2717 new_en.ae_u.gui.sp_color = INVALCOLOR;
2718 if (char_attr <= HL_ALL)
2719 new_en.ae_attr = char_attr;
2720 }
2721
2722 if (prim_attr <= HL_ALL)
2723 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2724 else
2725 {
2726 spell_aep = syn_gui_attr2entry(prim_attr);
2727 if (spell_aep != NULL)
2728 {
2729 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2730 spell_aep->ae_attr);
2731 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2732 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2733 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2734 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2735 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2736 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2737 if (spell_aep->ae_u.gui.font != NOFONT)
2738 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2739# ifdef FEAT_XFONTSET
2740 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2741 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2742# endif
2743 }
2744 }
2745 return get_attr_entry(&gui_attr_table, &new_en);
2746 }
2747#endif
2748
2749 if (IS_CTERM)
2750 {
2751 if (char_attr > HL_ALL)
2752 char_aep = syn_cterm_attr2entry(char_attr);
2753 if (char_aep != NULL)
2754 new_en = *char_aep;
2755 else
2756 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002757 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002758#ifdef FEAT_TERMGUICOLORS
2759 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2760 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002761 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002762#endif
2763 if (char_attr <= HL_ALL)
2764 new_en.ae_attr = char_attr;
2765 }
2766
2767 if (prim_attr <= HL_ALL)
2768 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2769 else
2770 {
2771 spell_aep = syn_cterm_attr2entry(prim_attr);
2772 if (spell_aep != NULL)
2773 {
2774 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2775 spell_aep->ae_attr);
2776 if (spell_aep->ae_u.cterm.fg_color > 0)
2777 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2778 if (spell_aep->ae_u.cterm.bg_color > 0)
2779 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002780 if (spell_aep->ae_u.cterm.ul_color > 0)
2781 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002782#ifdef FEAT_TERMGUICOLORS
2783 // If both fg and bg are not set fall back to cterm colors.
2784 // Helps for SpellBad which uses undercurl in the GUI.
2785 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2786 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2787 {
2788 if (spell_aep->ae_u.cterm.fg_color > 0)
2789 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2790 if (spell_aep->ae_u.cterm.bg_color > 0)
2791 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2792 }
2793 else
2794 {
2795 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2796 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2797 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2798 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2799 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002800 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2801 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002802#endif
2803 }
2804 }
2805 return get_attr_entry(&cterm_attr_table, &new_en);
2806 }
2807
2808 if (char_attr > HL_ALL)
2809 char_aep = syn_term_attr2entry(char_attr);
2810 if (char_aep != NULL)
2811 new_en = *char_aep;
2812 else
2813 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002814 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002815 if (char_attr <= HL_ALL)
2816 new_en.ae_attr = char_attr;
2817 }
2818
2819 if (prim_attr <= HL_ALL)
2820 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2821 else
2822 {
2823 spell_aep = syn_term_attr2entry(prim_attr);
2824 if (spell_aep != NULL)
2825 {
2826 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2827 if (spell_aep->ae_u.term.start != NULL)
2828 {
2829 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2830 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2831 }
2832 }
2833 }
2834 return get_attr_entry(&term_attr_table, &new_en);
2835}
2836
2837#ifdef FEAT_GUI
2838 attrentry_T *
2839syn_gui_attr2entry(int attr)
2840{
2841 attr -= ATTR_OFF;
2842 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2843 return NULL;
2844 return &(GUI_ATTR_ENTRY(attr));
2845}
2846#endif
2847
2848/*
2849 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2850 * Only to be used when "attr" > HL_ALL.
2851 */
2852 int
2853syn_attr2attr(int attr)
2854{
2855 attrentry_T *aep;
2856
2857#ifdef FEAT_GUI
2858 if (gui.in_use)
2859 aep = syn_gui_attr2entry(attr);
2860 else
2861#endif
2862 if (IS_CTERM)
2863 aep = syn_cterm_attr2entry(attr);
2864 else
2865 aep = syn_term_attr2entry(attr);
2866
2867 if (aep == NULL) // highlighting not set
2868 return 0;
2869 return aep->ae_attr;
2870}
2871
2872
2873 attrentry_T *
2874syn_term_attr2entry(int attr)
2875{
2876 attr -= ATTR_OFF;
2877 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2878 return NULL;
2879 return &(TERM_ATTR_ENTRY(attr));
2880}
2881
2882 attrentry_T *
2883syn_cterm_attr2entry(int attr)
2884{
2885 attr -= ATTR_OFF;
2886 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2887 return NULL;
2888 return &(CTERM_ATTR_ENTRY(attr));
2889}
2890
2891#define LIST_ATTR 1
2892#define LIST_STRING 2
2893#define LIST_INT 3
2894
2895 static void
2896highlight_list_one(int id)
2897{
2898 hl_group_T *sgp;
2899 int didh = FALSE;
2900
2901 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2902
2903 if (message_filtered(sgp->sg_name))
2904 return;
2905
2906 didh = highlight_list_arg(id, didh, LIST_ATTR,
2907 sgp->sg_term, NULL, "term");
2908 didh = highlight_list_arg(id, didh, LIST_STRING,
2909 0, sgp->sg_start, "start");
2910 didh = highlight_list_arg(id, didh, LIST_STRING,
2911 0, sgp->sg_stop, "stop");
2912
2913 didh = highlight_list_arg(id, didh, LIST_ATTR,
2914 sgp->sg_cterm, NULL, "cterm");
2915 didh = highlight_list_arg(id, didh, LIST_INT,
2916 sgp->sg_cterm_fg, NULL, "ctermfg");
2917 didh = highlight_list_arg(id, didh, LIST_INT,
2918 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002919 didh = highlight_list_arg(id, didh, LIST_INT,
2920 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002921
2922#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2923 didh = highlight_list_arg(id, didh, LIST_ATTR,
2924 sgp->sg_gui, NULL, "gui");
2925 didh = highlight_list_arg(id, didh, LIST_STRING,
2926 0, sgp->sg_gui_fg_name, "guifg");
2927 didh = highlight_list_arg(id, didh, LIST_STRING,
2928 0, sgp->sg_gui_bg_name, "guibg");
2929 didh = highlight_list_arg(id, didh, LIST_STRING,
2930 0, sgp->sg_gui_sp_name, "guisp");
2931#endif
2932#ifdef FEAT_GUI
2933 didh = highlight_list_arg(id, didh, LIST_STRING,
2934 0, sgp->sg_font_name, "font");
2935#endif
2936
2937 if (sgp->sg_link && !got_int)
2938 {
2939 (void)syn_list_header(didh, 9999, id);
2940 didh = TRUE;
2941 msg_puts_attr("links to", HL_ATTR(HLF_D));
2942 msg_putchar(' ');
2943 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2944 }
2945
2946 if (!didh)
2947 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2948#ifdef FEAT_EVAL
2949 if (p_verbose > 0)
2950 last_set_msg(sgp->sg_script_ctx);
2951#endif
2952}
2953
2954 static int
2955highlight_list_arg(
2956 int id,
2957 int didh,
2958 int type,
2959 int iarg,
2960 char_u *sarg,
2961 char *name)
2962{
2963 char_u buf[100];
2964 char_u *ts;
2965 int i;
2966
2967 if (got_int)
2968 return FALSE;
2969 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2970 {
2971 ts = buf;
2972 if (type == LIST_INT)
2973 sprintf((char *)buf, "%d", iarg - 1);
2974 else if (type == LIST_STRING)
2975 ts = sarg;
2976 else // type == LIST_ATTR
2977 {
2978 buf[0] = NUL;
2979 for (i = 0; hl_attr_table[i] != 0; ++i)
2980 {
2981 if (iarg & hl_attr_table[i])
2982 {
2983 if (buf[0] != NUL)
2984 vim_strcat(buf, (char_u *)",", 100);
2985 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2986 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2987 }
2988 }
2989 }
2990
2991 (void)syn_list_header(didh,
2992 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2993 didh = TRUE;
2994 if (!got_int)
2995 {
2996 if (*name != NUL)
2997 {
2998 msg_puts_attr(name, HL_ATTR(HLF_D));
2999 msg_puts_attr("=", HL_ATTR(HLF_D));
3000 }
3001 msg_outtrans(ts);
3002 }
3003 }
3004 return didh;
3005}
3006
3007#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3008/*
3009 * Return "1" if highlight group "id" has attribute "flag".
3010 * Return NULL otherwise.
3011 */
3012 char_u *
3013highlight_has_attr(
3014 int id,
3015 int flag,
3016 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3017{
3018 int attr;
3019
3020 if (id <= 0 || id > highlight_ga.ga_len)
3021 return NULL;
3022
3023#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3024 if (modec == 'g')
3025 attr = HL_TABLE()[id - 1].sg_gui;
3026 else
3027#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003028 {
3029 if (modec == 'c')
3030 attr = HL_TABLE()[id - 1].sg_cterm;
3031 else
3032 attr = HL_TABLE()[id - 1].sg_term;
3033 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003034
3035 if (attr & flag)
3036 return (char_u *)"1";
3037 return NULL;
3038}
3039#endif
3040
3041#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3042/*
3043 * Return color name of highlight group "id".
3044 */
3045 char_u *
3046highlight_color(
3047 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003048 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003049 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3050{
3051 static char_u name[20];
3052 int n;
3053 int fg = FALSE;
3054 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003055 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003056 int font = FALSE;
3057
3058 if (id <= 0 || id > highlight_ga.ga_len)
3059 return NULL;
3060
3061 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3062 fg = TRUE;
3063 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3064 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3065 font = TRUE;
3066 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3067 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003068 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3069 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003070 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3071 return NULL;
3072 if (modec == 'g')
3073 {
3074# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3075# ifdef FEAT_GUI
3076 // return font name
3077 if (font)
3078 return HL_TABLE()[id - 1].sg_font_name;
3079# endif
3080
3081 // return #RRGGBB form (only possible when GUI is running)
3082 if ((USE_24BIT) && what[2] == '#')
3083 {
3084 guicolor_T color;
3085 long_u rgb;
3086 static char_u buf[10];
3087
3088 if (fg)
3089 color = HL_TABLE()[id - 1].sg_gui_fg;
3090 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003091 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003092 else
3093 color = HL_TABLE()[id - 1].sg_gui_bg;
3094 if (color == INVALCOLOR)
3095 return NULL;
3096 rgb = (long_u)GUI_MCH_GET_RGB(color);
3097 sprintf((char *)buf, "#%02x%02x%02x",
3098 (unsigned)(rgb >> 16),
3099 (unsigned)(rgb >> 8) & 255,
3100 (unsigned)rgb & 255);
3101 return buf;
3102 }
3103# endif
3104 if (fg)
3105 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3106 if (sp)
3107 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3108 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3109 }
3110 if (font || sp)
3111 return NULL;
3112 if (modec == 'c')
3113 {
3114 if (fg)
3115 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003116 else if (ul)
3117 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003118 else
3119 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3120 if (n < 0)
3121 return NULL;
3122 sprintf((char *)name, "%d", n);
3123 return name;
3124 }
3125 // term doesn't have color
3126 return NULL;
3127}
3128#endif
3129
3130#if (defined(FEAT_SYN_HL) \
3131 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3132 && defined(FEAT_PRINTER)) || defined(PROTO)
3133/*
3134 * Return color name of highlight group "id" as RGB value.
3135 */
3136 long_u
3137highlight_gui_color_rgb(
3138 int id,
3139 int fg) // TRUE = fg, FALSE = bg
3140{
3141 guicolor_T color;
3142
3143 if (id <= 0 || id > highlight_ga.ga_len)
3144 return 0L;
3145
3146 if (fg)
3147 color = HL_TABLE()[id - 1].sg_gui_fg;
3148 else
3149 color = HL_TABLE()[id - 1].sg_gui_bg;
3150
3151 if (color == INVALCOLOR)
3152 return 0L;
3153
3154 return GUI_MCH_GET_RGB(color);
3155}
3156#endif
3157
3158/*
3159 * Output the syntax list header.
3160 * Return TRUE when started a new line.
3161 */
3162 int
3163syn_list_header(
3164 int did_header, // did header already
3165 int outlen, // length of string that comes
3166 int id) // highlight group id
3167{
3168 int endcol = 19;
3169 int newline = TRUE;
3170 int name_col = 0;
3171
3172 if (!did_header)
3173 {
3174 msg_putchar('\n');
3175 if (got_int)
3176 return TRUE;
3177 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3178 name_col = msg_col;
3179 endcol = 15;
3180 }
3181 else if (msg_col + outlen + 1 >= Columns)
3182 {
3183 msg_putchar('\n');
3184 if (got_int)
3185 return TRUE;
3186 }
3187 else
3188 {
3189 if (msg_col >= endcol) // wrap around is like starting a new line
3190 newline = FALSE;
3191 }
3192
3193 if (msg_col >= endcol) // output at least one space
3194 endcol = msg_col + 1;
3195 if (Columns <= endcol) // avoid hang for tiny window
3196 endcol = Columns - 1;
3197
3198 msg_advance(endcol);
3199
3200 // Show "xxx" with the attributes.
3201 if (!did_header)
3202 {
3203 if (endcol == Columns - 1 && endcol <= name_col)
3204 msg_putchar(' ');
3205 msg_puts_attr("xxx", syn_id2attr(id));
3206 msg_putchar(' ');
3207 }
3208
3209 return newline;
3210}
3211
3212/*
3213 * Set the attribute numbers for a highlight group.
3214 * Called after one of the attributes has changed.
3215 */
3216 static void
3217set_hl_attr(
3218 int idx) // index in array
3219{
3220 attrentry_T at_en;
3221 hl_group_T *sgp = HL_TABLE() + idx;
3222
3223 // The "Normal" group doesn't need an attribute number
3224 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3225 return;
3226
3227#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003228 // For the GUI mode: If there are other than "normal" highlighting
3229 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003230 if (sgp->sg_gui_fg == INVALCOLOR
3231 && sgp->sg_gui_bg == INVALCOLOR
3232 && sgp->sg_gui_sp == INVALCOLOR
3233 && sgp->sg_font == NOFONT
3234# ifdef FEAT_XFONTSET
3235 && sgp->sg_fontset == NOFONTSET
3236# endif
3237 )
3238 {
3239 sgp->sg_gui_attr = sgp->sg_gui;
3240 }
3241 else
3242 {
3243 at_en.ae_attr = sgp->sg_gui;
3244 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3245 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3246 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3247 at_en.ae_u.gui.font = sgp->sg_font;
3248# ifdef FEAT_XFONTSET
3249 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3250# endif
3251 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3252 }
3253#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003254 // For the term mode: If there are other than "normal" highlighting
3255 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003256 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3257 sgp->sg_term_attr = sgp->sg_term;
3258 else
3259 {
3260 at_en.ae_attr = sgp->sg_term;
3261 at_en.ae_u.term.start = sgp->sg_start;
3262 at_en.ae_u.term.stop = sgp->sg_stop;
3263 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3264 }
3265
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003266 // For the color term mode: If there are other than "normal"
3267 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003268 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003269# ifdef FEAT_TERMGUICOLORS
3270 && sgp->sg_gui_fg == INVALCOLOR
3271 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003272 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003273# endif
3274 )
3275 sgp->sg_cterm_attr = sgp->sg_cterm;
3276 else
3277 {
3278 at_en.ae_attr = sgp->sg_cterm;
3279 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3280 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003281 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003282# ifdef FEAT_TERMGUICOLORS
3283# ifdef MSWIN
3284# ifdef VIMDLL
3285 // Only when not using the GUI.
3286 if (!gui.in_use && !gui.starting)
3287# endif
3288 {
3289 int id;
3290 guicolor_T fg, bg;
3291
3292 id = syn_name2id((char_u *)"Normal");
3293 if (id > 0)
3294 {
3295 syn_id2colors(id, &fg, &bg);
3296 if (sgp->sg_gui_fg == INVALCOLOR)
3297 sgp->sg_gui_fg = fg;
3298 if (sgp->sg_gui_bg == INVALCOLOR)
3299 sgp->sg_gui_bg = bg;
3300 }
3301
3302 }
3303# endif
3304 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3305 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003306 // Only use the underline/undercurl color when used, it may clear the
3307 // background color if not supported.
3308 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
3309 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3310 else
3311 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003312 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3313 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3314 {
3315 // If both fg and bg are invalid fall back to the cterm colors.
3316 // Helps when the GUI only uses an attribute, e.g. undercurl.
3317 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3318 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3319 }
3320# endif
3321 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3322 }
3323}
3324
3325/*
3326 * Lookup a highlight group name and return its ID.
3327 * If it is not found, 0 is returned.
3328 */
3329 int
3330syn_name2id(char_u *name)
3331{
3332 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003333 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003334
3335 // Avoid using stricmp() too much, it's slow on some systems
3336 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3337 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003338 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003339 vim_strup(name_u);
3340 for (i = highlight_ga.ga_len; --i >= 0; )
3341 if (HL_TABLE()[i].sg_name_u != NULL
3342 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3343 break;
3344 return i + 1;
3345}
3346
3347/*
3348 * Lookup a highlight group name and return its attributes.
3349 * Return zero if not found.
3350 */
3351 int
3352syn_name2attr(char_u *name)
3353{
3354 int id = syn_name2id(name);
3355
3356 if (id != 0)
3357 return syn_id2attr(id);
3358 return 0;
3359}
3360
3361#if defined(FEAT_EVAL) || defined(PROTO)
3362/*
3363 * Return TRUE if highlight group "name" exists.
3364 */
3365 int
3366highlight_exists(char_u *name)
3367{
3368 return (syn_name2id(name) > 0);
3369}
3370
3371# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3372/*
3373 * Return the name of highlight group "id".
3374 * When not a valid ID return an empty string.
3375 */
3376 char_u *
3377syn_id2name(int id)
3378{
3379 if (id <= 0 || id > highlight_ga.ga_len)
3380 return (char_u *)"";
3381 return HL_TABLE()[id - 1].sg_name;
3382}
3383# endif
3384#endif
3385
3386/*
3387 * Like syn_name2id(), but take a pointer + length argument.
3388 */
3389 int
3390syn_namen2id(char_u *linep, int len)
3391{
3392 char_u *name;
3393 int id = 0;
3394
3395 name = vim_strnsave(linep, len);
3396 if (name != NULL)
3397 {
3398 id = syn_name2id(name);
3399 vim_free(name);
3400 }
3401 return id;
3402}
3403
3404/*
3405 * Find highlight group name in the table and return its ID.
3406 * The argument is a pointer to the name and the length of the name.
3407 * If it doesn't exist yet, a new entry is created.
3408 * Return 0 for failure.
3409 */
3410 int
3411syn_check_group(char_u *pp, int len)
3412{
3413 int id;
3414 char_u *name;
3415
erw7f7f7aaf2021-12-07 21:29:20 +00003416 if (len > MAX_SYN_NAME)
3417 {
3418 emsg(_(e_highlight_group_name_too_long));
3419 return 0;
3420 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003421 name = vim_strnsave(pp, len);
3422 if (name == NULL)
3423 return 0;
3424
3425 id = syn_name2id(name);
3426 if (id == 0) // doesn't exist yet
3427 id = syn_add_group(name);
3428 else
3429 vim_free(name);
3430 return id;
3431}
3432
3433/*
3434 * Add new highlight group and return its ID.
3435 * "name" must be an allocated string, it will be consumed.
3436 * Return 0 for failure.
3437 */
3438 static int
3439syn_add_group(char_u *name)
3440{
3441 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003442 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003443
3444 // Check that the name is ASCII letters, digits and underscore.
3445 for (p = name; *p != NUL; ++p)
3446 {
3447 if (!vim_isprintc(*p))
3448 {
3449 emsg(_("E669: Unprintable character in group name"));
3450 vim_free(name);
3451 return 0;
3452 }
3453 else if (!ASCII_ISALNUM(*p) && *p != '_')
3454 {
3455 // This is an error, but since there previously was no check only
3456 // give a warning.
3457 msg_source(HL_ATTR(HLF_W));
3458 msg(_("W18: Invalid character in group name"));
3459 break;
3460 }
3461 }
3462
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003463 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003464 if (highlight_ga.ga_data == NULL)
3465 {
3466 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3467 highlight_ga.ga_growsize = 10;
3468 }
3469
3470 if (highlight_ga.ga_len >= MAX_HL_ID)
3471 {
3472 emsg(_("E849: Too many highlight and syntax groups"));
3473 vim_free(name);
3474 return 0;
3475 }
3476
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003477 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003478 if (ga_grow(&highlight_ga, 1) == FAIL)
3479 {
3480 vim_free(name);
3481 return 0;
3482 }
3483
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003484 name_up = vim_strsave_up(name);
3485 if (name_up == NULL)
3486 {
3487 vim_free(name);
3488 return 0;
3489 }
3490
Bram Moolenaara80faa82020-04-12 19:37:17 +02003491 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003492 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003493 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003494#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3495 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3496 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003497 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003498#endif
3499 ++highlight_ga.ga_len;
3500
3501 return highlight_ga.ga_len; // ID is index plus one
3502}
3503
3504/*
3505 * When, just after calling syn_add_group(), an error is discovered, this
3506 * function deletes the new name.
3507 */
3508 static void
3509syn_unadd_group(void)
3510{
3511 --highlight_ga.ga_len;
3512 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3513 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3514}
3515
3516/*
3517 * Translate a group ID to highlight attributes.
3518 */
3519 int
3520syn_id2attr(int hl_id)
3521{
3522 int attr;
3523 hl_group_T *sgp;
3524
3525 hl_id = syn_get_final_id(hl_id);
3526 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3527
3528#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003529 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003530 if (gui.in_use)
3531 attr = sgp->sg_gui_attr;
3532 else
3533#endif
3534 if (IS_CTERM)
3535 attr = sgp->sg_cterm_attr;
3536 else
3537 attr = sgp->sg_term_attr;
3538
3539 return attr;
3540}
3541
3542#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3543/*
3544 * Get the GUI colors and attributes for a group ID.
3545 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3546 */
3547 int
3548syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *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
3555 *fgp = sgp->sg_gui_fg;
3556 *bgp = sgp->sg_gui_bg;
3557 return sgp->sg_gui;
3558}
3559#endif
3560
3561#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003562 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3563 && defined(FEAT_TERMGUICOLORS)) \
3564 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003565 void
3566syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3567{
3568 hl_group_T *sgp;
3569
3570 hl_id = syn_get_final_id(hl_id);
3571 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3572 *fgp = sgp->sg_cterm_fg - 1;
3573 *bgp = sgp->sg_cterm_bg - 1;
3574}
3575#endif
3576
3577/*
3578 * Translate a group ID to the final group ID (following links).
3579 */
3580 int
3581syn_get_final_id(int hl_id)
3582{
3583 int count;
3584 hl_group_T *sgp;
3585
3586 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3587 return 0; // Can be called from eval!!
3588
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003589 // Follow links until there is no more.
3590 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003591 for (count = 100; --count >= 0; )
3592 {
3593 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3594 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3595 break;
3596 hl_id = sgp->sg_link;
3597 }
3598
3599 return hl_id;
3600}
3601
3602#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3603/*
3604 * Call this function just after the GUI has started.
3605 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3606 * It finds the font and color handles for the highlighting groups.
3607 */
3608 void
3609highlight_gui_started(void)
3610{
3611 int idx;
3612
3613 // First get the colors from the "Normal" and "Menu" group, if set
3614 if (USE_24BIT)
3615 set_normal_colors();
3616
3617 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3618 gui_do_one_color(idx, FALSE, FALSE);
3619
3620 highlight_changed();
3621}
3622
3623 static void
3624gui_do_one_color(
3625 int idx,
3626 int do_menu UNUSED, // TRUE: might set the menu font
3627 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3628{
3629 int didit = FALSE;
3630
3631# ifdef FEAT_GUI
3632# ifdef FEAT_TERMGUICOLORS
3633 if (gui.in_use)
3634# endif
3635 if (HL_TABLE()[idx].sg_font_name != NULL)
3636 {
3637 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3638 do_tooltip, TRUE);
3639 didit = TRUE;
3640 }
3641# endif
3642 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3643 {
3644 HL_TABLE()[idx].sg_gui_fg =
3645 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3646 didit = TRUE;
3647 }
3648 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3649 {
3650 HL_TABLE()[idx].sg_gui_bg =
3651 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3652 didit = TRUE;
3653 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003654 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3655 {
3656 HL_TABLE()[idx].sg_gui_sp =
3657 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3658 didit = TRUE;
3659 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003660 if (didit) // need to get a new attr number
3661 set_hl_attr(idx);
3662}
3663#endif
3664
3665#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3666/*
3667 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3668 */
3669 static void
3670combine_stl_hlt(
3671 int id,
3672 int id_S,
3673 int id_alt,
3674 int hlcnt,
3675 int i,
3676 int hlf,
3677 int *table)
3678{
3679 hl_group_T *hlt = HL_TABLE();
3680
3681 if (id_alt == 0)
3682 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003683 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003684 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3685 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3686# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3687 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3688# endif
3689 }
3690 else
3691 mch_memmove(&hlt[hlcnt + i],
3692 &hlt[id_alt - 1],
3693 sizeof(hl_group_T));
3694 hlt[hlcnt + i].sg_link = 0;
3695
3696 hlt[hlcnt + i].sg_term ^=
3697 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3698 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3699 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3700 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3701 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3702 hlt[hlcnt + i].sg_cterm ^=
3703 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3704 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3705 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3706 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3707 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3708# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3709 hlt[hlcnt + i].sg_gui ^=
3710 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3711# endif
3712# ifdef FEAT_GUI
3713 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3714 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3715 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3716 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3717 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3718 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3719 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3720 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3721# ifdef FEAT_XFONTSET
3722 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3723 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3724# endif
3725# endif
3726 highlight_ga.ga_len = hlcnt + i + 1;
3727 set_hl_attr(hlcnt + i); // At long last we can apply
3728 table[i] = syn_id2attr(hlcnt + i + 1);
3729}
3730#endif
3731
3732/*
3733 * Translate the 'highlight' option into attributes in highlight_attr[] and
3734 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3735 * corresponding highlights to use on top of HLF_SNC is computed.
3736 * Called only when the 'highlight' option has been changed and upon first
3737 * screen redraw after any :highlight command.
3738 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3739 */
3740 int
3741highlight_changed(void)
3742{
3743 int hlf;
3744 int i;
3745 char_u *p;
3746 int attr;
3747 char_u *end;
3748 int id;
3749#ifdef USER_HIGHLIGHT
3750 char_u userhl[30]; // use 30 to avoid compiler warning
3751# ifdef FEAT_STL_OPT
3752 int id_S = -1;
3753 int id_SNC = 0;
3754# ifdef FEAT_TERMINAL
3755 int id_ST = 0;
3756 int id_STNC = 0;
3757# endif
3758 int hlcnt;
3759# endif
3760#endif
3761 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3762
3763 need_highlight_changed = FALSE;
3764
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003765#ifdef FEAT_TERMINAL
3766 term_update_colors_all();
3767 term_update_wincolor_all();
3768#endif
3769
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003770 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003771 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3772 highlight_attr[hlf] = 0;
3773
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003774 // First set all attributes to their default value.
3775 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003776 for (i = 0; i < 2; ++i)
3777 {
3778 if (i)
3779 p = p_hl;
3780 else
3781 p = get_highlight_default();
3782 if (p == NULL) // just in case
3783 continue;
3784
3785 while (*p)
3786 {
3787 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3788 if (hl_flags[hlf] == *p)
3789 break;
3790 ++p;
3791 if (hlf == (int)HLF_COUNT || *p == NUL)
3792 return FAIL;
3793
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003794 // Allow several hl_flags to be combined, like "bu" for
3795 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003796 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003797 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003798 {
3799 if (VIM_ISWHITE(*p)) // ignore white space
3800 continue;
3801
3802 if (attr > HL_ALL) // Combination with ':' is not allowed.
3803 return FAIL;
3804
3805 switch (*p)
3806 {
3807 case 'b': attr |= HL_BOLD;
3808 break;
3809 case 'i': attr |= HL_ITALIC;
3810 break;
3811 case '-':
3812 case 'n': // no highlighting
3813 break;
3814 case 'r': attr |= HL_INVERSE;
3815 break;
3816 case 's': attr |= HL_STANDOUT;
3817 break;
3818 case 'u': attr |= HL_UNDERLINE;
3819 break;
3820 case 'c': attr |= HL_UNDERCURL;
3821 break;
3822 case 't': attr |= HL_STRIKETHROUGH;
3823 break;
3824 case ':': ++p; // highlight group name
3825 if (attr || *p == NUL) // no combinations
3826 return FAIL;
3827 end = vim_strchr(p, ',');
3828 if (end == NULL)
3829 end = p + STRLEN(p);
3830 id = syn_check_group(p, (int)(end - p));
3831 if (id == 0)
3832 return FAIL;
3833 attr = syn_id2attr(id);
3834 p = end - 1;
3835#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3836 if (hlf == (int)HLF_SNC)
3837 id_SNC = syn_get_final_id(id);
3838# ifdef FEAT_TERMINAL
3839 else if (hlf == (int)HLF_ST)
3840 id_ST = syn_get_final_id(id);
3841 else if (hlf == (int)HLF_STNC)
3842 id_STNC = syn_get_final_id(id);
3843# endif
3844 else if (hlf == (int)HLF_S)
3845 id_S = syn_get_final_id(id);
3846#endif
3847 break;
3848 default: return FAIL;
3849 }
3850 }
3851 highlight_attr[hlf] = attr;
3852
3853 p = skip_to_option_part(p); // skip comma and spaces
3854 }
3855 }
3856
3857#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003858 // Setup the user highlights
3859 //
3860 // Temporarily utilize 28 more hl entries:
3861 // 9 for User1-User9 combined with StatusLineNC
3862 // 9 for User1-User9 combined with StatusLineTerm
3863 // 9 for User1-User9 combined with StatusLineTermNC
3864 // 1 for StatusLine default
3865 // Have to be in there simultaneously in case of table overflows in
3866 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003867# ifdef FEAT_STL_OPT
3868 if (ga_grow(&highlight_ga, 28) == FAIL)
3869 return FAIL;
3870 hlcnt = highlight_ga.ga_len;
3871 if (id_S == -1)
3872 {
3873 // Make sure id_S is always valid to simplify code below. Use the last
3874 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003875 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003876 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3877 id_S = hlcnt + 19;
3878 }
3879# endif
3880 for (i = 0; i < 9; i++)
3881 {
3882 sprintf((char *)userhl, "User%d", i + 1);
3883 id = syn_name2id(userhl);
3884 if (id == 0)
3885 {
3886 highlight_user[i] = 0;
3887# ifdef FEAT_STL_OPT
3888 highlight_stlnc[i] = 0;
3889# ifdef FEAT_TERMINAL
3890 highlight_stlterm[i] = 0;
3891 highlight_stltermnc[i] = 0;
3892# endif
3893# endif
3894 }
3895 else
3896 {
3897 highlight_user[i] = syn_id2attr(id);
3898# ifdef FEAT_STL_OPT
3899 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3900 HLF_SNC, highlight_stlnc);
3901# ifdef FEAT_TERMINAL
3902 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3903 HLF_ST, highlight_stlterm);
3904 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3905 HLF_STNC, highlight_stltermnc);
3906# endif
3907# endif
3908 }
3909 }
3910# ifdef FEAT_STL_OPT
3911 highlight_ga.ga_len = hlcnt;
3912# endif
3913
3914#endif // USER_HIGHLIGHT
3915
3916 return OK;
3917}
3918
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003919static void highlight_list(void);
3920static void highlight_list_two(int cnt, int attr);
3921
3922/*
3923 * Handle command line completion for :highlight command.
3924 */
3925 void
3926set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3927{
3928 char_u *p;
3929
3930 // Default: expand group names
3931 xp->xp_context = EXPAND_HIGHLIGHT;
3932 xp->xp_pattern = arg;
3933 include_link = 2;
3934 include_default = 1;
3935
3936 // (part of) subcommand already typed
3937 if (*arg != NUL)
3938 {
3939 p = skiptowhite(arg);
3940 if (*p != NUL) // past "default" or group name
3941 {
3942 include_default = 0;
3943 if (STRNCMP("default", arg, p - arg) == 0)
3944 {
3945 arg = skipwhite(p);
3946 xp->xp_pattern = arg;
3947 p = skiptowhite(arg);
3948 }
3949 if (*p != NUL) // past group name
3950 {
3951 include_link = 0;
3952 if (arg[1] == 'i' && arg[0] == 'N')
3953 highlight_list();
3954 if (STRNCMP("link", arg, p - arg) == 0
3955 || STRNCMP("clear", arg, p - arg) == 0)
3956 {
3957 xp->xp_pattern = skipwhite(p);
3958 p = skiptowhite(xp->xp_pattern);
3959 if (*p != NUL) // past first group name
3960 {
3961 xp->xp_pattern = skipwhite(p);
3962 p = skiptowhite(xp->xp_pattern);
3963 }
3964 }
3965 if (*p != NUL) // past group name(s)
3966 xp->xp_context = EXPAND_NOTHING;
3967 }
3968 }
3969 }
3970}
3971
3972/*
3973 * List highlighting matches in a nice way.
3974 */
3975 static void
3976highlight_list(void)
3977{
3978 int i;
3979
3980 for (i = 10; --i >= 0; )
3981 highlight_list_two(i, HL_ATTR(HLF_D));
3982 for (i = 40; --i >= 0; )
3983 highlight_list_two(99, 0);
3984}
3985
3986 static void
3987highlight_list_two(int cnt, int attr)
3988{
3989 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3990 msg_clr_eos();
3991 out_flush();
3992 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3993}
3994
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003995/*
3996 * Function given to ExpandGeneric() to obtain the list of group names.
3997 */
3998 char_u *
3999get_highlight_name(expand_T *xp UNUSED, int idx)
4000{
4001 return get_highlight_name_ext(xp, idx, TRUE);
4002}
4003
4004/*
4005 * Obtain a highlight group name.
4006 * When "skip_cleared" is TRUE don't return a cleared entry.
4007 */
4008 char_u *
4009get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4010{
4011 if (idx < 0)
4012 return NULL;
4013
4014 // Items are never removed from the table, skip the ones that were
4015 // cleared.
4016 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4017 return (char_u *)"";
4018
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004019 if (idx == highlight_ga.ga_len && include_none != 0)
4020 return (char_u *)"none";
4021 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4022 return (char_u *)"default";
4023 if (idx == highlight_ga.ga_len + include_none + include_default
4024 && include_link != 0)
4025 return (char_u *)"link";
4026 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4027 && include_link != 0)
4028 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004029 if (idx >= highlight_ga.ga_len)
4030 return NULL;
4031 return HL_TABLE()[idx].sg_name;
4032}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004033
4034#if defined(FEAT_GUI) || defined(PROTO)
4035/*
4036 * Free all the highlight group fonts.
4037 * Used when quitting for systems which need it.
4038 */
4039 void
4040free_highlight_fonts(void)
4041{
4042 int idx;
4043
4044 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4045 {
4046 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4047 HL_TABLE()[idx].sg_font = NOFONT;
4048# ifdef FEAT_XFONTSET
4049 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4050 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4051# endif
4052 }
4053
4054 gui_mch_free_font(gui.norm_font);
4055# ifdef FEAT_XFONTSET
4056 gui_mch_free_fontset(gui.fontset);
4057# endif
4058# ifndef FEAT_GUI_GTK
4059 gui_mch_free_font(gui.bold_font);
4060 gui_mch_free_font(gui.ital_font);
4061 gui_mch_free_font(gui.boldital_font);
4062# endif
4063}
4064#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004065
4066#if defined(FEAT_EVAL) || defined(PROTO)
4067/*
4068 * Convert each of the highlight attribute bits (bold, standout, underline,
4069 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4070 * the attribute name as the key.
4071 */
4072 static dict_T *
4073highlight_get_attr_dict(int hlattr)
4074{
4075 dict_T *dict;
4076 int i;
4077
4078 dict = dict_alloc();
4079 if (dict == NULL)
4080 return NULL;
4081
4082 for (i = 0; hl_attr_table[i] != 0; ++i)
4083 {
4084 if (hlattr & hl_attr_table[i])
4085 {
4086 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4087 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4088 }
4089 }
4090
4091 return dict;
4092}
4093
4094/*
4095 * Return the attributes of the highlight group at index 'hl_idx' as a
4096 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4097 * links recursively.
4098 */
4099 static dict_T *
4100highlight_get_info(int hl_idx, int resolve_link)
4101{
4102 dict_T *dict;
4103 hl_group_T *sgp;
4104 dict_T *attr_dict;
4105 int hlgid;
4106
4107 dict = dict_alloc();
4108 if (dict == NULL)
4109 return dict;
4110
4111 sgp = &HL_TABLE()[hl_idx];
4112 // highlight group id is 1-based
4113 hlgid = hl_idx + 1;
4114
4115 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4116 goto error;
4117 if (dict_add_number(dict, "id", hlgid) == FAIL)
4118 goto error;
4119
4120 if (sgp->sg_link && resolve_link)
4121 {
4122 // resolve the highlight group link recursively
4123 while (sgp->sg_link)
4124 {
4125 hlgid = sgp->sg_link;
4126 sgp = &HL_TABLE()[sgp->sg_link - 1];
4127 }
4128 }
4129
4130 if (sgp->sg_term != 0)
4131 {
4132 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4133 if (attr_dict != NULL)
4134 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4135 goto error;
4136 }
4137 if (sgp->sg_start != NULL)
4138 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4139 goto error;
4140 if (sgp->sg_stop != NULL)
4141 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4142 goto error;
4143 if (sgp->sg_cterm != 0)
4144 {
4145 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4146 if (attr_dict != NULL)
4147 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4148 goto error;
4149 }
4150 if (sgp->sg_cterm_fg != 0)
4151 if (dict_add_string(dict, "ctermfg",
4152 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4153 goto error;
4154 if (sgp->sg_cterm_bg != 0)
4155 if (dict_add_string(dict, "ctermbg",
4156 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4157 goto error;
4158 if (sgp->sg_cterm_ul != 0)
4159 if (dict_add_string(dict, "ctermul",
4160 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4161 goto error;
4162 if (sgp->sg_gui != 0)
4163 {
4164 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4165 if (attr_dict != NULL)
4166 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4167 goto error;
4168 }
4169 if (sgp->sg_gui_fg_name != NULL)
4170 if (dict_add_string(dict, "guifg",
4171 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4172 goto error;
4173 if (sgp->sg_gui_bg_name != NULL)
4174 if (dict_add_string(dict, "guibg",
4175 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4176 goto error;
4177 if (sgp->sg_gui_sp_name != NULL)
4178 if (dict_add_string(dict, "guisp",
4179 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4180 goto error;
4181# ifdef FEAT_GUI
4182 if (sgp->sg_font_name != NULL)
4183 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4184 goto error;
4185# endif
4186 if (sgp->sg_link)
4187 {
4188 char_u *link;
4189
4190 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4191 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4192 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004193
4194 if (sgp->sg_deflink)
4195 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004196 }
4197 if (dict_len(dict) == 2)
4198 // If only 'name' is present, then the highlight group is cleared.
4199 dict_add_bool(dict, "cleared", VVAL_TRUE);
4200
4201 return dict;
4202
4203error:
4204 vim_free(dict);
4205 return NULL;
4206}
4207
4208/*
4209 * "hlget([name])" function
4210 * Return the attributes of a specific highlight group (if specified) or all
4211 * the highlight groups.
4212 */
4213 void
4214f_hlget(typval_T *argvars, typval_T *rettv)
4215{
4216 list_T *list;
4217 dict_T *dict;
4218 int i;
4219 char_u *hlarg = NULL;
4220 int resolve_link = FALSE;
4221
4222 if (rettv_list_alloc(rettv) == FAIL)
4223 return;
4224
4225 if (check_for_opt_string_arg(argvars, 0) == FAIL
4226 || (argvars[0].v_type != VAR_UNKNOWN
4227 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4228 return;
4229
4230 if (argvars[0].v_type != VAR_UNKNOWN)
4231 {
4232 // highlight group name supplied
4233 hlarg = tv_get_string_chk(&argvars[0]);
4234 if (hlarg == NULL)
4235 return;
4236
4237 if (argvars[1].v_type != VAR_UNKNOWN)
4238 {
4239 int error = FALSE;
4240
4241 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4242 if (error)
4243 return;
4244 }
4245 }
4246
4247 list = rettv->vval.v_list;
4248 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4249 {
4250 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4251 {
4252 dict = highlight_get_info(i, resolve_link);
4253 if (dict != NULL)
4254 list_append_dict(list, dict);
4255 }
4256 }
4257}
4258
4259/*
4260 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4261 * 'dict' or the value is not a string type. If the value is not a string type
4262 * or is NULL, then 'error' is set to TRUE.
4263 */
4264 static char_u *
4265hldict_get_string(dict_T *dict, char_u *key, int *error)
4266{
4267 dictitem_T *di;
4268
4269 *error = FALSE;
4270 di = dict_find(dict, key, -1);
4271 if (di == NULL)
4272 return NULL;
4273
4274 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4275 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004276 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004277 *error = TRUE;
4278 return NULL;
4279 }
4280
4281 return di->di_tv.vval.v_string;
4282}
4283
4284/*
4285 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4286 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4287 * Dictionary or is NULL.
4288 */
4289 static int
4290hldict_attr_to_str(
4291 dict_T *dict,
4292 char_u *key,
4293 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004294 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004295{
4296 dictitem_T *di;
4297 dict_T *attrdict;
4298 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004299 char_u *p;
4300 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004301
4302 attr_str[0] = NUL;
4303 di = dict_find(dict, key, -1);
4304 if (di == NULL)
4305 return TRUE;
4306
4307 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4308 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004309 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004310 return FALSE;
4311 }
4312
4313 attrdict = di->di_tv.vval.v_dict;
4314
4315 // If the attribute dict is empty, then return NONE to clear the attributes
4316 if (dict_len(attrdict) == 0)
4317 {
4318 vim_strcat(attr_str, (char_u *)"NONE", len);
4319 return TRUE;
4320 }
4321
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004322 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004323 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4324 {
4325 if (dict_get_bool(attrdict, (char_u *)hl_name_table[i],
4326 VVAL_FALSE) == VVAL_TRUE)
4327 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004328 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4329 STRCPY(p, (char_u *)",");
4330 sz = STRLEN(hl_name_table[i]);
4331 if (p - attr_str + sz + 1 < len)
4332 {
4333 STRCPY(p, (char_u *)hl_name_table[i]);
4334 p += sz;
4335 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004336 }
4337 }
4338
4339 return TRUE;
4340}
4341
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004342// Temporary buffer used to store the command string produced by hlset().
4343// IObuff cannot be used for this as the error messages produced by hlset()
4344// internally use IObuff.
4345#define HLSETBUFSZ 512
4346static char_u hlsetBuf[HLSETBUFSZ + 1];
4347
4348/*
4349 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4350 * "dptr", which points into "hlsetBuf".
4351 * Returns the updated pointer.
4352 */
4353 static char_u *
4354add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4355{
4356 size_t vallen;
4357
4358 // Do nothing if the value is not specified or is empty
4359 if (value == NULL || *value == NUL)
4360 return dptr;
4361
4362 vallen = STRLEN(value);
4363 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4364 {
4365 STRCPY(dptr, attr);
4366 dptr += attrlen;
4367 STRCPY(dptr, value);
4368 dptr += vallen;
4369 }
4370
4371 return dptr;
4372}
4373
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004374/*
4375 * Add or update a highlight group using 'dict' items. Returns TRUE if
4376 * successfully updated the highlight group.
4377 */
4378 static int
4379hlg_add_or_update(dict_T *dict)
4380{
4381 char_u *name;
4382 int error;
4383 char_u term_attr[80];
4384 char_u cterm_attr[80];
4385 char_u gui_attr[80];
4386 char_u *start;
4387 char_u *stop;
4388 char_u *ctermfg;
4389 char_u *ctermbg;
4390 char_u *ctermul;
4391 char_u *guifg;
4392 char_u *guibg;
4393 char_u *guisp;
4394# ifdef FEAT_GUI
4395 char_u *font;
4396# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004397 int forceit = FALSE;
4398 int dodefault = FALSE;
4399 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004400 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004401
4402 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004403 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004404 return FALSE;
4405
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004406 if (dict_get_bool(dict, (char_u *)"force", VVAL_FALSE) == VVAL_TRUE)
4407 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004408
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004409 if (dict_get_bool(dict, (char_u *)"default", VVAL_FALSE) == VVAL_TRUE)
4410 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004411
4412 if (dict_find(dict, (char_u *)"cleared", -1) != NULL)
4413 {
4414 varnumber_T cleared;
4415
4416 // clear a highlight group
4417 cleared = dict_get_bool(dict, (char_u *)"cleared", FALSE);
4418 if (cleared == TRUE)
4419 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004420 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4421 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004422 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004423 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004424 }
4425
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004426 if (dict_find(dict, (char_u *)"linksto", -1) != NULL)
4427 {
4428 char_u *linksto;
4429
4430 // link highlight groups
4431 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004432 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004433 return FALSE;
4434
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004435 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004436 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004437 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004438
4439 done = TRUE;
4440 }
4441
4442 // If 'cleared' or 'linksto' are specified, then don't process the other
4443 // attributes.
4444 if (done)
4445 return TRUE;
4446
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004447 start = hldict_get_string(dict, (char_u *)"start", &error);
4448 if (error)
4449 return FALSE;
4450
4451 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4452 if (error)
4453 return FALSE;
4454
4455 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004456 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004457 return FALSE;
4458
4459 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004460 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004461 return FALSE;
4462
4463 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4464 if (error)
4465 return FALSE;
4466
4467 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4468 if (error)
4469 return FALSE;
4470
4471 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4472 if (error)
4473 return FALSE;
4474
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004475 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004476 return FALSE;
4477
4478 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4479 if (error)
4480 return FALSE;
4481
4482 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4483 if (error)
4484 return FALSE;
4485
4486 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4487 if (error)
4488 return FALSE;
4489
4490# ifdef FEAT_GUI
4491 font = hldict_get_string(dict, (char_u *)"font", &error);
4492 if (error)
4493 return FALSE;
4494# endif
4495
4496 // If none of the attributes are specified, then do nothing.
4497 if (term_attr[0] == NUL && start == NULL && stop == NULL
4498 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4499 && ctermul == NULL && gui_attr[0] == NUL
4500# ifdef FEAT_GUI
4501 && font == NULL
4502# endif
4503 && guifg == NULL && guibg == NULL && guisp == NULL
4504 )
4505 return TRUE;
4506
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004507 hlsetBuf[0] = NUL;
4508 p = hlsetBuf;
4509 if (dodefault)
4510 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4511 p = add_attr_and_value(p, (char_u *)"", 0, name);
4512 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4513 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4514 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4515 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4516 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4517 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4518 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4519 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004520# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004521 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004522# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004523 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4524 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
4525 p = add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004526
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004527 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004528
4529 return TRUE;
4530}
4531
4532/*
4533 * "hlset([{highlight_attr}])" function
4534 * Add or modify highlight groups
4535 */
4536 void
4537f_hlset(typval_T *argvars, typval_T *rettv)
4538{
4539 listitem_T *li;
4540 dict_T *dict;
4541
4542 rettv->vval.v_number = -1;
4543
4544 if (check_for_list_arg(argvars, 0) == FAIL)
4545 return;
4546
4547 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4548 {
4549 if (li->li_tv.v_type != VAR_DICT)
4550 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004551 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004552 return;
4553 }
4554
4555 dict = li->li_tv.vval.v_dict;
4556 if (!hlg_add_or_update(dict))
4557 return;
4558 }
4559
4560 rettv->vval.v_number = 0;
4561}
4562#endif