blob: ec3d11568c28cb70c0ae9582d79567d91a715ea9 [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 */
John Marriott34f00dd2024-04-08 23:28:12 +020027// must be sorted by the 'value' field because it is used by bsearch()!
28// note: inverse and reverse use the same key
29static keyvalue_T highlight_tab[] = {
30 KEYVALUE_ENTRY(HL_BOLD, "bold"), // index 0
31 KEYVALUE_ENTRY(HL_INVERSE, "inverse"), // index 1
32 KEYVALUE_ENTRY(HL_ITALIC, "italic"), // index 2
33 KEYVALUE_ENTRY(HL_NOCOMBINE, "nocombine"), // index 3
34 KEYVALUE_ENTRY(HL_NORMAL, "NONE"), // index 4
35 KEYVALUE_ENTRY(HL_INVERSE, "reverse"), // index 5
36 KEYVALUE_ENTRY(HL_STANDOUT, "standout"), // index 6
37 KEYVALUE_ENTRY(HL_STRIKETHROUGH, "strikethrough"), // index 7
38 KEYVALUE_ENTRY(HL_UNDERCURL, "undercurl"), // index 8
39 KEYVALUE_ENTRY(HL_UNDERDASHED, "underdashed"), // index 9
40 KEYVALUE_ENTRY(HL_UNDERDOTTED, "underdotted"), // index 10
41 KEYVALUE_ENTRY(HL_UNDERDOUBLE, "underdouble"), // index 11
42 KEYVALUE_ENTRY(HL_UNDERLINE, "underline") // index 12
43};
44
45// this table is used to display highlight names in the "correct" sequence.
46// keep this in sync with highlight_tab[].
47static keyvalue_T *highlight_index_tab[] = {
48 &highlight_tab[0], // HL_BOLD
49 &highlight_tab[6], // HL_STANDOUT
50 &highlight_tab[12], // HL_UNDERLINE
51 &highlight_tab[8], // HL_UNDERCURL
52 &highlight_tab[11], // HL_UNDERDOUBLE
53 &highlight_tab[10], // HL_UNDERDOTTED
54 &highlight_tab[9], // HL_UNDERDASHED
55 &highlight_tab[2], // HL_ITALIC
56 &highlight_tab[5], // HL_REVERSE
57 &highlight_tab[1], // HL_INVERSE
58 &highlight_tab[3], // HL_NOCOMBINE
59 &highlight_tab[7], // HL_STRIKETHROUGH
60 &highlight_tab[4] // HL_NORMAL
61};
62
Bram Moolenaar84f54632022-06-29 18:39:11 +010063// length of all attribute names, plus commas, together (and a bit more)
64#define MAX_ATTR_LEN 120
65
kylo252ae6f1d82022-02-16 19:24:07 +000066#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? (attr_b) : (attr_a)) | (attr_b))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020067
John Marriott34f00dd2024-04-08 23:28:12 +020068enum {
69 BLACK = 0,
70 DARKBLUE,
71 DARKGREEN,
72 DARKCYAN,
73 DARKRED,
74 DARKMAGENTA,
75 BROWN,
76 DARKYELLOW,
77 GRAY,
78 GREY,
79 LIGHTGRAY,
80 LIGHTGREY,
81 DARKGRAY,
82 DARKGREY,
83 BLUE,
84 LIGHTBLUE,
85 GREEN,
86 LIGHTGREEN,
87 CYAN,
88 LIGHTCYAN,
89 RED,
90 LIGHTRED,
91 MAGENTA,
92 LIGHTMAGENTA,
93 YELLOW,
94 LIGHTYELLOW,
95 WHITE,
96 NONE
97};
98
99// must be sorted by the 'value' field because it is used by bsearch()!
100static keyvalue_T color_name_tab[] = {
101 KEYVALUE_ENTRY(BLACK, "Black"),
102 KEYVALUE_ENTRY(BLUE, "Blue"),
103 KEYVALUE_ENTRY(BROWN, "Brown"),
104 KEYVALUE_ENTRY(CYAN, "Cyan"),
105 KEYVALUE_ENTRY(DARKBLUE, "DarkBlue"),
106 KEYVALUE_ENTRY(DARKCYAN, "DarkCyan"),
107 KEYVALUE_ENTRY(DARKGRAY, "DarkGray"),
108 KEYVALUE_ENTRY(DARKGREEN, "DarkGreen"),
109 KEYVALUE_ENTRY(DARKGREY, "DarkGrey"),
110 KEYVALUE_ENTRY(DARKMAGENTA, "DarkMagenta"),
111 KEYVALUE_ENTRY(DARKRED, "DarkRed"),
112 KEYVALUE_ENTRY(DARKYELLOW, "DarkYellow"),
113 KEYVALUE_ENTRY(GRAY, "Gray"),
114 KEYVALUE_ENTRY(GREEN, "Green"),
115 KEYVALUE_ENTRY(GREY, "Grey"),
116 KEYVALUE_ENTRY(LIGHTBLUE, "LightBlue"),
117 KEYVALUE_ENTRY(LIGHTCYAN, "LightCyan"),
118 KEYVALUE_ENTRY(LIGHTGRAY, "LightGray"),
119 KEYVALUE_ENTRY(LIGHTGREEN, "LightGreen"),
120 KEYVALUE_ENTRY(LIGHTGREY, "LightGrey"),
121 KEYVALUE_ENTRY(LIGHTMAGENTA, "LightMagenta"),
122 KEYVALUE_ENTRY(LIGHTRED, "LightRed"),
123 KEYVALUE_ENTRY(LIGHTYELLOW, "LightYellow"),
124 KEYVALUE_ENTRY(MAGENTA, "Magenta"),
125 KEYVALUE_ENTRY(NONE, "NONE"),
126 KEYVALUE_ENTRY(RED, "Red"),
127 KEYVALUE_ENTRY(WHITE, "White"),
128 KEYVALUE_ENTRY(YELLOW, "Yellow")
129};
130
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200131/*
132 * Structure that stores information about a highlight group.
133 * The ID of a highlight group is also called group ID. It is the index in
134 * the highlight_ga array PLUS ONE.
135 */
136typedef struct
137{
138 char_u *sg_name; // highlight group name
139 char_u *sg_name_u; // uppercase of sg_name
140 int sg_cleared; // "hi clear" was used
141// for normal terminals
142 int sg_term; // "term=" highlighting attributes
143 char_u *sg_start; // terminal string for start highl
144 char_u *sg_stop; // terminal string for stop highl
145 int sg_term_attr; // Screen attr for term mode
146// for color terminals
147 int sg_cterm; // "cterm=" highlighting attr
148 int sg_cterm_bold; // bold attr was set for light color
149 int sg_cterm_fg; // terminal fg color number + 1
150 int sg_cterm_bg; // terminal bg color number + 1
Bram Moolenaare023e882020-05-31 16:42:30 +0200151 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200152 int sg_cterm_attr; // Screen attr for color term mode
PMuncha606f3a2023-11-15 15:35:49 +0100153 int sg_cterm_font; // terminal alternative font (0 for normal)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200154// for when using the GUI
155#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
156 guicolor_T sg_gui_fg; // GUI foreground color handle
157 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +0200158 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200159#endif
160#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200161 GuiFont sg_font; // GUI font handle
162#ifdef FEAT_XFONTSET
163 GuiFontset sg_fontset; // GUI fontset handle
164#endif
165 char_u *sg_font_name; // GUI font or fontset name
166 int sg_gui_attr; // Screen attr for GUI mode
167#endif
168#if defined(FEAT_GUI) || defined(FEAT_EVAL)
169// Store the sp color name for the GUI or synIDattr()
170 int sg_gui; // "gui=" highlighting attributes
171 char_u *sg_gui_fg_name;// GUI foreground color name
172 char_u *sg_gui_bg_name;// GUI background color name
173 char_u *sg_gui_sp_name;// GUI special color name
174#endif
175 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +0200176 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200177 int sg_set; // combination of SG_* flags
178#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +0200179 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200180 sctx_T sg_script_ctx; // script in which the group was last set
181#endif
182} hl_group_T;
183
184// highlight groups for 'highlight' option
185static garray_T highlight_ga;
186#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
187
188/*
189 * An attribute number is the index in attr_table plus ATTR_OFF.
190 */
191#define ATTR_OFF (HL_ALL + 1)
192
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200193static void syn_unadd_group(void);
194static void set_hl_attr(int idx);
195static void highlight_list_one(int id);
196static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
197static int syn_add_group(char_u *name);
198static int hl_has_settings(int idx, int check_link);
199static void highlight_clear(int idx);
200
201#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
202static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
203#endif
204#ifdef FEAT_GUI
205static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
206static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
207#endif
208
209/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200210 * The default highlight groups. These are compiled-in for fast startup and
211 * they still work when the runtime files can't be found.
212 * When making changes here, also change runtime/colors/default.vim!
213 * The #ifdefs are needed to reduce the amount of static data. Helps to make
214 * the 16 bit DOS (museum) version compile.
215 */
216#if defined(FEAT_GUI) || defined(FEAT_EVAL)
217# define CENT(a, b) b
218#else
219# define CENT(a, b) a
220#endif
221static char *(highlight_init_both[]) = {
222 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
223 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
224 CENT("IncSearch term=reverse cterm=reverse",
225 "IncSearch term=reverse cterm=reverse gui=reverse"),
226 CENT("ModeMsg term=bold cterm=bold",
227 "ModeMsg term=bold cterm=bold gui=bold"),
228 CENT("NonText term=bold ctermfg=Blue",
229 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
230 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
231 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
232 CENT("StatusLineNC term=reverse cterm=reverse",
233 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
234 "default link EndOfBuffer NonText",
235 CENT("VertSplit term=reverse cterm=reverse",
236 "VertSplit term=reverse cterm=reverse gui=reverse"),
237#ifdef FEAT_CLIPBOARD
238 CENT("VisualNOS term=underline,bold cterm=underline,bold",
239 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
240#endif
241#ifdef FEAT_DIFF
242 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
243 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
Yee Cheng Chin9943d472025-03-26 19:41:02 +0100244 "default link DiffTextAdd DiffText",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200245#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200246 CENT("PmenuSbar ctermbg=Grey",
247 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200248 CENT("TabLineSel term=bold cterm=bold",
249 "TabLineSel term=bold cterm=bold gui=bold"),
250 CENT("TabLineFill term=reverse cterm=reverse",
251 "TabLineFill term=reverse cterm=reverse gui=reverse"),
Naruhiko Nishinobe5bd4d2025-05-14 21:20:28 +0200252 "default link TabPanel TabLine",
253 "default link TabPanelSel TabLineSel",
254 "default link TabPanelFill TabLineFill",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200255#ifdef FEAT_GUI
256 "Cursor guibg=fg guifg=bg",
257 "lCursor guibg=fg guifg=bg", // should be different, but what?
258#endif
259 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000260 "default link CursorLineSign SignColumn",
261 "default link CursorLineFold FoldColumn",
LemonBoya4399382022-04-09 21:04:08 +0100262 "default link CurSearch Search",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000263 "default link PmenuKind Pmenu",
264 "default link PmenuKindSel PmenuSel",
glepnir40c1c332024-06-11 19:37:04 +0200265 "default link PmenuMatch Pmenu",
266 "default link PmenuMatchSel PmenuSel",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000267 "default link PmenuExtra Pmenu",
268 "default link PmenuExtraSel PmenuSel",
Yee Cheng Chine700dde2025-02-20 21:58:21 +0100269 "default link PopupSelected PmenuSel",
270 "default link MessageWindow WarningMsg",
271 "default link PopupNotification WarningMsg",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200272 CENT("Normal cterm=NONE", "Normal gui=NONE"),
273 NULL
274};
275
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200276// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200277static char *(highlight_init_light[]) = {
278 CENT("Directory term=bold ctermfg=DarkBlue",
279 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
280 CENT("LineNr term=underline ctermfg=Brown",
281 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200282 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
283 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200284 CENT("MoreMsg term=bold ctermfg=DarkGreen",
285 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
286 CENT("Question term=standout ctermfg=DarkGreen",
287 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
288 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
289 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
290#ifdef FEAT_SPELL
291 CENT("SpellBad term=reverse ctermbg=LightRed",
292 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
293 CENT("SpellCap term=reverse ctermbg=LightBlue",
294 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
295 CENT("SpellRare term=reverse ctermbg=LightMagenta",
296 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
297 CENT("SpellLocal term=underline ctermbg=Cyan",
298 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
299#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200300 CENT("PmenuThumb ctermbg=Black",
301 "PmenuThumb ctermbg=Black guibg=Black"),
302 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
303 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
304 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
305 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200306 CENT("SpecialKey term=bold ctermfg=DarkBlue",
307 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
308 CENT("Title term=bold ctermfg=DarkMagenta",
309 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
310 CENT("WarningMsg term=standout ctermfg=DarkRed",
311 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200312 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
313 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200314#ifdef FEAT_FOLDING
315 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
316 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
317 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
318 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
319#endif
320#ifdef FEAT_SIGNS
321 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
322 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
323#endif
Maxim Kim59bafc82024-02-01 21:07:51 +0100324 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100325 "Visual ctermbg=Grey ctermfg=Black guibg=LightGrey guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200326#ifdef FEAT_DIFF
327 CENT("DiffAdd term=bold ctermbg=LightBlue",
328 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
329 CENT("DiffChange term=bold ctermbg=LightMagenta",
330 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
331 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
332 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
333#endif
334 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
335 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
336#ifdef FEAT_SYN_HL
337 CENT("CursorColumn term=reverse ctermbg=LightGrey",
338 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
339 CENT("CursorLine term=underline cterm=underline",
340 "CursorLine term=underline cterm=underline guibg=Grey90"),
341 CENT("ColorColumn term=reverse ctermbg=LightRed",
342 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
343#endif
344#ifdef FEAT_CONCEAL
345 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
346 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
347#endif
348 CENT("MatchParen term=reverse ctermbg=Cyan",
349 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
350#ifdef FEAT_TERMINAL
351 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
352 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
353 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
354 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
355#endif
356#ifdef FEAT_MENU
357 CENT("ToolbarLine term=underline ctermbg=LightGrey",
358 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
359 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
360 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
361#endif
362 NULL
363};
364
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200365// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200366static char *(highlight_init_dark[]) = {
367 CENT("Directory term=bold ctermfg=LightCyan",
368 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
369 CENT("LineNr term=underline ctermfg=Yellow",
370 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200371 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
372 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200373 CENT("MoreMsg term=bold ctermfg=LightGreen",
374 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
375 CENT("Question term=standout ctermfg=LightGreen",
376 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
377 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
378 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
379 CENT("SpecialKey term=bold ctermfg=LightBlue",
380 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
381#ifdef FEAT_SPELL
382 CENT("SpellBad term=reverse ctermbg=Red",
383 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
384 CENT("SpellCap term=reverse ctermbg=Blue",
385 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
386 CENT("SpellRare term=reverse ctermbg=Magenta",
387 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
388 CENT("SpellLocal term=underline ctermbg=Cyan",
389 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
390#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200391 CENT("PmenuThumb ctermbg=White",
392 "PmenuThumb ctermbg=White guibg=White"),
393 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
394 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
395 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
396 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200397 CENT("Title term=bold ctermfg=LightMagenta",
398 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
399 CENT("WarningMsg term=standout ctermfg=LightRed",
400 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200401 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
402 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200403#ifdef FEAT_FOLDING
404 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
405 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
406 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
407 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
408#endif
409#ifdef FEAT_SIGNS
410 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
411 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
412#endif
Christian Brabandte6d8b462024-01-28 23:33:29 +0100413 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100414 "Visual ctermbg=Grey ctermfg=Black guibg=#575757 guifg=LightGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200415#ifdef FEAT_DIFF
416 CENT("DiffAdd term=bold ctermbg=DarkBlue",
417 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
418 CENT("DiffChange term=bold ctermbg=DarkMagenta",
419 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
420 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
421 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
422#endif
423 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
424 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
425#ifdef FEAT_SYN_HL
426 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
427 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
428 CENT("CursorLine term=underline cterm=underline",
429 "CursorLine term=underline cterm=underline guibg=Grey40"),
430 CENT("ColorColumn term=reverse ctermbg=DarkRed",
431 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
432#endif
433 CENT("MatchParen term=reverse ctermbg=DarkCyan",
434 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
435#ifdef FEAT_CONCEAL
436 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
437 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
438#endif
439#ifdef FEAT_TERMINAL
440 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
441 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
442 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
443 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
444#endif
445#ifdef FEAT_MENU
446 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
447 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
448 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
449 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
450#endif
451 NULL
452};
453
Dominique Pelle748b3082022-01-08 12:41:16 +0000454#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200455/*
456 * Returns the number of highlight groups.
457 */
458 int
459highlight_num_groups(void)
460{
461 return highlight_ga.ga_len;
462}
463
464/*
465 * Returns the name of a highlight group.
466 */
467 char_u *
468highlight_group_name(int id)
469{
470 return HL_TABLE()[id].sg_name;
471}
472
473/*
474 * Returns the ID of the link to a highlight group.
475 */
476 int
477highlight_link_id(int id)
478{
479 return HL_TABLE()[id].sg_link;
480}
Dominique Pelle748b3082022-01-08 12:41:16 +0000481#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200482
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200483 void
484init_highlight(
485 int both, // include groups where 'bg' doesn't matter
486 int reset) // clear group first
487{
488 int i;
489 char **pp;
490 static int had_both = FALSE;
491#ifdef FEAT_EVAL
492 char_u *p;
493
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100494 // Try finding the color scheme file. Used when a color file was loaded
495 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200496 p = get_var_value((char_u *)"g:colors_name");
497 if (p != NULL)
498 {
499 // The value of g:colors_name could be freed when sourcing the script,
500 // making "p" invalid, so copy it.
501 char_u *copy_p = vim_strsave(p);
502 int r;
503
504 if (copy_p != NULL)
505 {
506 r = load_colors(copy_p);
507 vim_free(copy_p);
508 if (r == OK)
509 return;
510 }
511 }
512
513#endif
514
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100515 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200516 if (both)
517 {
518 had_both = TRUE;
519 pp = highlight_init_both;
520 for (i = 0; pp[i] != NULL; ++i)
521 do_highlight((char_u *)pp[i], reset, TRUE);
522 }
523 else if (!had_both)
524 // Don't do anything before the call with both == TRUE from main().
525 // Not everything has been setup then, and that call will overrule
526 // everything anyway.
527 return;
528
529 if (*p_bg == 'l')
530 pp = highlight_init_light;
531 else
532 pp = highlight_init_dark;
533 for (i = 0; pp[i] != NULL; ++i)
534 do_highlight((char_u *)pp[i], reset, TRUE);
535
Maxim Kim59bafc82024-02-01 21:07:51 +0100536 // Reverse looks ugly, but grey may not work for less than 8 colors. Thus
537 // let it depend on the number of colors available.
538 if (t_colors < 8)
539 do_highlight((char_u *)"Visual term=reverse cterm=reverse ctermbg=NONE ctermfg=NONE",
540 FALSE, TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200541 // With 8 colors brown is equal to yellow, need to use black for Search fg
542 // to avoid Statement highlighted text disappears.
543 // Clear the attributes, needed when changing the t_Co value.
Christian Brabandte6d8b462024-01-28 23:33:29 +0100544 if (t_colors <= 8)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200545 {
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200546 if (*p_bg == 'l')
547 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
548 }
549
550#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100551 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200552 if (get_var_value((char_u *)"g:syntax_on") != NULL)
553 {
554 static int recursive = 0;
555
556 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000557 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200558 else
559 {
560 ++recursive;
561 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
562 --recursive;
563 }
564 }
565#endif
566}
567
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000568#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
569/*
570 * Load a default color list. Intended to support legacy color names but allows
571 * the user to override the color values. Only loaded once.
572 */
573 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000574load_default_colors_lists(void)
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000575{
576 // Lacking a default color list isn't the end of the world but it is likely
577 // an inconvenience so users should know when it is missing.
578 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
579 msg("failed to load colors/lists/default.vim");
580}
581#endif
582
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200583/*
584 * Load color file "name".
585 * Return OK for success, FAIL for failure.
586 */
587 int
588load_colors(char_u *name)
589{
590 char_u *buf;
591 int retval = FAIL;
592 static int recursive = FALSE;
593
594 // When being called recursively, this is probably because setting
595 // 'background' caused the highlighting to be reloaded. This means it is
596 // working, thus we should return OK.
597 if (recursive)
598 return OK;
599
600 recursive = TRUE;
601 buf = alloc(STRLEN(name) + 12);
602 if (buf != NULL)
603 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100604#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100605 load_default_colors_lists();
606#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200607 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
608 curbuf->b_fname, FALSE, curbuf);
609 sprintf((char *)buf, "colors/%s.vim", name);
610 retval = source_runtime(buf, DIP_START + DIP_OPT);
611 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100612 if (retval == OK)
613 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
614 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200615 }
616 recursive = FALSE;
617
618 return retval;
619}
620
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200621static int color_numbers_16[28] = {0, 1, 2, 3,
622 4, 5, 6, 6,
623 7, 7, 7, 7,
624 8, 8,
625 9, 9, 10, 10,
626 11, 11, 12, 12, 13,
627 13, 14, 14, 15, -1};
628// for xterm with 88 colors...
629static int color_numbers_88[28] = {0, 4, 2, 6,
630 1, 5, 32, 72,
631 84, 84, 7, 7,
632 82, 82,
633 12, 43, 10, 61,
634 14, 63, 9, 74, 13,
635 75, 11, 78, 15, -1};
636// for xterm with 256 colors...
637static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200638 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200639 248, 248, 7, 7,
640 242, 242,
641 12, 81, 10, 121,
642 14, 159, 9, 224, 13,
643 225, 11, 229, 15, -1};
644// for terminals with less than 16 colors...
645static int color_numbers_8[28] = {0, 4, 2, 6,
646 1, 5, 3, 3,
647 7, 7, 7, 7,
648 0+8, 0+8,
649 4+8, 4+8, 2+8, 2+8,
650 6+8, 6+8, 1+8, 1+8, 5+8,
651 5+8, 3+8, 3+8, 7+8, -1};
652
653/*
654 * Lookup the "cterm" value to be used for color with index "idx" in
John Marriott34f00dd2024-04-08 23:28:12 +0200655 * color_name_tab[].
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200656 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
657 * colors, otherwise it will be unchanged.
658 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100659 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200660lookup_color(int idx, int foreground, int *boldp)
661{
662 int color = color_numbers_16[idx];
663 char_u *p;
664
665 // Use the _16 table to check if it's a valid color name.
666 if (color < 0)
667 return -1;
668
669 if (t_colors == 8)
670 {
671 // t_Co is 8: use the 8 colors table
672#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100673 // On qnx, the 8 & 16 color arrays are the same
674 if (STRNCMP(T_NAME, "qansi", 5) == 0)
675 color = color_numbers_16[idx];
676 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200677#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100678 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200679 if (foreground)
680 {
681 // set/reset bold attribute to get light foreground
682 // colors (on some terminals, e.g. "linux")
683 if (color & 8)
684 *boldp = TRUE;
685 else
686 *boldp = FALSE;
687 }
688 color &= 7; // truncate to 8 colors
689 }
690 else if (t_colors == 16 || t_colors == 88
691 || t_colors >= 256)
692 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100693 // Guess: if the termcap entry ends in 'm', it is
694 // probably an xterm-like terminal. Use the changed
695 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200696 if (*T_CAF != NUL)
697 p = T_CAF;
698 else
699 p = T_CSF;
700 if (*p != NUL && (t_colors > 256
701 || *(p + STRLEN(p) - 1) == 'm'))
702 {
703 if (t_colors == 88)
704 color = color_numbers_88[idx];
705 else if (t_colors >= 256)
706 color = color_numbers_256[idx];
707 else
708 color = color_numbers_8[idx];
709 }
710#ifdef FEAT_TERMRESPONSE
711 if (t_colors >= 256 && color == 15 && is_mac_terminal)
712 // Terminal.app has a bug: 15 is light grey. Use white
713 // from the color cube instead.
714 color = 231;
715#endif
716 }
717 return color;
718}
719
720/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100721 * Link highlight group 'from_hg' to 'to_hg'.
722 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000723 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100724 * 'init' is set to TRUE when initializing all the highlight groups.
725 */
726 static void
727highlight_group_link(
728 char_u *from_hg,
729 int from_len,
730 char_u *to_hg,
731 int to_len,
732 int dodefault,
733 int forceit,
734 int init)
735{
736 int from_id;
737 int to_id;
738 hl_group_T *hlgroup = NULL;
739
740 from_id = syn_check_group(from_hg, from_len);
741 if (STRNCMP(to_hg, "NONE", 4) == 0)
742 to_id = 0;
743 else
744 to_id = syn_check_group(to_hg, to_len);
745
746 if (from_id > 0)
747 {
748 hlgroup = &HL_TABLE()[from_id - 1];
749 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
750 {
751 hlgroup->sg_deflink = to_id;
752#ifdef FEAT_EVAL
753 hlgroup->sg_deflink_sctx = current_sctx;
754 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
755#endif
756 }
757 }
758
759 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
760 {
761 // Don't allow a link when there already is some highlighting
762 // for the group, unless '!' is used
763 if (to_id > 0 && !forceit && !init
764 && hl_has_settings(from_id - 1, dodefault))
765 {
766 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000767 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100768 }
769 else if (hlgroup->sg_link != to_id
770#ifdef FEAT_EVAL
771 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
772#endif
773 || hlgroup->sg_cleared)
774 {
775 if (!init)
776 hlgroup->sg_set |= SG_LINK;
777 hlgroup->sg_link = to_id;
778#ifdef FEAT_EVAL
779 hlgroup->sg_script_ctx = current_sctx;
780 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
781#endif
782 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100783 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100784
785 // Only call highlight_changed() once after multiple changes.
786 need_highlight_changed = TRUE;
787 }
788 }
789
790}
791
792/*
793 * Reset all highlighting to the defaults. Removes all highlighting for the
794 * groups added by the user.
795 */
796 static void
797highlight_reset_all(void)
798{
799 int idx;
800
801#ifdef FEAT_GUI
802 // First, we do not destroy the old values, but allocate the new
803 // ones and update the display. THEN we destroy the old values.
804 // If we destroy the old values first, then the old values
805 // (such as GuiFont's or GuiFontset's) will still be displayed but
806 // invalid because they were free'd.
807 if (gui.in_use)
808 {
809# ifdef FEAT_BEVAL_TIP
810 gui_init_tooltip_font();
811# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100812# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100813 gui_init_menu_font();
814# endif
815 }
816# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
817 gui_mch_def_colors();
818# endif
819# ifdef FEAT_GUI_X11
820# ifdef FEAT_MENU
821
822 // This only needs to be done when there is no Menu highlight
823 // group defined by default, which IS currently the case.
824 gui_mch_new_menu_colors();
825# endif
826 if (gui.in_use)
827 {
828 gui_new_scrollbar_colors();
829# ifdef FEAT_BEVAL_GUI
830 gui_mch_new_tooltip_colors();
831# endif
832# ifdef FEAT_MENU
833 gui_mch_new_menu_font();
834# endif
835 }
836# endif
837
838 // Ok, we're done allocating the new default graphics items.
839 // The screen should already be refreshed at this point.
840 // It is now Ok to clear out the old data.
841#endif
842#ifdef FEAT_EVAL
843 do_unlet((char_u *)"g:colors_name", TRUE);
844#endif
845 restore_cterm_colors();
846
847 // Clear all default highlight groups and load the defaults.
848 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
849 highlight_clear(idx);
850 init_highlight(TRUE, TRUE);
851#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
852 if (USE_24BIT)
853 highlight_gui_started();
854 else
855#endif
856 highlight_changed();
857 redraw_later_clear();
858}
859
860/*
861 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
862 * index 'idx'.
863 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
864 * 'arg' is the list of attribute names separated by comma.
865 * 'init' is set to TRUE when initializing all the highlight groups.
866 * Returns TRUE if the attributes are set.
867 */
868 static int
869highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
870{
871 int attr;
Mike Williams72a156b2024-04-09 22:04:54 +0200872 size_t off;
John Marriott34f00dd2024-04-08 23:28:12 +0200873 keyvalue_T target;
874 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100875
876 attr = 0;
877 off = 0;
John Marriott34f00dd2024-04-08 23:28:12 +0200878 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +0100879 target.value.length = 0; // not used, see cmp_keyvalue_value_ni()
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100880 while (arg[off] != NUL)
881 {
John Marriott8d4477e2024-11-02 15:59:01 +0100882 target.value.string = arg + off;
883 entry = (keyvalue_T *)bsearch(&target, &highlight_tab,
884 ARRAY_LENGTH(highlight_tab), sizeof(highlight_tab[0]),
885 cmp_keyvalue_value_ni);
John Marriott34f00dd2024-04-08 23:28:12 +0200886 if (entry == NULL)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100887 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000888 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100889 return FALSE;
890 }
John Marriott34f00dd2024-04-08 23:28:12 +0200891
892 attr |= entry->key;
John Marriott8d4477e2024-11-02 15:59:01 +0100893 off += entry->value.length;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100894 if (arg[off] == ',') // another one follows
895 ++off;
896 }
897 if (*key == 'T')
898 {
899 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
900 {
901 if (!init)
902 HL_TABLE()[idx].sg_set |= SG_TERM;
903 HL_TABLE()[idx].sg_term = attr;
904 }
905 }
906 else if (*key == 'C')
907 {
908 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
909 {
910 if (!init)
911 HL_TABLE()[idx].sg_set |= SG_CTERM;
912 HL_TABLE()[idx].sg_cterm = attr;
913 HL_TABLE()[idx].sg_cterm_bold = FALSE;
914 }
915 }
916#if defined(FEAT_GUI) || defined(FEAT_EVAL)
917 else
918 {
919 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
920 {
921 if (!init)
922 HL_TABLE()[idx].sg_set |= SG_GUI;
923 HL_TABLE()[idx].sg_gui = attr;
924 }
925 }
926#endif
927
928 return TRUE;
929}
930
931#ifdef FEAT_GUI
932/*
933 * Set the font for the highlight group at 'idx'.
934 * 'arg' is the font name.
935 * Returns TRUE if the font is changed.
936 */
937 static int
938highlight_set_font(
939 int idx,
940 char_u *arg,
941 int is_normal_group,
942 int is_menu_group,
943 int is_tooltip_group)
944{
945 int did_change = FALSE;
946
947 // in non-GUI fonts are simply ignored
948 if (HL_TABLE()[idx].sg_font_name != NULL
949 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
950 {
951 // Font name didn't change, ignore.
952 }
953 else if (!gui.shell_created)
954 {
955 // GUI not started yet, always accept the name.
956 vim_free(HL_TABLE()[idx].sg_font_name);
957 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
958 did_change = TRUE;
959 }
960 else
961 {
962 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
963# ifdef FEAT_XFONTSET
964 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
965# endif
966 // First, save the current font/fontset.
967 // Then try to allocate the font/fontset.
968 // If the allocation fails, HL_TABLE()[idx].sg_font OR
969 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
970
971 HL_TABLE()[idx].sg_font = NOFONT;
972# ifdef FEAT_XFONTSET
973 HL_TABLE()[idx].sg_fontset = NOFONTSET;
974# endif
975 hl_do_font(idx, arg, is_normal_group, is_menu_group,
976 is_tooltip_group, FALSE);
977
978# ifdef FEAT_XFONTSET
979 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
980 {
981 // New fontset was accepted. Free the old one, if there
982 // was one.
983 gui_mch_free_fontset(temp_sg_fontset);
984 vim_free(HL_TABLE()[idx].sg_font_name);
985 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
986 did_change = TRUE;
987 }
988 else
989 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
990# endif
991 if (HL_TABLE()[idx].sg_font != NOFONT)
992 {
993 // New font was accepted. Free the old one, if there was
994 // one.
995 gui_mch_free_font(temp_sg_font);
996 vim_free(HL_TABLE()[idx].sg_font_name);
997 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
998 did_change = TRUE;
999 }
1000 else
1001 HL_TABLE()[idx].sg_font = temp_sg_font;
1002 }
1003
1004 return did_change;
1005}
1006#endif
1007
1008/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001009 * Set the cterm foreground color for the Normal highlight group to "color" and
1010 * the bold attribute to "bold".
1011 */
1012 static void
1013hl_set_ctermfg_normal_group(int color, int bold)
1014{
1015 cterm_normal_fg_color = color + 1;
1016 cterm_normal_fg_bold = bold;
1017#ifdef FEAT_GUI
1018 // Don't do this if the GUI is used.
1019 if (!gui.in_use && !gui.starting)
1020#endif
1021 {
1022 set_must_redraw(UPD_CLEAR);
1023 if (termcap_active && color >= 0)
1024 term_fg_color(color);
1025 }
1026}
1027
1028/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001029 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001030 */
1031 static void
1032highlight_set_ctermfg(int idx, int color, int is_normal_group)
1033{
1034 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1035 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001036 hl_set_ctermfg_normal_group(color,
1037 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001038}
1039
1040/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001041 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001042 */
1043 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001044hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001045{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001046 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001047#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001048 // Don't mess with 'background' if the GUI is used.
1049 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001050#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001051 {
1052 set_must_redraw(UPD_CLEAR);
1053 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001054 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001055 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001056
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001057 if (termcap_active)
1058 term_bg_color(color);
1059 if (t_colors < 16)
1060 dark = (color == 0 || color == 4);
1061 // Limit the heuristic to the standard 16 colors
1062 else if (color < 16)
1063 dark = (color < 7 || color == 8);
1064 // Set the 'background' option if the value is
1065 // wrong.
1066 if (dark != -1
1067 && dark != (*p_bg == 'd')
1068 && !option_was_set((char_u *)"bg"))
1069 {
1070 set_option_value_give_err((char_u *)"bg",
1071 0L, (char_u *)(dark ? "dark" : "light"), 0);
1072 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001073 }
1074 }
1075 }
1076}
1077
1078/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001079 * Set the cterm background color for the highlight group at 'idx' to 'color'.
1080 */
1081 static void
1082highlight_set_ctermbg(int idx, int color, int is_normal_group)
1083{
1084 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1085 if (is_normal_group)
1086 hl_set_ctermbg_normal_group(color);
1087}
1088
1089/*
1090 * Set the cterm underline color for the Normal highlight group to "color".
1091 */
1092 static void
1093hl_set_ctermul_normal_group(int color)
1094{
1095 cterm_normal_ul_color = color + 1;
1096#ifdef FEAT_GUI
1097 // Don't do this if the GUI is used.
1098 if (!gui.in_use && !gui.starting)
1099#endif
1100 {
1101 set_must_redraw(UPD_CLEAR);
1102 if (termcap_active && color >= 0)
1103 term_ul_color(color);
1104 }
1105}
1106
1107/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001108 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001109 */
1110 static void
1111highlight_set_ctermul(int idx, int color, int is_normal_group)
1112{
1113 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1114 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001115 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001116}
1117
1118/*
PMuncha606f3a2023-11-15 15:35:49 +01001119 * Set the cterm font for the highlight group at 'idx'.
1120 * 'arg' is the color name or the numeric value as a string.
1121 * 'init' is set to TRUE when initializing highlighting.
1122 * Called for the ":highlight" command and the "hlset()" function.
1123 *
1124 * Returns TRUE if the font is set.
1125 */
1126 static int
1127highlight_set_cterm_font(
1128 int idx,
1129 char_u *arg,
1130 int init)
1131{
1132 int font;
1133
1134 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1135 return FALSE;
1136
1137 if (!init)
1138 HL_TABLE()[idx].sg_set |= SG_CTERM;
1139
1140 if (VIM_ISDIGIT(*arg))
1141 font = atoi((char *)arg);
1142 else if (STRICMP(arg, "NONE") == 0)
1143 font = -1;
1144 else
1145 return FALSE;
1146
1147 HL_TABLE()[idx].sg_cterm_font = font + 1;
1148 return TRUE;
1149}
1150
1151/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001152 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1153 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1154 * 'keystart' is the color name/value.
1155 * 'arg' is the color name or the numeric value as a string.
1156 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1157 * 'init' is set to TRUE when initializing highlighting.
1158 * Called for the ":highlight" command and the "hlset()" function.
1159 *
1160 * Returns TRUE if the color is set.
1161 */
1162 static int
1163highlight_set_cterm_color(
1164 int idx,
1165 char_u *key,
1166 char_u *key_start,
1167 char_u *arg,
1168 int is_normal_group,
1169 int init)
1170{
1171 int color;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001172
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001173 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1174 return FALSE;
1175
1176 if (!init)
1177 HL_TABLE()[idx].sg_set |= SG_CTERM;
1178
1179 // When setting the foreground color, and previously the "bold"
1180 // flag was set for a light color, reset it now
1181 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001182 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001183 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1184 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1185 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001186
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001187 if (VIM_ISDIGIT(*arg))
1188 color = atoi((char *)arg);
1189 else if (STRICMP(arg, "fg") == 0)
1190 {
1191 if (cterm_normal_fg_color)
1192 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001193 else
1194 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001195 emsg(_(e_fg_color_unknown));
1196 return FALSE;
1197 }
1198 }
1199 else if (STRICMP(arg, "bg") == 0)
1200 {
1201 if (cterm_normal_bg_color > 0)
1202 color = cterm_normal_bg_color - 1;
1203 else
1204 {
1205 emsg(_(e_bg_color_unknown));
1206 return FALSE;
1207 }
1208 }
1209 else if (STRICMP(arg, "ul") == 0)
1210 {
1211 if (cterm_normal_ul_color > 0)
1212 color = cterm_normal_ul_color - 1;
1213 else
1214 {
1215 emsg(_(e_ul_color_unknown));
1216 return FALSE;
1217 }
1218 }
1219 else
1220 {
1221 int bold = MAYBE;
John Marriott34f00dd2024-04-08 23:28:12 +02001222 keyvalue_T target;
1223 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001224
John Marriott34f00dd2024-04-08 23:28:12 +02001225 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01001226 target.value.string = arg;
1227 target.value.length = 0; // not used, see cmp_keyvalue_value_i()
1228 entry = (keyvalue_T *)bsearch(&target, &color_name_tab,
1229 ARRAY_LENGTH(color_name_tab), sizeof(color_name_tab[0]),
1230 cmp_keyvalue_value_i);
John Marriott34f00dd2024-04-08 23:28:12 +02001231 if (entry == NULL)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001232 {
1233 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1234 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001235 }
1236
John Marriott34f00dd2024-04-08 23:28:12 +02001237 color = lookup_color(entry->key, key[5] == 'F', &bold);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001238
1239 // set/reset bold attribute to get light foreground
1240 // colors (on some terminals, e.g. "linux")
1241 if (bold == TRUE)
1242 {
1243 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1244 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1245 }
1246 else if (bold == FALSE)
1247 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001248 }
1249
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001250 // Add one to the argument, to avoid zero. Zero is used for
1251 // "NONE", then "color" is -1.
1252 if (key[5] == 'F')
1253 highlight_set_ctermfg(idx, color, is_normal_group);
1254 else if (key[5] == 'B')
1255 highlight_set_ctermbg(idx, color, is_normal_group);
1256 else // ctermul
1257 highlight_set_ctermul(idx, color, is_normal_group);
1258
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001259 return TRUE;
1260}
1261
1262#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1263/*
1264 * Set the GUI foreground color for the highlight group at 'idx'.
1265 * Returns TRUE if the color is set.
1266 */
1267 static int
1268highlight_set_guifg(
1269 int idx,
1270 char_u *arg,
1271 int is_menu_group UNUSED,
1272 int is_scrollbar_group UNUSED,
1273 int is_tooltip_group UNUSED,
1274 int *do_colors UNUSED,
1275 int init)
1276{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001277# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001278 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001279# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001280 char_u **namep;
1281 int did_change = FALSE;
1282
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001283 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1284 return FALSE;
1285
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001286 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001287 if (!init)
1288 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001289
1290# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001291 // In GUI guifg colors are only used when recognized
1292 i = color_name2handle(arg);
1293 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1294 {
1295 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001296# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001297 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1298 {
1299 vim_free(*namep);
1300 if (STRCMP(arg, "NONE") != 0)
1301 *namep = vim_strsave(arg);
1302 else
1303 *namep = NULL;
1304 did_change = TRUE;
1305 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001306# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1307# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001308 if (is_menu_group && gui.menu_fg_pixel != i)
1309 {
1310 gui.menu_fg_pixel = i;
1311 *do_colors = TRUE;
1312 }
1313 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1314 {
1315 gui.scroll_fg_pixel = i;
1316 *do_colors = TRUE;
1317 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001318# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001319 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1320 {
1321 gui.tooltip_fg_pixel = i;
1322 *do_colors = TRUE;
1323 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001324# endif
1325# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001326 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001327# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001328
1329 return did_change;
1330}
1331
1332/*
1333 * Set the GUI background color for the highlight group at 'idx'.
1334 * Returns TRUE if the color is set.
1335 */
1336 static int
1337highlight_set_guibg(
1338 int idx,
1339 char_u *arg,
1340 int is_menu_group UNUSED,
1341 int is_scrollbar_group UNUSED,
1342 int is_tooltip_group UNUSED,
1343 int *do_colors UNUSED,
1344 int init)
1345{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001346# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001347 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001348# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001349 char_u **namep;
1350 int did_change = FALSE;
1351
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001352 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1353 return FALSE;
1354
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001355 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001356 if (!init)
1357 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001358
1359# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001360 // In GUI guibg colors are only used when recognized
1361 i = color_name2handle(arg);
1362 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1363 {
1364 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001365# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001366 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1367 {
1368 vim_free(*namep);
1369 if (STRCMP(arg, "NONE") != 0)
1370 *namep = vim_strsave(arg);
1371 else
1372 *namep = NULL;
1373 did_change = TRUE;
1374 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001375# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1376# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001377 if (is_menu_group && gui.menu_bg_pixel != i)
1378 {
1379 gui.menu_bg_pixel = i;
1380 *do_colors = TRUE;
1381 }
1382 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1383 {
1384 gui.scroll_bg_pixel = i;
1385 *do_colors = TRUE;
1386 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001387# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001388 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1389 {
1390 gui.tooltip_bg_pixel = i;
1391 *do_colors = TRUE;
1392 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001393# endif
1394# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001395 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001396# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001397
1398 return did_change;
1399}
1400
1401/*
1402 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1403 * Returns TRUE if the color is set.
1404 */
1405 static int
1406highlight_set_guisp(int idx, char_u *arg, int init)
1407{
1408# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1409 int i;
1410# endif
1411 int did_change = FALSE;
1412 char_u **namep;
1413
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001414 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1415 return FALSE;
1416
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001417 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001418 if (!init)
1419 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001420
1421# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001422 // In GUI guisp colors are only used when recognized
1423 i = color_name2handle(arg);
1424 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1425 {
1426 HL_TABLE()[idx].sg_gui_sp = i;
1427# endif
1428 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001429 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001430 vim_free(*namep);
1431 if (STRCMP(arg, "NONE") != 0)
1432 *namep = vim_strsave(arg);
1433 else
1434 *namep = NULL;
1435 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001436 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001437# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001438 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001439# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001440
1441 return did_change;
1442}
1443#endif
1444
1445/*
1446 * Set the start/stop terminal codes for a highlight group.
1447 * Returns TRUE if the terminal code is set.
1448 */
1449 static int
1450highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1451{
1452 int off;
1453 char_u buf[100];
1454 int len;
1455 char_u *tname;
1456 char_u *p;
1457
1458 if (!init)
1459 HL_TABLE()[idx].sg_set |= SG_TERM;
1460
1461 // The "start" and "stop" arguments can be a literal escape
1462 // sequence, or a comma separated list of terminal codes.
1463 if (STRNCMP(arg, "t_", 2) == 0)
1464 {
1465 off = 0;
1466 buf[0] = 0;
1467 while (arg[off] != NUL)
1468 {
1469 // Isolate one termcap name
1470 for (len = 0; arg[off + len] &&
1471 arg[off + len] != ','; ++len)
1472 ;
1473 tname = vim_strnsave(arg + off, len);
1474 if (tname == NULL) // out of memory
1475 return FALSE;
1476 // lookup the escape sequence for the item
1477 p = get_term_code(tname);
1478 vim_free(tname);
1479 if (p == NULL) // ignore non-existing things
1480 p = (char_u *)"";
1481
1482 // Append it to the already found stuff
1483 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1484 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001485 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001486 return FALSE;
1487 }
1488 STRCAT(buf, p);
1489
1490 // Advance to the next item
1491 off += len;
1492 if (arg[off] == ',') // another one follows
1493 ++off;
1494 }
1495 }
1496 else
1497 {
1498 // Copy characters from arg[] to buf[], translating <> codes.
1499 for (p = arg, off = 0; off < 100 - 6 && *p; )
1500 {
zeertzjqdb088872022-05-02 22:53:45 +01001501 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001502 if (len > 0) // recognized special char
1503 off += len;
1504 else // copy as normal char
1505 buf[off++] = *p++;
1506 }
1507 buf[off] = NUL;
1508 }
1509
1510 if (STRCMP(buf, "NONE") == 0) // resetting the value
1511 p = NULL;
1512 else
1513 p = vim_strsave(buf);
1514 if (key[2] == 'A')
1515 {
1516 vim_free(HL_TABLE()[idx].sg_start);
1517 HL_TABLE()[idx].sg_start = p;
1518 }
1519 else
1520 {
1521 vim_free(HL_TABLE()[idx].sg_stop);
1522 HL_TABLE()[idx].sg_stop = p;
1523 }
1524 return TRUE;
1525}
1526
1527/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001528 * Handle the ":highlight .." command.
1529 * When using ":hi clear" this is called recursively for each group with
1530 * "forceit" and "init" both TRUE.
1531 */
1532 void
1533do_highlight(
1534 char_u *line,
1535 int forceit,
1536 int init) // TRUE when called for initializing
1537{
1538 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001539 char_u *linep;
1540 char_u *key_start;
1541 char_u *arg_start;
1542 char_u *key = NULL, *arg = NULL;
1543 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001544 int id;
1545 int idx;
1546 hl_group_T item_before;
1547 int did_change = FALSE;
1548 int dodefault = FALSE;
1549 int doclear = FALSE;
1550 int dolink = FALSE;
1551 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001552 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001553#ifdef FEAT_GUI_X11
1554 int is_menu_group = FALSE; // "Menu" group
1555 int is_scrollbar_group = FALSE; // "Scrollbar" group
1556 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001557#else
1558# define is_menu_group 0
1559# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001560# define is_scrollbar_group 0
1561#endif
1562#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1563 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001564#endif
1565#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1566 int did_highlight_changed = FALSE;
1567#endif
1568
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001569 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001570 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001571 {
1572 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1573 // TODO: only call when the group has attributes set
1574 highlight_list_one((int)i);
1575 return;
1576 }
1577
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001578 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001579 name_end = skiptowhite(line);
1580 linep = skipwhite(name_end);
1581
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001582 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001583 if (STRNCMP(line, "default", name_end - line) == 0)
1584 {
1585 dodefault = TRUE;
1586 line = linep;
1587 name_end = skiptowhite(line);
1588 linep = skipwhite(name_end);
1589 }
1590
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001591 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001592 if (STRNCMP(line, "clear", name_end - line) == 0)
1593 doclear = TRUE;
1594 if (STRNCMP(line, "link", name_end - line) == 0)
1595 dolink = TRUE;
1596
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001597 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001598 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001599 {
1600 id = syn_namen2id(line, (int)(name_end - line));
1601 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001602 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001603 else
1604 highlight_list_one(id);
1605 return;
1606 }
1607
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001608 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001609 if (dolink)
1610 {
1611 char_u *from_start = linep;
1612 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001613 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001614 char_u *to_start;
1615 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001616 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001617
1618 from_end = skiptowhite(from_start);
1619 to_start = skipwhite(from_end);
1620 to_end = skiptowhite(to_start);
1621
Bram Moolenaar1966c242020-04-20 22:42:32 +02001622 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001623 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001624 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001625 return;
1626 }
1627
Bram Moolenaar1966c242020-04-20 22:42:32 +02001628 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001629 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001630 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001631 return;
1632 }
1633
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001634 from_len = (int)(from_end - from_start);
1635 to_len = (int)(to_end - to_start);
1636 highlight_group_link(from_start, from_len, to_start, to_len,
1637 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001638 return;
1639 }
1640
1641 if (doclear)
1642 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001643 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001644 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001645 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001646 // ":highlight clear" without group name
1647 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001648 return;
1649 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001650 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001651 name_end = skiptowhite(line);
1652 linep = skipwhite(name_end);
1653 }
1654
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001655 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001656 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001657 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001658 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001659 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001660
1661 // Return if "default" was used and the group already has settings.
1662 if (dodefault && hl_has_settings(idx, TRUE))
1663 return;
1664
1665 // Make a copy so we can check if any attribute actually changed.
1666 item_before = HL_TABLE()[idx];
1667
1668 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1669 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001670#ifdef FEAT_GUI_X11
1671 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1672 is_menu_group = TRUE;
1673 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1674 is_scrollbar_group = TRUE;
1675 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1676 is_tooltip_group = TRUE;
1677#endif
1678
1679 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1680 if (doclear || (forceit && init))
1681 {
1682 highlight_clear(idx);
1683 if (!doclear)
1684 HL_TABLE()[idx].sg_set = 0;
1685 }
1686
1687 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001688 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001689 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001690 key_start = linep;
1691 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001692 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001693 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001694 error = TRUE;
1695 break;
1696 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001697
Yee Cheng China7b81202025-02-23 09:32:47 +01001698 // Note: Keep this in sync with get_highlight_group_key.
1699
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001700 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1701 // or "guibg").
1702 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1703 ++linep;
1704 vim_free(key);
1705 key = vim_strnsave_up(key_start, linep - key_start);
1706 if (key == NULL)
1707 {
1708 error = TRUE;
1709 break;
1710 }
1711 linep = skipwhite(linep);
1712
1713 if (STRCMP(key, "NONE") == 0)
1714 {
1715 if (!init || HL_TABLE()[idx].sg_set == 0)
1716 {
1717 if (!init)
1718 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1719 highlight_clear(idx);
1720 }
1721 continue;
1722 }
1723
1724 // Check for the equal sign.
1725 if (*linep != '=')
1726 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001727 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001728 error = TRUE;
1729 break;
1730 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001731 ++linep;
1732
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001733 // Isolate the argument.
1734 linep = skipwhite(linep);
1735 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001736 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001737 arg_start = ++linep;
1738 linep = vim_strchr(linep, '\'');
1739 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001740 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001741 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001742 error = TRUE;
1743 break;
1744 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001745 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001746 else
1747 {
1748 arg_start = linep;
1749 linep = skiptowhite(linep);
1750 }
1751 if (linep == arg_start)
1752 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001753 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001754 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001755 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001756 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001757 vim_free(arg);
1758 arg = vim_strnsave(arg_start, linep - arg_start);
1759 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001760 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001761 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001762 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001763 }
1764 if (*linep == '\'')
1765 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001766
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001767 // Store the argument.
1768 if (STRCMP(key, "TERM") == 0
1769 || STRCMP(key, "CTERM") == 0
1770 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001771 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001772 if (!highlight_set_termgui_attr(idx, key, arg, init))
1773 {
1774 error = TRUE;
1775 break;
1776 }
1777 }
1778 else if (STRCMP(key, "FONT") == 0)
1779 {
1780 // in non-GUI fonts are simply ignored
1781#ifdef FEAT_GUI
1782 if (highlight_set_font(idx, arg, is_normal_group,
1783 is_menu_group, is_tooltip_group))
1784 did_change = TRUE;
1785#endif
1786 }
1787 else if (STRCMP(key, "CTERMFG") == 0
1788 || STRCMP(key, "CTERMBG") == 0
1789 || STRCMP(key, "CTERMUL") == 0)
1790 {
1791 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1792 is_normal_group, init))
1793 {
1794 error = TRUE;
1795 break;
1796 }
1797 }
PMuncha606f3a2023-11-15 15:35:49 +01001798 else if (STRCMP(key, "CTERMFONT") == 0)
1799 {
1800 if (!highlight_set_cterm_font(idx, arg, init))
1801 {
1802 error = TRUE;
1803 break;
1804 }
1805 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001806 else if (STRCMP(key, "GUIFG") == 0)
1807 {
1808#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1809 if (highlight_set_guifg(idx, arg, is_menu_group,
1810 is_scrollbar_group, is_tooltip_group,
1811 &do_colors, init))
1812 did_change = TRUE;
1813#endif
1814 }
1815 else if (STRCMP(key, "GUIBG") == 0)
1816 {
1817#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1818 if (highlight_set_guibg(idx, arg, is_menu_group,
1819 is_scrollbar_group, is_tooltip_group,
1820 &do_colors, init))
1821 did_change = TRUE;
1822#endif
1823 }
1824 else if (STRCMP(key, "GUISP") == 0)
1825 {
1826#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1827 if (highlight_set_guisp(idx, arg, init))
1828 did_change = TRUE;
1829#endif
1830 }
1831 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1832 {
1833 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1834 {
1835 error = TRUE;
1836 break;
1837 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001838 }
1839 else
1840 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001841 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001842 error = TRUE;
1843 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001844 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001845 HL_TABLE()[idx].sg_cleared = FALSE;
1846
1847 // When highlighting has been given for a group, don't link it.
1848 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1849 HL_TABLE()[idx].sg_link = 0;
1850
1851 // Continue with next argument.
1852 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001853 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001854
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001855 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001856 if (error && idx == highlight_ga.ga_len)
1857 syn_unadd_group();
1858 else
1859 {
1860 if (is_normal_group)
1861 {
1862 HL_TABLE()[idx].sg_term_attr = 0;
1863 HL_TABLE()[idx].sg_cterm_attr = 0;
1864#ifdef FEAT_GUI
1865 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001866 // Need to update all groups, because they might be using "bg"
1867 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001868#endif
1869#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1870 if (USE_24BIT)
1871 {
1872 highlight_gui_started();
1873 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001874 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001875 }
1876#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001877#ifdef FEAT_VTP
1878 control_console_color_rgb();
1879#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001880 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001881#ifdef FEAT_GUI_X11
1882# ifdef FEAT_MENU
1883 else if (is_menu_group)
1884 {
1885 if (gui.in_use && do_colors)
1886 gui_mch_new_menu_colors();
1887 }
1888# endif
1889 else if (is_scrollbar_group)
1890 {
1891 if (gui.in_use && do_colors)
1892 gui_new_scrollbar_colors();
1893 else
1894 set_hl_attr(idx);
1895 }
1896# ifdef FEAT_BEVAL_GUI
1897 else if (is_tooltip_group)
1898 {
1899 if (gui.in_use && do_colors)
1900 gui_mch_new_tooltip_colors();
1901 }
1902# endif
1903#endif
1904 else
1905 set_hl_attr(idx);
1906#ifdef FEAT_EVAL
1907 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001908 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001909#endif
1910 }
1911
1912 vim_free(key);
1913 vim_free(arg);
1914
1915 // Only call highlight_changed() once, after a sequence of highlight
1916 // commands, and only if an attribute actually changed.
1917 if ((did_change
1918 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1919#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1920 && !did_highlight_changed
1921#endif
1922 )
1923 {
1924 // Do not trigger a redraw when highlighting is changed while
1925 // redrawing. This may happen when evaluating 'statusline' changes the
1926 // StatusLine group.
1927 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001928 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001929 need_highlight_changed = TRUE;
1930 }
1931}
1932
1933#if defined(EXITFREE) || defined(PROTO)
1934 void
1935free_highlight(void)
1936{
1937 int i;
1938
1939 for (i = 0; i < highlight_ga.ga_len; ++i)
1940 {
1941 highlight_clear(i);
1942 vim_free(HL_TABLE()[i].sg_name);
1943 vim_free(HL_TABLE()[i].sg_name_u);
1944 }
1945 ga_clear(&highlight_ga);
1946}
1947#endif
1948
1949/*
1950 * Reset the cterm colors to what they were before Vim was started, if
1951 * possible. Otherwise reset them to zero.
1952 */
1953 void
1954restore_cterm_colors(void)
1955{
1956#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1957 // Since t_me has been set, this probably means that the user
1958 // wants to use this as default colors. Need to reset default
1959 // background/foreground colors.
1960 mch_set_normal_colors();
1961#else
1962# ifdef VIMDLL
1963 if (!gui.in_use)
1964 {
1965 mch_set_normal_colors();
1966 return;
1967 }
1968# endif
1969 cterm_normal_fg_color = 0;
1970 cterm_normal_fg_bold = 0;
1971 cterm_normal_bg_color = 0;
1972# ifdef FEAT_TERMGUICOLORS
1973 cterm_normal_fg_gui_color = INVALCOLOR;
1974 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001975 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001976# endif
1977#endif
1978}
1979
1980/*
1981 * Return TRUE if highlight group "idx" has any settings.
1982 * When "check_link" is TRUE also check for an existing link.
1983 */
1984 static int
1985hl_has_settings(int idx, int check_link)
1986{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001987 return HL_TABLE()[idx].sg_cleared == 0
1988 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001989 || HL_TABLE()[idx].sg_cterm_attr != 0
1990 || HL_TABLE()[idx].sg_cterm_fg != 0
1991 || HL_TABLE()[idx].sg_cterm_bg != 0
PMuncha606f3a2023-11-15 15:35:49 +01001992 || HL_TABLE()[idx].sg_cterm_font != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001993#ifdef FEAT_GUI
1994 || HL_TABLE()[idx].sg_gui_attr != 0
1995 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1996 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1997 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1998 || HL_TABLE()[idx].sg_font_name != NULL
1999#endif
2000 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
2001}
2002
2003/*
2004 * Clear highlighting for one group.
2005 */
2006 static void
2007highlight_clear(int idx)
2008{
2009 HL_TABLE()[idx].sg_cleared = TRUE;
2010
2011 HL_TABLE()[idx].sg_term = 0;
2012 VIM_CLEAR(HL_TABLE()[idx].sg_start);
2013 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
2014 HL_TABLE()[idx].sg_term_attr = 0;
2015 HL_TABLE()[idx].sg_cterm = 0;
2016 HL_TABLE()[idx].sg_cterm_bold = FALSE;
2017 HL_TABLE()[idx].sg_cterm_fg = 0;
2018 HL_TABLE()[idx].sg_cterm_bg = 0;
2019 HL_TABLE()[idx].sg_cterm_attr = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002020 HL_TABLE()[idx].sg_cterm_font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002021#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2022 HL_TABLE()[idx].sg_gui = 0;
2023 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
2024 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
2025 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
2026#endif
2027#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2028 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
2029 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002030 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002031#endif
2032#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002033 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2034 HL_TABLE()[idx].sg_font = NOFONT;
2035# ifdef FEAT_XFONTSET
2036 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2037 HL_TABLE()[idx].sg_fontset = NOFONTSET;
2038# endif
2039 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
2040 HL_TABLE()[idx].sg_gui_attr = 0;
2041#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02002042 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02002043 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02002044#ifdef FEAT_EVAL
2045 // Since we set the default link, set the location to where the default
2046 // link was set.
2047 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002048#endif
2049}
2050
2051#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2052/*
2053 * Set the normal foreground and background colors according to the "Normal"
2054 * highlighting group. For X11 also set "Menu", "Scrollbar", and
2055 * "Tooltip" colors.
2056 */
2057 void
2058set_normal_colors(void)
2059{
2060# ifdef FEAT_GUI
2061# ifdef FEAT_TERMGUICOLORS
2062 if (gui.in_use)
2063# endif
2064 {
2065 if (set_group_colors((char_u *)"Normal",
2066 &gui.norm_pixel, &gui.back_pixel,
2067 FALSE, TRUE, FALSE))
2068 {
2069 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002070 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002071 }
2072# ifdef FEAT_GUI_X11
2073 if (set_group_colors((char_u *)"Menu",
2074 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
2075 TRUE, FALSE, FALSE))
2076 {
2077# ifdef FEAT_MENU
2078 gui_mch_new_menu_colors();
2079# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002080 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002081 }
2082# ifdef FEAT_BEVAL_GUI
2083 if (set_group_colors((char_u *)"Tooltip",
2084 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
2085 FALSE, FALSE, TRUE))
2086 {
2087# ifdef FEAT_TOOLBAR
2088 gui_mch_new_tooltip_colors();
2089# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002090 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002091 }
2092# endif
2093 if (set_group_colors((char_u *)"Scrollbar",
2094 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
2095 FALSE, FALSE, FALSE))
2096 {
2097 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002098 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002099 }
2100# endif
2101 }
2102# endif
2103# ifdef FEAT_TERMGUICOLORS
2104# ifdef FEAT_GUI
2105 else
2106# endif
2107 {
2108 int idx;
2109
2110 idx = syn_name2id((char_u *)"Normal") - 1;
2111 if (idx >= 0)
2112 {
2113 gui_do_one_color(idx, FALSE, FALSE);
2114
2115 // If the normal fg or bg color changed a complete redraw is
2116 // required.
2117 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
2118 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
2119 {
2120 // if the GUI color is INVALCOLOR then we use the default cterm
2121 // color
2122 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
2123 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002124 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002125 }
2126 }
2127 }
2128# endif
2129}
2130#endif
2131
2132#if defined(FEAT_GUI) || defined(PROTO)
2133/*
2134 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2135 */
2136 static int
2137set_group_colors(
2138 char_u *name,
2139 guicolor_T *fgp,
2140 guicolor_T *bgp,
2141 int do_menu,
2142 int use_norm,
2143 int do_tooltip)
2144{
2145 int idx;
2146
2147 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002148 if (idx < 0)
2149 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002150
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002151 gui_do_one_color(idx, do_menu, do_tooltip);
2152
2153 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2154 *fgp = HL_TABLE()[idx].sg_gui_fg;
2155 else if (use_norm)
2156 *fgp = gui.def_norm_pixel;
2157 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2158 *bgp = HL_TABLE()[idx].sg_gui_bg;
2159 else if (use_norm)
2160 *bgp = gui.def_back_pixel;
2161 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002162}
2163
2164/*
2165 * Get the font of the "Normal" group.
2166 * Returns "" when it's not found or not set.
2167 */
2168 char_u *
2169hl_get_font_name(void)
2170{
2171 int id;
2172 char_u *s;
2173
2174 id = syn_name2id((char_u *)"Normal");
2175 if (id > 0)
2176 {
2177 s = HL_TABLE()[id - 1].sg_font_name;
2178 if (s != NULL)
2179 return s;
2180 }
2181 return (char_u *)"";
2182}
2183
2184/*
2185 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2186 * actually chosen to be used.
2187 */
2188 void
2189hl_set_font_name(char_u *font_name)
2190{
2191 int id;
2192
2193 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002194 if (id <= 0)
2195 return;
2196
2197 vim_free(HL_TABLE()[id - 1].sg_font_name);
2198 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002199}
2200
2201/*
2202 * Set background color for "Normal" group. Called by gui_set_bg_color()
2203 * when the color is known.
2204 */
2205 void
2206hl_set_bg_color_name(
2207 char_u *name) // must have been allocated
2208{
2209 int id;
2210
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002211 if (name == NULL)
2212 return;
2213
2214 id = syn_name2id((char_u *)"Normal");
2215 if (id <= 0)
2216 return;
2217
2218 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2219 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002220}
2221
2222/*
2223 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2224 * when the color is known.
2225 */
2226 void
2227hl_set_fg_color_name(
2228 char_u *name) // must have been allocated
2229{
2230 int id;
2231
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002232 if (name == NULL)
2233 return;
2234
2235 id = syn_name2id((char_u *)"Normal");
2236 if (id <= 0)
2237 return;
2238
2239 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2240 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002241}
2242
2243/*
2244 * Return the handle for a font name.
2245 * Returns NOFONT when failed.
2246 */
2247 static GuiFont
2248font_name2handle(char_u *name)
2249{
2250 if (STRCMP(name, "NONE") == 0)
2251 return NOFONT;
2252
2253 return gui_mch_get_font(name, TRUE);
2254}
2255
2256# ifdef FEAT_XFONTSET
2257/*
2258 * Return the handle for a fontset name.
2259 * Returns NOFONTSET when failed.
2260 */
2261 static GuiFontset
2262fontset_name2handle(char_u *name, int fixed_width)
2263{
2264 if (STRCMP(name, "NONE") == 0)
2265 return NOFONTSET;
2266
2267 return gui_mch_get_fontset(name, TRUE, fixed_width);
2268}
2269# endif
2270
2271/*
2272 * Get the font or fontset for one highlight group.
2273 */
2274 static void
2275hl_do_font(
2276 int idx,
2277 char_u *arg,
2278 int do_normal, // set normal font
2279 int do_menu UNUSED, // set menu font
2280 int do_tooltip UNUSED, // set tooltip font
2281 int free_font) // free current font/fontset
2282{
2283# ifdef FEAT_XFONTSET
2284 // If 'guifontset' is not empty, first try using the name as a
2285 // fontset. If that doesn't work, use it as a font name.
2286 if (*p_guifontset != NUL
2287# ifdef FONTSET_ALWAYS
2288 || do_menu
2289# endif
2290# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002291 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002292 || do_tooltip
2293# endif
2294 )
2295 {
2296 if (free_font)
2297 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2298 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2299# ifdef FONTSET_ALWAYS
2300 || do_menu
2301# endif
2302# ifdef FEAT_BEVAL_TIP
2303 || do_tooltip
2304# endif
2305 );
2306 }
2307 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2308 {
2309 // If it worked and it's the Normal group, use it as the normal
2310 // fontset. Same for the Menu group.
2311 if (do_normal)
2312 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002313# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002314 if (do_menu)
2315 {
2316# ifdef FONTSET_ALWAYS
2317 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2318# else
2319 // YIKES! This is a bug waiting to crash the program
2320 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2321# endif
2322 gui_mch_new_menu_font();
2323 }
2324# ifdef FEAT_BEVAL_GUI
2325 if (do_tooltip)
2326 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002327 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002328 // displaying a single font and a fontset.
2329 // If the XtNinternational resource is set to True at widget
2330 // creation, then a fontset is always used, otherwise an
2331 // XFontStruct is used.
2332 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2333 gui_mch_new_tooltip_font();
2334 }
2335# endif
2336# endif
2337 }
2338 else
2339# endif
2340 {
2341 if (free_font)
2342 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2343 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2344 // If it worked and it's the Normal group, use it as the
2345 // normal font. Same for the Menu group.
2346 if (HL_TABLE()[idx].sg_font != NOFONT)
2347 {
2348 if (do_normal)
2349 gui_init_font(arg, FALSE);
2350#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002351# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002352 if (do_menu)
2353 {
2354 gui.menu_font = HL_TABLE()[idx].sg_font;
2355 gui_mch_new_menu_font();
2356 }
2357# endif
2358#endif
2359 }
2360 }
2361}
2362
2363#endif // FEAT_GUI
2364
2365#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2366/*
2367 * Return the handle for a color name.
2368 * Returns INVALCOLOR when failed.
2369 */
2370 guicolor_T
2371color_name2handle(char_u *name)
2372{
2373 if (STRCMP(name, "NONE") == 0)
2374 return INVALCOLOR;
2375
2376 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2377 {
2378#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2379 if (gui.in_use)
2380#endif
2381#ifdef FEAT_GUI
2382 return gui.norm_pixel;
2383#endif
2384#ifdef FEAT_TERMGUICOLORS
2385 if (cterm_normal_fg_gui_color != INVALCOLOR)
2386 return cterm_normal_fg_gui_color;
2387 // Guess that the foreground is black or white.
2388 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2389#endif
2390 }
2391 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2392 {
2393#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2394 if (gui.in_use)
2395#endif
2396#ifdef FEAT_GUI
2397 return gui.back_pixel;
2398#endif
2399#ifdef FEAT_TERMGUICOLORS
2400 if (cterm_normal_bg_gui_color != INVALCOLOR)
2401 return cterm_normal_bg_gui_color;
2402 // Guess that the background is white or black.
2403 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2404#endif
2405 }
2406
2407 return GUI_GET_COLOR(name);
2408}
Drew Vogele30d1022021-10-24 20:35:07 +01002409
2410// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2411// values as used by the MS-Windows GDI api. It should be used only for
2412// MS-Windows GDI builds.
2413# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2414# undef RGB
2415# endif
2416# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002417# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002418# endif
2419
2420# ifdef VIMDLL
2421 static guicolor_T
2422gui_adjust_rgb(guicolor_T c)
2423{
2424 if (gui.in_use)
2425 return c;
2426 else
2427 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2428}
2429# else
2430# define gui_adjust_rgb(c) (c)
2431# endif
2432
2433 static int
2434hex_digit(int c)
2435{
Keith Thompson184f71c2024-01-04 21:19:04 +01002436 if (SAFE_isdigit(c))
Drew Vogele30d1022021-10-24 20:35:07 +01002437 return c - '0';
2438 c = TOLOWER_ASC(c);
2439 if (c >= 'a' && c <= 'f')
2440 return c - 'a' + 10;
2441 return 0x1ffffff;
2442}
2443
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002444 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002445decode_hex_color(char_u *hex)
2446{
2447 guicolor_T color;
2448
2449 if (hex[0] != '#' || STRLEN(hex) != 7)
2450 return INVALCOLOR;
2451
2452 // Name is in "#rrggbb" format
2453 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2454 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2455 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2456 if (color > 0xffffff)
2457 return INVALCOLOR;
2458 return gui_adjust_rgb(color);
2459}
2460
Bram Moolenaar2a521962021-10-25 10:30:14 +01002461#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002462// Returns the color currently mapped to the given name or INVALCOLOR if no
2463// such name exists in the color table. The convention is to use lowercase for
2464// all keys in the v:colornames dictionary. The value can be either a string in
2465// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002466 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002467colorname2rgb(char_u *name)
2468{
2469 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2470 char_u *lc_name;
2471 dictitem_T *colentry;
2472 char_u *colstr;
2473 varnumber_T colnum;
2474
2475 lc_name = strlow_save(name);
2476 if (lc_name == NULL)
2477 return INVALCOLOR;
2478
2479 colentry = dict_find(colornames_table, lc_name, -1);
2480 vim_free(lc_name);
2481 if (colentry == NULL)
2482 return INVALCOLOR;
2483
2484 if (colentry->di_tv.v_type == VAR_STRING)
2485 {
2486 colstr = tv_get_string_strict(&colentry->di_tv);
2487 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2488 {
2489 return decode_hex_color(colstr);
2490 }
2491 else
2492 {
2493 semsg(_(e_bad_color_string_str), colstr);
2494 return INVALCOLOR;
2495 }
2496 }
2497
2498 if (colentry->di_tv.v_type == VAR_NUMBER)
2499 {
2500 colnum = tv_get_number(&colentry->di_tv);
2501 return (guicolor_T)colnum;
2502 }
2503
2504 return INVALCOLOR;
2505}
2506
Drew Vogele30d1022021-10-24 20:35:07 +01002507#endif
2508
2509 guicolor_T
2510gui_get_color_cmn(char_u *name)
2511{
Drew Vogele30d1022021-10-24 20:35:07 +01002512 guicolor_T color;
Drew Vogele30d1022021-10-24 20:35:07 +01002513 // Only non X11 colors (not present in rgb.txt) and colors in
John Marriott34f00dd2024-04-08 23:28:12 +02002514 // color_name_tab[], useful when $VIMRUNTIME is not found,.
2515 // must be sorted by the 'value' field because it is used by bsearch()!
2516 static keyvalue_T rgb_tab[] = {
2517 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x00), "black"),
2518 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0xFF), "blue"),
2519 KEYVALUE_ENTRY(RGB(0xA5, 0x2A, 0x2A), "brown"),
2520 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0xFF), "cyan"),
2521 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x8B), "darkblue"),
2522 KEYVALUE_ENTRY(RGB(0x00, 0x8B, 0x8B), "darkcyan"),
2523 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgray"),
2524 KEYVALUE_ENTRY(RGB(0x00, 0x64, 0x00), "darkgreen"),
2525 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgrey"),
2526 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x8B), "darkmagenta"),
2527 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x00), "darkred"),
2528 KEYVALUE_ENTRY(RGB(0x8B, 0x8B, 0x00), "darkyellow"), // No X11
2529 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "gray"),
2530 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0x00), "green"),
2531 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "grey"),
2532 KEYVALUE_ENTRY(RGB(0x66, 0x66, 0x66), "grey40"),
2533 KEYVALUE_ENTRY(RGB(0x7F, 0x7F, 0x7F), "grey50"),
2534 KEYVALUE_ENTRY(RGB(0xE5, 0xE5, 0xE5), "grey90"),
2535 KEYVALUE_ENTRY(RGB(0xAD, 0xD8, 0xE6), "lightblue"),
2536 KEYVALUE_ENTRY(RGB(0xE0, 0xFF, 0xFF), "lightcyan"),
2537 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgray"),
2538 KEYVALUE_ENTRY(RGB(0x90, 0xEE, 0x90), "lightgreen"),
2539 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgrey"),
2540 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0xFF), "lightmagenta"), // No XX
2541 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0x8B), "lightred"), // No XX
2542 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xE0), "lightyellow"),
2543 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0xFF), "magenta"),
2544 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0x00), "red"),
2545 KEYVALUE_ENTRY(RGB(0x2E, 0x8B, 0x57), "seagreen"),
2546 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xFF), "white"),
2547 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0x00), "yellow")
Drew Vogele30d1022021-10-24 20:35:07 +01002548 };
John Marriott34f00dd2024-04-08 23:28:12 +02002549 keyvalue_T target;
2550 keyvalue_T *entry;
Drew Vogele30d1022021-10-24 20:35:07 +01002551
2552 color = decode_hex_color(name);
2553 if (color != INVALCOLOR)
2554 return color;
2555
John Marriott34f00dd2024-04-08 23:28:12 +02002556 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01002557 target.value.string = name;
2558 target.value.length = 0; // not used, see cmp_keyvalue_value_i()
2559 entry = (keyvalue_T *)bsearch(&target, &rgb_tab, ARRAY_LENGTH(rgb_tab),
2560 sizeof(rgb_tab[0]), cmp_keyvalue_value_i);
John Marriott34f00dd2024-04-08 23:28:12 +02002561 if (entry != NULL)
John Marriott34f00dd2024-04-08 23:28:12 +02002562 return gui_adjust_rgb((guicolor_T)entry->key);
Drew Vogele30d1022021-10-24 20:35:07 +01002563
2564#if defined(FEAT_EVAL)
2565 /*
2566 * Not a traditional color. Load additional color aliases and then consult the alias table.
2567 */
2568
2569 color = colorname2rgb(name);
2570 if (color == INVALCOLOR)
2571 {
2572 load_default_colors_lists();
2573 color = colorname2rgb(name);
2574 }
2575
2576 return color;
2577#else
2578 return INVALCOLOR;
2579#endif
2580}
2581
2582 guicolor_T
2583gui_get_rgb_color_cmn(int r, int g, int b)
2584{
2585 guicolor_T color = RGB(r, g, b);
2586
2587 if (color > 0xffffff)
2588 return INVALCOLOR;
2589 return gui_adjust_rgb(color);
2590}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002591#endif
2592
2593/*
2594 * Table with the specifications for an attribute number.
2595 * Note that this table is used by ALL buffers. This is required because the
2596 * GUI can redraw at any time for any buffer.
2597 */
2598static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2599
2600#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2601
2602static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2603
2604#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2605
2606#ifdef FEAT_GUI
2607static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2608
2609#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2610#endif
2611
2612/*
2613 * Return the attr number for a set of colors and font.
2614 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2615 * if the combination is new.
2616 * Return 0 for error (no more room).
2617 */
2618 static int
2619get_attr_entry(garray_T *table, attrentry_T *aep)
2620{
2621 int i;
2622 attrentry_T *taep;
2623 static int recursive = FALSE;
2624
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002625 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002626 table->ga_itemsize = sizeof(attrentry_T);
2627 table->ga_growsize = 7;
2628
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002629 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002630 for (i = 0; i < table->ga_len; ++i)
2631 {
2632 taep = &(((attrentry_T *)table->ga_data)[i]);
2633 if ( aep->ae_attr == taep->ae_attr
2634 && (
2635#ifdef FEAT_GUI
2636 (table == &gui_attr_table
2637 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2638 && aep->ae_u.gui.bg_color
2639 == taep->ae_u.gui.bg_color
2640 && aep->ae_u.gui.sp_color
2641 == taep->ae_u.gui.sp_color
2642 && aep->ae_u.gui.font == taep->ae_u.gui.font
2643# ifdef FEAT_XFONTSET
2644 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2645# endif
2646 ))
2647 ||
2648#endif
2649 (table == &term_attr_table
2650 && (aep->ae_u.term.start == NULL)
2651 == (taep->ae_u.term.start == NULL)
2652 && (aep->ae_u.term.start == NULL
2653 || STRCMP(aep->ae_u.term.start,
2654 taep->ae_u.term.start) == 0)
2655 && (aep->ae_u.term.stop == NULL)
2656 == (taep->ae_u.term.stop == NULL)
2657 && (aep->ae_u.term.stop == NULL
2658 || STRCMP(aep->ae_u.term.stop,
2659 taep->ae_u.term.stop) == 0))
2660 || (table == &cterm_attr_table
2661 && aep->ae_u.cterm.fg_color
2662 == taep->ae_u.cterm.fg_color
2663 && aep->ae_u.cterm.bg_color
2664 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002665 && aep->ae_u.cterm.ul_color
2666 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002667 && aep->ae_u.cterm.font
2668 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002669#ifdef FEAT_TERMGUICOLORS
2670 && aep->ae_u.cterm.fg_rgb
2671 == taep->ae_u.cterm.fg_rgb
2672 && aep->ae_u.cterm.bg_rgb
2673 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002674 && aep->ae_u.cterm.ul_rgb
2675 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002676#endif
2677 )))
2678
2679 return i + ATTR_OFF;
2680 }
2681
2682 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2683 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002684 // Running out of attribute entries! remove all attributes, and
2685 // compute new ones for all groups.
2686 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002687 if (recursive)
2688 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002689 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002690 return 0;
2691 }
2692 recursive = TRUE;
2693
2694 clear_hl_tables();
2695
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002696 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002697
2698 for (i = 0; i < highlight_ga.ga_len; ++i)
2699 set_hl_attr(i);
2700
2701 recursive = FALSE;
2702 }
2703
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002704 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002705 if (ga_grow(table, 1) == FAIL)
2706 return 0;
2707
2708 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002709 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002710 taep->ae_attr = aep->ae_attr;
2711#ifdef FEAT_GUI
2712 if (table == &gui_attr_table)
2713 {
2714 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2715 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2716 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2717 taep->ae_u.gui.font = aep->ae_u.gui.font;
2718# ifdef FEAT_XFONTSET
2719 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2720# endif
2721 }
2722#endif
2723 if (table == &term_attr_table)
2724 {
2725 if (aep->ae_u.term.start == NULL)
2726 taep->ae_u.term.start = NULL;
2727 else
2728 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2729 if (aep->ae_u.term.stop == NULL)
2730 taep->ae_u.term.stop = NULL;
2731 else
2732 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2733 }
2734 else if (table == &cterm_attr_table)
2735 {
2736 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2737 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002738 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002739 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002740#ifdef FEAT_TERMGUICOLORS
2741 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2742 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002743 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002744#endif
2745 }
2746 ++table->ga_len;
2747 return (table->ga_len - 1 + ATTR_OFF);
2748}
2749
2750#if defined(FEAT_TERMINAL) || defined(PROTO)
2751/*
2752 * Get an attribute index for a cterm entry.
2753 * Uses an existing entry when possible or adds one when needed.
2754 */
2755 int
2756get_cterm_attr_idx(int attr, int fg, int bg)
2757{
2758 attrentry_T at_en;
2759
Bram Moolenaara80faa82020-04-12 19:37:17 +02002760 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002761#ifdef FEAT_TERMGUICOLORS
2762 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2763 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002764 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002765#endif
2766 at_en.ae_attr = attr;
2767 at_en.ae_u.cterm.fg_color = fg;
2768 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002769 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002770 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002771 return get_attr_entry(&cterm_attr_table, &at_en);
2772}
2773#endif
2774
2775#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2776/*
2777 * Get an attribute index for a 'termguicolors' entry.
2778 * Uses an existing entry when possible or adds one when needed.
2779 */
2780 int
2781get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2782{
2783 attrentry_T at_en;
2784
Bram Moolenaara80faa82020-04-12 19:37:17 +02002785 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002786 at_en.ae_attr = attr;
2787 if (fg == INVALCOLOR && bg == INVALCOLOR)
2788 {
2789 // If both GUI colors are not set fall back to the cterm colors. Helps
2790 // if the GUI only has an attribute, such as undercurl.
2791 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2792 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2793 }
2794 else
2795 {
2796 at_en.ae_u.cterm.fg_rgb = fg;
2797 at_en.ae_u.cterm.bg_rgb = bg;
2798 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002799 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002800 return get_attr_entry(&cterm_attr_table, &at_en);
2801}
2802#endif
2803
2804#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2805/*
2806 * Get an attribute index for a cterm entry.
2807 * Uses an existing entry when possible or adds one when needed.
2808 */
2809 int
2810get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2811{
2812 attrentry_T at_en;
2813
Bram Moolenaara80faa82020-04-12 19:37:17 +02002814 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002815 at_en.ae_attr = attr;
2816 at_en.ae_u.gui.fg_color = fg;
2817 at_en.ae_u.gui.bg_color = bg;
2818 return get_attr_entry(&gui_attr_table, &at_en);
2819}
2820#endif
2821
2822/*
2823 * Clear all highlight tables.
2824 */
2825 void
2826clear_hl_tables(void)
2827{
2828 int i;
2829 attrentry_T *taep;
2830
2831#ifdef FEAT_GUI
2832 ga_clear(&gui_attr_table);
2833#endif
2834 for (i = 0; i < term_attr_table.ga_len; ++i)
2835 {
2836 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2837 vim_free(taep->ae_u.term.start);
2838 vim_free(taep->ae_u.term.stop);
2839 }
2840 ga_clear(&term_attr_table);
2841 ga_clear(&cterm_attr_table);
2842}
2843
2844/*
2845 * Combine special attributes (e.g., for spelling) with other attributes
2846 * (e.g., for syntax highlighting).
2847 * "prim_attr" overrules "char_attr".
2848 * This creates a new group when required.
2849 * Since we expect there to be few spelling mistakes we don't cache the
2850 * result.
2851 * Return the resulting attributes.
2852 */
2853 int
2854hl_combine_attr(int char_attr, int prim_attr)
2855{
2856 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002857 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002858 attrentry_T new_en;
2859
2860 if (char_attr == 0)
2861 return prim_attr;
2862 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2863 return ATTR_COMBINE(char_attr, prim_attr);
2864#ifdef FEAT_GUI
2865 if (gui.in_use)
2866 {
2867 if (char_attr > HL_ALL)
2868 char_aep = syn_gui_attr2entry(char_attr);
2869 if (char_aep != NULL)
2870 new_en = *char_aep;
2871 else
2872 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002873 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002874 new_en.ae_u.gui.fg_color = INVALCOLOR;
2875 new_en.ae_u.gui.bg_color = INVALCOLOR;
2876 new_en.ae_u.gui.sp_color = INVALCOLOR;
2877 if (char_attr <= HL_ALL)
2878 new_en.ae_attr = char_attr;
2879 }
2880
2881 if (prim_attr <= HL_ALL)
2882 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2883 else
2884 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002885 prim_aep = syn_gui_attr2entry(prim_attr);
2886 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002887 {
2888 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002889 prim_aep->ae_attr);
2890 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2891 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2892 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2893 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2894 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2895 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2896 if (prim_aep->ae_u.gui.font != NOFONT)
2897 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002898# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002899 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2900 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002901# endif
2902 }
2903 }
2904 return get_attr_entry(&gui_attr_table, &new_en);
2905 }
2906#endif
2907
2908 if (IS_CTERM)
2909 {
2910 if (char_attr > HL_ALL)
2911 char_aep = syn_cterm_attr2entry(char_attr);
2912 if (char_aep != NULL)
2913 new_en = *char_aep;
2914 else
2915 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002916 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002917#ifdef FEAT_TERMGUICOLORS
2918 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2919 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002920 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002921#endif
2922 if (char_attr <= HL_ALL)
2923 new_en.ae_attr = char_attr;
2924 }
2925
2926 if (prim_attr <= HL_ALL)
2927 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2928 else
2929 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002930 prim_aep = syn_cterm_attr2entry(prim_attr);
2931 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002932 {
2933 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002934 prim_aep->ae_attr);
2935 if (prim_aep->ae_u.cterm.fg_color > 0)
2936 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2937 if (prim_aep->ae_u.cterm.bg_color > 0)
2938 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2939 if (prim_aep->ae_u.cterm.ul_color > 0)
2940 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002941 if (prim_aep->ae_u.cterm.font > 0)
2942 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002943#ifdef FEAT_TERMGUICOLORS
2944 // If both fg and bg are not set fall back to cterm colors.
2945 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002946 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2947 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002948 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002949 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002950 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002951 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002952 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2953 }
2954 else
2955 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002956 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2957 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2958 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2959 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002960 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002961 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2962 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002963#endif
2964 }
2965 }
2966 return get_attr_entry(&cterm_attr_table, &new_en);
2967 }
2968
2969 if (char_attr > HL_ALL)
2970 char_aep = syn_term_attr2entry(char_attr);
2971 if (char_aep != NULL)
2972 new_en = *char_aep;
2973 else
2974 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002975 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002976 if (char_attr <= HL_ALL)
2977 new_en.ae_attr = char_attr;
2978 }
2979
2980 if (prim_attr <= HL_ALL)
2981 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2982 else
2983 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002984 prim_aep = syn_term_attr2entry(prim_attr);
2985 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002986 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002987 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2988 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002989 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002990 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2991 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002992 }
2993 }
2994 }
2995 return get_attr_entry(&term_attr_table, &new_en);
2996}
2997
2998#ifdef FEAT_GUI
2999 attrentry_T *
3000syn_gui_attr2entry(int attr)
3001{
3002 attr -= ATTR_OFF;
3003 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
3004 return NULL;
3005 return &(GUI_ATTR_ENTRY(attr));
3006}
3007#endif
3008
3009/*
3010 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
3011 * Only to be used when "attr" > HL_ALL.
3012 */
3013 int
3014syn_attr2attr(int attr)
3015{
3016 attrentry_T *aep;
3017
3018#ifdef FEAT_GUI
3019 if (gui.in_use)
3020 aep = syn_gui_attr2entry(attr);
3021 else
3022#endif
3023 if (IS_CTERM)
3024 aep = syn_cterm_attr2entry(attr);
3025 else
3026 aep = syn_term_attr2entry(attr);
3027
3028 if (aep == NULL) // highlighting not set
3029 return 0;
3030 return aep->ae_attr;
3031}
3032
3033
3034 attrentry_T *
3035syn_term_attr2entry(int attr)
3036{
3037 attr -= ATTR_OFF;
3038 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
3039 return NULL;
3040 return &(TERM_ATTR_ENTRY(attr));
3041}
3042
3043 attrentry_T *
3044syn_cterm_attr2entry(int attr)
3045{
3046 attr -= ATTR_OFF;
3047 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
3048 return NULL;
3049 return &(CTERM_ATTR_ENTRY(attr));
3050}
3051
3052#define LIST_ATTR 1
3053#define LIST_STRING 2
3054#define LIST_INT 3
3055
3056 static void
3057highlight_list_one(int id)
3058{
3059 hl_group_T *sgp;
3060 int didh = FALSE;
3061
3062 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
3063
3064 if (message_filtered(sgp->sg_name))
3065 return;
3066
Yee Cheng China7b81202025-02-23 09:32:47 +01003067 // Note: Keep this in sync with expand_highlight_group().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003068 didh = highlight_list_arg(id, didh, LIST_ATTR,
3069 sgp->sg_term, NULL, "term");
3070 didh = highlight_list_arg(id, didh, LIST_STRING,
3071 0, sgp->sg_start, "start");
3072 didh = highlight_list_arg(id, didh, LIST_STRING,
3073 0, sgp->sg_stop, "stop");
3074
3075 didh = highlight_list_arg(id, didh, LIST_ATTR,
3076 sgp->sg_cterm, NULL, "cterm");
3077 didh = highlight_list_arg(id, didh, LIST_INT,
3078 sgp->sg_cterm_fg, NULL, "ctermfg");
3079 didh = highlight_list_arg(id, didh, LIST_INT,
3080 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02003081 didh = highlight_list_arg(id, didh, LIST_INT,
3082 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01003083 didh = highlight_list_arg(id, didh, LIST_INT,
3084 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003085
3086#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3087 didh = highlight_list_arg(id, didh, LIST_ATTR,
3088 sgp->sg_gui, NULL, "gui");
3089 didh = highlight_list_arg(id, didh, LIST_STRING,
3090 0, sgp->sg_gui_fg_name, "guifg");
3091 didh = highlight_list_arg(id, didh, LIST_STRING,
3092 0, sgp->sg_gui_bg_name, "guibg");
3093 didh = highlight_list_arg(id, didh, LIST_STRING,
3094 0, sgp->sg_gui_sp_name, "guisp");
3095#endif
3096#ifdef FEAT_GUI
3097 didh = highlight_list_arg(id, didh, LIST_STRING,
3098 0, sgp->sg_font_name, "font");
3099#endif
3100
3101 if (sgp->sg_link && !got_int)
3102 {
3103 (void)syn_list_header(didh, 9999, id);
3104 didh = TRUE;
3105 msg_puts_attr("links to", HL_ATTR(HLF_D));
3106 msg_putchar(' ');
3107 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3108 }
3109
3110 if (!didh)
3111 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3112#ifdef FEAT_EVAL
3113 if (p_verbose > 0)
3114 last_set_msg(sgp->sg_script_ctx);
3115#endif
3116}
3117
Yee Cheng China7b81202025-02-23 09:32:47 +01003118 static char_u*
3119highlight_arg_to_string(
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003120 int type,
3121 int iarg,
3122 char_u *sarg,
Yee Cheng China7b81202025-02-23 09:32:47 +01003123 char_u *buf)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003124{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003125 if (type == LIST_INT)
3126 sprintf((char *)buf, "%d", iarg - 1);
3127 else if (type == LIST_STRING)
Yee Cheng China7b81202025-02-23 09:32:47 +01003128 return sarg;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003129 else // type == LIST_ATTR
3130 {
John Marriott34f00dd2024-04-08 23:28:12 +02003131 size_t buflen;
3132
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003133 buf[0] = NUL;
John Marriott34f00dd2024-04-08 23:28:12 +02003134 buflen = 0;
Yee Cheng China7b81202025-02-23 09:32:47 +01003135 for (int i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003136 {
John Marriott34f00dd2024-04-08 23:28:12 +02003137 if (iarg & highlight_index_tab[i]->key)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003138 {
John Marriott34f00dd2024-04-08 23:28:12 +02003139 if (buflen > 0)
3140 {
3141 STRCPY(buf + buflen, (char_u *)",");
3142 ++buflen;
3143 }
John Marriott8d4477e2024-11-02 15:59:01 +01003144 STRCPY(buf + buflen, highlight_index_tab[i]->value.string);
3145 buflen += highlight_index_tab[i]->value.length;
John Marriott34f00dd2024-04-08 23:28:12 +02003146 iarg &= ~highlight_index_tab[i]->key; // don't want "inverse"/"reverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003147 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003148 }
3149 }
Yee Cheng China7b81202025-02-23 09:32:47 +01003150 return buf;
3151}
3152
3153 static int
3154highlight_list_arg(
3155 int id,
3156 int didh,
3157 int type,
3158 int iarg,
3159 char_u *sarg,
3160 char *name)
3161{
3162 char_u buf[MAX_ATTR_LEN];
3163 char_u *ts;
3164
3165 if (got_int)
3166 return FALSE;
3167
3168 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3169 return didh;
3170
3171 ts = highlight_arg_to_string(type, iarg, sarg, buf);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003172
3173 (void)syn_list_header(didh,
3174 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3175 didh = TRUE;
3176 if (!got_int)
3177 {
3178 if (*name != NUL)
3179 {
3180 msg_puts_attr(name, HL_ATTR(HLF_D));
3181 msg_puts_attr("=", HL_ATTR(HLF_D));
3182 }
3183 msg_outtrans(ts);
3184 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003185 return didh;
3186}
3187
3188#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3189/*
3190 * Return "1" if highlight group "id" has attribute "flag".
3191 * Return NULL otherwise.
3192 */
3193 char_u *
3194highlight_has_attr(
3195 int id,
3196 int flag,
3197 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3198{
3199 int attr;
3200
3201 if (id <= 0 || id > highlight_ga.ga_len)
3202 return NULL;
3203
3204#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3205 if (modec == 'g')
3206 attr = HL_TABLE()[id - 1].sg_gui;
3207 else
3208#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003209 {
3210 if (modec == 'c')
3211 attr = HL_TABLE()[id - 1].sg_cterm;
3212 else
3213 attr = HL_TABLE()[id - 1].sg_term;
3214 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003215
3216 if (attr & flag)
3217 return (char_u *)"1";
3218 return NULL;
3219}
3220#endif
3221
3222#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3223/*
3224 * Return color name of highlight group "id".
3225 */
3226 char_u *
3227highlight_color(
3228 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003229 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003230 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3231{
3232 static char_u name[20];
3233 int n;
3234 int fg = FALSE;
3235 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003236 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003237 int font = FALSE;
3238
3239 if (id <= 0 || id > highlight_ga.ga_len)
3240 return NULL;
3241
3242 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3243 fg = TRUE;
3244 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3245 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3246 font = TRUE;
3247 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3248 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003249 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3250 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003251 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3252 return NULL;
3253 if (modec == 'g')
3254 {
3255# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3256# ifdef FEAT_GUI
3257 // return font name
3258 if (font)
3259 return HL_TABLE()[id - 1].sg_font_name;
3260# endif
3261
3262 // return #RRGGBB form (only possible when GUI is running)
3263 if ((USE_24BIT) && what[2] == '#')
3264 {
3265 guicolor_T color;
3266 long_u rgb;
3267 static char_u buf[10];
3268
3269 if (fg)
3270 color = HL_TABLE()[id - 1].sg_gui_fg;
3271 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003272 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003273 else
3274 color = HL_TABLE()[id - 1].sg_gui_bg;
3275 if (color == INVALCOLOR)
3276 return NULL;
3277 rgb = (long_u)GUI_MCH_GET_RGB(color);
3278 sprintf((char *)buf, "#%02x%02x%02x",
3279 (unsigned)(rgb >> 16),
3280 (unsigned)(rgb >> 8) & 255,
3281 (unsigned)rgb & 255);
3282 return buf;
3283 }
3284# endif
3285 if (fg)
3286 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3287 if (sp)
3288 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3289 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3290 }
PMuncha606f3a2023-11-15 15:35:49 +01003291 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003292 return NULL;
3293 if (modec == 'c')
3294 {
3295 if (fg)
3296 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003297 else if (ul)
3298 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003299 else if (font)
3300 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003301 else
3302 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3303 if (n < 0)
3304 return NULL;
3305 sprintf((char *)name, "%d", n);
3306 return name;
3307 }
3308 // term doesn't have color
3309 return NULL;
3310}
3311#endif
3312
3313#if (defined(FEAT_SYN_HL) \
3314 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3315 && defined(FEAT_PRINTER)) || defined(PROTO)
3316/*
3317 * Return color name of highlight group "id" as RGB value.
3318 */
3319 long_u
3320highlight_gui_color_rgb(
3321 int id,
3322 int fg) // TRUE = fg, FALSE = bg
3323{
3324 guicolor_T color;
3325
3326 if (id <= 0 || id > highlight_ga.ga_len)
3327 return 0L;
3328
3329 if (fg)
3330 color = HL_TABLE()[id - 1].sg_gui_fg;
3331 else
3332 color = HL_TABLE()[id - 1].sg_gui_bg;
3333
3334 if (color == INVALCOLOR)
3335 return 0L;
3336
3337 return GUI_MCH_GET_RGB(color);
3338}
3339#endif
3340
3341/*
3342 * Output the syntax list header.
3343 * Return TRUE when started a new line.
3344 */
3345 int
3346syn_list_header(
3347 int did_header, // did header already
3348 int outlen, // length of string that comes
3349 int id) // highlight group id
3350{
3351 int endcol = 19;
3352 int newline = TRUE;
3353 int name_col = 0;
3354
3355 if (!did_header)
3356 {
3357 msg_putchar('\n');
3358 if (got_int)
3359 return TRUE;
3360 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3361 name_col = msg_col;
3362 endcol = 15;
3363 }
3364 else if (msg_col + outlen + 1 >= Columns)
3365 {
3366 msg_putchar('\n');
3367 if (got_int)
3368 return TRUE;
3369 }
3370 else
3371 {
3372 if (msg_col >= endcol) // wrap around is like starting a new line
3373 newline = FALSE;
3374 }
3375
3376 if (msg_col >= endcol) // output at least one space
3377 endcol = msg_col + 1;
Christian Brabandt220474d2024-07-20 13:26:44 +02003378 if (Columns <= (long)endcol) // avoid hang for tiny window
3379 endcol = (int)(Columns - 1);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003380
3381 msg_advance(endcol);
3382
3383 // Show "xxx" with the attributes.
3384 if (!did_header)
3385 {
3386 if (endcol == Columns - 1 && endcol <= name_col)
3387 msg_putchar(' ');
3388 msg_puts_attr("xxx", syn_id2attr(id));
3389 msg_putchar(' ');
3390 }
3391
3392 return newline;
3393}
3394
3395/*
3396 * Set the attribute numbers for a highlight group.
3397 * Called after one of the attributes has changed.
3398 */
3399 static void
3400set_hl_attr(
3401 int idx) // index in array
3402{
3403 attrentry_T at_en;
3404 hl_group_T *sgp = HL_TABLE() + idx;
3405
3406 // The "Normal" group doesn't need an attribute number
3407 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3408 return;
3409
3410#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003411 // For the GUI mode: If there are other than "normal" highlighting
3412 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003413 if (sgp->sg_gui_fg == INVALCOLOR
3414 && sgp->sg_gui_bg == INVALCOLOR
3415 && sgp->sg_gui_sp == INVALCOLOR
3416 && sgp->sg_font == NOFONT
3417# ifdef FEAT_XFONTSET
3418 && sgp->sg_fontset == NOFONTSET
3419# endif
3420 )
3421 {
3422 sgp->sg_gui_attr = sgp->sg_gui;
3423 }
3424 else
3425 {
3426 at_en.ae_attr = sgp->sg_gui;
3427 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3428 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3429 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3430 at_en.ae_u.gui.font = sgp->sg_font;
3431# ifdef FEAT_XFONTSET
3432 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3433# endif
3434 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3435 }
3436#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003437 // For the term mode: If there are other than "normal" highlighting
3438 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003439 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3440 sgp->sg_term_attr = sgp->sg_term;
3441 else
3442 {
3443 at_en.ae_attr = sgp->sg_term;
3444 at_en.ae_u.term.start = sgp->sg_start;
3445 at_en.ae_u.term.stop = sgp->sg_stop;
3446 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3447 }
3448
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003449 // For the color term mode: If there are other than "normal"
3450 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003451 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3452 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003453# ifdef FEAT_TERMGUICOLORS
3454 && sgp->sg_gui_fg == INVALCOLOR
3455 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003456 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003457# endif
3458 )
3459 sgp->sg_cterm_attr = sgp->sg_cterm;
3460 else
3461 {
3462 at_en.ae_attr = sgp->sg_cterm;
3463 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3464 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003465 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003466 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003467# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003468 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3469 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003470 // Only use the underline/undercurl color when used, it may clear the
3471 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003472 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3473 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003474 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3475 else
3476 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003477 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3478 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3479 {
3480 // If both fg and bg are invalid fall back to the cterm colors.
3481 // Helps when the GUI only uses an attribute, e.g. undercurl.
3482 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3483 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3484 }
3485# endif
3486 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3487 }
3488}
3489
3490/*
3491 * Lookup a highlight group name and return its ID.
3492 * If it is not found, 0 is returned.
3493 */
3494 int
3495syn_name2id(char_u *name)
3496{
3497 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003498 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003499
3500 // Avoid using stricmp() too much, it's slow on some systems
3501 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3502 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003503 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003504 vim_strup(name_u);
3505 for (i = highlight_ga.ga_len; --i >= 0; )
3506 if (HL_TABLE()[i].sg_name_u != NULL
3507 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3508 break;
3509 return i + 1;
3510}
3511
3512/*
3513 * Lookup a highlight group name and return its attributes.
3514 * Return zero if not found.
3515 */
3516 int
3517syn_name2attr(char_u *name)
3518{
3519 int id = syn_name2id(name);
3520
3521 if (id != 0)
3522 return syn_id2attr(id);
3523 return 0;
3524}
3525
3526#if defined(FEAT_EVAL) || defined(PROTO)
3527/*
3528 * Return TRUE if highlight group "name" exists.
3529 */
3530 int
3531highlight_exists(char_u *name)
3532{
3533 return (syn_name2id(name) > 0);
3534}
3535
3536# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3537/*
3538 * Return the name of highlight group "id".
3539 * When not a valid ID return an empty string.
3540 */
3541 char_u *
3542syn_id2name(int id)
3543{
3544 if (id <= 0 || id > highlight_ga.ga_len)
3545 return (char_u *)"";
3546 return HL_TABLE()[id - 1].sg_name;
3547}
3548# endif
3549#endif
3550
3551/*
3552 * Like syn_name2id(), but take a pointer + length argument.
3553 */
3554 int
3555syn_namen2id(char_u *linep, int len)
3556{
3557 char_u *name;
3558 int id = 0;
3559
3560 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003561 if (name == NULL)
3562 return 0;
3563
3564 id = syn_name2id(name);
3565 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003566 return id;
3567}
3568
3569/*
3570 * Find highlight group name in the table and return its ID.
3571 * The argument is a pointer to the name and the length of the name.
3572 * If it doesn't exist yet, a new entry is created.
3573 * Return 0 for failure.
3574 */
3575 int
3576syn_check_group(char_u *pp, int len)
3577{
3578 int id;
3579 char_u *name;
3580
erw7f7f7aaf2021-12-07 21:29:20 +00003581 if (len > MAX_SYN_NAME)
3582 {
3583 emsg(_(e_highlight_group_name_too_long));
3584 return 0;
3585 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003586 name = vim_strnsave(pp, len);
3587 if (name == NULL)
3588 return 0;
3589
3590 id = syn_name2id(name);
3591 if (id == 0) // doesn't exist yet
3592 id = syn_add_group(name);
3593 else
3594 vim_free(name);
3595 return id;
3596}
3597
3598/*
3599 * Add new highlight group and return its ID.
3600 * "name" must be an allocated string, it will be consumed.
3601 * Return 0 for failure.
3602 */
3603 static int
3604syn_add_group(char_u *name)
3605{
3606 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003607 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003608
Gregory Andersd4376dc2023-08-20 19:14:03 +02003609 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003610 for (p = name; *p != NUL; ++p)
3611 {
3612 if (!vim_isprintc(*p))
3613 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003614 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003615 vim_free(name);
3616 return 0;
3617 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003618 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003619 {
3620 // This is an error, but since there previously was no check only
3621 // give a warning.
3622 msg_source(HL_ATTR(HLF_W));
3623 msg(_("W18: Invalid character in group name"));
3624 break;
3625 }
3626 }
3627
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003628 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003629 if (highlight_ga.ga_data == NULL)
3630 {
3631 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3632 highlight_ga.ga_growsize = 10;
3633 }
3634
3635 if (highlight_ga.ga_len >= MAX_HL_ID)
3636 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003637 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003638 vim_free(name);
3639 return 0;
3640 }
3641
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003642 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003643 if (ga_grow(&highlight_ga, 1) == FAIL)
3644 {
3645 vim_free(name);
3646 return 0;
3647 }
3648
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003649 name_up = vim_strsave_up(name);
3650 if (name_up == NULL)
3651 {
3652 vim_free(name);
3653 return 0;
3654 }
3655
Bram Moolenaara80faa82020-04-12 19:37:17 +02003656 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003657 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003658 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003659#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3660 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3661 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003662 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003663#endif
3664 ++highlight_ga.ga_len;
3665
3666 return highlight_ga.ga_len; // ID is index plus one
3667}
3668
3669/*
3670 * When, just after calling syn_add_group(), an error is discovered, this
3671 * function deletes the new name.
3672 */
3673 static void
3674syn_unadd_group(void)
3675{
3676 --highlight_ga.ga_len;
3677 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3678 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3679}
3680
3681/*
3682 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003683 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003684 */
3685 int
3686syn_id2attr(int hl_id)
3687{
3688 int attr;
3689 hl_group_T *sgp;
3690
3691 hl_id = syn_get_final_id(hl_id);
Christian Brabandt9d065a42025-06-18 18:28:19 +02003692 // shouldn't happen
3693 assert(hl_id > 0);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003694 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3695
3696#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003697 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003698 if (gui.in_use)
3699 attr = sgp->sg_gui_attr;
3700 else
3701#endif
3702 if (IS_CTERM)
3703 attr = sgp->sg_cterm_attr;
3704 else
3705 attr = sgp->sg_term_attr;
3706
3707 return attr;
3708}
3709
3710#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3711/*
3712 * Get the GUI colors and attributes for a group ID.
3713 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3714 */
3715 int
3716syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3717{
3718 hl_group_T *sgp;
3719
3720 hl_id = syn_get_final_id(hl_id);
Christian Brabandt9d065a42025-06-18 18:28:19 +02003721 // shouldn't happen
3722 assert(hl_id > 0);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003723 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3724
3725 *fgp = sgp->sg_gui_fg;
3726 *bgp = sgp->sg_gui_bg;
3727 return sgp->sg_gui;
3728}
3729#endif
3730
3731#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003732 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3733 && defined(FEAT_TERMGUICOLORS)) \
3734 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003735 void
3736syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3737{
3738 hl_group_T *sgp;
3739
3740 hl_id = syn_get_final_id(hl_id);
Christian Brabandt9d065a42025-06-18 18:28:19 +02003741 // shouldn't happen
3742 assert(hl_id > 0);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003743 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3744 *fgp = sgp->sg_cterm_fg - 1;
3745 *bgp = sgp->sg_cterm_bg - 1;
3746}
3747#endif
3748
3749/*
3750 * Translate a group ID to the final group ID (following links).
3751 */
3752 int
3753syn_get_final_id(int hl_id)
3754{
3755 int count;
3756 hl_group_T *sgp;
3757
3758 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3759 return 0; // Can be called from eval!!
3760
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003761 // Follow links until there is no more.
3762 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003763 for (count = 100; --count >= 0; )
3764 {
3765 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3766 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3767 break;
3768 hl_id = sgp->sg_link;
3769 }
3770
3771 return hl_id;
3772}
3773
3774#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3775/*
3776 * Call this function just after the GUI has started.
3777 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3778 * It finds the font and color handles for the highlighting groups.
3779 */
3780 void
3781highlight_gui_started(void)
3782{
3783 int idx;
3784
3785 // First get the colors from the "Normal" and "Menu" group, if set
3786 if (USE_24BIT)
3787 set_normal_colors();
3788
3789 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3790 gui_do_one_color(idx, FALSE, FALSE);
3791
3792 highlight_changed();
3793}
3794
3795 static void
3796gui_do_one_color(
3797 int idx,
3798 int do_menu UNUSED, // TRUE: might set the menu font
3799 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3800{
3801 int didit = FALSE;
3802
3803# ifdef FEAT_GUI
3804# ifdef FEAT_TERMGUICOLORS
3805 if (gui.in_use)
3806# endif
3807 if (HL_TABLE()[idx].sg_font_name != NULL)
3808 {
3809 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3810 do_tooltip, TRUE);
3811 didit = TRUE;
3812 }
3813# endif
3814 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3815 {
3816 HL_TABLE()[idx].sg_gui_fg =
3817 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3818 didit = TRUE;
3819 }
3820 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3821 {
3822 HL_TABLE()[idx].sg_gui_bg =
3823 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3824 didit = TRUE;
3825 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003826 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3827 {
3828 HL_TABLE()[idx].sg_gui_sp =
3829 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3830 didit = TRUE;
3831 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003832 if (didit) // need to get a new attr number
3833 set_hl_attr(idx);
3834}
3835#endif
3836
3837#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3838/*
3839 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3840 */
3841 static void
3842combine_stl_hlt(
3843 int id,
3844 int id_S,
3845 int id_alt,
3846 int hlcnt,
3847 int i,
3848 int hlf,
3849 int *table)
3850{
3851 hl_group_T *hlt = HL_TABLE();
3852
3853 if (id_alt == 0)
3854 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003855 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003856 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3857 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3858# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3859 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3860# endif
3861 }
3862 else
3863 mch_memmove(&hlt[hlcnt + i],
3864 &hlt[id_alt - 1],
3865 sizeof(hl_group_T));
3866 hlt[hlcnt + i].sg_link = 0;
3867
3868 hlt[hlcnt + i].sg_term ^=
3869 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3870 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3871 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3872 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3873 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3874 hlt[hlcnt + i].sg_cterm ^=
3875 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3876 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3877 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3878 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3879 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003880 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3881 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003882# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3883 hlt[hlcnt + i].sg_gui ^=
3884 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3885# endif
3886# ifdef FEAT_GUI
3887 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3888 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3889 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3890 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3891 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3892 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3893 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3894 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3895# ifdef FEAT_XFONTSET
3896 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3897 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3898# endif
3899# endif
3900 highlight_ga.ga_len = hlcnt + i + 1;
3901 set_hl_attr(hlcnt + i); // At long last we can apply
3902 table[i] = syn_id2attr(hlcnt + i + 1);
3903}
3904#endif
3905
3906/*
3907 * Translate the 'highlight' option into attributes in highlight_attr[] and
3908 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3909 * corresponding highlights to use on top of HLF_SNC is computed.
3910 * Called only when the 'highlight' option has been changed and upon first
3911 * screen redraw after any :highlight command.
3912 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3913 */
3914 int
3915highlight_changed(void)
3916{
3917 int hlf;
3918 int i;
3919 char_u *p;
3920 int attr;
3921 char_u *end;
3922 int id;
3923#ifdef USER_HIGHLIGHT
3924 char_u userhl[30]; // use 30 to avoid compiler warning
3925# ifdef FEAT_STL_OPT
3926 int id_S = -1;
3927 int id_SNC = 0;
3928# ifdef FEAT_TERMINAL
3929 int id_ST = 0;
3930 int id_STNC = 0;
3931# endif
3932 int hlcnt;
3933# endif
3934#endif
3935 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3936
3937 need_highlight_changed = FALSE;
3938
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003939#ifdef FEAT_TERMINAL
3940 term_update_colors_all();
3941 term_update_wincolor_all();
3942#endif
3943
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003944 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003945 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3946 highlight_attr[hlf] = 0;
3947
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003948 // First set all attributes to their default value.
3949 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003950 for (i = 0; i < 2; ++i)
3951 {
3952 if (i)
3953 p = p_hl;
3954 else
3955 p = get_highlight_default();
3956 if (p == NULL) // just in case
3957 continue;
3958
3959 while (*p)
3960 {
3961 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3962 if (hl_flags[hlf] == *p)
3963 break;
3964 ++p;
3965 if (hlf == (int)HLF_COUNT || *p == NUL)
3966 return FAIL;
3967
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003968 // Allow several hl_flags to be combined, like "bu" for
3969 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003970 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003971 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003972 {
3973 if (VIM_ISWHITE(*p)) // ignore white space
3974 continue;
3975
3976 if (attr > HL_ALL) // Combination with ':' is not allowed.
3977 return FAIL;
3978
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003979 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003980 switch (*p)
3981 {
3982 case 'b': attr |= HL_BOLD;
3983 break;
3984 case 'i': attr |= HL_ITALIC;
3985 break;
3986 case '-':
3987 case 'n': // no highlighting
3988 break;
3989 case 'r': attr |= HL_INVERSE;
3990 break;
3991 case 's': attr |= HL_STANDOUT;
3992 break;
3993 case 'u': attr |= HL_UNDERLINE;
3994 break;
3995 case 'c': attr |= HL_UNDERCURL;
3996 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003997 case '2': attr |= HL_UNDERDOUBLE;
3998 break;
3999 case 'd': attr |= HL_UNDERDOTTED;
4000 break;
4001 case '=': attr |= HL_UNDERDASHED;
4002 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004003 case 't': attr |= HL_STRIKETHROUGH;
4004 break;
4005 case ':': ++p; // highlight group name
4006 if (attr || *p == NUL) // no combinations
4007 return FAIL;
4008 end = vim_strchr(p, ',');
4009 if (end == NULL)
4010 end = p + STRLEN(p);
4011 id = syn_check_group(p, (int)(end - p));
4012 if (id == 0)
4013 return FAIL;
4014 attr = syn_id2attr(id);
4015 p = end - 1;
4016#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
4017 if (hlf == (int)HLF_SNC)
4018 id_SNC = syn_get_final_id(id);
4019# ifdef FEAT_TERMINAL
4020 else if (hlf == (int)HLF_ST)
4021 id_ST = syn_get_final_id(id);
4022 else if (hlf == (int)HLF_STNC)
4023 id_STNC = syn_get_final_id(id);
4024# endif
4025 else if (hlf == (int)HLF_S)
4026 id_S = syn_get_final_id(id);
4027#endif
4028 break;
4029 default: return FAIL;
4030 }
4031 }
4032 highlight_attr[hlf] = attr;
4033
4034 p = skip_to_option_part(p); // skip comma and spaces
4035 }
4036 }
4037
4038#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01004039 // Setup the user highlights
4040 //
4041 // Temporarily utilize 28 more hl entries:
4042 // 9 for User1-User9 combined with StatusLineNC
4043 // 9 for User1-User9 combined with StatusLineTerm
4044 // 9 for User1-User9 combined with StatusLineTermNC
4045 // 1 for StatusLine default
4046 // Have to be in there simultaneously in case of table overflows in
4047 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004048# ifdef FEAT_STL_OPT
4049 if (ga_grow(&highlight_ga, 28) == FAIL)
4050 return FAIL;
4051 hlcnt = highlight_ga.ga_len;
4052 if (id_S == -1)
4053 {
4054 // Make sure id_S is always valid to simplify code below. Use the last
4055 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02004056 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004057 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
4058 id_S = hlcnt + 19;
4059 }
4060# endif
4061 for (i = 0; i < 9; i++)
4062 {
4063 sprintf((char *)userhl, "User%d", i + 1);
4064 id = syn_name2id(userhl);
4065 if (id == 0)
4066 {
4067 highlight_user[i] = 0;
4068# ifdef FEAT_STL_OPT
4069 highlight_stlnc[i] = 0;
4070# ifdef FEAT_TERMINAL
4071 highlight_stlterm[i] = 0;
4072 highlight_stltermnc[i] = 0;
4073# endif
4074# endif
4075 }
4076 else
4077 {
4078 highlight_user[i] = syn_id2attr(id);
4079# ifdef FEAT_STL_OPT
4080 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
4081 HLF_SNC, highlight_stlnc);
4082# ifdef FEAT_TERMINAL
4083 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
4084 HLF_ST, highlight_stlterm);
4085 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
4086 HLF_STNC, highlight_stltermnc);
4087# endif
4088# endif
4089 }
4090 }
4091# ifdef FEAT_STL_OPT
4092 highlight_ga.ga_len = hlcnt;
4093# endif
4094
4095#endif // USER_HIGHLIGHT
4096
4097 return OK;
4098}
4099
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004100static void highlight_list(void);
4101static void highlight_list_two(int cnt, int attr);
4102
Yee Cheng China7b81202025-02-23 09:32:47 +01004103// context for :highlight <group> <arg> expansion
4104static int expand_hi_synid = 0; // ID for highlight group being completed
4105static int expand_hi_equal_col = 0; // column where the '=' is
4106static int expand_hi_include_orig = 0; // whether to fill the existing current value or not
4107static char_u *expand_hi_curvalue = NULL; // the existing current value
4108#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4109static dict_iterator_T expand_colornames_iter; // iterator for looping through v:colornames
4110#endif
4111
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004112/*
4113 * Handle command line completion for :highlight command.
4114 */
4115 void
4116set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
4117{
4118 char_u *p;
Yee Cheng China7b81202025-02-23 09:32:47 +01004119 int expand_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004120
4121 // Default: expand group names
4122 xp->xp_context = EXPAND_HIGHLIGHT;
4123 xp->xp_pattern = arg;
Yee Cheng China7b81202025-02-23 09:32:47 +01004124 include_none = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004125 include_link = 2;
4126 include_default = 1;
4127
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004128 if (*arg == NUL)
4129 return;
4130
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004131 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004132 p = skiptowhite(arg);
4133 if (*p == NUL)
4134 return;
4135
4136 // past "default" or group name
4137 include_default = 0;
4138 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004139 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004140 arg = skipwhite(p);
4141 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004142 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004143 }
4144 if (*p == NUL)
4145 return;
4146
4147 // past group name
4148 include_link = 0;
4149 if (arg[1] == 'i' && arg[0] == 'N')
Yee Cheng China7b81202025-02-23 09:32:47 +01004150 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004151 highlight_list();
Yee Cheng China7b81202025-02-23 09:32:47 +01004152 expand_group = FALSE;
4153 }
4154 if (STRNCMP("link", arg, p - arg) == 0)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004155 {
4156 xp->xp_pattern = skipwhite(p);
4157 p = skiptowhite(xp->xp_pattern);
4158 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004159 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004160 xp->xp_pattern = skipwhite(p);
4161 p = skiptowhite(xp->xp_pattern);
Yee Cheng China7b81202025-02-23 09:32:47 +01004162 include_none = 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004163 }
Yee Cheng China7b81202025-02-23 09:32:47 +01004164 expand_group = FALSE;
4165 }
4166 else if (STRNCMP("clear", arg, p - arg) == 0)
4167 {
4168 xp->xp_pattern = skipwhite(p);
4169 p = skiptowhite(xp->xp_pattern);
4170 expand_group = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004171 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004172 if (*p != NUL) // past group name(s)
Yee Cheng China7b81202025-02-23 09:32:47 +01004173 {
4174 if (expand_group)
4175 {
4176 // expansion will be done in expand_highlight_group()
4177 xp->xp_context = EXPAND_HIGHLIGHT_GROUP;
4178
4179 expand_hi_synid = syn_namen2id(arg, (int)(p - arg));
4180
4181 while (*p != NUL)
4182 {
4183 arg = skipwhite(p);
4184 p = skiptowhite(arg);
4185 }
4186
4187 p = vim_strchr(arg, '=');
4188 if (p == NULL)
4189 {
4190 // Didn't find a key=<value> pattern
4191 xp->xp_pattern = arg;
4192 expand_hi_equal_col = -1;
4193 expand_hi_include_orig = FALSE;
4194 }
4195 else
4196 {
4197 // Found key=<value> pattern, record the exact location
4198 expand_hi_equal_col = (int)(p - xp->xp_line);
4199
4200 // Only include the original value if the pattern is empty
4201 if (*(p + 1) == NUL)
4202 expand_hi_include_orig = TRUE;
4203 else
4204 expand_hi_include_orig = FALSE;
4205
4206 // Account for comma-separated values
4207 if (STRNCMP(arg, "term=", 5) == 0 ||
4208 STRNCMP(arg, "cterm=", 6) == 0 ||
4209 STRNCMP(arg, "gui=", 4) == 0)
4210 {
4211 char_u *comma = vim_strrchr(p + 1, ',');
4212 if (comma != NULL)
4213 p = comma;
4214 }
4215 xp->xp_pattern = p + 1;
4216 }
4217 }
4218 else
4219 {
4220 xp->xp_context = EXPAND_NOTHING;
4221 }
4222 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004223}
4224
4225/*
4226 * List highlighting matches in a nice way.
4227 */
4228 static void
4229highlight_list(void)
4230{
4231 int i;
4232
4233 for (i = 10; --i >= 0; )
4234 highlight_list_two(i, HL_ATTR(HLF_D));
4235 for (i = 40; --i >= 0; )
4236 highlight_list_two(99, 0);
4237}
4238
4239 static void
4240highlight_list_two(int cnt, int attr)
4241{
4242 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4243 msg_clr_eos();
4244 out_flush();
4245 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4246}
4247
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004248/*
4249 * Function given to ExpandGeneric() to obtain the list of group names.
4250 */
4251 char_u *
4252get_highlight_name(expand_T *xp UNUSED, int idx)
4253{
4254 return get_highlight_name_ext(xp, idx, TRUE);
4255}
4256
4257/*
4258 * Obtain a highlight group name.
4259 * When "skip_cleared" is TRUE don't return a cleared entry.
4260 */
4261 char_u *
4262get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4263{
4264 if (idx < 0)
4265 return NULL;
4266
4267 // Items are never removed from the table, skip the ones that were
4268 // cleared.
4269 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4270 return (char_u *)"";
4271
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004272 if (idx == highlight_ga.ga_len && include_none != 0)
Yee Cheng China7b81202025-02-23 09:32:47 +01004273 return (char_u *)"NONE";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004274 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4275 return (char_u *)"default";
4276 if (idx == highlight_ga.ga_len + include_none + include_default
4277 && include_link != 0)
4278 return (char_u *)"link";
4279 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4280 && include_link != 0)
4281 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004282 if (idx >= highlight_ga.ga_len)
4283 return NULL;
4284 return HL_TABLE()[idx].sg_name;
4285}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004286
Yee Cheng China7b81202025-02-23 09:32:47 +01004287 static char_u *
4288get_highlight_attr_name(expand_T *xp UNUSED, int idx)
4289{
4290 if (idx == 0)
4291 {
4292 // Fill with current value first
4293 if (expand_hi_curvalue != NULL)
4294 return expand_hi_curvalue;
4295 else
4296 return (char_u*)"";
4297 }
4298 if (idx < (int)ARRAY_LENGTH(highlight_index_tab) + 1)
4299 {
4300 char_u *value = highlight_index_tab[idx-1]->value.string;
4301 if (expand_hi_curvalue != NULL && STRCMP(expand_hi_curvalue, value) == 0)
4302 {
4303 // Already returned the current value above, just skip.
4304 return (char_u*)"";
4305 }
4306 return value;
4307 }
4308 return NULL;
4309}
4310
4311 static char_u *
4312get_highlight_cterm_color(expand_T *xp UNUSED, int idx)
4313{
4314 if (idx == 0)
4315 {
4316 // Fill with current value first
4317 if (expand_hi_curvalue != NULL)
4318 return expand_hi_curvalue;
4319 else
4320 return (char_u*)"";
4321 }
4322 // See highlight_set_cterm_color()
4323 else if (idx == 1)
4324 return (char_u*)"fg";
4325 else if (idx == 2)
4326 return (char_u*)"bg";
4327 if (idx < (int)ARRAY_LENGTH(color_name_tab) + 3)
4328 {
4329 char_u *value = color_name_tab[idx-3].value.string;
4330 return value;
4331 }
4332 return NULL;
4333}
4334
4335#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4336 static char_u *
4337get_highlight_gui_color(expand_T *xp UNUSED, int idx)
4338{
4339 if (idx == 0)
4340 {
4341 // Fill with current value first
4342 if (expand_hi_curvalue != NULL)
4343 return expand_hi_curvalue;
4344 else
4345 return (char_u*)"";
4346 }
4347 // See color_name2handle()
4348 else if (idx == 1)
4349 return (char_u*)"fg";
4350 else if (idx == 2)
4351 return (char_u*)"bg";
4352 else if (idx == 3)
4353 return (char_u*)"NONE";
4354
4355 // Complete from v:colornames. Don't do platform specific names for now.
4356 typval_T *tv_result;
4357 char_u *colorname = dict_iterate_next(&expand_colornames_iter, &tv_result);
4358 if (colorname != NULL)
4359 {
4360 // :hi command doesn't allow space, so don't suggest any malformed items
4361 if (vim_strchr(colorname, ' ') != NULL)
4362 return (char_u*)"";
4363
4364 if (expand_hi_curvalue != NULL && STRICMP(expand_hi_curvalue, colorname) == 0)
4365 {
4366 // Already returned the current value above, just skip.
4367 return (char_u*)"";
4368 }
4369 }
4370 return colorname;
4371}
4372#endif
4373
4374 static char_u *
4375get_highlight_group_key(expand_T *xp UNUSED, int idx)
4376{
4377 // Note: Keep this in sync with do_highlight.
4378 static char *(p_hi_group_key_values[]) =
4379 {
4380 "term=",
4381 "start=",
4382 "stop=",
4383 "cterm=",
4384 "ctermfg=",
4385 "ctermbg=",
4386 "ctermul=",
4387 "ctermfont=",
4388#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
4389 "gui=",
4390 "guifg=",
4391 "guibg=",
4392 "guisp=",
4393#endif
4394#ifdef FEAT_GUI
4395 "font=",
4396#endif
4397 "NONE",
4398 };
4399
4400 if (idx < (int)ARRAY_LENGTH(p_hi_group_key_values))
4401 return (char_u*)p_hi_group_key_values[idx];
4402 return NULL;
4403}
4404
4405/*
4406 * Command-line expansion for :hi {group-name} <args>...
4407 */
4408 int
4409expand_highlight_group(
4410 char_u *pat,
4411 expand_T *xp,
4412 regmatch_T *rmp,
4413 char_u ***matches,
4414 int *numMatches)
4415{
4416 if (expand_hi_equal_col != -1)
4417 {
4418 // List the values. First fill in the current value, then if possible colors
4419 // or attribute names.
4420 char_u *(*expandfunc)(expand_T *, int) = NULL;
4421 int type = 0;
4422 hl_group_T *sgp = NULL;
4423 int iarg = 0;
4424 char_u *sarg = NULL;
4425
4426 int unsortedItems = -1; // don't sort by default
4427
4428 if (expand_hi_synid != 0)
4429 sgp = &HL_TABLE()[expand_hi_synid - 1]; // index is ID minus one
4430
4431 // Note: Keep this in sync with highlight_list_one().
4432 char_u *name_end = xp->xp_line + expand_hi_equal_col;
4433 if (name_end - xp->xp_line >= 5
4434 && STRNCMP(name_end - 5, " term", 5) == 0)
4435 {
4436 expandfunc = get_highlight_attr_name;
4437 if (sgp)
4438 {
4439 type = LIST_ATTR;
4440 iarg = sgp->sg_term;
4441 }
4442 }
4443 else if (name_end - xp->xp_line >= 6
4444 && STRNCMP(name_end - 6, " cterm", 6) == 0)
4445 {
4446 expandfunc = get_highlight_attr_name;
4447 if (sgp)
4448 {
4449 type = LIST_ATTR;
4450 iarg = sgp->sg_cterm;
4451 }
4452 }
4453#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
4454 else if (name_end - xp->xp_line >= 4
4455 && STRNCMP(name_end - 4, " gui", 4) == 0)
4456 {
4457 expandfunc = get_highlight_attr_name;
4458 if (sgp)
4459 {
4460 type = LIST_ATTR;
4461 iarg = sgp->sg_gui;
4462 }
4463 }
4464#endif
4465 else if (name_end - xp->xp_line >= 8
4466 && STRNCMP(name_end - 8, " ctermfg", 8) == 0)
4467 {
4468 expandfunc = get_highlight_cterm_color;
4469 if (sgp)
4470 {
4471 type = LIST_INT;
4472 iarg = sgp->sg_cterm_fg;
4473 }
4474 }
4475 else if (name_end - xp->xp_line >= 8
4476 && STRNCMP(name_end - 8, " ctermbg", 8) == 0)
4477 {
4478 expandfunc = get_highlight_cterm_color;
4479 if (sgp)
4480 {
4481 type = LIST_INT;
4482 iarg = sgp->sg_cterm_bg;
4483 }
4484 }
4485 else if (name_end - xp->xp_line >= 8
4486 && STRNCMP(name_end - 8, " ctermul", 8) == 0)
4487 {
4488 expandfunc = get_highlight_cterm_color;
4489 if (sgp)
4490 {
4491 type = LIST_INT;
4492 iarg = sgp->sg_cterm_ul;
4493 }
4494 }
4495#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4496 else if (name_end - xp->xp_line >= 6
4497 && STRNCMP(name_end - 6, " guifg", 6) == 0)
4498 {
4499 expandfunc = get_highlight_gui_color;
4500 if (sgp)
4501 {
4502 type = LIST_STRING;
4503 sarg = sgp->sg_gui_fg_name;
4504 }
4505 }
4506 else if (name_end - xp->xp_line >= 6
4507 && STRNCMP(name_end - 6, " guibg", 6) == 0)
4508 {
4509 expandfunc = get_highlight_gui_color;
4510 if (sgp)
4511 {
4512 type = LIST_STRING;
4513 sarg = sgp->sg_gui_bg_name;
4514 }
4515 }
4516 else if (name_end - xp->xp_line >= 6
4517 && STRNCMP(name_end - 6, " guisp", 6) == 0)
4518 {
4519 expandfunc = get_highlight_gui_color;
4520 if (sgp)
4521 {
4522 type = LIST_STRING;
4523 sarg = sgp->sg_gui_sp_name;
4524 }
4525 }
4526#endif
4527
4528#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4529 if (expandfunc == get_highlight_gui_color)
4530 {
4531 // Top 4 items are special, after that sort all the color names
4532 unsortedItems = 4;
4533
4534 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
4535 typval_T colornames_val;
4536 colornames_val.v_type = VAR_DICT;
4537 colornames_val.vval.v_dict = colornames_table;
4538 dict_iterate_start(&colornames_val, &expand_colornames_iter);
4539 }
4540#endif
4541
4542 char_u buf[MAX_ATTR_LEN];
4543
Yee Cheng Chin9b41e8f2025-02-25 20:41:52 +01004544 expand_hi_curvalue = NULL;
4545 if (expand_hi_include_orig)
Yee Cheng China7b81202025-02-23 09:32:47 +01004546 {
Yee Cheng Chin9b41e8f2025-02-25 20:41:52 +01004547 if (((type == LIST_ATTR || type == LIST_INT) && iarg != 0) ||
4548 (type == LIST_STRING && sarg != NULL))
4549 {
4550 // Retrieve the current value to go first in completion
4551 expand_hi_curvalue = highlight_arg_to_string(
4552 type, iarg, sarg, buf);
4553 }
Yee Cheng China7b81202025-02-23 09:32:47 +01004554 }
Yee Cheng China7b81202025-02-23 09:32:47 +01004555
4556 if (expandfunc != NULL)
4557 {
4558 return ExpandGenericExt(
4559 pat,
4560 xp,
4561 rmp,
4562 matches,
4563 numMatches,
4564 expandfunc,
4565 FALSE,
4566 unsortedItems);
4567 }
4568
4569 return FAIL;
4570 }
4571
4572 // List all the key names
4573 return ExpandGenericExt(
4574 pat,
4575 xp,
4576 rmp,
4577 matches,
4578 numMatches,
4579 get_highlight_group_key,
4580 FALSE,
4581 -1);
4582}
4583
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004584#if defined(FEAT_GUI) || defined(PROTO)
4585/*
4586 * Free all the highlight group fonts.
4587 * Used when quitting for systems which need it.
4588 */
4589 void
4590free_highlight_fonts(void)
4591{
4592 int idx;
4593
4594 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4595 {
4596 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4597 HL_TABLE()[idx].sg_font = NOFONT;
4598# ifdef FEAT_XFONTSET
4599 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4600 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4601# endif
4602 }
4603
4604 gui_mch_free_font(gui.norm_font);
4605# ifdef FEAT_XFONTSET
4606 gui_mch_free_fontset(gui.fontset);
4607# endif
4608# ifndef FEAT_GUI_GTK
4609 gui_mch_free_font(gui.bold_font);
4610 gui_mch_free_font(gui.ital_font);
4611 gui_mch_free_font(gui.boldital_font);
4612# endif
4613}
4614#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004615
4616#if defined(FEAT_EVAL) || defined(PROTO)
4617/*
4618 * Convert each of the highlight attribute bits (bold, standout, underline,
4619 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4620 * the attribute name as the key.
4621 */
4622 static dict_T *
4623highlight_get_attr_dict(int hlattr)
4624{
4625 dict_T *dict;
4626 int i;
4627
4628 dict = dict_alloc();
4629 if (dict == NULL)
4630 return NULL;
4631
John Marriott34f00dd2024-04-08 23:28:12 +02004632 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004633 {
John Marriott34f00dd2024-04-08 23:28:12 +02004634 if (hlattr & highlight_index_tab[i]->key)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004635 {
John Marriott8d4477e2024-11-02 15:59:01 +01004636 dict_add_bool(dict, (char *)highlight_index_tab[i]->value.string,
4637 VVAL_TRUE);
4638 // don't want "inverse"/"reverse"
4639 hlattr &= ~highlight_index_tab[i]->key;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004640 }
4641 }
4642
4643 return dict;
4644}
4645
4646/*
4647 * Return the attributes of the highlight group at index 'hl_idx' as a
4648 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4649 * links recursively.
4650 */
4651 static dict_T *
4652highlight_get_info(int hl_idx, int resolve_link)
4653{
4654 dict_T *dict;
4655 hl_group_T *sgp;
4656 dict_T *attr_dict;
4657 int hlgid;
4658
4659 dict = dict_alloc();
4660 if (dict == NULL)
4661 return dict;
4662
4663 sgp = &HL_TABLE()[hl_idx];
4664 // highlight group id is 1-based
4665 hlgid = hl_idx + 1;
4666
4667 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4668 goto error;
4669 if (dict_add_number(dict, "id", hlgid) == FAIL)
4670 goto error;
4671
4672 if (sgp->sg_link && resolve_link)
4673 {
4674 // resolve the highlight group link recursively
4675 while (sgp->sg_link)
4676 {
4677 hlgid = sgp->sg_link;
4678 sgp = &HL_TABLE()[sgp->sg_link - 1];
4679 }
4680 }
4681
4682 if (sgp->sg_term != 0)
4683 {
4684 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4685 if (attr_dict != NULL)
4686 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4687 goto error;
4688 }
4689 if (sgp->sg_start != NULL)
4690 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4691 goto error;
4692 if (sgp->sg_stop != NULL)
4693 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4694 goto error;
4695 if (sgp->sg_cterm != 0)
4696 {
4697 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4698 if (attr_dict != NULL)
4699 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4700 goto error;
4701 }
4702 if (sgp->sg_cterm_fg != 0)
4703 if (dict_add_string(dict, "ctermfg",
4704 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4705 goto error;
4706 if (sgp->sg_cterm_bg != 0)
4707 if (dict_add_string(dict, "ctermbg",
4708 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4709 goto error;
4710 if (sgp->sg_cterm_ul != 0)
4711 if (dict_add_string(dict, "ctermul",
4712 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4713 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004714 if (sgp->sg_cterm_font != 0)
4715 if (dict_add_string(dict, "ctermfont",
4716 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4717 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004718 if (sgp->sg_gui != 0)
4719 {
4720 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4721 if (attr_dict != NULL)
4722 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4723 goto error;
4724 }
4725 if (sgp->sg_gui_fg_name != NULL)
4726 if (dict_add_string(dict, "guifg",
4727 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4728 goto error;
4729 if (sgp->sg_gui_bg_name != NULL)
4730 if (dict_add_string(dict, "guibg",
4731 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4732 goto error;
4733 if (sgp->sg_gui_sp_name != NULL)
4734 if (dict_add_string(dict, "guisp",
4735 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4736 goto error;
4737# ifdef FEAT_GUI
4738 if (sgp->sg_font_name != NULL)
4739 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4740 goto error;
4741# endif
4742 if (sgp->sg_link)
4743 {
4744 char_u *link;
4745
4746 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4747 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4748 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004749
4750 if (sgp->sg_deflink)
4751 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004752 }
4753 if (dict_len(dict) == 2)
4754 // If only 'name' is present, then the highlight group is cleared.
4755 dict_add_bool(dict, "cleared", VVAL_TRUE);
4756
4757 return dict;
4758
4759error:
4760 vim_free(dict);
4761 return NULL;
4762}
4763
4764/*
4765 * "hlget([name])" function
4766 * Return the attributes of a specific highlight group (if specified) or all
4767 * the highlight groups.
4768 */
4769 void
4770f_hlget(typval_T *argvars, typval_T *rettv)
4771{
4772 list_T *list;
4773 dict_T *dict;
4774 int i;
4775 char_u *hlarg = NULL;
4776 int resolve_link = FALSE;
4777
4778 if (rettv_list_alloc(rettv) == FAIL)
4779 return;
4780
4781 if (check_for_opt_string_arg(argvars, 0) == FAIL
4782 || (argvars[0].v_type != VAR_UNKNOWN
4783 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4784 return;
4785
4786 if (argvars[0].v_type != VAR_UNKNOWN)
4787 {
4788 // highlight group name supplied
4789 hlarg = tv_get_string_chk(&argvars[0]);
4790 if (hlarg == NULL)
4791 return;
4792
4793 if (argvars[1].v_type != VAR_UNKNOWN)
4794 {
4795 int error = FALSE;
4796
4797 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4798 if (error)
4799 return;
4800 }
4801 }
4802
4803 list = rettv->vval.v_list;
4804 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4805 {
4806 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4807 {
4808 dict = highlight_get_info(i, resolve_link);
4809 if (dict != NULL)
4810 list_append_dict(list, dict);
4811 }
4812 }
4813}
4814
4815/*
4816 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4817 * 'dict' or the value is not a string type. If the value is not a string type
4818 * or is NULL, then 'error' is set to TRUE.
4819 */
4820 static char_u *
4821hldict_get_string(dict_T *dict, char_u *key, int *error)
4822{
4823 dictitem_T *di;
4824
4825 *error = FALSE;
4826 di = dict_find(dict, key, -1);
4827 if (di == NULL)
4828 return NULL;
4829
4830 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4831 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004832 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004833 *error = TRUE;
4834 return NULL;
4835 }
4836
4837 return di->di_tv.vval.v_string;
4838}
4839
4840/*
4841 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4842 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4843 * Dictionary or is NULL.
4844 */
4845 static int
4846hldict_attr_to_str(
4847 dict_T *dict,
4848 char_u *key,
4849 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004850 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004851{
4852 dictitem_T *di;
4853 dict_T *attrdict;
4854 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004855 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004856
4857 attr_str[0] = NUL;
4858 di = dict_find(dict, key, -1);
4859 if (di == NULL)
4860 return TRUE;
4861
4862 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4863 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004864 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004865 return FALSE;
4866 }
4867
4868 attrdict = di->di_tv.vval.v_dict;
4869
4870 // If the attribute dict is empty, then return NONE to clear the attributes
4871 if (dict_len(attrdict) == 0)
4872 {
4873 vim_strcat(attr_str, (char_u *)"NONE", len);
4874 return TRUE;
4875 }
4876
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004877 p = attr_str;
John Marriott34f00dd2024-04-08 23:28:12 +02004878 for (i = 0; i < (int)ARRAY_LENGTH(highlight_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004879 {
John Marriott8d4477e2024-11-02 15:59:01 +01004880 if (dict_get_bool(attrdict, (char *)highlight_tab[i].value.string,
4881 VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004882 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004883 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4884 STRCPY(p, (char_u *)",");
John Marriott8d4477e2024-11-02 15:59:01 +01004885 if (p - attr_str + highlight_tab[i].value.length + 1 < len)
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004886 {
John Marriott8d4477e2024-11-02 15:59:01 +01004887 STRCPY(p, highlight_tab[i].value.string);
4888 p += highlight_tab[i].value.length;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004889 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004890 }
4891 }
4892
4893 return TRUE;
4894}
4895
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004896// Temporary buffer used to store the command string produced by hlset().
4897// IObuff cannot be used for this as the error messages produced by hlset()
4898// internally use IObuff.
4899#define HLSETBUFSZ 512
4900static char_u hlsetBuf[HLSETBUFSZ + 1];
4901
4902/*
4903 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4904 * "dptr", which points into "hlsetBuf".
4905 * Returns the updated pointer.
4906 */
4907 static char_u *
4908add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4909{
4910 size_t vallen;
4911
4912 // Do nothing if the value is not specified or is empty
4913 if (value == NULL || *value == NUL)
4914 return dptr;
4915
4916 vallen = STRLEN(value);
4917 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4918 {
4919 STRCPY(dptr, attr);
4920 dptr += attrlen;
4921 STRCPY(dptr, value);
4922 dptr += vallen;
4923 }
4924
4925 return dptr;
4926}
4927
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004928/*
4929 * Add or update a highlight group using 'dict' items. Returns TRUE if
4930 * successfully updated the highlight group.
4931 */
4932 static int
4933hlg_add_or_update(dict_T *dict)
4934{
4935 char_u *name;
4936 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004937 char_u term_attr[MAX_ATTR_LEN];
4938 char_u cterm_attr[MAX_ATTR_LEN];
4939 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004940 char_u *start;
4941 char_u *stop;
4942 char_u *ctermfg;
4943 char_u *ctermbg;
4944 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004945 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004946 char_u *guifg;
4947 char_u *guibg;
4948 char_u *guisp;
4949# ifdef FEAT_GUI
4950 char_u *font;
4951# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004952 int forceit = FALSE;
4953 int dodefault = FALSE;
4954 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004955 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004956
4957 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004958 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004959 return FALSE;
4960
Bram Moolenaard61efa52022-07-23 09:52:04 +01004961 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004962 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004963
Bram Moolenaard61efa52022-07-23 09:52:04 +01004964 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004965 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004966
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004967 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004968 {
4969 varnumber_T cleared;
4970
4971 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004972 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004973 if (cleared == TRUE)
4974 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004975 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4976 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004977 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004978 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004979 }
4980
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004981 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004982 {
4983 char_u *linksto;
4984
4985 // link highlight groups
4986 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004987 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004988 return FALSE;
4989
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004990 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004991 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004992 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004993
4994 done = TRUE;
4995 }
4996
4997 // If 'cleared' or 'linksto' are specified, then don't process the other
4998 // attributes.
4999 if (done)
5000 return TRUE;
5001
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005002 start = hldict_get_string(dict, (char_u *)"start", &error);
5003 if (error)
5004 return FALSE;
5005
5006 stop = hldict_get_string(dict, (char_u *)"stop", &error);
5007 if (error)
5008 return FALSE;
5009
5010 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005011 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005012 return FALSE;
5013
5014 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005015 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005016 return FALSE;
5017
5018 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
5019 if (error)
5020 return FALSE;
5021
5022 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
5023 if (error)
5024 return FALSE;
5025
5026 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
5027 if (error)
5028 return FALSE;
5029
PMuncha606f3a2023-11-15 15:35:49 +01005030 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
5031 if (error)
5032 return FALSE;
5033
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005034 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005035 return FALSE;
5036
5037 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
5038 if (error)
5039 return FALSE;
5040
5041 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
5042 if (error)
5043 return FALSE;
5044
5045 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
5046 if (error)
5047 return FALSE;
5048
5049# ifdef FEAT_GUI
5050 font = hldict_get_string(dict, (char_u *)"font", &error);
5051 if (error)
5052 return FALSE;
5053# endif
5054
5055 // If none of the attributes are specified, then do nothing.
5056 if (term_attr[0] == NUL && start == NULL && stop == NULL
5057 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01005058 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005059# ifdef FEAT_GUI
5060 && font == NULL
5061# endif
5062 && guifg == NULL && guibg == NULL && guisp == NULL
5063 )
5064 return TRUE;
5065
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005066 hlsetBuf[0] = NUL;
5067 p = hlsetBuf;
5068 if (dodefault)
5069 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
5070 p = add_attr_and_value(p, (char_u *)"", 0, name);
5071 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
5072 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
5073 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
5074 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
5075 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
5076 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
5077 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01005078 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005079 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005080# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005081 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005082# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005083 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
5084 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01005085 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005086
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005087 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005088
5089 return TRUE;
5090}
5091
5092/*
5093 * "hlset([{highlight_attr}])" function
5094 * Add or modify highlight groups
5095 */
5096 void
5097f_hlset(typval_T *argvars, typval_T *rettv)
5098{
5099 listitem_T *li;
5100 dict_T *dict;
5101
5102 rettv->vval.v_number = -1;
5103
5104 if (check_for_list_arg(argvars, 0) == FAIL)
5105 return;
5106
5107 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
5108 {
5109 if (li->li_tv.v_type != VAR_DICT)
5110 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005111 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005112 return;
5113 }
5114
5115 dict = li->li_tv.vval.v_dict;
5116 if (!hlg_add_or_update(dict))
5117 return;
5118 }
5119
5120 rettv->vval.v_number = 0;
5121}
5122#endif