blob: a71a100dcdd68ff3ecb7d35fdf458c137765f14d [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"),
244#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200245 CENT("PmenuSbar ctermbg=Grey",
246 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200247 CENT("TabLineSel term=bold cterm=bold",
248 "TabLineSel term=bold cterm=bold gui=bold"),
249 CENT("TabLineFill term=reverse cterm=reverse",
250 "TabLineFill term=reverse cterm=reverse gui=reverse"),
251#ifdef FEAT_GUI
252 "Cursor guibg=fg guifg=bg",
253 "lCursor guibg=fg guifg=bg", // should be different, but what?
254#endif
255 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000256 "default link CursorLineSign SignColumn",
257 "default link CursorLineFold FoldColumn",
LemonBoya4399382022-04-09 21:04:08 +0100258 "default link CurSearch Search",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000259 "default link PmenuKind Pmenu",
260 "default link PmenuKindSel PmenuSel",
glepnir40c1c332024-06-11 19:37:04 +0200261 "default link PmenuMatch Pmenu",
262 "default link PmenuMatchSel PmenuSel",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000263 "default link PmenuExtra Pmenu",
264 "default link PmenuExtraSel PmenuSel",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200265 CENT("Normal cterm=NONE", "Normal gui=NONE"),
266 NULL
267};
268
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200269// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200270static char *(highlight_init_light[]) = {
271 CENT("Directory term=bold ctermfg=DarkBlue",
272 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
273 CENT("LineNr term=underline ctermfg=Brown",
274 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200275 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
276 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200277 CENT("MoreMsg term=bold ctermfg=DarkGreen",
278 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
279 CENT("Question term=standout ctermfg=DarkGreen",
280 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
281 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
282 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
283#ifdef FEAT_SPELL
284 CENT("SpellBad term=reverse ctermbg=LightRed",
285 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
286 CENT("SpellCap term=reverse ctermbg=LightBlue",
287 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
288 CENT("SpellRare term=reverse ctermbg=LightMagenta",
289 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
290 CENT("SpellLocal term=underline ctermbg=Cyan",
291 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
292#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200293 CENT("PmenuThumb ctermbg=Black",
294 "PmenuThumb ctermbg=Black guibg=Black"),
295 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
296 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
297 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
298 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200299 CENT("SpecialKey term=bold ctermfg=DarkBlue",
300 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
301 CENT("Title term=bold ctermfg=DarkMagenta",
302 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
303 CENT("WarningMsg term=standout ctermfg=DarkRed",
304 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200305 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
306 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200307#ifdef FEAT_FOLDING
308 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
309 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
310 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
311 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
312#endif
313#ifdef FEAT_SIGNS
314 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
315 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
316#endif
Maxim Kim59bafc82024-02-01 21:07:51 +0100317 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100318 "Visual ctermbg=Grey ctermfg=Black guibg=LightGrey guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200319#ifdef FEAT_DIFF
320 CENT("DiffAdd term=bold ctermbg=LightBlue",
321 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
322 CENT("DiffChange term=bold ctermbg=LightMagenta",
323 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
324 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
325 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
326#endif
327 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
328 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
329#ifdef FEAT_SYN_HL
330 CENT("CursorColumn term=reverse ctermbg=LightGrey",
331 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
332 CENT("CursorLine term=underline cterm=underline",
333 "CursorLine term=underline cterm=underline guibg=Grey90"),
334 CENT("ColorColumn term=reverse ctermbg=LightRed",
335 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
336#endif
337#ifdef FEAT_CONCEAL
338 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
339 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
340#endif
341 CENT("MatchParen term=reverse ctermbg=Cyan",
342 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
343#ifdef FEAT_TERMINAL
344 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
345 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
346 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
347 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
348#endif
349#ifdef FEAT_MENU
350 CENT("ToolbarLine term=underline ctermbg=LightGrey",
351 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
352 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
353 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
354#endif
355 NULL
356};
357
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200358// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200359static char *(highlight_init_dark[]) = {
360 CENT("Directory term=bold ctermfg=LightCyan",
361 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
362 CENT("LineNr term=underline ctermfg=Yellow",
363 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200364 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
365 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200366 CENT("MoreMsg term=bold ctermfg=LightGreen",
367 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
368 CENT("Question term=standout ctermfg=LightGreen",
369 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
370 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
371 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
372 CENT("SpecialKey term=bold ctermfg=LightBlue",
373 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
374#ifdef FEAT_SPELL
375 CENT("SpellBad term=reverse ctermbg=Red",
376 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
377 CENT("SpellCap term=reverse ctermbg=Blue",
378 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
379 CENT("SpellRare term=reverse ctermbg=Magenta",
380 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
381 CENT("SpellLocal term=underline ctermbg=Cyan",
382 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
383#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200384 CENT("PmenuThumb ctermbg=White",
385 "PmenuThumb ctermbg=White guibg=White"),
386 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
387 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
388 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
389 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200390 CENT("Title term=bold ctermfg=LightMagenta",
391 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
392 CENT("WarningMsg term=standout ctermfg=LightRed",
393 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200394 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
395 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200396#ifdef FEAT_FOLDING
397 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
398 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
399 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
400 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
401#endif
402#ifdef FEAT_SIGNS
403 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
404 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
405#endif
Christian Brabandte6d8b462024-01-28 23:33:29 +0100406 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100407 "Visual ctermbg=Grey ctermfg=Black guibg=#575757 guifg=LightGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200408#ifdef FEAT_DIFF
409 CENT("DiffAdd term=bold ctermbg=DarkBlue",
410 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
411 CENT("DiffChange term=bold ctermbg=DarkMagenta",
412 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
413 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
414 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
415#endif
416 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
417 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
418#ifdef FEAT_SYN_HL
419 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
420 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
421 CENT("CursorLine term=underline cterm=underline",
422 "CursorLine term=underline cterm=underline guibg=Grey40"),
423 CENT("ColorColumn term=reverse ctermbg=DarkRed",
424 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
425#endif
426 CENT("MatchParen term=reverse ctermbg=DarkCyan",
427 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
428#ifdef FEAT_CONCEAL
429 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
430 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
431#endif
432#ifdef FEAT_TERMINAL
433 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
434 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
435 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
436 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
437#endif
438#ifdef FEAT_MENU
439 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
440 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
441 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
442 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
443#endif
444 NULL
445};
446
Dominique Pelle748b3082022-01-08 12:41:16 +0000447#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200448/*
449 * Returns the number of highlight groups.
450 */
451 int
452highlight_num_groups(void)
453{
454 return highlight_ga.ga_len;
455}
456
457/*
458 * Returns the name of a highlight group.
459 */
460 char_u *
461highlight_group_name(int id)
462{
463 return HL_TABLE()[id].sg_name;
464}
465
466/*
467 * Returns the ID of the link to a highlight group.
468 */
469 int
470highlight_link_id(int id)
471{
472 return HL_TABLE()[id].sg_link;
473}
Dominique Pelle748b3082022-01-08 12:41:16 +0000474#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200475
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200476 void
477init_highlight(
478 int both, // include groups where 'bg' doesn't matter
479 int reset) // clear group first
480{
481 int i;
482 char **pp;
483 static int had_both = FALSE;
484#ifdef FEAT_EVAL
485 char_u *p;
486
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100487 // Try finding the color scheme file. Used when a color file was loaded
488 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200489 p = get_var_value((char_u *)"g:colors_name");
490 if (p != NULL)
491 {
492 // The value of g:colors_name could be freed when sourcing the script,
493 // making "p" invalid, so copy it.
494 char_u *copy_p = vim_strsave(p);
495 int r;
496
497 if (copy_p != NULL)
498 {
499 r = load_colors(copy_p);
500 vim_free(copy_p);
501 if (r == OK)
502 return;
503 }
504 }
505
506#endif
507
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100508 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200509 if (both)
510 {
511 had_both = TRUE;
512 pp = highlight_init_both;
513 for (i = 0; pp[i] != NULL; ++i)
514 do_highlight((char_u *)pp[i], reset, TRUE);
515 }
516 else if (!had_both)
517 // Don't do anything before the call with both == TRUE from main().
518 // Not everything has been setup then, and that call will overrule
519 // everything anyway.
520 return;
521
522 if (*p_bg == 'l')
523 pp = highlight_init_light;
524 else
525 pp = highlight_init_dark;
526 for (i = 0; pp[i] != NULL; ++i)
527 do_highlight((char_u *)pp[i], reset, TRUE);
528
Maxim Kim59bafc82024-02-01 21:07:51 +0100529 // Reverse looks ugly, but grey may not work for less than 8 colors. Thus
530 // let it depend on the number of colors available.
531 if (t_colors < 8)
532 do_highlight((char_u *)"Visual term=reverse cterm=reverse ctermbg=NONE ctermfg=NONE",
533 FALSE, TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200534 // With 8 colors brown is equal to yellow, need to use black for Search fg
535 // to avoid Statement highlighted text disappears.
536 // Clear the attributes, needed when changing the t_Co value.
Christian Brabandte6d8b462024-01-28 23:33:29 +0100537 if (t_colors <= 8)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200538 {
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200539 if (*p_bg == 'l')
540 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
541 }
542
543#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100544 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200545 if (get_var_value((char_u *)"g:syntax_on") != NULL)
546 {
547 static int recursive = 0;
548
549 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000550 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200551 else
552 {
553 ++recursive;
554 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
555 --recursive;
556 }
557 }
558#endif
559}
560
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000561#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
562/*
563 * Load a default color list. Intended to support legacy color names but allows
564 * the user to override the color values. Only loaded once.
565 */
566 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000567load_default_colors_lists(void)
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000568{
569 // Lacking a default color list isn't the end of the world but it is likely
570 // an inconvenience so users should know when it is missing.
571 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
572 msg("failed to load colors/lists/default.vim");
573}
574#endif
575
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200576/*
577 * Load color file "name".
578 * Return OK for success, FAIL for failure.
579 */
580 int
581load_colors(char_u *name)
582{
583 char_u *buf;
584 int retval = FAIL;
585 static int recursive = FALSE;
586
587 // When being called recursively, this is probably because setting
588 // 'background' caused the highlighting to be reloaded. This means it is
589 // working, thus we should return OK.
590 if (recursive)
591 return OK;
592
593 recursive = TRUE;
594 buf = alloc(STRLEN(name) + 12);
595 if (buf != NULL)
596 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100597#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100598 load_default_colors_lists();
599#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200600 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
601 curbuf->b_fname, FALSE, curbuf);
602 sprintf((char *)buf, "colors/%s.vim", name);
603 retval = source_runtime(buf, DIP_START + DIP_OPT);
604 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100605 if (retval == OK)
606 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
607 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200608 }
609 recursive = FALSE;
610
611 return retval;
612}
613
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200614static int color_numbers_16[28] = {0, 1, 2, 3,
615 4, 5, 6, 6,
616 7, 7, 7, 7,
617 8, 8,
618 9, 9, 10, 10,
619 11, 11, 12, 12, 13,
620 13, 14, 14, 15, -1};
621// for xterm with 88 colors...
622static int color_numbers_88[28] = {0, 4, 2, 6,
623 1, 5, 32, 72,
624 84, 84, 7, 7,
625 82, 82,
626 12, 43, 10, 61,
627 14, 63, 9, 74, 13,
628 75, 11, 78, 15, -1};
629// for xterm with 256 colors...
630static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200631 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200632 248, 248, 7, 7,
633 242, 242,
634 12, 81, 10, 121,
635 14, 159, 9, 224, 13,
636 225, 11, 229, 15, -1};
637// for terminals with less than 16 colors...
638static int color_numbers_8[28] = {0, 4, 2, 6,
639 1, 5, 3, 3,
640 7, 7, 7, 7,
641 0+8, 0+8,
642 4+8, 4+8, 2+8, 2+8,
643 6+8, 6+8, 1+8, 1+8, 5+8,
644 5+8, 3+8, 3+8, 7+8, -1};
645
646/*
647 * Lookup the "cterm" value to be used for color with index "idx" in
John Marriott34f00dd2024-04-08 23:28:12 +0200648 * color_name_tab[].
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200649 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
650 * colors, otherwise it will be unchanged.
651 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100652 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200653lookup_color(int idx, int foreground, int *boldp)
654{
655 int color = color_numbers_16[idx];
656 char_u *p;
657
658 // Use the _16 table to check if it's a valid color name.
659 if (color < 0)
660 return -1;
661
662 if (t_colors == 8)
663 {
664 // t_Co is 8: use the 8 colors table
665#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100666 // On qnx, the 8 & 16 color arrays are the same
667 if (STRNCMP(T_NAME, "qansi", 5) == 0)
668 color = color_numbers_16[idx];
669 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200670#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100671 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200672 if (foreground)
673 {
674 // set/reset bold attribute to get light foreground
675 // colors (on some terminals, e.g. "linux")
676 if (color & 8)
677 *boldp = TRUE;
678 else
679 *boldp = FALSE;
680 }
681 color &= 7; // truncate to 8 colors
682 }
683 else if (t_colors == 16 || t_colors == 88
684 || t_colors >= 256)
685 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100686 // Guess: if the termcap entry ends in 'm', it is
687 // probably an xterm-like terminal. Use the changed
688 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200689 if (*T_CAF != NUL)
690 p = T_CAF;
691 else
692 p = T_CSF;
693 if (*p != NUL && (t_colors > 256
694 || *(p + STRLEN(p) - 1) == 'm'))
695 {
696 if (t_colors == 88)
697 color = color_numbers_88[idx];
698 else if (t_colors >= 256)
699 color = color_numbers_256[idx];
700 else
701 color = color_numbers_8[idx];
702 }
703#ifdef FEAT_TERMRESPONSE
704 if (t_colors >= 256 && color == 15 && is_mac_terminal)
705 // Terminal.app has a bug: 15 is light grey. Use white
706 // from the color cube instead.
707 color = 231;
708#endif
709 }
710 return color;
711}
712
713/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100714 * Link highlight group 'from_hg' to 'to_hg'.
715 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000716 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100717 * 'init' is set to TRUE when initializing all the highlight groups.
718 */
719 static void
720highlight_group_link(
721 char_u *from_hg,
722 int from_len,
723 char_u *to_hg,
724 int to_len,
725 int dodefault,
726 int forceit,
727 int init)
728{
729 int from_id;
730 int to_id;
731 hl_group_T *hlgroup = NULL;
732
733 from_id = syn_check_group(from_hg, from_len);
734 if (STRNCMP(to_hg, "NONE", 4) == 0)
735 to_id = 0;
736 else
737 to_id = syn_check_group(to_hg, to_len);
738
739 if (from_id > 0)
740 {
741 hlgroup = &HL_TABLE()[from_id - 1];
742 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
743 {
744 hlgroup->sg_deflink = to_id;
745#ifdef FEAT_EVAL
746 hlgroup->sg_deflink_sctx = current_sctx;
747 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
748#endif
749 }
750 }
751
752 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
753 {
754 // Don't allow a link when there already is some highlighting
755 // for the group, unless '!' is used
756 if (to_id > 0 && !forceit && !init
757 && hl_has_settings(from_id - 1, dodefault))
758 {
759 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000760 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100761 }
762 else if (hlgroup->sg_link != to_id
763#ifdef FEAT_EVAL
764 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
765#endif
766 || hlgroup->sg_cleared)
767 {
768 if (!init)
769 hlgroup->sg_set |= SG_LINK;
770 hlgroup->sg_link = to_id;
771#ifdef FEAT_EVAL
772 hlgroup->sg_script_ctx = current_sctx;
773 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
774#endif
775 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100776 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100777
778 // Only call highlight_changed() once after multiple changes.
779 need_highlight_changed = TRUE;
780 }
781 }
782
783}
784
785/*
786 * Reset all highlighting to the defaults. Removes all highlighting for the
787 * groups added by the user.
788 */
789 static void
790highlight_reset_all(void)
791{
792 int idx;
793
794#ifdef FEAT_GUI
795 // First, we do not destroy the old values, but allocate the new
796 // ones and update the display. THEN we destroy the old values.
797 // If we destroy the old values first, then the old values
798 // (such as GuiFont's or GuiFontset's) will still be displayed but
799 // invalid because they were free'd.
800 if (gui.in_use)
801 {
802# ifdef FEAT_BEVAL_TIP
803 gui_init_tooltip_font();
804# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100805# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100806 gui_init_menu_font();
807# endif
808 }
809# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
810 gui_mch_def_colors();
811# endif
812# ifdef FEAT_GUI_X11
813# ifdef FEAT_MENU
814
815 // This only needs to be done when there is no Menu highlight
816 // group defined by default, which IS currently the case.
817 gui_mch_new_menu_colors();
818# endif
819 if (gui.in_use)
820 {
821 gui_new_scrollbar_colors();
822# ifdef FEAT_BEVAL_GUI
823 gui_mch_new_tooltip_colors();
824# endif
825# ifdef FEAT_MENU
826 gui_mch_new_menu_font();
827# endif
828 }
829# endif
830
831 // Ok, we're done allocating the new default graphics items.
832 // The screen should already be refreshed at this point.
833 // It is now Ok to clear out the old data.
834#endif
835#ifdef FEAT_EVAL
836 do_unlet((char_u *)"g:colors_name", TRUE);
837#endif
838 restore_cterm_colors();
839
840 // Clear all default highlight groups and load the defaults.
841 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
842 highlight_clear(idx);
843 init_highlight(TRUE, TRUE);
844#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
845 if (USE_24BIT)
846 highlight_gui_started();
847 else
848#endif
849 highlight_changed();
850 redraw_later_clear();
851}
852
853/*
854 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
855 * index 'idx'.
856 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
857 * 'arg' is the list of attribute names separated by comma.
858 * 'init' is set to TRUE when initializing all the highlight groups.
859 * Returns TRUE if the attributes are set.
860 */
861 static int
862highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
863{
864 int attr;
Mike Williams72a156b2024-04-09 22:04:54 +0200865 size_t off;
John Marriott34f00dd2024-04-08 23:28:12 +0200866 keyvalue_T target;
867 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100868
869 attr = 0;
870 off = 0;
John Marriott34f00dd2024-04-08 23:28:12 +0200871 target.key = 0;
872 target.length = 0; // not used, see cmp_keyvalue_value_ni()
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100873 while (arg[off] != NUL)
874 {
John Marriott34f00dd2024-04-08 23:28:12 +0200875 target.value = (char *)arg + off;
876 entry = (keyvalue_T *)bsearch(&target, &highlight_tab, ARRAY_LENGTH(highlight_tab), sizeof(highlight_tab[0]), cmp_keyvalue_value_ni);
877 if (entry == NULL)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100878 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000879 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100880 return FALSE;
881 }
John Marriott34f00dd2024-04-08 23:28:12 +0200882
883 attr |= entry->key;
884 off += entry->length;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100885 if (arg[off] == ',') // another one follows
886 ++off;
887 }
888 if (*key == 'T')
889 {
890 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
891 {
892 if (!init)
893 HL_TABLE()[idx].sg_set |= SG_TERM;
894 HL_TABLE()[idx].sg_term = attr;
895 }
896 }
897 else if (*key == 'C')
898 {
899 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
900 {
901 if (!init)
902 HL_TABLE()[idx].sg_set |= SG_CTERM;
903 HL_TABLE()[idx].sg_cterm = attr;
904 HL_TABLE()[idx].sg_cterm_bold = FALSE;
905 }
906 }
907#if defined(FEAT_GUI) || defined(FEAT_EVAL)
908 else
909 {
910 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
911 {
912 if (!init)
913 HL_TABLE()[idx].sg_set |= SG_GUI;
914 HL_TABLE()[idx].sg_gui = attr;
915 }
916 }
917#endif
918
919 return TRUE;
920}
921
922#ifdef FEAT_GUI
923/*
924 * Set the font for the highlight group at 'idx'.
925 * 'arg' is the font name.
926 * Returns TRUE if the font is changed.
927 */
928 static int
929highlight_set_font(
930 int idx,
931 char_u *arg,
932 int is_normal_group,
933 int is_menu_group,
934 int is_tooltip_group)
935{
936 int did_change = FALSE;
937
938 // in non-GUI fonts are simply ignored
939 if (HL_TABLE()[idx].sg_font_name != NULL
940 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
941 {
942 // Font name didn't change, ignore.
943 }
944 else if (!gui.shell_created)
945 {
946 // GUI not started yet, always accept the name.
947 vim_free(HL_TABLE()[idx].sg_font_name);
948 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
949 did_change = TRUE;
950 }
951 else
952 {
953 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
954# ifdef FEAT_XFONTSET
955 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
956# endif
957 // First, save the current font/fontset.
958 // Then try to allocate the font/fontset.
959 // If the allocation fails, HL_TABLE()[idx].sg_font OR
960 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
961
962 HL_TABLE()[idx].sg_font = NOFONT;
963# ifdef FEAT_XFONTSET
964 HL_TABLE()[idx].sg_fontset = NOFONTSET;
965# endif
966 hl_do_font(idx, arg, is_normal_group, is_menu_group,
967 is_tooltip_group, FALSE);
968
969# ifdef FEAT_XFONTSET
970 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
971 {
972 // New fontset was accepted. Free the old one, if there
973 // was one.
974 gui_mch_free_fontset(temp_sg_fontset);
975 vim_free(HL_TABLE()[idx].sg_font_name);
976 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
977 did_change = TRUE;
978 }
979 else
980 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
981# endif
982 if (HL_TABLE()[idx].sg_font != NOFONT)
983 {
984 // New font was accepted. Free the old one, if there was
985 // one.
986 gui_mch_free_font(temp_sg_font);
987 vim_free(HL_TABLE()[idx].sg_font_name);
988 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
989 did_change = TRUE;
990 }
991 else
992 HL_TABLE()[idx].sg_font = temp_sg_font;
993 }
994
995 return did_change;
996}
997#endif
998
999/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001000 * Set the cterm foreground color for the Normal highlight group to "color" and
1001 * the bold attribute to "bold".
1002 */
1003 static void
1004hl_set_ctermfg_normal_group(int color, int bold)
1005{
1006 cterm_normal_fg_color = color + 1;
1007 cterm_normal_fg_bold = bold;
1008#ifdef FEAT_GUI
1009 // Don't do this if the GUI is used.
1010 if (!gui.in_use && !gui.starting)
1011#endif
1012 {
1013 set_must_redraw(UPD_CLEAR);
1014 if (termcap_active && color >= 0)
1015 term_fg_color(color);
1016 }
1017}
1018
1019/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001020 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001021 */
1022 static void
1023highlight_set_ctermfg(int idx, int color, int is_normal_group)
1024{
1025 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1026 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001027 hl_set_ctermfg_normal_group(color,
1028 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001029}
1030
1031/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001032 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001033 */
1034 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001035hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001036{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001037 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001038#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001039 // Don't mess with 'background' if the GUI is used.
1040 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001041#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001042 {
1043 set_must_redraw(UPD_CLEAR);
1044 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001045 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001046 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001047
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001048 if (termcap_active)
1049 term_bg_color(color);
1050 if (t_colors < 16)
1051 dark = (color == 0 || color == 4);
1052 // Limit the heuristic to the standard 16 colors
1053 else if (color < 16)
1054 dark = (color < 7 || color == 8);
1055 // Set the 'background' option if the value is
1056 // wrong.
1057 if (dark != -1
1058 && dark != (*p_bg == 'd')
1059 && !option_was_set((char_u *)"bg"))
1060 {
1061 set_option_value_give_err((char_u *)"bg",
1062 0L, (char_u *)(dark ? "dark" : "light"), 0);
1063 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001064 }
1065 }
1066 }
1067}
1068
1069/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001070 * Set the cterm background color for the highlight group at 'idx' to 'color'.
1071 */
1072 static void
1073highlight_set_ctermbg(int idx, int color, int is_normal_group)
1074{
1075 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1076 if (is_normal_group)
1077 hl_set_ctermbg_normal_group(color);
1078}
1079
1080/*
1081 * Set the cterm underline color for the Normal highlight group to "color".
1082 */
1083 static void
1084hl_set_ctermul_normal_group(int color)
1085{
1086 cterm_normal_ul_color = color + 1;
1087#ifdef FEAT_GUI
1088 // Don't do this if the GUI is used.
1089 if (!gui.in_use && !gui.starting)
1090#endif
1091 {
1092 set_must_redraw(UPD_CLEAR);
1093 if (termcap_active && color >= 0)
1094 term_ul_color(color);
1095 }
1096}
1097
1098/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001099 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001100 */
1101 static void
1102highlight_set_ctermul(int idx, int color, int is_normal_group)
1103{
1104 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1105 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001106 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001107}
1108
1109/*
PMuncha606f3a2023-11-15 15:35:49 +01001110 * Set the cterm font for the highlight group at 'idx'.
1111 * 'arg' is the color name or the numeric value as a string.
1112 * 'init' is set to TRUE when initializing highlighting.
1113 * Called for the ":highlight" command and the "hlset()" function.
1114 *
1115 * Returns TRUE if the font is set.
1116 */
1117 static int
1118highlight_set_cterm_font(
1119 int idx,
1120 char_u *arg,
1121 int init)
1122{
1123 int font;
1124
1125 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1126 return FALSE;
1127
1128 if (!init)
1129 HL_TABLE()[idx].sg_set |= SG_CTERM;
1130
1131 if (VIM_ISDIGIT(*arg))
1132 font = atoi((char *)arg);
1133 else if (STRICMP(arg, "NONE") == 0)
1134 font = -1;
1135 else
1136 return FALSE;
1137
1138 HL_TABLE()[idx].sg_cterm_font = font + 1;
1139 return TRUE;
1140}
1141
1142/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001143 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1144 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1145 * 'keystart' is the color name/value.
1146 * 'arg' is the color name or the numeric value as a string.
1147 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1148 * 'init' is set to TRUE when initializing highlighting.
1149 * Called for the ":highlight" command and the "hlset()" function.
1150 *
1151 * Returns TRUE if the color is set.
1152 */
1153 static int
1154highlight_set_cterm_color(
1155 int idx,
1156 char_u *key,
1157 char_u *key_start,
1158 char_u *arg,
1159 int is_normal_group,
1160 int init)
1161{
1162 int color;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001163
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001164 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1165 return FALSE;
1166
1167 if (!init)
1168 HL_TABLE()[idx].sg_set |= SG_CTERM;
1169
1170 // When setting the foreground color, and previously the "bold"
1171 // flag was set for a light color, reset it now
1172 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001173 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001174 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1175 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1176 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001177
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001178 if (VIM_ISDIGIT(*arg))
1179 color = atoi((char *)arg);
1180 else if (STRICMP(arg, "fg") == 0)
1181 {
1182 if (cterm_normal_fg_color)
1183 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001184 else
1185 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001186 emsg(_(e_fg_color_unknown));
1187 return FALSE;
1188 }
1189 }
1190 else if (STRICMP(arg, "bg") == 0)
1191 {
1192 if (cterm_normal_bg_color > 0)
1193 color = cterm_normal_bg_color - 1;
1194 else
1195 {
1196 emsg(_(e_bg_color_unknown));
1197 return FALSE;
1198 }
1199 }
1200 else if (STRICMP(arg, "ul") == 0)
1201 {
1202 if (cterm_normal_ul_color > 0)
1203 color = cterm_normal_ul_color - 1;
1204 else
1205 {
1206 emsg(_(e_ul_color_unknown));
1207 return FALSE;
1208 }
1209 }
1210 else
1211 {
1212 int bold = MAYBE;
John Marriott34f00dd2024-04-08 23:28:12 +02001213 keyvalue_T target;
1214 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001215
John Marriott34f00dd2024-04-08 23:28:12 +02001216 target.key = 0;
1217 target.value = (char *)arg;
1218 target.length = 0; // not used, see cmp_keyvalue_value_i()
1219 entry = (keyvalue_T *)bsearch(&target, &color_name_tab, ARRAY_LENGTH(color_name_tab), sizeof(color_name_tab[0]), cmp_keyvalue_value_i);
1220 if (entry == NULL)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001221 {
1222 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1223 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001224 }
1225
John Marriott34f00dd2024-04-08 23:28:12 +02001226 color = lookup_color(entry->key, key[5] == 'F', &bold);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001227
1228 // set/reset bold attribute to get light foreground
1229 // colors (on some terminals, e.g. "linux")
1230 if (bold == TRUE)
1231 {
1232 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1233 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1234 }
1235 else if (bold == FALSE)
1236 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001237 }
1238
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001239 // Add one to the argument, to avoid zero. Zero is used for
1240 // "NONE", then "color" is -1.
1241 if (key[5] == 'F')
1242 highlight_set_ctermfg(idx, color, is_normal_group);
1243 else if (key[5] == 'B')
1244 highlight_set_ctermbg(idx, color, is_normal_group);
1245 else // ctermul
1246 highlight_set_ctermul(idx, color, is_normal_group);
1247
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001248 return TRUE;
1249}
1250
1251#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1252/*
1253 * Set the GUI foreground color for the highlight group at 'idx'.
1254 * Returns TRUE if the color is set.
1255 */
1256 static int
1257highlight_set_guifg(
1258 int idx,
1259 char_u *arg,
1260 int is_menu_group UNUSED,
1261 int is_scrollbar_group UNUSED,
1262 int is_tooltip_group UNUSED,
1263 int *do_colors UNUSED,
1264 int init)
1265{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001266# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001267 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001268# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001269 char_u **namep;
1270 int did_change = FALSE;
1271
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001272 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1273 return FALSE;
1274
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001275 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001276 if (!init)
1277 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001278
1279# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001280 // In GUI guifg colors are only used when recognized
1281 i = color_name2handle(arg);
1282 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1283 {
1284 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001285# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001286 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1287 {
1288 vim_free(*namep);
1289 if (STRCMP(arg, "NONE") != 0)
1290 *namep = vim_strsave(arg);
1291 else
1292 *namep = NULL;
1293 did_change = TRUE;
1294 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001295# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1296# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001297 if (is_menu_group && gui.menu_fg_pixel != i)
1298 {
1299 gui.menu_fg_pixel = i;
1300 *do_colors = TRUE;
1301 }
1302 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1303 {
1304 gui.scroll_fg_pixel = i;
1305 *do_colors = TRUE;
1306 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001307# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001308 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1309 {
1310 gui.tooltip_fg_pixel = i;
1311 *do_colors = TRUE;
1312 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001313# endif
1314# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001315 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001316# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001317
1318 return did_change;
1319}
1320
1321/*
1322 * Set the GUI background color for the highlight group at 'idx'.
1323 * Returns TRUE if the color is set.
1324 */
1325 static int
1326highlight_set_guibg(
1327 int idx,
1328 char_u *arg,
1329 int is_menu_group UNUSED,
1330 int is_scrollbar_group UNUSED,
1331 int is_tooltip_group UNUSED,
1332 int *do_colors UNUSED,
1333 int init)
1334{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001335# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001336 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001337# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001338 char_u **namep;
1339 int did_change = FALSE;
1340
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001341 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1342 return FALSE;
1343
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001344 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001345 if (!init)
1346 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001347
1348# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001349 // In GUI guibg colors are only used when recognized
1350 i = color_name2handle(arg);
1351 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1352 {
1353 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001354# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001355 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1356 {
1357 vim_free(*namep);
1358 if (STRCMP(arg, "NONE") != 0)
1359 *namep = vim_strsave(arg);
1360 else
1361 *namep = NULL;
1362 did_change = TRUE;
1363 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001364# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1365# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001366 if (is_menu_group && gui.menu_bg_pixel != i)
1367 {
1368 gui.menu_bg_pixel = i;
1369 *do_colors = TRUE;
1370 }
1371 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1372 {
1373 gui.scroll_bg_pixel = i;
1374 *do_colors = TRUE;
1375 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001376# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001377 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1378 {
1379 gui.tooltip_bg_pixel = i;
1380 *do_colors = TRUE;
1381 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001382# endif
1383# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001384 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001385# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001386
1387 return did_change;
1388}
1389
1390/*
1391 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1392 * Returns TRUE if the color is set.
1393 */
1394 static int
1395highlight_set_guisp(int idx, char_u *arg, int init)
1396{
1397# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1398 int i;
1399# endif
1400 int did_change = FALSE;
1401 char_u **namep;
1402
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001403 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1404 return FALSE;
1405
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001406 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001407 if (!init)
1408 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001409
1410# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001411 // In GUI guisp colors are only used when recognized
1412 i = color_name2handle(arg);
1413 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1414 {
1415 HL_TABLE()[idx].sg_gui_sp = i;
1416# endif
1417 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001418 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001419 vim_free(*namep);
1420 if (STRCMP(arg, "NONE") != 0)
1421 *namep = vim_strsave(arg);
1422 else
1423 *namep = NULL;
1424 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001425 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001426# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001427 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001428# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001429
1430 return did_change;
1431}
1432#endif
1433
1434/*
1435 * Set the start/stop terminal codes for a highlight group.
1436 * Returns TRUE if the terminal code is set.
1437 */
1438 static int
1439highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1440{
1441 int off;
1442 char_u buf[100];
1443 int len;
1444 char_u *tname;
1445 char_u *p;
1446
1447 if (!init)
1448 HL_TABLE()[idx].sg_set |= SG_TERM;
1449
1450 // The "start" and "stop" arguments can be a literal escape
1451 // sequence, or a comma separated list of terminal codes.
1452 if (STRNCMP(arg, "t_", 2) == 0)
1453 {
1454 off = 0;
1455 buf[0] = 0;
1456 while (arg[off] != NUL)
1457 {
1458 // Isolate one termcap name
1459 for (len = 0; arg[off + len] &&
1460 arg[off + len] != ','; ++len)
1461 ;
1462 tname = vim_strnsave(arg + off, len);
1463 if (tname == NULL) // out of memory
1464 return FALSE;
1465 // lookup the escape sequence for the item
1466 p = get_term_code(tname);
1467 vim_free(tname);
1468 if (p == NULL) // ignore non-existing things
1469 p = (char_u *)"";
1470
1471 // Append it to the already found stuff
1472 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1473 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001474 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001475 return FALSE;
1476 }
1477 STRCAT(buf, p);
1478
1479 // Advance to the next item
1480 off += len;
1481 if (arg[off] == ',') // another one follows
1482 ++off;
1483 }
1484 }
1485 else
1486 {
1487 // Copy characters from arg[] to buf[], translating <> codes.
1488 for (p = arg, off = 0; off < 100 - 6 && *p; )
1489 {
zeertzjqdb088872022-05-02 22:53:45 +01001490 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001491 if (len > 0) // recognized special char
1492 off += len;
1493 else // copy as normal char
1494 buf[off++] = *p++;
1495 }
1496 buf[off] = NUL;
1497 }
1498
1499 if (STRCMP(buf, "NONE") == 0) // resetting the value
1500 p = NULL;
1501 else
1502 p = vim_strsave(buf);
1503 if (key[2] == 'A')
1504 {
1505 vim_free(HL_TABLE()[idx].sg_start);
1506 HL_TABLE()[idx].sg_start = p;
1507 }
1508 else
1509 {
1510 vim_free(HL_TABLE()[idx].sg_stop);
1511 HL_TABLE()[idx].sg_stop = p;
1512 }
1513 return TRUE;
1514}
1515
1516/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001517 * Handle the ":highlight .." command.
1518 * When using ":hi clear" this is called recursively for each group with
1519 * "forceit" and "init" both TRUE.
1520 */
1521 void
1522do_highlight(
1523 char_u *line,
1524 int forceit,
1525 int init) // TRUE when called for initializing
1526{
1527 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001528 char_u *linep;
1529 char_u *key_start;
1530 char_u *arg_start;
1531 char_u *key = NULL, *arg = NULL;
1532 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001533 int id;
1534 int idx;
1535 hl_group_T item_before;
1536 int did_change = FALSE;
1537 int dodefault = FALSE;
1538 int doclear = FALSE;
1539 int dolink = FALSE;
1540 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001541 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001542#ifdef FEAT_GUI_X11
1543 int is_menu_group = FALSE; // "Menu" group
1544 int is_scrollbar_group = FALSE; // "Scrollbar" group
1545 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001546#else
1547# define is_menu_group 0
1548# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001549# define is_scrollbar_group 0
1550#endif
1551#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1552 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001553#endif
1554#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1555 int did_highlight_changed = FALSE;
1556#endif
1557
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001558 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001559 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001560 {
1561 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1562 // TODO: only call when the group has attributes set
1563 highlight_list_one((int)i);
1564 return;
1565 }
1566
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001567 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001568 name_end = skiptowhite(line);
1569 linep = skipwhite(name_end);
1570
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001571 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001572 if (STRNCMP(line, "default", name_end - line) == 0)
1573 {
1574 dodefault = TRUE;
1575 line = linep;
1576 name_end = skiptowhite(line);
1577 linep = skipwhite(name_end);
1578 }
1579
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001580 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001581 if (STRNCMP(line, "clear", name_end - line) == 0)
1582 doclear = TRUE;
1583 if (STRNCMP(line, "link", name_end - line) == 0)
1584 dolink = TRUE;
1585
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001586 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001587 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001588 {
1589 id = syn_namen2id(line, (int)(name_end - line));
1590 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001591 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001592 else
1593 highlight_list_one(id);
1594 return;
1595 }
1596
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001597 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001598 if (dolink)
1599 {
1600 char_u *from_start = linep;
1601 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001602 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001603 char_u *to_start;
1604 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001605 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001606
1607 from_end = skiptowhite(from_start);
1608 to_start = skipwhite(from_end);
1609 to_end = skiptowhite(to_start);
1610
Bram Moolenaar1966c242020-04-20 22:42:32 +02001611 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001612 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001613 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001614 return;
1615 }
1616
Bram Moolenaar1966c242020-04-20 22:42:32 +02001617 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001618 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001619 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001620 return;
1621 }
1622
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001623 from_len = (int)(from_end - from_start);
1624 to_len = (int)(to_end - to_start);
1625 highlight_group_link(from_start, from_len, to_start, to_len,
1626 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001627 return;
1628 }
1629
1630 if (doclear)
1631 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001632 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001633 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001634 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001635 // ":highlight clear" without group name
1636 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001637 return;
1638 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001639 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001640 name_end = skiptowhite(line);
1641 linep = skipwhite(name_end);
1642 }
1643
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001644 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001645 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001646 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001647 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001648 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001649
1650 // Return if "default" was used and the group already has settings.
1651 if (dodefault && hl_has_settings(idx, TRUE))
1652 return;
1653
1654 // Make a copy so we can check if any attribute actually changed.
1655 item_before = HL_TABLE()[idx];
1656
1657 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1658 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001659#ifdef FEAT_GUI_X11
1660 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1661 is_menu_group = TRUE;
1662 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1663 is_scrollbar_group = TRUE;
1664 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1665 is_tooltip_group = TRUE;
1666#endif
1667
1668 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1669 if (doclear || (forceit && init))
1670 {
1671 highlight_clear(idx);
1672 if (!doclear)
1673 HL_TABLE()[idx].sg_set = 0;
1674 }
1675
1676 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001677 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001678 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001679 key_start = linep;
1680 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001681 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001682 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001683 error = TRUE;
1684 break;
1685 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001686
1687 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1688 // or "guibg").
1689 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1690 ++linep;
1691 vim_free(key);
1692 key = vim_strnsave_up(key_start, linep - key_start);
1693 if (key == NULL)
1694 {
1695 error = TRUE;
1696 break;
1697 }
1698 linep = skipwhite(linep);
1699
1700 if (STRCMP(key, "NONE") == 0)
1701 {
1702 if (!init || HL_TABLE()[idx].sg_set == 0)
1703 {
1704 if (!init)
1705 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1706 highlight_clear(idx);
1707 }
1708 continue;
1709 }
1710
1711 // Check for the equal sign.
1712 if (*linep != '=')
1713 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001714 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001715 error = TRUE;
1716 break;
1717 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001718 ++linep;
1719
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001720 // Isolate the argument.
1721 linep = skipwhite(linep);
1722 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001723 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001724 arg_start = ++linep;
1725 linep = vim_strchr(linep, '\'');
1726 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001727 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001728 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001729 error = TRUE;
1730 break;
1731 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001732 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001733 else
1734 {
1735 arg_start = linep;
1736 linep = skiptowhite(linep);
1737 }
1738 if (linep == arg_start)
1739 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001740 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001741 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001742 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001743 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001744 vim_free(arg);
1745 arg = vim_strnsave(arg_start, linep - arg_start);
1746 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001747 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001748 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001749 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001750 }
1751 if (*linep == '\'')
1752 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001753
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001754 // Store the argument.
1755 if (STRCMP(key, "TERM") == 0
1756 || STRCMP(key, "CTERM") == 0
1757 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001758 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001759 if (!highlight_set_termgui_attr(idx, key, arg, init))
1760 {
1761 error = TRUE;
1762 break;
1763 }
1764 }
1765 else if (STRCMP(key, "FONT") == 0)
1766 {
1767 // in non-GUI fonts are simply ignored
1768#ifdef FEAT_GUI
1769 if (highlight_set_font(idx, arg, is_normal_group,
1770 is_menu_group, is_tooltip_group))
1771 did_change = TRUE;
1772#endif
1773 }
1774 else if (STRCMP(key, "CTERMFG") == 0
1775 || STRCMP(key, "CTERMBG") == 0
1776 || STRCMP(key, "CTERMUL") == 0)
1777 {
1778 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1779 is_normal_group, init))
1780 {
1781 error = TRUE;
1782 break;
1783 }
1784 }
PMuncha606f3a2023-11-15 15:35:49 +01001785 else if (STRCMP(key, "CTERMFONT") == 0)
1786 {
1787 if (!highlight_set_cterm_font(idx, arg, init))
1788 {
1789 error = TRUE;
1790 break;
1791 }
1792 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001793 else if (STRCMP(key, "GUIFG") == 0)
1794 {
1795#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1796 if (highlight_set_guifg(idx, arg, is_menu_group,
1797 is_scrollbar_group, is_tooltip_group,
1798 &do_colors, init))
1799 did_change = TRUE;
1800#endif
1801 }
1802 else if (STRCMP(key, "GUIBG") == 0)
1803 {
1804#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1805 if (highlight_set_guibg(idx, arg, is_menu_group,
1806 is_scrollbar_group, is_tooltip_group,
1807 &do_colors, init))
1808 did_change = TRUE;
1809#endif
1810 }
1811 else if (STRCMP(key, "GUISP") == 0)
1812 {
1813#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1814 if (highlight_set_guisp(idx, arg, init))
1815 did_change = TRUE;
1816#endif
1817 }
1818 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1819 {
1820 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1821 {
1822 error = TRUE;
1823 break;
1824 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001825 }
1826 else
1827 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001828 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001829 error = TRUE;
1830 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001831 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001832 HL_TABLE()[idx].sg_cleared = FALSE;
1833
1834 // When highlighting has been given for a group, don't link it.
1835 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1836 HL_TABLE()[idx].sg_link = 0;
1837
1838 // Continue with next argument.
1839 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001840 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001841
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001842 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001843 if (error && idx == highlight_ga.ga_len)
1844 syn_unadd_group();
1845 else
1846 {
1847 if (is_normal_group)
1848 {
1849 HL_TABLE()[idx].sg_term_attr = 0;
1850 HL_TABLE()[idx].sg_cterm_attr = 0;
1851#ifdef FEAT_GUI
1852 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001853 // Need to update all groups, because they might be using "bg"
1854 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001855#endif
1856#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1857 if (USE_24BIT)
1858 {
1859 highlight_gui_started();
1860 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001861 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001862 }
1863#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001864#ifdef FEAT_VTP
1865 control_console_color_rgb();
1866#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001867 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001868#ifdef FEAT_GUI_X11
1869# ifdef FEAT_MENU
1870 else if (is_menu_group)
1871 {
1872 if (gui.in_use && do_colors)
1873 gui_mch_new_menu_colors();
1874 }
1875# endif
1876 else if (is_scrollbar_group)
1877 {
1878 if (gui.in_use && do_colors)
1879 gui_new_scrollbar_colors();
1880 else
1881 set_hl_attr(idx);
1882 }
1883# ifdef FEAT_BEVAL_GUI
1884 else if (is_tooltip_group)
1885 {
1886 if (gui.in_use && do_colors)
1887 gui_mch_new_tooltip_colors();
1888 }
1889# endif
1890#endif
1891 else
1892 set_hl_attr(idx);
1893#ifdef FEAT_EVAL
1894 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001895 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001896#endif
1897 }
1898
1899 vim_free(key);
1900 vim_free(arg);
1901
1902 // Only call highlight_changed() once, after a sequence of highlight
1903 // commands, and only if an attribute actually changed.
1904 if ((did_change
1905 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1906#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1907 && !did_highlight_changed
1908#endif
1909 )
1910 {
1911 // Do not trigger a redraw when highlighting is changed while
1912 // redrawing. This may happen when evaluating 'statusline' changes the
1913 // StatusLine group.
1914 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001915 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001916 need_highlight_changed = TRUE;
1917 }
1918}
1919
1920#if defined(EXITFREE) || defined(PROTO)
1921 void
1922free_highlight(void)
1923{
1924 int i;
1925
1926 for (i = 0; i < highlight_ga.ga_len; ++i)
1927 {
1928 highlight_clear(i);
1929 vim_free(HL_TABLE()[i].sg_name);
1930 vim_free(HL_TABLE()[i].sg_name_u);
1931 }
1932 ga_clear(&highlight_ga);
1933}
1934#endif
1935
1936/*
1937 * Reset the cterm colors to what they were before Vim was started, if
1938 * possible. Otherwise reset them to zero.
1939 */
1940 void
1941restore_cterm_colors(void)
1942{
1943#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1944 // Since t_me has been set, this probably means that the user
1945 // wants to use this as default colors. Need to reset default
1946 // background/foreground colors.
1947 mch_set_normal_colors();
1948#else
1949# ifdef VIMDLL
1950 if (!gui.in_use)
1951 {
1952 mch_set_normal_colors();
1953 return;
1954 }
1955# endif
1956 cterm_normal_fg_color = 0;
1957 cterm_normal_fg_bold = 0;
1958 cterm_normal_bg_color = 0;
1959# ifdef FEAT_TERMGUICOLORS
1960 cterm_normal_fg_gui_color = INVALCOLOR;
1961 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001962 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001963# endif
1964#endif
1965}
1966
1967/*
1968 * Return TRUE if highlight group "idx" has any settings.
1969 * When "check_link" is TRUE also check for an existing link.
1970 */
1971 static int
1972hl_has_settings(int idx, int check_link)
1973{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001974 return HL_TABLE()[idx].sg_cleared == 0
1975 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001976 || HL_TABLE()[idx].sg_cterm_attr != 0
1977 || HL_TABLE()[idx].sg_cterm_fg != 0
1978 || HL_TABLE()[idx].sg_cterm_bg != 0
PMuncha606f3a2023-11-15 15:35:49 +01001979 || HL_TABLE()[idx].sg_cterm_font != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001980#ifdef FEAT_GUI
1981 || HL_TABLE()[idx].sg_gui_attr != 0
1982 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1983 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1984 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1985 || HL_TABLE()[idx].sg_font_name != NULL
1986#endif
1987 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1988}
1989
1990/*
1991 * Clear highlighting for one group.
1992 */
1993 static void
1994highlight_clear(int idx)
1995{
1996 HL_TABLE()[idx].sg_cleared = TRUE;
1997
1998 HL_TABLE()[idx].sg_term = 0;
1999 VIM_CLEAR(HL_TABLE()[idx].sg_start);
2000 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
2001 HL_TABLE()[idx].sg_term_attr = 0;
2002 HL_TABLE()[idx].sg_cterm = 0;
2003 HL_TABLE()[idx].sg_cterm_bold = FALSE;
2004 HL_TABLE()[idx].sg_cterm_fg = 0;
2005 HL_TABLE()[idx].sg_cterm_bg = 0;
2006 HL_TABLE()[idx].sg_cterm_attr = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002007 HL_TABLE()[idx].sg_cterm_font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002008#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2009 HL_TABLE()[idx].sg_gui = 0;
2010 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
2011 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
2012 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
2013#endif
2014#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2015 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
2016 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002017 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002018#endif
2019#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002020 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2021 HL_TABLE()[idx].sg_font = NOFONT;
2022# ifdef FEAT_XFONTSET
2023 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2024 HL_TABLE()[idx].sg_fontset = NOFONTSET;
2025# endif
2026 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
2027 HL_TABLE()[idx].sg_gui_attr = 0;
2028#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02002029 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02002030 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02002031#ifdef FEAT_EVAL
2032 // Since we set the default link, set the location to where the default
2033 // link was set.
2034 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002035#endif
2036}
2037
2038#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2039/*
2040 * Set the normal foreground and background colors according to the "Normal"
2041 * highlighting group. For X11 also set "Menu", "Scrollbar", and
2042 * "Tooltip" colors.
2043 */
2044 void
2045set_normal_colors(void)
2046{
2047# ifdef FEAT_GUI
2048# ifdef FEAT_TERMGUICOLORS
2049 if (gui.in_use)
2050# endif
2051 {
2052 if (set_group_colors((char_u *)"Normal",
2053 &gui.norm_pixel, &gui.back_pixel,
2054 FALSE, TRUE, FALSE))
2055 {
2056 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002057 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002058 }
2059# ifdef FEAT_GUI_X11
2060 if (set_group_colors((char_u *)"Menu",
2061 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
2062 TRUE, FALSE, FALSE))
2063 {
2064# ifdef FEAT_MENU
2065 gui_mch_new_menu_colors();
2066# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002067 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002068 }
2069# ifdef FEAT_BEVAL_GUI
2070 if (set_group_colors((char_u *)"Tooltip",
2071 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
2072 FALSE, FALSE, TRUE))
2073 {
2074# ifdef FEAT_TOOLBAR
2075 gui_mch_new_tooltip_colors();
2076# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002077 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002078 }
2079# endif
2080 if (set_group_colors((char_u *)"Scrollbar",
2081 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
2082 FALSE, FALSE, FALSE))
2083 {
2084 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002085 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002086 }
2087# endif
2088 }
2089# endif
2090# ifdef FEAT_TERMGUICOLORS
2091# ifdef FEAT_GUI
2092 else
2093# endif
2094 {
2095 int idx;
2096
2097 idx = syn_name2id((char_u *)"Normal") - 1;
2098 if (idx >= 0)
2099 {
2100 gui_do_one_color(idx, FALSE, FALSE);
2101
2102 // If the normal fg or bg color changed a complete redraw is
2103 // required.
2104 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
2105 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
2106 {
2107 // if the GUI color is INVALCOLOR then we use the default cterm
2108 // color
2109 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
2110 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002111 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002112 }
2113 }
2114 }
2115# endif
2116}
2117#endif
2118
2119#if defined(FEAT_GUI) || defined(PROTO)
2120/*
2121 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2122 */
2123 static int
2124set_group_colors(
2125 char_u *name,
2126 guicolor_T *fgp,
2127 guicolor_T *bgp,
2128 int do_menu,
2129 int use_norm,
2130 int do_tooltip)
2131{
2132 int idx;
2133
2134 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002135 if (idx < 0)
2136 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002137
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002138 gui_do_one_color(idx, do_menu, do_tooltip);
2139
2140 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2141 *fgp = HL_TABLE()[idx].sg_gui_fg;
2142 else if (use_norm)
2143 *fgp = gui.def_norm_pixel;
2144 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2145 *bgp = HL_TABLE()[idx].sg_gui_bg;
2146 else if (use_norm)
2147 *bgp = gui.def_back_pixel;
2148 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002149}
2150
2151/*
2152 * Get the font of the "Normal" group.
2153 * Returns "" when it's not found or not set.
2154 */
2155 char_u *
2156hl_get_font_name(void)
2157{
2158 int id;
2159 char_u *s;
2160
2161 id = syn_name2id((char_u *)"Normal");
2162 if (id > 0)
2163 {
2164 s = HL_TABLE()[id - 1].sg_font_name;
2165 if (s != NULL)
2166 return s;
2167 }
2168 return (char_u *)"";
2169}
2170
2171/*
2172 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2173 * actually chosen to be used.
2174 */
2175 void
2176hl_set_font_name(char_u *font_name)
2177{
2178 int id;
2179
2180 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002181 if (id <= 0)
2182 return;
2183
2184 vim_free(HL_TABLE()[id - 1].sg_font_name);
2185 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002186}
2187
2188/*
2189 * Set background color for "Normal" group. Called by gui_set_bg_color()
2190 * when the color is known.
2191 */
2192 void
2193hl_set_bg_color_name(
2194 char_u *name) // must have been allocated
2195{
2196 int id;
2197
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002198 if (name == NULL)
2199 return;
2200
2201 id = syn_name2id((char_u *)"Normal");
2202 if (id <= 0)
2203 return;
2204
2205 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2206 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002207}
2208
2209/*
2210 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2211 * when the color is known.
2212 */
2213 void
2214hl_set_fg_color_name(
2215 char_u *name) // must have been allocated
2216{
2217 int id;
2218
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002219 if (name == NULL)
2220 return;
2221
2222 id = syn_name2id((char_u *)"Normal");
2223 if (id <= 0)
2224 return;
2225
2226 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2227 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002228}
2229
2230/*
2231 * Return the handle for a font name.
2232 * Returns NOFONT when failed.
2233 */
2234 static GuiFont
2235font_name2handle(char_u *name)
2236{
2237 if (STRCMP(name, "NONE") == 0)
2238 return NOFONT;
2239
2240 return gui_mch_get_font(name, TRUE);
2241}
2242
2243# ifdef FEAT_XFONTSET
2244/*
2245 * Return the handle for a fontset name.
2246 * Returns NOFONTSET when failed.
2247 */
2248 static GuiFontset
2249fontset_name2handle(char_u *name, int fixed_width)
2250{
2251 if (STRCMP(name, "NONE") == 0)
2252 return NOFONTSET;
2253
2254 return gui_mch_get_fontset(name, TRUE, fixed_width);
2255}
2256# endif
2257
2258/*
2259 * Get the font or fontset for one highlight group.
2260 */
2261 static void
2262hl_do_font(
2263 int idx,
2264 char_u *arg,
2265 int do_normal, // set normal font
2266 int do_menu UNUSED, // set menu font
2267 int do_tooltip UNUSED, // set tooltip font
2268 int free_font) // free current font/fontset
2269{
2270# ifdef FEAT_XFONTSET
2271 // If 'guifontset' is not empty, first try using the name as a
2272 // fontset. If that doesn't work, use it as a font name.
2273 if (*p_guifontset != NUL
2274# ifdef FONTSET_ALWAYS
2275 || do_menu
2276# endif
2277# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002278 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002279 || do_tooltip
2280# endif
2281 )
2282 {
2283 if (free_font)
2284 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2285 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2286# ifdef FONTSET_ALWAYS
2287 || do_menu
2288# endif
2289# ifdef FEAT_BEVAL_TIP
2290 || do_tooltip
2291# endif
2292 );
2293 }
2294 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2295 {
2296 // If it worked and it's the Normal group, use it as the normal
2297 // fontset. Same for the Menu group.
2298 if (do_normal)
2299 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002300# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002301 if (do_menu)
2302 {
2303# ifdef FONTSET_ALWAYS
2304 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2305# else
2306 // YIKES! This is a bug waiting to crash the program
2307 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2308# endif
2309 gui_mch_new_menu_font();
2310 }
2311# ifdef FEAT_BEVAL_GUI
2312 if (do_tooltip)
2313 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002314 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002315 // displaying a single font and a fontset.
2316 // If the XtNinternational resource is set to True at widget
2317 // creation, then a fontset is always used, otherwise an
2318 // XFontStruct is used.
2319 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2320 gui_mch_new_tooltip_font();
2321 }
2322# endif
2323# endif
2324 }
2325 else
2326# endif
2327 {
2328 if (free_font)
2329 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2330 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2331 // If it worked and it's the Normal group, use it as the
2332 // normal font. Same for the Menu group.
2333 if (HL_TABLE()[idx].sg_font != NOFONT)
2334 {
2335 if (do_normal)
2336 gui_init_font(arg, FALSE);
2337#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002338# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002339 if (do_menu)
2340 {
2341 gui.menu_font = HL_TABLE()[idx].sg_font;
2342 gui_mch_new_menu_font();
2343 }
2344# endif
2345#endif
2346 }
2347 }
2348}
2349
2350#endif // FEAT_GUI
2351
2352#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2353/*
2354 * Return the handle for a color name.
2355 * Returns INVALCOLOR when failed.
2356 */
2357 guicolor_T
2358color_name2handle(char_u *name)
2359{
2360 if (STRCMP(name, "NONE") == 0)
2361 return INVALCOLOR;
2362
2363 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2364 {
2365#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2366 if (gui.in_use)
2367#endif
2368#ifdef FEAT_GUI
2369 return gui.norm_pixel;
2370#endif
2371#ifdef FEAT_TERMGUICOLORS
2372 if (cterm_normal_fg_gui_color != INVALCOLOR)
2373 return cterm_normal_fg_gui_color;
2374 // Guess that the foreground is black or white.
2375 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2376#endif
2377 }
2378 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2379 {
2380#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2381 if (gui.in_use)
2382#endif
2383#ifdef FEAT_GUI
2384 return gui.back_pixel;
2385#endif
2386#ifdef FEAT_TERMGUICOLORS
2387 if (cterm_normal_bg_gui_color != INVALCOLOR)
2388 return cterm_normal_bg_gui_color;
2389 // Guess that the background is white or black.
2390 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2391#endif
2392 }
2393
2394 return GUI_GET_COLOR(name);
2395}
Drew Vogele30d1022021-10-24 20:35:07 +01002396
2397// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2398// values as used by the MS-Windows GDI api. It should be used only for
2399// MS-Windows GDI builds.
2400# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2401# undef RGB
2402# endif
2403# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002404# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002405# endif
2406
2407# ifdef VIMDLL
2408 static guicolor_T
2409gui_adjust_rgb(guicolor_T c)
2410{
2411 if (gui.in_use)
2412 return c;
2413 else
2414 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2415}
2416# else
2417# define gui_adjust_rgb(c) (c)
2418# endif
2419
2420 static int
2421hex_digit(int c)
2422{
Keith Thompson184f71c2024-01-04 21:19:04 +01002423 if (SAFE_isdigit(c))
Drew Vogele30d1022021-10-24 20:35:07 +01002424 return c - '0';
2425 c = TOLOWER_ASC(c);
2426 if (c >= 'a' && c <= 'f')
2427 return c - 'a' + 10;
2428 return 0x1ffffff;
2429}
2430
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002431 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002432decode_hex_color(char_u *hex)
2433{
2434 guicolor_T color;
2435
2436 if (hex[0] != '#' || STRLEN(hex) != 7)
2437 return INVALCOLOR;
2438
2439 // Name is in "#rrggbb" format
2440 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2441 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2442 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2443 if (color > 0xffffff)
2444 return INVALCOLOR;
2445 return gui_adjust_rgb(color);
2446}
2447
Bram Moolenaar2a521962021-10-25 10:30:14 +01002448#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002449// Returns the color currently mapped to the given name or INVALCOLOR if no
2450// such name exists in the color table. The convention is to use lowercase for
2451// all keys in the v:colornames dictionary. The value can be either a string in
2452// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002453 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002454colorname2rgb(char_u *name)
2455{
2456 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2457 char_u *lc_name;
2458 dictitem_T *colentry;
2459 char_u *colstr;
2460 varnumber_T colnum;
2461
2462 lc_name = strlow_save(name);
2463 if (lc_name == NULL)
2464 return INVALCOLOR;
2465
2466 colentry = dict_find(colornames_table, lc_name, -1);
2467 vim_free(lc_name);
2468 if (colentry == NULL)
2469 return INVALCOLOR;
2470
2471 if (colentry->di_tv.v_type == VAR_STRING)
2472 {
2473 colstr = tv_get_string_strict(&colentry->di_tv);
2474 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2475 {
2476 return decode_hex_color(colstr);
2477 }
2478 else
2479 {
2480 semsg(_(e_bad_color_string_str), colstr);
2481 return INVALCOLOR;
2482 }
2483 }
2484
2485 if (colentry->di_tv.v_type == VAR_NUMBER)
2486 {
2487 colnum = tv_get_number(&colentry->di_tv);
2488 return (guicolor_T)colnum;
2489 }
2490
2491 return INVALCOLOR;
2492}
2493
Drew Vogele30d1022021-10-24 20:35:07 +01002494#endif
2495
2496 guicolor_T
2497gui_get_color_cmn(char_u *name)
2498{
Drew Vogele30d1022021-10-24 20:35:07 +01002499 guicolor_T color;
Drew Vogele30d1022021-10-24 20:35:07 +01002500 // Only non X11 colors (not present in rgb.txt) and colors in
John Marriott34f00dd2024-04-08 23:28:12 +02002501 // color_name_tab[], useful when $VIMRUNTIME is not found,.
2502 // must be sorted by the 'value' field because it is used by bsearch()!
2503 static keyvalue_T rgb_tab[] = {
2504 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x00), "black"),
2505 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0xFF), "blue"),
2506 KEYVALUE_ENTRY(RGB(0xA5, 0x2A, 0x2A), "brown"),
2507 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0xFF), "cyan"),
2508 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x8B), "darkblue"),
2509 KEYVALUE_ENTRY(RGB(0x00, 0x8B, 0x8B), "darkcyan"),
2510 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgray"),
2511 KEYVALUE_ENTRY(RGB(0x00, 0x64, 0x00), "darkgreen"),
2512 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgrey"),
2513 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x8B), "darkmagenta"),
2514 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x00), "darkred"),
2515 KEYVALUE_ENTRY(RGB(0x8B, 0x8B, 0x00), "darkyellow"), // No X11
2516 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "gray"),
2517 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0x00), "green"),
2518 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "grey"),
2519 KEYVALUE_ENTRY(RGB(0x66, 0x66, 0x66), "grey40"),
2520 KEYVALUE_ENTRY(RGB(0x7F, 0x7F, 0x7F), "grey50"),
2521 KEYVALUE_ENTRY(RGB(0xE5, 0xE5, 0xE5), "grey90"),
2522 KEYVALUE_ENTRY(RGB(0xAD, 0xD8, 0xE6), "lightblue"),
2523 KEYVALUE_ENTRY(RGB(0xE0, 0xFF, 0xFF), "lightcyan"),
2524 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgray"),
2525 KEYVALUE_ENTRY(RGB(0x90, 0xEE, 0x90), "lightgreen"),
2526 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgrey"),
2527 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0xFF), "lightmagenta"), // No XX
2528 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0x8B), "lightred"), // No XX
2529 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xE0), "lightyellow"),
2530 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0xFF), "magenta"),
2531 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0x00), "red"),
2532 KEYVALUE_ENTRY(RGB(0x2E, 0x8B, 0x57), "seagreen"),
2533 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xFF), "white"),
2534 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0x00), "yellow")
Drew Vogele30d1022021-10-24 20:35:07 +01002535 };
John Marriott34f00dd2024-04-08 23:28:12 +02002536 keyvalue_T target;
2537 keyvalue_T *entry;
Drew Vogele30d1022021-10-24 20:35:07 +01002538
2539 color = decode_hex_color(name);
2540 if (color != INVALCOLOR)
2541 return color;
2542
John Marriott34f00dd2024-04-08 23:28:12 +02002543 target.key = 0;
2544 target.value = (char *)name;
2545 target.length = 0; // not used, see cmp_keyvalue_value_i()
2546 entry = (keyvalue_T *)bsearch(&target, &rgb_tab, ARRAY_LENGTH(rgb_tab), sizeof(rgb_tab[0]), cmp_keyvalue_value_i);
2547 if (entry != NULL)
John Marriott34f00dd2024-04-08 23:28:12 +02002548 return gui_adjust_rgb((guicolor_T)entry->key);
Drew Vogele30d1022021-10-24 20:35:07 +01002549
2550#if defined(FEAT_EVAL)
2551 /*
2552 * Not a traditional color. Load additional color aliases and then consult the alias table.
2553 */
2554
2555 color = colorname2rgb(name);
2556 if (color == INVALCOLOR)
2557 {
2558 load_default_colors_lists();
2559 color = colorname2rgb(name);
2560 }
2561
2562 return color;
2563#else
2564 return INVALCOLOR;
2565#endif
2566}
2567
2568 guicolor_T
2569gui_get_rgb_color_cmn(int r, int g, int b)
2570{
2571 guicolor_T color = RGB(r, g, b);
2572
2573 if (color > 0xffffff)
2574 return INVALCOLOR;
2575 return gui_adjust_rgb(color);
2576}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002577#endif
2578
2579/*
2580 * Table with the specifications for an attribute number.
2581 * Note that this table is used by ALL buffers. This is required because the
2582 * GUI can redraw at any time for any buffer.
2583 */
2584static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2585
2586#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2587
2588static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2589
2590#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2591
2592#ifdef FEAT_GUI
2593static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2594
2595#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2596#endif
2597
2598/*
2599 * Return the attr number for a set of colors and font.
2600 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2601 * if the combination is new.
2602 * Return 0 for error (no more room).
2603 */
2604 static int
2605get_attr_entry(garray_T *table, attrentry_T *aep)
2606{
2607 int i;
2608 attrentry_T *taep;
2609 static int recursive = FALSE;
2610
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002611 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002612 table->ga_itemsize = sizeof(attrentry_T);
2613 table->ga_growsize = 7;
2614
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002615 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002616 for (i = 0; i < table->ga_len; ++i)
2617 {
2618 taep = &(((attrentry_T *)table->ga_data)[i]);
2619 if ( aep->ae_attr == taep->ae_attr
2620 && (
2621#ifdef FEAT_GUI
2622 (table == &gui_attr_table
2623 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2624 && aep->ae_u.gui.bg_color
2625 == taep->ae_u.gui.bg_color
2626 && aep->ae_u.gui.sp_color
2627 == taep->ae_u.gui.sp_color
2628 && aep->ae_u.gui.font == taep->ae_u.gui.font
2629# ifdef FEAT_XFONTSET
2630 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2631# endif
2632 ))
2633 ||
2634#endif
2635 (table == &term_attr_table
2636 && (aep->ae_u.term.start == NULL)
2637 == (taep->ae_u.term.start == NULL)
2638 && (aep->ae_u.term.start == NULL
2639 || STRCMP(aep->ae_u.term.start,
2640 taep->ae_u.term.start) == 0)
2641 && (aep->ae_u.term.stop == NULL)
2642 == (taep->ae_u.term.stop == NULL)
2643 && (aep->ae_u.term.stop == NULL
2644 || STRCMP(aep->ae_u.term.stop,
2645 taep->ae_u.term.stop) == 0))
2646 || (table == &cterm_attr_table
2647 && aep->ae_u.cterm.fg_color
2648 == taep->ae_u.cterm.fg_color
2649 && aep->ae_u.cterm.bg_color
2650 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002651 && aep->ae_u.cterm.ul_color
2652 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002653 && aep->ae_u.cterm.font
2654 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002655#ifdef FEAT_TERMGUICOLORS
2656 && aep->ae_u.cterm.fg_rgb
2657 == taep->ae_u.cterm.fg_rgb
2658 && aep->ae_u.cterm.bg_rgb
2659 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002660 && aep->ae_u.cterm.ul_rgb
2661 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002662#endif
2663 )))
2664
2665 return i + ATTR_OFF;
2666 }
2667
2668 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2669 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002670 // Running out of attribute entries! remove all attributes, and
2671 // compute new ones for all groups.
2672 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002673 if (recursive)
2674 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002675 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002676 return 0;
2677 }
2678 recursive = TRUE;
2679
2680 clear_hl_tables();
2681
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002682 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002683
2684 for (i = 0; i < highlight_ga.ga_len; ++i)
2685 set_hl_attr(i);
2686
2687 recursive = FALSE;
2688 }
2689
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002690 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002691 if (ga_grow(table, 1) == FAIL)
2692 return 0;
2693
2694 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002695 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002696 taep->ae_attr = aep->ae_attr;
2697#ifdef FEAT_GUI
2698 if (table == &gui_attr_table)
2699 {
2700 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2701 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2702 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2703 taep->ae_u.gui.font = aep->ae_u.gui.font;
2704# ifdef FEAT_XFONTSET
2705 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2706# endif
2707 }
2708#endif
2709 if (table == &term_attr_table)
2710 {
2711 if (aep->ae_u.term.start == NULL)
2712 taep->ae_u.term.start = NULL;
2713 else
2714 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2715 if (aep->ae_u.term.stop == NULL)
2716 taep->ae_u.term.stop = NULL;
2717 else
2718 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2719 }
2720 else if (table == &cterm_attr_table)
2721 {
2722 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2723 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002724 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002725 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002726#ifdef FEAT_TERMGUICOLORS
2727 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2728 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002729 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002730#endif
2731 }
2732 ++table->ga_len;
2733 return (table->ga_len - 1 + ATTR_OFF);
2734}
2735
2736#if defined(FEAT_TERMINAL) || defined(PROTO)
2737/*
2738 * Get an attribute index for a cterm entry.
2739 * Uses an existing entry when possible or adds one when needed.
2740 */
2741 int
2742get_cterm_attr_idx(int attr, int fg, int bg)
2743{
2744 attrentry_T at_en;
2745
Bram Moolenaara80faa82020-04-12 19:37:17 +02002746 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002747#ifdef FEAT_TERMGUICOLORS
2748 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2749 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002750 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002751#endif
2752 at_en.ae_attr = attr;
2753 at_en.ae_u.cterm.fg_color = fg;
2754 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002755 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002756 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002757 return get_attr_entry(&cterm_attr_table, &at_en);
2758}
2759#endif
2760
2761#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2762/*
2763 * Get an attribute index for a 'termguicolors' entry.
2764 * Uses an existing entry when possible or adds one when needed.
2765 */
2766 int
2767get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2768{
2769 attrentry_T at_en;
2770
Bram Moolenaara80faa82020-04-12 19:37:17 +02002771 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002772 at_en.ae_attr = attr;
2773 if (fg == INVALCOLOR && bg == INVALCOLOR)
2774 {
2775 // If both GUI colors are not set fall back to the cterm colors. Helps
2776 // if the GUI only has an attribute, such as undercurl.
2777 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2778 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2779 }
2780 else
2781 {
2782 at_en.ae_u.cterm.fg_rgb = fg;
2783 at_en.ae_u.cterm.bg_rgb = bg;
2784 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002785 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002786 return get_attr_entry(&cterm_attr_table, &at_en);
2787}
2788#endif
2789
2790#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2791/*
2792 * Get an attribute index for a cterm entry.
2793 * Uses an existing entry when possible or adds one when needed.
2794 */
2795 int
2796get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2797{
2798 attrentry_T at_en;
2799
Bram Moolenaara80faa82020-04-12 19:37:17 +02002800 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002801 at_en.ae_attr = attr;
2802 at_en.ae_u.gui.fg_color = fg;
2803 at_en.ae_u.gui.bg_color = bg;
2804 return get_attr_entry(&gui_attr_table, &at_en);
2805}
2806#endif
2807
2808/*
2809 * Clear all highlight tables.
2810 */
2811 void
2812clear_hl_tables(void)
2813{
2814 int i;
2815 attrentry_T *taep;
2816
2817#ifdef FEAT_GUI
2818 ga_clear(&gui_attr_table);
2819#endif
2820 for (i = 0; i < term_attr_table.ga_len; ++i)
2821 {
2822 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2823 vim_free(taep->ae_u.term.start);
2824 vim_free(taep->ae_u.term.stop);
2825 }
2826 ga_clear(&term_attr_table);
2827 ga_clear(&cterm_attr_table);
2828}
2829
2830/*
2831 * Combine special attributes (e.g., for spelling) with other attributes
2832 * (e.g., for syntax highlighting).
2833 * "prim_attr" overrules "char_attr".
2834 * This creates a new group when required.
2835 * Since we expect there to be few spelling mistakes we don't cache the
2836 * result.
2837 * Return the resulting attributes.
2838 */
2839 int
2840hl_combine_attr(int char_attr, int prim_attr)
2841{
2842 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002843 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002844 attrentry_T new_en;
2845
2846 if (char_attr == 0)
2847 return prim_attr;
2848 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2849 return ATTR_COMBINE(char_attr, prim_attr);
2850#ifdef FEAT_GUI
2851 if (gui.in_use)
2852 {
2853 if (char_attr > HL_ALL)
2854 char_aep = syn_gui_attr2entry(char_attr);
2855 if (char_aep != NULL)
2856 new_en = *char_aep;
2857 else
2858 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002859 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002860 new_en.ae_u.gui.fg_color = INVALCOLOR;
2861 new_en.ae_u.gui.bg_color = INVALCOLOR;
2862 new_en.ae_u.gui.sp_color = INVALCOLOR;
2863 if (char_attr <= HL_ALL)
2864 new_en.ae_attr = char_attr;
2865 }
2866
2867 if (prim_attr <= HL_ALL)
2868 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2869 else
2870 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002871 prim_aep = syn_gui_attr2entry(prim_attr);
2872 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002873 {
2874 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002875 prim_aep->ae_attr);
2876 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2877 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2878 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2879 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2880 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2881 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2882 if (prim_aep->ae_u.gui.font != NOFONT)
2883 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002884# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002885 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2886 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002887# endif
2888 }
2889 }
2890 return get_attr_entry(&gui_attr_table, &new_en);
2891 }
2892#endif
2893
2894 if (IS_CTERM)
2895 {
2896 if (char_attr > HL_ALL)
2897 char_aep = syn_cterm_attr2entry(char_attr);
2898 if (char_aep != NULL)
2899 new_en = *char_aep;
2900 else
2901 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002902 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002903#ifdef FEAT_TERMGUICOLORS
2904 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2905 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002906 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002907#endif
2908 if (char_attr <= HL_ALL)
2909 new_en.ae_attr = char_attr;
2910 }
2911
2912 if (prim_attr <= HL_ALL)
2913 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2914 else
2915 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002916 prim_aep = syn_cterm_attr2entry(prim_attr);
2917 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002918 {
2919 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002920 prim_aep->ae_attr);
2921 if (prim_aep->ae_u.cterm.fg_color > 0)
2922 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2923 if (prim_aep->ae_u.cterm.bg_color > 0)
2924 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2925 if (prim_aep->ae_u.cterm.ul_color > 0)
2926 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002927 if (prim_aep->ae_u.cterm.font > 0)
2928 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002929#ifdef FEAT_TERMGUICOLORS
2930 // If both fg and bg are not set fall back to cterm colors.
2931 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002932 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2933 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002934 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002935 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002936 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002937 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002938 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2939 }
2940 else
2941 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002942 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2943 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2944 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2945 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002946 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002947 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2948 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002949#endif
2950 }
2951 }
2952 return get_attr_entry(&cterm_attr_table, &new_en);
2953 }
2954
2955 if (char_attr > HL_ALL)
2956 char_aep = syn_term_attr2entry(char_attr);
2957 if (char_aep != NULL)
2958 new_en = *char_aep;
2959 else
2960 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002961 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002962 if (char_attr <= HL_ALL)
2963 new_en.ae_attr = char_attr;
2964 }
2965
2966 if (prim_attr <= HL_ALL)
2967 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2968 else
2969 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002970 prim_aep = syn_term_attr2entry(prim_attr);
2971 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002972 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002973 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2974 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002975 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002976 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2977 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002978 }
2979 }
2980 }
2981 return get_attr_entry(&term_attr_table, &new_en);
2982}
2983
2984#ifdef FEAT_GUI
2985 attrentry_T *
2986syn_gui_attr2entry(int attr)
2987{
2988 attr -= ATTR_OFF;
2989 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2990 return NULL;
2991 return &(GUI_ATTR_ENTRY(attr));
2992}
2993#endif
2994
2995/*
2996 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2997 * Only to be used when "attr" > HL_ALL.
2998 */
2999 int
3000syn_attr2attr(int attr)
3001{
3002 attrentry_T *aep;
3003
3004#ifdef FEAT_GUI
3005 if (gui.in_use)
3006 aep = syn_gui_attr2entry(attr);
3007 else
3008#endif
3009 if (IS_CTERM)
3010 aep = syn_cterm_attr2entry(attr);
3011 else
3012 aep = syn_term_attr2entry(attr);
3013
3014 if (aep == NULL) // highlighting not set
3015 return 0;
3016 return aep->ae_attr;
3017}
3018
3019
3020 attrentry_T *
3021syn_term_attr2entry(int attr)
3022{
3023 attr -= ATTR_OFF;
3024 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
3025 return NULL;
3026 return &(TERM_ATTR_ENTRY(attr));
3027}
3028
3029 attrentry_T *
3030syn_cterm_attr2entry(int attr)
3031{
3032 attr -= ATTR_OFF;
3033 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
3034 return NULL;
3035 return &(CTERM_ATTR_ENTRY(attr));
3036}
3037
3038#define LIST_ATTR 1
3039#define LIST_STRING 2
3040#define LIST_INT 3
3041
3042 static void
3043highlight_list_one(int id)
3044{
3045 hl_group_T *sgp;
3046 int didh = FALSE;
3047
3048 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
3049
3050 if (message_filtered(sgp->sg_name))
3051 return;
3052
3053 didh = highlight_list_arg(id, didh, LIST_ATTR,
3054 sgp->sg_term, NULL, "term");
3055 didh = highlight_list_arg(id, didh, LIST_STRING,
3056 0, sgp->sg_start, "start");
3057 didh = highlight_list_arg(id, didh, LIST_STRING,
3058 0, sgp->sg_stop, "stop");
3059
3060 didh = highlight_list_arg(id, didh, LIST_ATTR,
3061 sgp->sg_cterm, NULL, "cterm");
3062 didh = highlight_list_arg(id, didh, LIST_INT,
3063 sgp->sg_cterm_fg, NULL, "ctermfg");
3064 didh = highlight_list_arg(id, didh, LIST_INT,
3065 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02003066 didh = highlight_list_arg(id, didh, LIST_INT,
3067 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01003068 didh = highlight_list_arg(id, didh, LIST_INT,
3069 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003070
3071#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3072 didh = highlight_list_arg(id, didh, LIST_ATTR,
3073 sgp->sg_gui, NULL, "gui");
3074 didh = highlight_list_arg(id, didh, LIST_STRING,
3075 0, sgp->sg_gui_fg_name, "guifg");
3076 didh = highlight_list_arg(id, didh, LIST_STRING,
3077 0, sgp->sg_gui_bg_name, "guibg");
3078 didh = highlight_list_arg(id, didh, LIST_STRING,
3079 0, sgp->sg_gui_sp_name, "guisp");
3080#endif
3081#ifdef FEAT_GUI
3082 didh = highlight_list_arg(id, didh, LIST_STRING,
3083 0, sgp->sg_font_name, "font");
3084#endif
3085
3086 if (sgp->sg_link && !got_int)
3087 {
3088 (void)syn_list_header(didh, 9999, id);
3089 didh = TRUE;
3090 msg_puts_attr("links to", HL_ATTR(HLF_D));
3091 msg_putchar(' ');
3092 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3093 }
3094
3095 if (!didh)
3096 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3097#ifdef FEAT_EVAL
3098 if (p_verbose > 0)
3099 last_set_msg(sgp->sg_script_ctx);
3100#endif
3101}
3102
3103 static int
3104highlight_list_arg(
3105 int id,
3106 int didh,
3107 int type,
3108 int iarg,
3109 char_u *sarg,
3110 char *name)
3111{
Bram Moolenaar84f54632022-06-29 18:39:11 +01003112 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003113 char_u *ts;
3114 int i;
3115
3116 if (got_int)
3117 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003118
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003119 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3120 return didh;
3121
3122 ts = buf;
3123 if (type == LIST_INT)
3124 sprintf((char *)buf, "%d", iarg - 1);
3125 else if (type == LIST_STRING)
3126 ts = sarg;
3127 else // type == LIST_ATTR
3128 {
John Marriott34f00dd2024-04-08 23:28:12 +02003129 size_t buflen;
3130
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003131 buf[0] = NUL;
John Marriott34f00dd2024-04-08 23:28:12 +02003132 buflen = 0;
3133 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003134 {
John Marriott34f00dd2024-04-08 23:28:12 +02003135 if (iarg & highlight_index_tab[i]->key)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003136 {
John Marriott34f00dd2024-04-08 23:28:12 +02003137 if (buflen > 0)
3138 {
3139 STRCPY(buf + buflen, (char_u *)",");
3140 ++buflen;
3141 }
3142 STRCPY(buf + buflen, (char_u *)highlight_index_tab[i]->value);
3143 buflen += highlight_index_tab[i]->length;
3144 iarg &= ~highlight_index_tab[i]->key; // don't want "inverse"/"reverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003145 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003146 }
3147 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003148
3149 (void)syn_list_header(didh,
3150 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3151 didh = TRUE;
3152 if (!got_int)
3153 {
3154 if (*name != NUL)
3155 {
3156 msg_puts_attr(name, HL_ATTR(HLF_D));
3157 msg_puts_attr("=", HL_ATTR(HLF_D));
3158 }
3159 msg_outtrans(ts);
3160 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003161 return didh;
3162}
3163
3164#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3165/*
3166 * Return "1" if highlight group "id" has attribute "flag".
3167 * Return NULL otherwise.
3168 */
3169 char_u *
3170highlight_has_attr(
3171 int id,
3172 int flag,
3173 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3174{
3175 int attr;
3176
3177 if (id <= 0 || id > highlight_ga.ga_len)
3178 return NULL;
3179
3180#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3181 if (modec == 'g')
3182 attr = HL_TABLE()[id - 1].sg_gui;
3183 else
3184#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003185 {
3186 if (modec == 'c')
3187 attr = HL_TABLE()[id - 1].sg_cterm;
3188 else
3189 attr = HL_TABLE()[id - 1].sg_term;
3190 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003191
3192 if (attr & flag)
3193 return (char_u *)"1";
3194 return NULL;
3195}
3196#endif
3197
3198#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3199/*
3200 * Return color name of highlight group "id".
3201 */
3202 char_u *
3203highlight_color(
3204 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003205 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003206 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3207{
3208 static char_u name[20];
3209 int n;
3210 int fg = FALSE;
3211 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003212 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003213 int font = FALSE;
3214
3215 if (id <= 0 || id > highlight_ga.ga_len)
3216 return NULL;
3217
3218 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3219 fg = TRUE;
3220 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3221 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3222 font = TRUE;
3223 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3224 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003225 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3226 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003227 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3228 return NULL;
3229 if (modec == 'g')
3230 {
3231# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3232# ifdef FEAT_GUI
3233 // return font name
3234 if (font)
3235 return HL_TABLE()[id - 1].sg_font_name;
3236# endif
3237
3238 // return #RRGGBB form (only possible when GUI is running)
3239 if ((USE_24BIT) && what[2] == '#')
3240 {
3241 guicolor_T color;
3242 long_u rgb;
3243 static char_u buf[10];
3244
3245 if (fg)
3246 color = HL_TABLE()[id - 1].sg_gui_fg;
3247 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003248 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003249 else
3250 color = HL_TABLE()[id - 1].sg_gui_bg;
3251 if (color == INVALCOLOR)
3252 return NULL;
3253 rgb = (long_u)GUI_MCH_GET_RGB(color);
3254 sprintf((char *)buf, "#%02x%02x%02x",
3255 (unsigned)(rgb >> 16),
3256 (unsigned)(rgb >> 8) & 255,
3257 (unsigned)rgb & 255);
3258 return buf;
3259 }
3260# endif
3261 if (fg)
3262 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3263 if (sp)
3264 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3265 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3266 }
PMuncha606f3a2023-11-15 15:35:49 +01003267 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003268 return NULL;
3269 if (modec == 'c')
3270 {
3271 if (fg)
3272 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003273 else if (ul)
3274 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003275 else if (font)
3276 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003277 else
3278 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3279 if (n < 0)
3280 return NULL;
3281 sprintf((char *)name, "%d", n);
3282 return name;
3283 }
3284 // term doesn't have color
3285 return NULL;
3286}
3287#endif
3288
3289#if (defined(FEAT_SYN_HL) \
3290 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3291 && defined(FEAT_PRINTER)) || defined(PROTO)
3292/*
3293 * Return color name of highlight group "id" as RGB value.
3294 */
3295 long_u
3296highlight_gui_color_rgb(
3297 int id,
3298 int fg) // TRUE = fg, FALSE = bg
3299{
3300 guicolor_T color;
3301
3302 if (id <= 0 || id > highlight_ga.ga_len)
3303 return 0L;
3304
3305 if (fg)
3306 color = HL_TABLE()[id - 1].sg_gui_fg;
3307 else
3308 color = HL_TABLE()[id - 1].sg_gui_bg;
3309
3310 if (color == INVALCOLOR)
3311 return 0L;
3312
3313 return GUI_MCH_GET_RGB(color);
3314}
3315#endif
3316
3317/*
3318 * Output the syntax list header.
3319 * Return TRUE when started a new line.
3320 */
3321 int
3322syn_list_header(
3323 int did_header, // did header already
3324 int outlen, // length of string that comes
3325 int id) // highlight group id
3326{
3327 int endcol = 19;
3328 int newline = TRUE;
3329 int name_col = 0;
3330
3331 if (!did_header)
3332 {
3333 msg_putchar('\n');
3334 if (got_int)
3335 return TRUE;
3336 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3337 name_col = msg_col;
3338 endcol = 15;
3339 }
3340 else if (msg_col + outlen + 1 >= Columns)
3341 {
3342 msg_putchar('\n');
3343 if (got_int)
3344 return TRUE;
3345 }
3346 else
3347 {
3348 if (msg_col >= endcol) // wrap around is like starting a new line
3349 newline = FALSE;
3350 }
3351
3352 if (msg_col >= endcol) // output at least one space
3353 endcol = msg_col + 1;
3354 if (Columns <= endcol) // avoid hang for tiny window
3355 endcol = Columns - 1;
3356
3357 msg_advance(endcol);
3358
3359 // Show "xxx" with the attributes.
3360 if (!did_header)
3361 {
3362 if (endcol == Columns - 1 && endcol <= name_col)
3363 msg_putchar(' ');
3364 msg_puts_attr("xxx", syn_id2attr(id));
3365 msg_putchar(' ');
3366 }
3367
3368 return newline;
3369}
3370
3371/*
3372 * Set the attribute numbers for a highlight group.
3373 * Called after one of the attributes has changed.
3374 */
3375 static void
3376set_hl_attr(
3377 int idx) // index in array
3378{
3379 attrentry_T at_en;
3380 hl_group_T *sgp = HL_TABLE() + idx;
3381
3382 // The "Normal" group doesn't need an attribute number
3383 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3384 return;
3385
3386#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003387 // For the GUI mode: If there are other than "normal" highlighting
3388 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003389 if (sgp->sg_gui_fg == INVALCOLOR
3390 && sgp->sg_gui_bg == INVALCOLOR
3391 && sgp->sg_gui_sp == INVALCOLOR
3392 && sgp->sg_font == NOFONT
3393# ifdef FEAT_XFONTSET
3394 && sgp->sg_fontset == NOFONTSET
3395# endif
3396 )
3397 {
3398 sgp->sg_gui_attr = sgp->sg_gui;
3399 }
3400 else
3401 {
3402 at_en.ae_attr = sgp->sg_gui;
3403 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3404 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3405 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3406 at_en.ae_u.gui.font = sgp->sg_font;
3407# ifdef FEAT_XFONTSET
3408 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3409# endif
3410 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3411 }
3412#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003413 // For the term mode: If there are other than "normal" highlighting
3414 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003415 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3416 sgp->sg_term_attr = sgp->sg_term;
3417 else
3418 {
3419 at_en.ae_attr = sgp->sg_term;
3420 at_en.ae_u.term.start = sgp->sg_start;
3421 at_en.ae_u.term.stop = sgp->sg_stop;
3422 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3423 }
3424
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003425 // For the color term mode: If there are other than "normal"
3426 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003427 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3428 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003429# ifdef FEAT_TERMGUICOLORS
3430 && sgp->sg_gui_fg == INVALCOLOR
3431 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003432 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003433# endif
3434 )
3435 sgp->sg_cterm_attr = sgp->sg_cterm;
3436 else
3437 {
3438 at_en.ae_attr = sgp->sg_cterm;
3439 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3440 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003441 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003442 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003443# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003444 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3445 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003446 // Only use the underline/undercurl color when used, it may clear the
3447 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003448 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3449 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003450 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3451 else
3452 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003453 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3454 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3455 {
3456 // If both fg and bg are invalid fall back to the cterm colors.
3457 // Helps when the GUI only uses an attribute, e.g. undercurl.
3458 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3459 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3460 }
3461# endif
3462 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3463 }
3464}
3465
3466/*
3467 * Lookup a highlight group name and return its ID.
3468 * If it is not found, 0 is returned.
3469 */
3470 int
3471syn_name2id(char_u *name)
3472{
3473 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003474 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003475
3476 // Avoid using stricmp() too much, it's slow on some systems
3477 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3478 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003479 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003480 vim_strup(name_u);
3481 for (i = highlight_ga.ga_len; --i >= 0; )
3482 if (HL_TABLE()[i].sg_name_u != NULL
3483 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3484 break;
3485 return i + 1;
3486}
3487
3488/*
3489 * Lookup a highlight group name and return its attributes.
3490 * Return zero if not found.
3491 */
3492 int
3493syn_name2attr(char_u *name)
3494{
3495 int id = syn_name2id(name);
3496
3497 if (id != 0)
3498 return syn_id2attr(id);
3499 return 0;
3500}
3501
3502#if defined(FEAT_EVAL) || defined(PROTO)
3503/*
3504 * Return TRUE if highlight group "name" exists.
3505 */
3506 int
3507highlight_exists(char_u *name)
3508{
3509 return (syn_name2id(name) > 0);
3510}
3511
3512# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3513/*
3514 * Return the name of highlight group "id".
3515 * When not a valid ID return an empty string.
3516 */
3517 char_u *
3518syn_id2name(int id)
3519{
3520 if (id <= 0 || id > highlight_ga.ga_len)
3521 return (char_u *)"";
3522 return HL_TABLE()[id - 1].sg_name;
3523}
3524# endif
3525#endif
3526
3527/*
3528 * Like syn_name2id(), but take a pointer + length argument.
3529 */
3530 int
3531syn_namen2id(char_u *linep, int len)
3532{
3533 char_u *name;
3534 int id = 0;
3535
3536 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003537 if (name == NULL)
3538 return 0;
3539
3540 id = syn_name2id(name);
3541 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003542 return id;
3543}
3544
3545/*
3546 * Find highlight group name in the table and return its ID.
3547 * The argument is a pointer to the name and the length of the name.
3548 * If it doesn't exist yet, a new entry is created.
3549 * Return 0 for failure.
3550 */
3551 int
3552syn_check_group(char_u *pp, int len)
3553{
3554 int id;
3555 char_u *name;
3556
erw7f7f7aaf2021-12-07 21:29:20 +00003557 if (len > MAX_SYN_NAME)
3558 {
3559 emsg(_(e_highlight_group_name_too_long));
3560 return 0;
3561 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003562 name = vim_strnsave(pp, len);
3563 if (name == NULL)
3564 return 0;
3565
3566 id = syn_name2id(name);
3567 if (id == 0) // doesn't exist yet
3568 id = syn_add_group(name);
3569 else
3570 vim_free(name);
3571 return id;
3572}
3573
3574/*
3575 * Add new highlight group and return its ID.
3576 * "name" must be an allocated string, it will be consumed.
3577 * Return 0 for failure.
3578 */
3579 static int
3580syn_add_group(char_u *name)
3581{
3582 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003583 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003584
Gregory Andersd4376dc2023-08-20 19:14:03 +02003585 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003586 for (p = name; *p != NUL; ++p)
3587 {
3588 if (!vim_isprintc(*p))
3589 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003590 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003591 vim_free(name);
3592 return 0;
3593 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003594 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003595 {
3596 // This is an error, but since there previously was no check only
3597 // give a warning.
3598 msg_source(HL_ATTR(HLF_W));
3599 msg(_("W18: Invalid character in group name"));
3600 break;
3601 }
3602 }
3603
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003604 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003605 if (highlight_ga.ga_data == NULL)
3606 {
3607 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3608 highlight_ga.ga_growsize = 10;
3609 }
3610
3611 if (highlight_ga.ga_len >= MAX_HL_ID)
3612 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003613 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003614 vim_free(name);
3615 return 0;
3616 }
3617
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003618 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003619 if (ga_grow(&highlight_ga, 1) == FAIL)
3620 {
3621 vim_free(name);
3622 return 0;
3623 }
3624
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003625 name_up = vim_strsave_up(name);
3626 if (name_up == NULL)
3627 {
3628 vim_free(name);
3629 return 0;
3630 }
3631
Bram Moolenaara80faa82020-04-12 19:37:17 +02003632 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003633 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003634 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003635#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3636 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3637 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003638 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003639#endif
3640 ++highlight_ga.ga_len;
3641
3642 return highlight_ga.ga_len; // ID is index plus one
3643}
3644
3645/*
3646 * When, just after calling syn_add_group(), an error is discovered, this
3647 * function deletes the new name.
3648 */
3649 static void
3650syn_unadd_group(void)
3651{
3652 --highlight_ga.ga_len;
3653 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3654 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3655}
3656
3657/*
3658 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003659 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003660 */
3661 int
3662syn_id2attr(int hl_id)
3663{
3664 int attr;
3665 hl_group_T *sgp;
3666
3667 hl_id = syn_get_final_id(hl_id);
3668 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3669
3670#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003671 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003672 if (gui.in_use)
3673 attr = sgp->sg_gui_attr;
3674 else
3675#endif
3676 if (IS_CTERM)
3677 attr = sgp->sg_cterm_attr;
3678 else
3679 attr = sgp->sg_term_attr;
3680
3681 return attr;
3682}
3683
3684#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3685/*
3686 * Get the GUI colors and attributes for a group ID.
3687 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3688 */
3689 int
3690syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3691{
3692 hl_group_T *sgp;
3693
3694 hl_id = syn_get_final_id(hl_id);
3695 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3696
3697 *fgp = sgp->sg_gui_fg;
3698 *bgp = sgp->sg_gui_bg;
3699 return sgp->sg_gui;
3700}
3701#endif
3702
3703#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003704 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3705 && defined(FEAT_TERMGUICOLORS)) \
3706 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003707 void
3708syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3709{
3710 hl_group_T *sgp;
3711
3712 hl_id = syn_get_final_id(hl_id);
3713 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3714 *fgp = sgp->sg_cterm_fg - 1;
3715 *bgp = sgp->sg_cterm_bg - 1;
3716}
3717#endif
3718
3719/*
3720 * Translate a group ID to the final group ID (following links).
3721 */
3722 int
3723syn_get_final_id(int hl_id)
3724{
3725 int count;
3726 hl_group_T *sgp;
3727
3728 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3729 return 0; // Can be called from eval!!
3730
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003731 // Follow links until there is no more.
3732 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003733 for (count = 100; --count >= 0; )
3734 {
3735 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3736 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3737 break;
3738 hl_id = sgp->sg_link;
3739 }
3740
3741 return hl_id;
3742}
3743
3744#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3745/*
3746 * Call this function just after the GUI has started.
3747 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3748 * It finds the font and color handles for the highlighting groups.
3749 */
3750 void
3751highlight_gui_started(void)
3752{
3753 int idx;
3754
3755 // First get the colors from the "Normal" and "Menu" group, if set
3756 if (USE_24BIT)
3757 set_normal_colors();
3758
3759 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3760 gui_do_one_color(idx, FALSE, FALSE);
3761
3762 highlight_changed();
3763}
3764
3765 static void
3766gui_do_one_color(
3767 int idx,
3768 int do_menu UNUSED, // TRUE: might set the menu font
3769 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3770{
3771 int didit = FALSE;
3772
3773# ifdef FEAT_GUI
3774# ifdef FEAT_TERMGUICOLORS
3775 if (gui.in_use)
3776# endif
3777 if (HL_TABLE()[idx].sg_font_name != NULL)
3778 {
3779 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3780 do_tooltip, TRUE);
3781 didit = TRUE;
3782 }
3783# endif
3784 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3785 {
3786 HL_TABLE()[idx].sg_gui_fg =
3787 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3788 didit = TRUE;
3789 }
3790 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3791 {
3792 HL_TABLE()[idx].sg_gui_bg =
3793 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3794 didit = TRUE;
3795 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003796 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3797 {
3798 HL_TABLE()[idx].sg_gui_sp =
3799 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3800 didit = TRUE;
3801 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003802 if (didit) // need to get a new attr number
3803 set_hl_attr(idx);
3804}
3805#endif
3806
3807#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3808/*
3809 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3810 */
3811 static void
3812combine_stl_hlt(
3813 int id,
3814 int id_S,
3815 int id_alt,
3816 int hlcnt,
3817 int i,
3818 int hlf,
3819 int *table)
3820{
3821 hl_group_T *hlt = HL_TABLE();
3822
3823 if (id_alt == 0)
3824 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003825 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003826 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3827 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3828# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3829 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3830# endif
3831 }
3832 else
3833 mch_memmove(&hlt[hlcnt + i],
3834 &hlt[id_alt - 1],
3835 sizeof(hl_group_T));
3836 hlt[hlcnt + i].sg_link = 0;
3837
3838 hlt[hlcnt + i].sg_term ^=
3839 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3840 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3841 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3842 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3843 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3844 hlt[hlcnt + i].sg_cterm ^=
3845 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3846 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3847 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3848 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3849 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003850 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3851 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003852# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3853 hlt[hlcnt + i].sg_gui ^=
3854 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3855# endif
3856# ifdef FEAT_GUI
3857 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3858 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3859 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3860 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3861 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3862 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3863 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3864 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3865# ifdef FEAT_XFONTSET
3866 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3867 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3868# endif
3869# endif
3870 highlight_ga.ga_len = hlcnt + i + 1;
3871 set_hl_attr(hlcnt + i); // At long last we can apply
3872 table[i] = syn_id2attr(hlcnt + i + 1);
3873}
3874#endif
3875
3876/*
3877 * Translate the 'highlight' option into attributes in highlight_attr[] and
3878 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3879 * corresponding highlights to use on top of HLF_SNC is computed.
3880 * Called only when the 'highlight' option has been changed and upon first
3881 * screen redraw after any :highlight command.
3882 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3883 */
3884 int
3885highlight_changed(void)
3886{
3887 int hlf;
3888 int i;
3889 char_u *p;
3890 int attr;
3891 char_u *end;
3892 int id;
3893#ifdef USER_HIGHLIGHT
3894 char_u userhl[30]; // use 30 to avoid compiler warning
3895# ifdef FEAT_STL_OPT
3896 int id_S = -1;
3897 int id_SNC = 0;
3898# ifdef FEAT_TERMINAL
3899 int id_ST = 0;
3900 int id_STNC = 0;
3901# endif
3902 int hlcnt;
3903# endif
3904#endif
3905 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3906
3907 need_highlight_changed = FALSE;
3908
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003909#ifdef FEAT_TERMINAL
3910 term_update_colors_all();
3911 term_update_wincolor_all();
3912#endif
3913
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003914 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003915 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3916 highlight_attr[hlf] = 0;
3917
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003918 // First set all attributes to their default value.
3919 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003920 for (i = 0; i < 2; ++i)
3921 {
3922 if (i)
3923 p = p_hl;
3924 else
3925 p = get_highlight_default();
3926 if (p == NULL) // just in case
3927 continue;
3928
3929 while (*p)
3930 {
3931 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3932 if (hl_flags[hlf] == *p)
3933 break;
3934 ++p;
3935 if (hlf == (int)HLF_COUNT || *p == NUL)
3936 return FAIL;
3937
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003938 // Allow several hl_flags to be combined, like "bu" for
3939 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003940 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003941 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003942 {
3943 if (VIM_ISWHITE(*p)) // ignore white space
3944 continue;
3945
3946 if (attr > HL_ALL) // Combination with ':' is not allowed.
3947 return FAIL;
3948
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003949 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003950 switch (*p)
3951 {
3952 case 'b': attr |= HL_BOLD;
3953 break;
3954 case 'i': attr |= HL_ITALIC;
3955 break;
3956 case '-':
3957 case 'n': // no highlighting
3958 break;
3959 case 'r': attr |= HL_INVERSE;
3960 break;
3961 case 's': attr |= HL_STANDOUT;
3962 break;
3963 case 'u': attr |= HL_UNDERLINE;
3964 break;
3965 case 'c': attr |= HL_UNDERCURL;
3966 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003967 case '2': attr |= HL_UNDERDOUBLE;
3968 break;
3969 case 'd': attr |= HL_UNDERDOTTED;
3970 break;
3971 case '=': attr |= HL_UNDERDASHED;
3972 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003973 case 't': attr |= HL_STRIKETHROUGH;
3974 break;
3975 case ':': ++p; // highlight group name
3976 if (attr || *p == NUL) // no combinations
3977 return FAIL;
3978 end = vim_strchr(p, ',');
3979 if (end == NULL)
3980 end = p + STRLEN(p);
3981 id = syn_check_group(p, (int)(end - p));
3982 if (id == 0)
3983 return FAIL;
3984 attr = syn_id2attr(id);
3985 p = end - 1;
3986#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3987 if (hlf == (int)HLF_SNC)
3988 id_SNC = syn_get_final_id(id);
3989# ifdef FEAT_TERMINAL
3990 else if (hlf == (int)HLF_ST)
3991 id_ST = syn_get_final_id(id);
3992 else if (hlf == (int)HLF_STNC)
3993 id_STNC = syn_get_final_id(id);
3994# endif
3995 else if (hlf == (int)HLF_S)
3996 id_S = syn_get_final_id(id);
3997#endif
3998 break;
3999 default: return FAIL;
4000 }
4001 }
4002 highlight_attr[hlf] = attr;
4003
4004 p = skip_to_option_part(p); // skip comma and spaces
4005 }
4006 }
4007
4008#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01004009 // Setup the user highlights
4010 //
4011 // Temporarily utilize 28 more hl entries:
4012 // 9 for User1-User9 combined with StatusLineNC
4013 // 9 for User1-User9 combined with StatusLineTerm
4014 // 9 for User1-User9 combined with StatusLineTermNC
4015 // 1 for StatusLine default
4016 // Have to be in there simultaneously in case of table overflows in
4017 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004018# ifdef FEAT_STL_OPT
4019 if (ga_grow(&highlight_ga, 28) == FAIL)
4020 return FAIL;
4021 hlcnt = highlight_ga.ga_len;
4022 if (id_S == -1)
4023 {
4024 // Make sure id_S is always valid to simplify code below. Use the last
4025 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02004026 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004027 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
4028 id_S = hlcnt + 19;
4029 }
4030# endif
4031 for (i = 0; i < 9; i++)
4032 {
4033 sprintf((char *)userhl, "User%d", i + 1);
4034 id = syn_name2id(userhl);
4035 if (id == 0)
4036 {
4037 highlight_user[i] = 0;
4038# ifdef FEAT_STL_OPT
4039 highlight_stlnc[i] = 0;
4040# ifdef FEAT_TERMINAL
4041 highlight_stlterm[i] = 0;
4042 highlight_stltermnc[i] = 0;
4043# endif
4044# endif
4045 }
4046 else
4047 {
4048 highlight_user[i] = syn_id2attr(id);
4049# ifdef FEAT_STL_OPT
4050 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
4051 HLF_SNC, highlight_stlnc);
4052# ifdef FEAT_TERMINAL
4053 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
4054 HLF_ST, highlight_stlterm);
4055 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
4056 HLF_STNC, highlight_stltermnc);
4057# endif
4058# endif
4059 }
4060 }
4061# ifdef FEAT_STL_OPT
4062 highlight_ga.ga_len = hlcnt;
4063# endif
4064
4065#endif // USER_HIGHLIGHT
4066
4067 return OK;
4068}
4069
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004070static void highlight_list(void);
4071static void highlight_list_two(int cnt, int attr);
4072
4073/*
4074 * Handle command line completion for :highlight command.
4075 */
4076 void
4077set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
4078{
4079 char_u *p;
4080
4081 // Default: expand group names
4082 xp->xp_context = EXPAND_HIGHLIGHT;
4083 xp->xp_pattern = arg;
4084 include_link = 2;
4085 include_default = 1;
4086
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004087 if (*arg == NUL)
4088 return;
4089
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004090 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004091 p = skiptowhite(arg);
4092 if (*p == NUL)
4093 return;
4094
4095 // past "default" or group name
4096 include_default = 0;
4097 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004098 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004099 arg = skipwhite(p);
4100 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004101 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004102 }
4103 if (*p == NUL)
4104 return;
4105
4106 // past group name
4107 include_link = 0;
4108 if (arg[1] == 'i' && arg[0] == 'N')
4109 highlight_list();
4110 if (STRNCMP("link", arg, p - arg) == 0
4111 || STRNCMP("clear", arg, p - arg) == 0)
4112 {
4113 xp->xp_pattern = skipwhite(p);
4114 p = skiptowhite(xp->xp_pattern);
4115 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004116 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004117 xp->xp_pattern = skipwhite(p);
4118 p = skiptowhite(xp->xp_pattern);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004119 }
4120 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004121 if (*p != NUL) // past group name(s)
4122 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004123}
4124
4125/*
4126 * List highlighting matches in a nice way.
4127 */
4128 static void
4129highlight_list(void)
4130{
4131 int i;
4132
4133 for (i = 10; --i >= 0; )
4134 highlight_list_two(i, HL_ATTR(HLF_D));
4135 for (i = 40; --i >= 0; )
4136 highlight_list_two(99, 0);
4137}
4138
4139 static void
4140highlight_list_two(int cnt, int attr)
4141{
4142 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4143 msg_clr_eos();
4144 out_flush();
4145 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4146}
4147
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004148/*
4149 * Function given to ExpandGeneric() to obtain the list of group names.
4150 */
4151 char_u *
4152get_highlight_name(expand_T *xp UNUSED, int idx)
4153{
4154 return get_highlight_name_ext(xp, idx, TRUE);
4155}
4156
4157/*
4158 * Obtain a highlight group name.
4159 * When "skip_cleared" is TRUE don't return a cleared entry.
4160 */
4161 char_u *
4162get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4163{
4164 if (idx < 0)
4165 return NULL;
4166
4167 // Items are never removed from the table, skip the ones that were
4168 // cleared.
4169 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4170 return (char_u *)"";
4171
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004172 if (idx == highlight_ga.ga_len && include_none != 0)
4173 return (char_u *)"none";
4174 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4175 return (char_u *)"default";
4176 if (idx == highlight_ga.ga_len + include_none + include_default
4177 && include_link != 0)
4178 return (char_u *)"link";
4179 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4180 && include_link != 0)
4181 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004182 if (idx >= highlight_ga.ga_len)
4183 return NULL;
4184 return HL_TABLE()[idx].sg_name;
4185}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004186
4187#if defined(FEAT_GUI) || defined(PROTO)
4188/*
4189 * Free all the highlight group fonts.
4190 * Used when quitting for systems which need it.
4191 */
4192 void
4193free_highlight_fonts(void)
4194{
4195 int idx;
4196
4197 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4198 {
4199 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4200 HL_TABLE()[idx].sg_font = NOFONT;
4201# ifdef FEAT_XFONTSET
4202 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4203 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4204# endif
4205 }
4206
4207 gui_mch_free_font(gui.norm_font);
4208# ifdef FEAT_XFONTSET
4209 gui_mch_free_fontset(gui.fontset);
4210# endif
4211# ifndef FEAT_GUI_GTK
4212 gui_mch_free_font(gui.bold_font);
4213 gui_mch_free_font(gui.ital_font);
4214 gui_mch_free_font(gui.boldital_font);
4215# endif
4216}
4217#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004218
4219#if defined(FEAT_EVAL) || defined(PROTO)
4220/*
4221 * Convert each of the highlight attribute bits (bold, standout, underline,
4222 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4223 * the attribute name as the key.
4224 */
4225 static dict_T *
4226highlight_get_attr_dict(int hlattr)
4227{
4228 dict_T *dict;
4229 int i;
4230
4231 dict = dict_alloc();
4232 if (dict == NULL)
4233 return NULL;
4234
John Marriott34f00dd2024-04-08 23:28:12 +02004235 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004236 {
John Marriott34f00dd2024-04-08 23:28:12 +02004237 if (hlattr & highlight_index_tab[i]->key)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004238 {
John Marriott34f00dd2024-04-08 23:28:12 +02004239 dict_add_bool(dict, highlight_index_tab[i]->value, VVAL_TRUE);
4240 hlattr &= ~highlight_index_tab[i]->key; // don't want "inverse"/"reverse"
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004241 }
4242 }
4243
4244 return dict;
4245}
4246
4247/*
4248 * Return the attributes of the highlight group at index 'hl_idx' as a
4249 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4250 * links recursively.
4251 */
4252 static dict_T *
4253highlight_get_info(int hl_idx, int resolve_link)
4254{
4255 dict_T *dict;
4256 hl_group_T *sgp;
4257 dict_T *attr_dict;
4258 int hlgid;
4259
4260 dict = dict_alloc();
4261 if (dict == NULL)
4262 return dict;
4263
4264 sgp = &HL_TABLE()[hl_idx];
4265 // highlight group id is 1-based
4266 hlgid = hl_idx + 1;
4267
4268 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4269 goto error;
4270 if (dict_add_number(dict, "id", hlgid) == FAIL)
4271 goto error;
4272
4273 if (sgp->sg_link && resolve_link)
4274 {
4275 // resolve the highlight group link recursively
4276 while (sgp->sg_link)
4277 {
4278 hlgid = sgp->sg_link;
4279 sgp = &HL_TABLE()[sgp->sg_link - 1];
4280 }
4281 }
4282
4283 if (sgp->sg_term != 0)
4284 {
4285 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4286 if (attr_dict != NULL)
4287 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4288 goto error;
4289 }
4290 if (sgp->sg_start != NULL)
4291 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4292 goto error;
4293 if (sgp->sg_stop != NULL)
4294 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4295 goto error;
4296 if (sgp->sg_cterm != 0)
4297 {
4298 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4299 if (attr_dict != NULL)
4300 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4301 goto error;
4302 }
4303 if (sgp->sg_cterm_fg != 0)
4304 if (dict_add_string(dict, "ctermfg",
4305 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4306 goto error;
4307 if (sgp->sg_cterm_bg != 0)
4308 if (dict_add_string(dict, "ctermbg",
4309 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4310 goto error;
4311 if (sgp->sg_cterm_ul != 0)
4312 if (dict_add_string(dict, "ctermul",
4313 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4314 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004315 if (sgp->sg_cterm_font != 0)
4316 if (dict_add_string(dict, "ctermfont",
4317 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4318 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004319 if (sgp->sg_gui != 0)
4320 {
4321 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4322 if (attr_dict != NULL)
4323 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4324 goto error;
4325 }
4326 if (sgp->sg_gui_fg_name != NULL)
4327 if (dict_add_string(dict, "guifg",
4328 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4329 goto error;
4330 if (sgp->sg_gui_bg_name != NULL)
4331 if (dict_add_string(dict, "guibg",
4332 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4333 goto error;
4334 if (sgp->sg_gui_sp_name != NULL)
4335 if (dict_add_string(dict, "guisp",
4336 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4337 goto error;
4338# ifdef FEAT_GUI
4339 if (sgp->sg_font_name != NULL)
4340 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4341 goto error;
4342# endif
4343 if (sgp->sg_link)
4344 {
4345 char_u *link;
4346
4347 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4348 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4349 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004350
4351 if (sgp->sg_deflink)
4352 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004353 }
4354 if (dict_len(dict) == 2)
4355 // If only 'name' is present, then the highlight group is cleared.
4356 dict_add_bool(dict, "cleared", VVAL_TRUE);
4357
4358 return dict;
4359
4360error:
4361 vim_free(dict);
4362 return NULL;
4363}
4364
4365/*
4366 * "hlget([name])" function
4367 * Return the attributes of a specific highlight group (if specified) or all
4368 * the highlight groups.
4369 */
4370 void
4371f_hlget(typval_T *argvars, typval_T *rettv)
4372{
4373 list_T *list;
4374 dict_T *dict;
4375 int i;
4376 char_u *hlarg = NULL;
4377 int resolve_link = FALSE;
4378
4379 if (rettv_list_alloc(rettv) == FAIL)
4380 return;
4381
4382 if (check_for_opt_string_arg(argvars, 0) == FAIL
4383 || (argvars[0].v_type != VAR_UNKNOWN
4384 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4385 return;
4386
4387 if (argvars[0].v_type != VAR_UNKNOWN)
4388 {
4389 // highlight group name supplied
4390 hlarg = tv_get_string_chk(&argvars[0]);
4391 if (hlarg == NULL)
4392 return;
4393
4394 if (argvars[1].v_type != VAR_UNKNOWN)
4395 {
4396 int error = FALSE;
4397
4398 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4399 if (error)
4400 return;
4401 }
4402 }
4403
4404 list = rettv->vval.v_list;
4405 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4406 {
4407 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4408 {
4409 dict = highlight_get_info(i, resolve_link);
4410 if (dict != NULL)
4411 list_append_dict(list, dict);
4412 }
4413 }
4414}
4415
4416/*
4417 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4418 * 'dict' or the value is not a string type. If the value is not a string type
4419 * or is NULL, then 'error' is set to TRUE.
4420 */
4421 static char_u *
4422hldict_get_string(dict_T *dict, char_u *key, int *error)
4423{
4424 dictitem_T *di;
4425
4426 *error = FALSE;
4427 di = dict_find(dict, key, -1);
4428 if (di == NULL)
4429 return NULL;
4430
4431 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4432 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004433 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004434 *error = TRUE;
4435 return NULL;
4436 }
4437
4438 return di->di_tv.vval.v_string;
4439}
4440
4441/*
4442 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4443 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4444 * Dictionary or is NULL.
4445 */
4446 static int
4447hldict_attr_to_str(
4448 dict_T *dict,
4449 char_u *key,
4450 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004451 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004452{
4453 dictitem_T *di;
4454 dict_T *attrdict;
4455 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004456 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004457
4458 attr_str[0] = NUL;
4459 di = dict_find(dict, key, -1);
4460 if (di == NULL)
4461 return TRUE;
4462
4463 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4464 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004465 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004466 return FALSE;
4467 }
4468
4469 attrdict = di->di_tv.vval.v_dict;
4470
4471 // If the attribute dict is empty, then return NONE to clear the attributes
4472 if (dict_len(attrdict) == 0)
4473 {
4474 vim_strcat(attr_str, (char_u *)"NONE", len);
4475 return TRUE;
4476 }
4477
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004478 p = attr_str;
John Marriott34f00dd2024-04-08 23:28:12 +02004479 for (i = 0; i < (int)ARRAY_LENGTH(highlight_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004480 {
John Marriott34f00dd2024-04-08 23:28:12 +02004481 if (dict_get_bool(attrdict, highlight_tab[i].value, VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004482 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004483 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4484 STRCPY(p, (char_u *)",");
John Marriott34f00dd2024-04-08 23:28:12 +02004485 if (p - attr_str + highlight_tab[i].length + 1 < len)
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004486 {
John Marriott34f00dd2024-04-08 23:28:12 +02004487 STRCPY(p, highlight_tab[i].value);
4488 p += highlight_tab[i].length;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004489 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004490 }
4491 }
4492
4493 return TRUE;
4494}
4495
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004496// Temporary buffer used to store the command string produced by hlset().
4497// IObuff cannot be used for this as the error messages produced by hlset()
4498// internally use IObuff.
4499#define HLSETBUFSZ 512
4500static char_u hlsetBuf[HLSETBUFSZ + 1];
4501
4502/*
4503 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4504 * "dptr", which points into "hlsetBuf".
4505 * Returns the updated pointer.
4506 */
4507 static char_u *
4508add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4509{
4510 size_t vallen;
4511
4512 // Do nothing if the value is not specified or is empty
4513 if (value == NULL || *value == NUL)
4514 return dptr;
4515
4516 vallen = STRLEN(value);
4517 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4518 {
4519 STRCPY(dptr, attr);
4520 dptr += attrlen;
4521 STRCPY(dptr, value);
4522 dptr += vallen;
4523 }
4524
4525 return dptr;
4526}
4527
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004528/*
4529 * Add or update a highlight group using 'dict' items. Returns TRUE if
4530 * successfully updated the highlight group.
4531 */
4532 static int
4533hlg_add_or_update(dict_T *dict)
4534{
4535 char_u *name;
4536 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004537 char_u term_attr[MAX_ATTR_LEN];
4538 char_u cterm_attr[MAX_ATTR_LEN];
4539 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004540 char_u *start;
4541 char_u *stop;
4542 char_u *ctermfg;
4543 char_u *ctermbg;
4544 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004545 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004546 char_u *guifg;
4547 char_u *guibg;
4548 char_u *guisp;
4549# ifdef FEAT_GUI
4550 char_u *font;
4551# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004552 int forceit = FALSE;
4553 int dodefault = FALSE;
4554 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004555 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004556
4557 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004558 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004559 return FALSE;
4560
Bram Moolenaard61efa52022-07-23 09:52:04 +01004561 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004562 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004563
Bram Moolenaard61efa52022-07-23 09:52:04 +01004564 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004565 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004566
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004567 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004568 {
4569 varnumber_T cleared;
4570
4571 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004572 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004573 if (cleared == TRUE)
4574 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004575 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4576 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004577 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004578 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004579 }
4580
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004581 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004582 {
4583 char_u *linksto;
4584
4585 // link highlight groups
4586 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004587 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004588 return FALSE;
4589
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004590 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004591 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004592 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004593
4594 done = TRUE;
4595 }
4596
4597 // If 'cleared' or 'linksto' are specified, then don't process the other
4598 // attributes.
4599 if (done)
4600 return TRUE;
4601
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004602 start = hldict_get_string(dict, (char_u *)"start", &error);
4603 if (error)
4604 return FALSE;
4605
4606 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4607 if (error)
4608 return FALSE;
4609
4610 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004611 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004612 return FALSE;
4613
4614 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004615 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004616 return FALSE;
4617
4618 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4619 if (error)
4620 return FALSE;
4621
4622 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4623 if (error)
4624 return FALSE;
4625
4626 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4627 if (error)
4628 return FALSE;
4629
PMuncha606f3a2023-11-15 15:35:49 +01004630 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
4631 if (error)
4632 return FALSE;
4633
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004634 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004635 return FALSE;
4636
4637 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4638 if (error)
4639 return FALSE;
4640
4641 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4642 if (error)
4643 return FALSE;
4644
4645 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4646 if (error)
4647 return FALSE;
4648
4649# ifdef FEAT_GUI
4650 font = hldict_get_string(dict, (char_u *)"font", &error);
4651 if (error)
4652 return FALSE;
4653# endif
4654
4655 // If none of the attributes are specified, then do nothing.
4656 if (term_attr[0] == NUL && start == NULL && stop == NULL
4657 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01004658 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004659# ifdef FEAT_GUI
4660 && font == NULL
4661# endif
4662 && guifg == NULL && guibg == NULL && guisp == NULL
4663 )
4664 return TRUE;
4665
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004666 hlsetBuf[0] = NUL;
4667 p = hlsetBuf;
4668 if (dodefault)
4669 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4670 p = add_attr_and_value(p, (char_u *)"", 0, name);
4671 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4672 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4673 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4674 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4675 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4676 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4677 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01004678 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004679 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004680# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004681 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004682# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004683 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4684 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004685 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004686
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004687 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004688
4689 return TRUE;
4690}
4691
4692/*
4693 * "hlset([{highlight_attr}])" function
4694 * Add or modify highlight groups
4695 */
4696 void
4697f_hlset(typval_T *argvars, typval_T *rettv)
4698{
4699 listitem_T *li;
4700 dict_T *dict;
4701
4702 rettv->vval.v_number = -1;
4703
4704 if (check_for_list_arg(argvars, 0) == FAIL)
4705 return;
4706
4707 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4708 {
4709 if (li->li_tv.v_type != VAR_DICT)
4710 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004711 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004712 return;
4713 }
4714
4715 dict = li->li_tv.vval.v_dict;
4716 if (!hlg_add_or_update(dict))
4717 return;
4718 }
4719
4720 rettv->vval.v_number = 0;
4721}
4722#endif