blob: 2d965660e20f6704fce6f788080e69c595c25795 [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",
Yee Cheng Chine700dde2025-02-20 21:58:21 +0100265 "default link PopupSelected PmenuSel",
266 "default link MessageWindow WarningMsg",
267 "default link PopupNotification WarningMsg",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200268 CENT("Normal cterm=NONE", "Normal gui=NONE"),
269 NULL
270};
271
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200272// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200273static char *(highlight_init_light[]) = {
274 CENT("Directory term=bold ctermfg=DarkBlue",
275 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
276 CENT("LineNr term=underline ctermfg=Brown",
277 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200278 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
279 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200280 CENT("MoreMsg term=bold ctermfg=DarkGreen",
281 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
282 CENT("Question term=standout ctermfg=DarkGreen",
283 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
284 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
285 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
286#ifdef FEAT_SPELL
287 CENT("SpellBad term=reverse ctermbg=LightRed",
288 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
289 CENT("SpellCap term=reverse ctermbg=LightBlue",
290 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
291 CENT("SpellRare term=reverse ctermbg=LightMagenta",
292 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
293 CENT("SpellLocal term=underline ctermbg=Cyan",
294 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
295#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200296 CENT("PmenuThumb ctermbg=Black",
297 "PmenuThumb ctermbg=Black guibg=Black"),
298 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
299 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
300 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
301 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200302 CENT("SpecialKey term=bold ctermfg=DarkBlue",
303 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
304 CENT("Title term=bold ctermfg=DarkMagenta",
305 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
306 CENT("WarningMsg term=standout ctermfg=DarkRed",
307 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200308 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
309 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200310#ifdef FEAT_FOLDING
311 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
312 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
313 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
314 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
315#endif
316#ifdef FEAT_SIGNS
317 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
318 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
319#endif
Maxim Kim59bafc82024-02-01 21:07:51 +0100320 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100321 "Visual ctermbg=Grey ctermfg=Black guibg=LightGrey guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200322#ifdef FEAT_DIFF
323 CENT("DiffAdd term=bold ctermbg=LightBlue",
324 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
325 CENT("DiffChange term=bold ctermbg=LightMagenta",
326 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
327 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
328 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
329#endif
330 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
331 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
332#ifdef FEAT_SYN_HL
333 CENT("CursorColumn term=reverse ctermbg=LightGrey",
334 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
335 CENT("CursorLine term=underline cterm=underline",
336 "CursorLine term=underline cterm=underline guibg=Grey90"),
337 CENT("ColorColumn term=reverse ctermbg=LightRed",
338 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
339#endif
340#ifdef FEAT_CONCEAL
341 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
342 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
343#endif
344 CENT("MatchParen term=reverse ctermbg=Cyan",
345 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
346#ifdef FEAT_TERMINAL
347 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
348 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
349 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
350 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
351#endif
352#ifdef FEAT_MENU
353 CENT("ToolbarLine term=underline ctermbg=LightGrey",
354 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
355 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
356 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
357#endif
358 NULL
359};
360
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200361// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200362static char *(highlight_init_dark[]) = {
363 CENT("Directory term=bold ctermfg=LightCyan",
364 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
365 CENT("LineNr term=underline ctermfg=Yellow",
366 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200367 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
368 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200369 CENT("MoreMsg term=bold ctermfg=LightGreen",
370 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
371 CENT("Question term=standout ctermfg=LightGreen",
372 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
373 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
374 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
375 CENT("SpecialKey term=bold ctermfg=LightBlue",
376 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
377#ifdef FEAT_SPELL
378 CENT("SpellBad term=reverse ctermbg=Red",
379 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
380 CENT("SpellCap term=reverse ctermbg=Blue",
381 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
382 CENT("SpellRare term=reverse ctermbg=Magenta",
383 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
384 CENT("SpellLocal term=underline ctermbg=Cyan",
385 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
386#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200387 CENT("PmenuThumb ctermbg=White",
388 "PmenuThumb ctermbg=White guibg=White"),
389 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
390 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
391 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
392 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200393 CENT("Title term=bold ctermfg=LightMagenta",
394 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
395 CENT("WarningMsg term=standout ctermfg=LightRed",
396 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200397 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
398 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200399#ifdef FEAT_FOLDING
400 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
401 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
402 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
403 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
404#endif
405#ifdef FEAT_SIGNS
406 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
407 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
408#endif
Christian Brabandte6d8b462024-01-28 23:33:29 +0100409 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100410 "Visual ctermbg=Grey ctermfg=Black guibg=#575757 guifg=LightGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200411#ifdef FEAT_DIFF
412 CENT("DiffAdd term=bold ctermbg=DarkBlue",
413 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
414 CENT("DiffChange term=bold ctermbg=DarkMagenta",
415 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
416 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
417 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
418#endif
419 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
420 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
421#ifdef FEAT_SYN_HL
422 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
423 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
424 CENT("CursorLine term=underline cterm=underline",
425 "CursorLine term=underline cterm=underline guibg=Grey40"),
426 CENT("ColorColumn term=reverse ctermbg=DarkRed",
427 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
428#endif
429 CENT("MatchParen term=reverse ctermbg=DarkCyan",
430 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
431#ifdef FEAT_CONCEAL
432 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
433 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
434#endif
435#ifdef FEAT_TERMINAL
436 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
437 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
438 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
439 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
440#endif
441#ifdef FEAT_MENU
442 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
443 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
444 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
445 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
446#endif
447 NULL
448};
449
Dominique Pelle748b3082022-01-08 12:41:16 +0000450#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200451/*
452 * Returns the number of highlight groups.
453 */
454 int
455highlight_num_groups(void)
456{
457 return highlight_ga.ga_len;
458}
459
460/*
461 * Returns the name of a highlight group.
462 */
463 char_u *
464highlight_group_name(int id)
465{
466 return HL_TABLE()[id].sg_name;
467}
468
469/*
470 * Returns the ID of the link to a highlight group.
471 */
472 int
473highlight_link_id(int id)
474{
475 return HL_TABLE()[id].sg_link;
476}
Dominique Pelle748b3082022-01-08 12:41:16 +0000477#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200478
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200479 void
480init_highlight(
481 int both, // include groups where 'bg' doesn't matter
482 int reset) // clear group first
483{
484 int i;
485 char **pp;
486 static int had_both = FALSE;
487#ifdef FEAT_EVAL
488 char_u *p;
489
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100490 // Try finding the color scheme file. Used when a color file was loaded
491 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200492 p = get_var_value((char_u *)"g:colors_name");
493 if (p != NULL)
494 {
495 // The value of g:colors_name could be freed when sourcing the script,
496 // making "p" invalid, so copy it.
497 char_u *copy_p = vim_strsave(p);
498 int r;
499
500 if (copy_p != NULL)
501 {
502 r = load_colors(copy_p);
503 vim_free(copy_p);
504 if (r == OK)
505 return;
506 }
507 }
508
509#endif
510
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100511 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200512 if (both)
513 {
514 had_both = TRUE;
515 pp = highlight_init_both;
516 for (i = 0; pp[i] != NULL; ++i)
517 do_highlight((char_u *)pp[i], reset, TRUE);
518 }
519 else if (!had_both)
520 // Don't do anything before the call with both == TRUE from main().
521 // Not everything has been setup then, and that call will overrule
522 // everything anyway.
523 return;
524
525 if (*p_bg == 'l')
526 pp = highlight_init_light;
527 else
528 pp = highlight_init_dark;
529 for (i = 0; pp[i] != NULL; ++i)
530 do_highlight((char_u *)pp[i], reset, TRUE);
531
Maxim Kim59bafc82024-02-01 21:07:51 +0100532 // Reverse looks ugly, but grey may not work for less than 8 colors. Thus
533 // let it depend on the number of colors available.
534 if (t_colors < 8)
535 do_highlight((char_u *)"Visual term=reverse cterm=reverse ctermbg=NONE ctermfg=NONE",
536 FALSE, TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200537 // With 8 colors brown is equal to yellow, need to use black for Search fg
538 // to avoid Statement highlighted text disappears.
539 // Clear the attributes, needed when changing the t_Co value.
Christian Brabandte6d8b462024-01-28 23:33:29 +0100540 if (t_colors <= 8)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200541 {
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200542 if (*p_bg == 'l')
543 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
544 }
545
546#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100547 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200548 if (get_var_value((char_u *)"g:syntax_on") != NULL)
549 {
550 static int recursive = 0;
551
552 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000553 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200554 else
555 {
556 ++recursive;
557 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
558 --recursive;
559 }
560 }
561#endif
562}
563
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000564#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
565/*
566 * Load a default color list. Intended to support legacy color names but allows
567 * the user to override the color values. Only loaded once.
568 */
569 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000570load_default_colors_lists(void)
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000571{
572 // Lacking a default color list isn't the end of the world but it is likely
573 // an inconvenience so users should know when it is missing.
574 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
575 msg("failed to load colors/lists/default.vim");
576}
577#endif
578
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200579/*
580 * Load color file "name".
581 * Return OK for success, FAIL for failure.
582 */
583 int
584load_colors(char_u *name)
585{
586 char_u *buf;
587 int retval = FAIL;
588 static int recursive = FALSE;
589
590 // When being called recursively, this is probably because setting
591 // 'background' caused the highlighting to be reloaded. This means it is
592 // working, thus we should return OK.
593 if (recursive)
594 return OK;
595
596 recursive = TRUE;
597 buf = alloc(STRLEN(name) + 12);
598 if (buf != NULL)
599 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100600#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100601 load_default_colors_lists();
602#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200603 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
604 curbuf->b_fname, FALSE, curbuf);
605 sprintf((char *)buf, "colors/%s.vim", name);
606 retval = source_runtime(buf, DIP_START + DIP_OPT);
607 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100608 if (retval == OK)
609 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
610 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200611 }
612 recursive = FALSE;
613
614 return retval;
615}
616
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200617static int color_numbers_16[28] = {0, 1, 2, 3,
618 4, 5, 6, 6,
619 7, 7, 7, 7,
620 8, 8,
621 9, 9, 10, 10,
622 11, 11, 12, 12, 13,
623 13, 14, 14, 15, -1};
624// for xterm with 88 colors...
625static int color_numbers_88[28] = {0, 4, 2, 6,
626 1, 5, 32, 72,
627 84, 84, 7, 7,
628 82, 82,
629 12, 43, 10, 61,
630 14, 63, 9, 74, 13,
631 75, 11, 78, 15, -1};
632// for xterm with 256 colors...
633static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200634 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200635 248, 248, 7, 7,
636 242, 242,
637 12, 81, 10, 121,
638 14, 159, 9, 224, 13,
639 225, 11, 229, 15, -1};
640// for terminals with less than 16 colors...
641static int color_numbers_8[28] = {0, 4, 2, 6,
642 1, 5, 3, 3,
643 7, 7, 7, 7,
644 0+8, 0+8,
645 4+8, 4+8, 2+8, 2+8,
646 6+8, 6+8, 1+8, 1+8, 5+8,
647 5+8, 3+8, 3+8, 7+8, -1};
648
649/*
650 * Lookup the "cterm" value to be used for color with index "idx" in
John Marriott34f00dd2024-04-08 23:28:12 +0200651 * color_name_tab[].
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200652 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
653 * colors, otherwise it will be unchanged.
654 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100655 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200656lookup_color(int idx, int foreground, int *boldp)
657{
658 int color = color_numbers_16[idx];
659 char_u *p;
660
661 // Use the _16 table to check if it's a valid color name.
662 if (color < 0)
663 return -1;
664
665 if (t_colors == 8)
666 {
667 // t_Co is 8: use the 8 colors table
668#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100669 // On qnx, the 8 & 16 color arrays are the same
670 if (STRNCMP(T_NAME, "qansi", 5) == 0)
671 color = color_numbers_16[idx];
672 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200673#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100674 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200675 if (foreground)
676 {
677 // set/reset bold attribute to get light foreground
678 // colors (on some terminals, e.g. "linux")
679 if (color & 8)
680 *boldp = TRUE;
681 else
682 *boldp = FALSE;
683 }
684 color &= 7; // truncate to 8 colors
685 }
686 else if (t_colors == 16 || t_colors == 88
687 || t_colors >= 256)
688 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100689 // Guess: if the termcap entry ends in 'm', it is
690 // probably an xterm-like terminal. Use the changed
691 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200692 if (*T_CAF != NUL)
693 p = T_CAF;
694 else
695 p = T_CSF;
696 if (*p != NUL && (t_colors > 256
697 || *(p + STRLEN(p) - 1) == 'm'))
698 {
699 if (t_colors == 88)
700 color = color_numbers_88[idx];
701 else if (t_colors >= 256)
702 color = color_numbers_256[idx];
703 else
704 color = color_numbers_8[idx];
705 }
706#ifdef FEAT_TERMRESPONSE
707 if (t_colors >= 256 && color == 15 && is_mac_terminal)
708 // Terminal.app has a bug: 15 is light grey. Use white
709 // from the color cube instead.
710 color = 231;
711#endif
712 }
713 return color;
714}
715
716/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100717 * Link highlight group 'from_hg' to 'to_hg'.
718 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000719 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100720 * 'init' is set to TRUE when initializing all the highlight groups.
721 */
722 static void
723highlight_group_link(
724 char_u *from_hg,
725 int from_len,
726 char_u *to_hg,
727 int to_len,
728 int dodefault,
729 int forceit,
730 int init)
731{
732 int from_id;
733 int to_id;
734 hl_group_T *hlgroup = NULL;
735
736 from_id = syn_check_group(from_hg, from_len);
737 if (STRNCMP(to_hg, "NONE", 4) == 0)
738 to_id = 0;
739 else
740 to_id = syn_check_group(to_hg, to_len);
741
742 if (from_id > 0)
743 {
744 hlgroup = &HL_TABLE()[from_id - 1];
745 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
746 {
747 hlgroup->sg_deflink = to_id;
748#ifdef FEAT_EVAL
749 hlgroup->sg_deflink_sctx = current_sctx;
750 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
751#endif
752 }
753 }
754
755 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
756 {
757 // Don't allow a link when there already is some highlighting
758 // for the group, unless '!' is used
759 if (to_id > 0 && !forceit && !init
760 && hl_has_settings(from_id - 1, dodefault))
761 {
762 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000763 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100764 }
765 else if (hlgroup->sg_link != to_id
766#ifdef FEAT_EVAL
767 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
768#endif
769 || hlgroup->sg_cleared)
770 {
771 if (!init)
772 hlgroup->sg_set |= SG_LINK;
773 hlgroup->sg_link = to_id;
774#ifdef FEAT_EVAL
775 hlgroup->sg_script_ctx = current_sctx;
776 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
777#endif
778 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100779 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100780
781 // Only call highlight_changed() once after multiple changes.
782 need_highlight_changed = TRUE;
783 }
784 }
785
786}
787
788/*
789 * Reset all highlighting to the defaults. Removes all highlighting for the
790 * groups added by the user.
791 */
792 static void
793highlight_reset_all(void)
794{
795 int idx;
796
797#ifdef FEAT_GUI
798 // First, we do not destroy the old values, but allocate the new
799 // ones and update the display. THEN we destroy the old values.
800 // If we destroy the old values first, then the old values
801 // (such as GuiFont's or GuiFontset's) will still be displayed but
802 // invalid because they were free'd.
803 if (gui.in_use)
804 {
805# ifdef FEAT_BEVAL_TIP
806 gui_init_tooltip_font();
807# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100808# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100809 gui_init_menu_font();
810# endif
811 }
812# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
813 gui_mch_def_colors();
814# endif
815# ifdef FEAT_GUI_X11
816# ifdef FEAT_MENU
817
818 // This only needs to be done when there is no Menu highlight
819 // group defined by default, which IS currently the case.
820 gui_mch_new_menu_colors();
821# endif
822 if (gui.in_use)
823 {
824 gui_new_scrollbar_colors();
825# ifdef FEAT_BEVAL_GUI
826 gui_mch_new_tooltip_colors();
827# endif
828# ifdef FEAT_MENU
829 gui_mch_new_menu_font();
830# endif
831 }
832# endif
833
834 // Ok, we're done allocating the new default graphics items.
835 // The screen should already be refreshed at this point.
836 // It is now Ok to clear out the old data.
837#endif
838#ifdef FEAT_EVAL
839 do_unlet((char_u *)"g:colors_name", TRUE);
840#endif
841 restore_cterm_colors();
842
843 // Clear all default highlight groups and load the defaults.
844 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
845 highlight_clear(idx);
846 init_highlight(TRUE, TRUE);
847#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
848 if (USE_24BIT)
849 highlight_gui_started();
850 else
851#endif
852 highlight_changed();
853 redraw_later_clear();
854}
855
856/*
857 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
858 * index 'idx'.
859 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
860 * 'arg' is the list of attribute names separated by comma.
861 * 'init' is set to TRUE when initializing all the highlight groups.
862 * Returns TRUE if the attributes are set.
863 */
864 static int
865highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
866{
867 int attr;
Mike Williams72a156b2024-04-09 22:04:54 +0200868 size_t off;
John Marriott34f00dd2024-04-08 23:28:12 +0200869 keyvalue_T target;
870 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100871
872 attr = 0;
873 off = 0;
John Marriott34f00dd2024-04-08 23:28:12 +0200874 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +0100875 target.value.length = 0; // not used, see cmp_keyvalue_value_ni()
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100876 while (arg[off] != NUL)
877 {
John Marriott8d4477e2024-11-02 15:59:01 +0100878 target.value.string = arg + off;
879 entry = (keyvalue_T *)bsearch(&target, &highlight_tab,
880 ARRAY_LENGTH(highlight_tab), sizeof(highlight_tab[0]),
881 cmp_keyvalue_value_ni);
John Marriott34f00dd2024-04-08 23:28:12 +0200882 if (entry == NULL)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100883 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000884 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100885 return FALSE;
886 }
John Marriott34f00dd2024-04-08 23:28:12 +0200887
888 attr |= entry->key;
John Marriott8d4477e2024-11-02 15:59:01 +0100889 off += entry->value.length;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100890 if (arg[off] == ',') // another one follows
891 ++off;
892 }
893 if (*key == 'T')
894 {
895 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
896 {
897 if (!init)
898 HL_TABLE()[idx].sg_set |= SG_TERM;
899 HL_TABLE()[idx].sg_term = attr;
900 }
901 }
902 else if (*key == 'C')
903 {
904 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
905 {
906 if (!init)
907 HL_TABLE()[idx].sg_set |= SG_CTERM;
908 HL_TABLE()[idx].sg_cterm = attr;
909 HL_TABLE()[idx].sg_cterm_bold = FALSE;
910 }
911 }
912#if defined(FEAT_GUI) || defined(FEAT_EVAL)
913 else
914 {
915 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
916 {
917 if (!init)
918 HL_TABLE()[idx].sg_set |= SG_GUI;
919 HL_TABLE()[idx].sg_gui = attr;
920 }
921 }
922#endif
923
924 return TRUE;
925}
926
927#ifdef FEAT_GUI
928/*
929 * Set the font for the highlight group at 'idx'.
930 * 'arg' is the font name.
931 * Returns TRUE if the font is changed.
932 */
933 static int
934highlight_set_font(
935 int idx,
936 char_u *arg,
937 int is_normal_group,
938 int is_menu_group,
939 int is_tooltip_group)
940{
941 int did_change = FALSE;
942
943 // in non-GUI fonts are simply ignored
944 if (HL_TABLE()[idx].sg_font_name != NULL
945 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
946 {
947 // Font name didn't change, ignore.
948 }
949 else if (!gui.shell_created)
950 {
951 // GUI not started yet, always accept the name.
952 vim_free(HL_TABLE()[idx].sg_font_name);
953 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
954 did_change = TRUE;
955 }
956 else
957 {
958 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
959# ifdef FEAT_XFONTSET
960 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
961# endif
962 // First, save the current font/fontset.
963 // Then try to allocate the font/fontset.
964 // If the allocation fails, HL_TABLE()[idx].sg_font OR
965 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
966
967 HL_TABLE()[idx].sg_font = NOFONT;
968# ifdef FEAT_XFONTSET
969 HL_TABLE()[idx].sg_fontset = NOFONTSET;
970# endif
971 hl_do_font(idx, arg, is_normal_group, is_menu_group,
972 is_tooltip_group, FALSE);
973
974# ifdef FEAT_XFONTSET
975 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
976 {
977 // New fontset was accepted. Free the old one, if there
978 // was one.
979 gui_mch_free_fontset(temp_sg_fontset);
980 vim_free(HL_TABLE()[idx].sg_font_name);
981 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
982 did_change = TRUE;
983 }
984 else
985 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
986# endif
987 if (HL_TABLE()[idx].sg_font != NOFONT)
988 {
989 // New font was accepted. Free the old one, if there was
990 // one.
991 gui_mch_free_font(temp_sg_font);
992 vim_free(HL_TABLE()[idx].sg_font_name);
993 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
994 did_change = TRUE;
995 }
996 else
997 HL_TABLE()[idx].sg_font = temp_sg_font;
998 }
999
1000 return did_change;
1001}
1002#endif
1003
1004/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001005 * Set the cterm foreground color for the Normal highlight group to "color" and
1006 * the bold attribute to "bold".
1007 */
1008 static void
1009hl_set_ctermfg_normal_group(int color, int bold)
1010{
1011 cterm_normal_fg_color = color + 1;
1012 cterm_normal_fg_bold = bold;
1013#ifdef FEAT_GUI
1014 // Don't do this if the GUI is used.
1015 if (!gui.in_use && !gui.starting)
1016#endif
1017 {
1018 set_must_redraw(UPD_CLEAR);
1019 if (termcap_active && color >= 0)
1020 term_fg_color(color);
1021 }
1022}
1023
1024/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001025 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001026 */
1027 static void
1028highlight_set_ctermfg(int idx, int color, int is_normal_group)
1029{
1030 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1031 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001032 hl_set_ctermfg_normal_group(color,
1033 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001034}
1035
1036/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001037 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001038 */
1039 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001040hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001041{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001042 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001043#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001044 // Don't mess with 'background' if the GUI is used.
1045 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001046#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001047 {
1048 set_must_redraw(UPD_CLEAR);
1049 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001050 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001051 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001052
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001053 if (termcap_active)
1054 term_bg_color(color);
1055 if (t_colors < 16)
1056 dark = (color == 0 || color == 4);
1057 // Limit the heuristic to the standard 16 colors
1058 else if (color < 16)
1059 dark = (color < 7 || color == 8);
1060 // Set the 'background' option if the value is
1061 // wrong.
1062 if (dark != -1
1063 && dark != (*p_bg == 'd')
1064 && !option_was_set((char_u *)"bg"))
1065 {
1066 set_option_value_give_err((char_u *)"bg",
1067 0L, (char_u *)(dark ? "dark" : "light"), 0);
1068 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001069 }
1070 }
1071 }
1072}
1073
1074/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001075 * Set the cterm background color for the highlight group at 'idx' to 'color'.
1076 */
1077 static void
1078highlight_set_ctermbg(int idx, int color, int is_normal_group)
1079{
1080 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1081 if (is_normal_group)
1082 hl_set_ctermbg_normal_group(color);
1083}
1084
1085/*
1086 * Set the cterm underline color for the Normal highlight group to "color".
1087 */
1088 static void
1089hl_set_ctermul_normal_group(int color)
1090{
1091 cterm_normal_ul_color = color + 1;
1092#ifdef FEAT_GUI
1093 // Don't do this if the GUI is used.
1094 if (!gui.in_use && !gui.starting)
1095#endif
1096 {
1097 set_must_redraw(UPD_CLEAR);
1098 if (termcap_active && color >= 0)
1099 term_ul_color(color);
1100 }
1101}
1102
1103/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001104 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001105 */
1106 static void
1107highlight_set_ctermul(int idx, int color, int is_normal_group)
1108{
1109 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1110 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001111 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001112}
1113
1114/*
PMuncha606f3a2023-11-15 15:35:49 +01001115 * Set the cterm font for the highlight group at 'idx'.
1116 * 'arg' is the color name or the numeric value as a string.
1117 * 'init' is set to TRUE when initializing highlighting.
1118 * Called for the ":highlight" command and the "hlset()" function.
1119 *
1120 * Returns TRUE if the font is set.
1121 */
1122 static int
1123highlight_set_cterm_font(
1124 int idx,
1125 char_u *arg,
1126 int init)
1127{
1128 int font;
1129
1130 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1131 return FALSE;
1132
1133 if (!init)
1134 HL_TABLE()[idx].sg_set |= SG_CTERM;
1135
1136 if (VIM_ISDIGIT(*arg))
1137 font = atoi((char *)arg);
1138 else if (STRICMP(arg, "NONE") == 0)
1139 font = -1;
1140 else
1141 return FALSE;
1142
1143 HL_TABLE()[idx].sg_cterm_font = font + 1;
1144 return TRUE;
1145}
1146
1147/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001148 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1149 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1150 * 'keystart' is the color name/value.
1151 * 'arg' is the color name or the numeric value as a string.
1152 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1153 * 'init' is set to TRUE when initializing highlighting.
1154 * Called for the ":highlight" command and the "hlset()" function.
1155 *
1156 * Returns TRUE if the color is set.
1157 */
1158 static int
1159highlight_set_cterm_color(
1160 int idx,
1161 char_u *key,
1162 char_u *key_start,
1163 char_u *arg,
1164 int is_normal_group,
1165 int init)
1166{
1167 int color;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001168
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001169 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1170 return FALSE;
1171
1172 if (!init)
1173 HL_TABLE()[idx].sg_set |= SG_CTERM;
1174
1175 // When setting the foreground color, and previously the "bold"
1176 // flag was set for a light color, reset it now
1177 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001178 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001179 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1180 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1181 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001182
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001183 if (VIM_ISDIGIT(*arg))
1184 color = atoi((char *)arg);
1185 else if (STRICMP(arg, "fg") == 0)
1186 {
1187 if (cterm_normal_fg_color)
1188 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001189 else
1190 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001191 emsg(_(e_fg_color_unknown));
1192 return FALSE;
1193 }
1194 }
1195 else if (STRICMP(arg, "bg") == 0)
1196 {
1197 if (cterm_normal_bg_color > 0)
1198 color = cterm_normal_bg_color - 1;
1199 else
1200 {
1201 emsg(_(e_bg_color_unknown));
1202 return FALSE;
1203 }
1204 }
1205 else if (STRICMP(arg, "ul") == 0)
1206 {
1207 if (cterm_normal_ul_color > 0)
1208 color = cterm_normal_ul_color - 1;
1209 else
1210 {
1211 emsg(_(e_ul_color_unknown));
1212 return FALSE;
1213 }
1214 }
1215 else
1216 {
1217 int bold = MAYBE;
John Marriott34f00dd2024-04-08 23:28:12 +02001218 keyvalue_T target;
1219 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001220
John Marriott34f00dd2024-04-08 23:28:12 +02001221 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01001222 target.value.string = arg;
1223 target.value.length = 0; // not used, see cmp_keyvalue_value_i()
1224 entry = (keyvalue_T *)bsearch(&target, &color_name_tab,
1225 ARRAY_LENGTH(color_name_tab), sizeof(color_name_tab[0]),
1226 cmp_keyvalue_value_i);
John Marriott34f00dd2024-04-08 23:28:12 +02001227 if (entry == NULL)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001228 {
1229 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1230 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001231 }
1232
John Marriott34f00dd2024-04-08 23:28:12 +02001233 color = lookup_color(entry->key, key[5] == 'F', &bold);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001234
1235 // set/reset bold attribute to get light foreground
1236 // colors (on some terminals, e.g. "linux")
1237 if (bold == TRUE)
1238 {
1239 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1240 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1241 }
1242 else if (bold == FALSE)
1243 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001244 }
1245
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001246 // Add one to the argument, to avoid zero. Zero is used for
1247 // "NONE", then "color" is -1.
1248 if (key[5] == 'F')
1249 highlight_set_ctermfg(idx, color, is_normal_group);
1250 else if (key[5] == 'B')
1251 highlight_set_ctermbg(idx, color, is_normal_group);
1252 else // ctermul
1253 highlight_set_ctermul(idx, color, is_normal_group);
1254
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001255 return TRUE;
1256}
1257
1258#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1259/*
1260 * Set the GUI foreground color for the highlight group at 'idx'.
1261 * Returns TRUE if the color is set.
1262 */
1263 static int
1264highlight_set_guifg(
1265 int idx,
1266 char_u *arg,
1267 int is_menu_group UNUSED,
1268 int is_scrollbar_group UNUSED,
1269 int is_tooltip_group UNUSED,
1270 int *do_colors UNUSED,
1271 int init)
1272{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001273# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001274 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001275# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001276 char_u **namep;
1277 int did_change = FALSE;
1278
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001279 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1280 return FALSE;
1281
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001282 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001283 if (!init)
1284 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001285
1286# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001287 // In GUI guifg colors are only used when recognized
1288 i = color_name2handle(arg);
1289 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1290 {
1291 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001292# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001293 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1294 {
1295 vim_free(*namep);
1296 if (STRCMP(arg, "NONE") != 0)
1297 *namep = vim_strsave(arg);
1298 else
1299 *namep = NULL;
1300 did_change = TRUE;
1301 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001302# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1303# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001304 if (is_menu_group && gui.menu_fg_pixel != i)
1305 {
1306 gui.menu_fg_pixel = i;
1307 *do_colors = TRUE;
1308 }
1309 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1310 {
1311 gui.scroll_fg_pixel = i;
1312 *do_colors = TRUE;
1313 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001314# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001315 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1316 {
1317 gui.tooltip_fg_pixel = i;
1318 *do_colors = TRUE;
1319 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001320# endif
1321# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001322 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001323# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001324
1325 return did_change;
1326}
1327
1328/*
1329 * Set the GUI background color for the highlight group at 'idx'.
1330 * Returns TRUE if the color is set.
1331 */
1332 static int
1333highlight_set_guibg(
1334 int idx,
1335 char_u *arg,
1336 int is_menu_group UNUSED,
1337 int is_scrollbar_group UNUSED,
1338 int is_tooltip_group UNUSED,
1339 int *do_colors UNUSED,
1340 int init)
1341{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001342# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001343 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001344# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001345 char_u **namep;
1346 int did_change = FALSE;
1347
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001348 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1349 return FALSE;
1350
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001351 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001352 if (!init)
1353 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001354
1355# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001356 // In GUI guibg colors are only used when recognized
1357 i = color_name2handle(arg);
1358 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1359 {
1360 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001361# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001362 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1363 {
1364 vim_free(*namep);
1365 if (STRCMP(arg, "NONE") != 0)
1366 *namep = vim_strsave(arg);
1367 else
1368 *namep = NULL;
1369 did_change = TRUE;
1370 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001371# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1372# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001373 if (is_menu_group && gui.menu_bg_pixel != i)
1374 {
1375 gui.menu_bg_pixel = i;
1376 *do_colors = TRUE;
1377 }
1378 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1379 {
1380 gui.scroll_bg_pixel = i;
1381 *do_colors = TRUE;
1382 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001383# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001384 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1385 {
1386 gui.tooltip_bg_pixel = i;
1387 *do_colors = TRUE;
1388 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001389# endif
1390# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001391 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001392# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001393
1394 return did_change;
1395}
1396
1397/*
1398 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1399 * Returns TRUE if the color is set.
1400 */
1401 static int
1402highlight_set_guisp(int idx, char_u *arg, int init)
1403{
1404# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1405 int i;
1406# endif
1407 int did_change = FALSE;
1408 char_u **namep;
1409
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001410 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1411 return FALSE;
1412
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001413 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001414 if (!init)
1415 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001416
1417# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001418 // In GUI guisp colors are only used when recognized
1419 i = color_name2handle(arg);
1420 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1421 {
1422 HL_TABLE()[idx].sg_gui_sp = i;
1423# endif
1424 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001425 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001426 vim_free(*namep);
1427 if (STRCMP(arg, "NONE") != 0)
1428 *namep = vim_strsave(arg);
1429 else
1430 *namep = NULL;
1431 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001432 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001433# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001434 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001435# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001436
1437 return did_change;
1438}
1439#endif
1440
1441/*
1442 * Set the start/stop terminal codes for a highlight group.
1443 * Returns TRUE if the terminal code is set.
1444 */
1445 static int
1446highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1447{
1448 int off;
1449 char_u buf[100];
1450 int len;
1451 char_u *tname;
1452 char_u *p;
1453
1454 if (!init)
1455 HL_TABLE()[idx].sg_set |= SG_TERM;
1456
1457 // The "start" and "stop" arguments can be a literal escape
1458 // sequence, or a comma separated list of terminal codes.
1459 if (STRNCMP(arg, "t_", 2) == 0)
1460 {
1461 off = 0;
1462 buf[0] = 0;
1463 while (arg[off] != NUL)
1464 {
1465 // Isolate one termcap name
1466 for (len = 0; arg[off + len] &&
1467 arg[off + len] != ','; ++len)
1468 ;
1469 tname = vim_strnsave(arg + off, len);
1470 if (tname == NULL) // out of memory
1471 return FALSE;
1472 // lookup the escape sequence for the item
1473 p = get_term_code(tname);
1474 vim_free(tname);
1475 if (p == NULL) // ignore non-existing things
1476 p = (char_u *)"";
1477
1478 // Append it to the already found stuff
1479 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1480 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001481 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001482 return FALSE;
1483 }
1484 STRCAT(buf, p);
1485
1486 // Advance to the next item
1487 off += len;
1488 if (arg[off] == ',') // another one follows
1489 ++off;
1490 }
1491 }
1492 else
1493 {
1494 // Copy characters from arg[] to buf[], translating <> codes.
1495 for (p = arg, off = 0; off < 100 - 6 && *p; )
1496 {
zeertzjqdb088872022-05-02 22:53:45 +01001497 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001498 if (len > 0) // recognized special char
1499 off += len;
1500 else // copy as normal char
1501 buf[off++] = *p++;
1502 }
1503 buf[off] = NUL;
1504 }
1505
1506 if (STRCMP(buf, "NONE") == 0) // resetting the value
1507 p = NULL;
1508 else
1509 p = vim_strsave(buf);
1510 if (key[2] == 'A')
1511 {
1512 vim_free(HL_TABLE()[idx].sg_start);
1513 HL_TABLE()[idx].sg_start = p;
1514 }
1515 else
1516 {
1517 vim_free(HL_TABLE()[idx].sg_stop);
1518 HL_TABLE()[idx].sg_stop = p;
1519 }
1520 return TRUE;
1521}
1522
1523/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001524 * Handle the ":highlight .." command.
1525 * When using ":hi clear" this is called recursively for each group with
1526 * "forceit" and "init" both TRUE.
1527 */
1528 void
1529do_highlight(
1530 char_u *line,
1531 int forceit,
1532 int init) // TRUE when called for initializing
1533{
1534 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001535 char_u *linep;
1536 char_u *key_start;
1537 char_u *arg_start;
1538 char_u *key = NULL, *arg = NULL;
1539 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001540 int id;
1541 int idx;
1542 hl_group_T item_before;
1543 int did_change = FALSE;
1544 int dodefault = FALSE;
1545 int doclear = FALSE;
1546 int dolink = FALSE;
1547 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001548 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001549#ifdef FEAT_GUI_X11
1550 int is_menu_group = FALSE; // "Menu" group
1551 int is_scrollbar_group = FALSE; // "Scrollbar" group
1552 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001553#else
1554# define is_menu_group 0
1555# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001556# define is_scrollbar_group 0
1557#endif
1558#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1559 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001560#endif
1561#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1562 int did_highlight_changed = FALSE;
1563#endif
1564
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001565 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001566 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001567 {
1568 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1569 // TODO: only call when the group has attributes set
1570 highlight_list_one((int)i);
1571 return;
1572 }
1573
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001574 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001575 name_end = skiptowhite(line);
1576 linep = skipwhite(name_end);
1577
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001578 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001579 if (STRNCMP(line, "default", name_end - line) == 0)
1580 {
1581 dodefault = TRUE;
1582 line = linep;
1583 name_end = skiptowhite(line);
1584 linep = skipwhite(name_end);
1585 }
1586
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001587 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001588 if (STRNCMP(line, "clear", name_end - line) == 0)
1589 doclear = TRUE;
1590 if (STRNCMP(line, "link", name_end - line) == 0)
1591 dolink = TRUE;
1592
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001593 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001594 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001595 {
1596 id = syn_namen2id(line, (int)(name_end - line));
1597 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001598 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001599 else
1600 highlight_list_one(id);
1601 return;
1602 }
1603
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001604 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001605 if (dolink)
1606 {
1607 char_u *from_start = linep;
1608 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001609 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001610 char_u *to_start;
1611 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001612 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001613
1614 from_end = skiptowhite(from_start);
1615 to_start = skipwhite(from_end);
1616 to_end = skiptowhite(to_start);
1617
Bram Moolenaar1966c242020-04-20 22:42:32 +02001618 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001619 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001620 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001621 return;
1622 }
1623
Bram Moolenaar1966c242020-04-20 22:42:32 +02001624 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001625 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001626 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001627 return;
1628 }
1629
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001630 from_len = (int)(from_end - from_start);
1631 to_len = (int)(to_end - to_start);
1632 highlight_group_link(from_start, from_len, to_start, to_len,
1633 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001634 return;
1635 }
1636
1637 if (doclear)
1638 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001639 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001640 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001641 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001642 // ":highlight clear" without group name
1643 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001644 return;
1645 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001646 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001647 name_end = skiptowhite(line);
1648 linep = skipwhite(name_end);
1649 }
1650
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001651 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001652 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001653 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001654 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001655 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001656
1657 // Return if "default" was used and the group already has settings.
1658 if (dodefault && hl_has_settings(idx, TRUE))
1659 return;
1660
1661 // Make a copy so we can check if any attribute actually changed.
1662 item_before = HL_TABLE()[idx];
1663
1664 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1665 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001666#ifdef FEAT_GUI_X11
1667 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1668 is_menu_group = TRUE;
1669 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1670 is_scrollbar_group = TRUE;
1671 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1672 is_tooltip_group = TRUE;
1673#endif
1674
1675 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1676 if (doclear || (forceit && init))
1677 {
1678 highlight_clear(idx);
1679 if (!doclear)
1680 HL_TABLE()[idx].sg_set = 0;
1681 }
1682
1683 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001684 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001685 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001686 key_start = linep;
1687 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001688 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001689 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001690 error = TRUE;
1691 break;
1692 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001693
Yee Cheng China7b81202025-02-23 09:32:47 +01001694 // Note: Keep this in sync with get_highlight_group_key.
1695
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001696 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1697 // or "guibg").
1698 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1699 ++linep;
1700 vim_free(key);
1701 key = vim_strnsave_up(key_start, linep - key_start);
1702 if (key == NULL)
1703 {
1704 error = TRUE;
1705 break;
1706 }
1707 linep = skipwhite(linep);
1708
1709 if (STRCMP(key, "NONE") == 0)
1710 {
1711 if (!init || HL_TABLE()[idx].sg_set == 0)
1712 {
1713 if (!init)
1714 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1715 highlight_clear(idx);
1716 }
1717 continue;
1718 }
1719
1720 // Check for the equal sign.
1721 if (*linep != '=')
1722 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001723 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001724 error = TRUE;
1725 break;
1726 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001727 ++linep;
1728
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001729 // Isolate the argument.
1730 linep = skipwhite(linep);
1731 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001732 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001733 arg_start = ++linep;
1734 linep = vim_strchr(linep, '\'');
1735 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001736 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001737 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001738 error = TRUE;
1739 break;
1740 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001741 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001742 else
1743 {
1744 arg_start = linep;
1745 linep = skiptowhite(linep);
1746 }
1747 if (linep == arg_start)
1748 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001749 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001750 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001751 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001752 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001753 vim_free(arg);
1754 arg = vim_strnsave(arg_start, linep - arg_start);
1755 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001756 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001757 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001758 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001759 }
1760 if (*linep == '\'')
1761 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001762
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001763 // Store the argument.
1764 if (STRCMP(key, "TERM") == 0
1765 || STRCMP(key, "CTERM") == 0
1766 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001767 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001768 if (!highlight_set_termgui_attr(idx, key, arg, init))
1769 {
1770 error = TRUE;
1771 break;
1772 }
1773 }
1774 else if (STRCMP(key, "FONT") == 0)
1775 {
1776 // in non-GUI fonts are simply ignored
1777#ifdef FEAT_GUI
1778 if (highlight_set_font(idx, arg, is_normal_group,
1779 is_menu_group, is_tooltip_group))
1780 did_change = TRUE;
1781#endif
1782 }
1783 else if (STRCMP(key, "CTERMFG") == 0
1784 || STRCMP(key, "CTERMBG") == 0
1785 || STRCMP(key, "CTERMUL") == 0)
1786 {
1787 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1788 is_normal_group, init))
1789 {
1790 error = TRUE;
1791 break;
1792 }
1793 }
PMuncha606f3a2023-11-15 15:35:49 +01001794 else if (STRCMP(key, "CTERMFONT") == 0)
1795 {
1796 if (!highlight_set_cterm_font(idx, arg, init))
1797 {
1798 error = TRUE;
1799 break;
1800 }
1801 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001802 else if (STRCMP(key, "GUIFG") == 0)
1803 {
1804#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1805 if (highlight_set_guifg(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, "GUIBG") == 0)
1812 {
1813#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1814 if (highlight_set_guibg(idx, arg, is_menu_group,
1815 is_scrollbar_group, is_tooltip_group,
1816 &do_colors, init))
1817 did_change = TRUE;
1818#endif
1819 }
1820 else if (STRCMP(key, "GUISP") == 0)
1821 {
1822#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1823 if (highlight_set_guisp(idx, arg, init))
1824 did_change = TRUE;
1825#endif
1826 }
1827 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1828 {
1829 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1830 {
1831 error = TRUE;
1832 break;
1833 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001834 }
1835 else
1836 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001837 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001838 error = TRUE;
1839 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001840 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001841 HL_TABLE()[idx].sg_cleared = FALSE;
1842
1843 // When highlighting has been given for a group, don't link it.
1844 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1845 HL_TABLE()[idx].sg_link = 0;
1846
1847 // Continue with next argument.
1848 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001849 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001850
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001851 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001852 if (error && idx == highlight_ga.ga_len)
1853 syn_unadd_group();
1854 else
1855 {
1856 if (is_normal_group)
1857 {
1858 HL_TABLE()[idx].sg_term_attr = 0;
1859 HL_TABLE()[idx].sg_cterm_attr = 0;
1860#ifdef FEAT_GUI
1861 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001862 // Need to update all groups, because they might be using "bg"
1863 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001864#endif
1865#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1866 if (USE_24BIT)
1867 {
1868 highlight_gui_started();
1869 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001870 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001871 }
1872#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001873#ifdef FEAT_VTP
1874 control_console_color_rgb();
1875#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001876 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001877#ifdef FEAT_GUI_X11
1878# ifdef FEAT_MENU
1879 else if (is_menu_group)
1880 {
1881 if (gui.in_use && do_colors)
1882 gui_mch_new_menu_colors();
1883 }
1884# endif
1885 else if (is_scrollbar_group)
1886 {
1887 if (gui.in_use && do_colors)
1888 gui_new_scrollbar_colors();
1889 else
1890 set_hl_attr(idx);
1891 }
1892# ifdef FEAT_BEVAL_GUI
1893 else if (is_tooltip_group)
1894 {
1895 if (gui.in_use && do_colors)
1896 gui_mch_new_tooltip_colors();
1897 }
1898# endif
1899#endif
1900 else
1901 set_hl_attr(idx);
1902#ifdef FEAT_EVAL
1903 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001904 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001905#endif
1906 }
1907
1908 vim_free(key);
1909 vim_free(arg);
1910
1911 // Only call highlight_changed() once, after a sequence of highlight
1912 // commands, and only if an attribute actually changed.
1913 if ((did_change
1914 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1915#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1916 && !did_highlight_changed
1917#endif
1918 )
1919 {
1920 // Do not trigger a redraw when highlighting is changed while
1921 // redrawing. This may happen when evaluating 'statusline' changes the
1922 // StatusLine group.
1923 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001924 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001925 need_highlight_changed = TRUE;
1926 }
1927}
1928
1929#if defined(EXITFREE) || defined(PROTO)
1930 void
1931free_highlight(void)
1932{
1933 int i;
1934
1935 for (i = 0; i < highlight_ga.ga_len; ++i)
1936 {
1937 highlight_clear(i);
1938 vim_free(HL_TABLE()[i].sg_name);
1939 vim_free(HL_TABLE()[i].sg_name_u);
1940 }
1941 ga_clear(&highlight_ga);
1942}
1943#endif
1944
1945/*
1946 * Reset the cterm colors to what they were before Vim was started, if
1947 * possible. Otherwise reset them to zero.
1948 */
1949 void
1950restore_cterm_colors(void)
1951{
1952#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1953 // Since t_me has been set, this probably means that the user
1954 // wants to use this as default colors. Need to reset default
1955 // background/foreground colors.
1956 mch_set_normal_colors();
1957#else
1958# ifdef VIMDLL
1959 if (!gui.in_use)
1960 {
1961 mch_set_normal_colors();
1962 return;
1963 }
1964# endif
1965 cterm_normal_fg_color = 0;
1966 cterm_normal_fg_bold = 0;
1967 cterm_normal_bg_color = 0;
1968# ifdef FEAT_TERMGUICOLORS
1969 cterm_normal_fg_gui_color = INVALCOLOR;
1970 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001971 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001972# endif
1973#endif
1974}
1975
1976/*
1977 * Return TRUE if highlight group "idx" has any settings.
1978 * When "check_link" is TRUE also check for an existing link.
1979 */
1980 static int
1981hl_has_settings(int idx, int check_link)
1982{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001983 return HL_TABLE()[idx].sg_cleared == 0
1984 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001985 || HL_TABLE()[idx].sg_cterm_attr != 0
1986 || HL_TABLE()[idx].sg_cterm_fg != 0
1987 || HL_TABLE()[idx].sg_cterm_bg != 0
PMuncha606f3a2023-11-15 15:35:49 +01001988 || HL_TABLE()[idx].sg_cterm_font != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001989#ifdef FEAT_GUI
1990 || HL_TABLE()[idx].sg_gui_attr != 0
1991 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1992 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1993 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1994 || HL_TABLE()[idx].sg_font_name != NULL
1995#endif
1996 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1997}
1998
1999/*
2000 * Clear highlighting for one group.
2001 */
2002 static void
2003highlight_clear(int idx)
2004{
2005 HL_TABLE()[idx].sg_cleared = TRUE;
2006
2007 HL_TABLE()[idx].sg_term = 0;
2008 VIM_CLEAR(HL_TABLE()[idx].sg_start);
2009 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
2010 HL_TABLE()[idx].sg_term_attr = 0;
2011 HL_TABLE()[idx].sg_cterm = 0;
2012 HL_TABLE()[idx].sg_cterm_bold = FALSE;
2013 HL_TABLE()[idx].sg_cterm_fg = 0;
2014 HL_TABLE()[idx].sg_cterm_bg = 0;
2015 HL_TABLE()[idx].sg_cterm_attr = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002016 HL_TABLE()[idx].sg_cterm_font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002017#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2018 HL_TABLE()[idx].sg_gui = 0;
2019 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
2020 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
2021 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
2022#endif
2023#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2024 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
2025 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002026 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002027#endif
2028#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002029 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2030 HL_TABLE()[idx].sg_font = NOFONT;
2031# ifdef FEAT_XFONTSET
2032 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2033 HL_TABLE()[idx].sg_fontset = NOFONTSET;
2034# endif
2035 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
2036 HL_TABLE()[idx].sg_gui_attr = 0;
2037#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02002038 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02002039 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02002040#ifdef FEAT_EVAL
2041 // Since we set the default link, set the location to where the default
2042 // link was set.
2043 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002044#endif
2045}
2046
2047#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2048/*
2049 * Set the normal foreground and background colors according to the "Normal"
2050 * highlighting group. For X11 also set "Menu", "Scrollbar", and
2051 * "Tooltip" colors.
2052 */
2053 void
2054set_normal_colors(void)
2055{
2056# ifdef FEAT_GUI
2057# ifdef FEAT_TERMGUICOLORS
2058 if (gui.in_use)
2059# endif
2060 {
2061 if (set_group_colors((char_u *)"Normal",
2062 &gui.norm_pixel, &gui.back_pixel,
2063 FALSE, TRUE, FALSE))
2064 {
2065 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002066 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002067 }
2068# ifdef FEAT_GUI_X11
2069 if (set_group_colors((char_u *)"Menu",
2070 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
2071 TRUE, FALSE, FALSE))
2072 {
2073# ifdef FEAT_MENU
2074 gui_mch_new_menu_colors();
2075# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002076 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002077 }
2078# ifdef FEAT_BEVAL_GUI
2079 if (set_group_colors((char_u *)"Tooltip",
2080 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
2081 FALSE, FALSE, TRUE))
2082 {
2083# ifdef FEAT_TOOLBAR
2084 gui_mch_new_tooltip_colors();
2085# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002086 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002087 }
2088# endif
2089 if (set_group_colors((char_u *)"Scrollbar",
2090 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
2091 FALSE, FALSE, FALSE))
2092 {
2093 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002094 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002095 }
2096# endif
2097 }
2098# endif
2099# ifdef FEAT_TERMGUICOLORS
2100# ifdef FEAT_GUI
2101 else
2102# endif
2103 {
2104 int idx;
2105
2106 idx = syn_name2id((char_u *)"Normal") - 1;
2107 if (idx >= 0)
2108 {
2109 gui_do_one_color(idx, FALSE, FALSE);
2110
2111 // If the normal fg or bg color changed a complete redraw is
2112 // required.
2113 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
2114 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
2115 {
2116 // if the GUI color is INVALCOLOR then we use the default cterm
2117 // color
2118 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
2119 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002120 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002121 }
2122 }
2123 }
2124# endif
2125}
2126#endif
2127
2128#if defined(FEAT_GUI) || defined(PROTO)
2129/*
2130 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2131 */
2132 static int
2133set_group_colors(
2134 char_u *name,
2135 guicolor_T *fgp,
2136 guicolor_T *bgp,
2137 int do_menu,
2138 int use_norm,
2139 int do_tooltip)
2140{
2141 int idx;
2142
2143 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002144 if (idx < 0)
2145 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002146
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002147 gui_do_one_color(idx, do_menu, do_tooltip);
2148
2149 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2150 *fgp = HL_TABLE()[idx].sg_gui_fg;
2151 else if (use_norm)
2152 *fgp = gui.def_norm_pixel;
2153 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2154 *bgp = HL_TABLE()[idx].sg_gui_bg;
2155 else if (use_norm)
2156 *bgp = gui.def_back_pixel;
2157 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002158}
2159
2160/*
2161 * Get the font of the "Normal" group.
2162 * Returns "" when it's not found or not set.
2163 */
2164 char_u *
2165hl_get_font_name(void)
2166{
2167 int id;
2168 char_u *s;
2169
2170 id = syn_name2id((char_u *)"Normal");
2171 if (id > 0)
2172 {
2173 s = HL_TABLE()[id - 1].sg_font_name;
2174 if (s != NULL)
2175 return s;
2176 }
2177 return (char_u *)"";
2178}
2179
2180/*
2181 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2182 * actually chosen to be used.
2183 */
2184 void
2185hl_set_font_name(char_u *font_name)
2186{
2187 int id;
2188
2189 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002190 if (id <= 0)
2191 return;
2192
2193 vim_free(HL_TABLE()[id - 1].sg_font_name);
2194 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002195}
2196
2197/*
2198 * Set background color for "Normal" group. Called by gui_set_bg_color()
2199 * when the color is known.
2200 */
2201 void
2202hl_set_bg_color_name(
2203 char_u *name) // must have been allocated
2204{
2205 int id;
2206
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002207 if (name == NULL)
2208 return;
2209
2210 id = syn_name2id((char_u *)"Normal");
2211 if (id <= 0)
2212 return;
2213
2214 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2215 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002216}
2217
2218/*
2219 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2220 * when the color is known.
2221 */
2222 void
2223hl_set_fg_color_name(
2224 char_u *name) // must have been allocated
2225{
2226 int id;
2227
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002228 if (name == NULL)
2229 return;
2230
2231 id = syn_name2id((char_u *)"Normal");
2232 if (id <= 0)
2233 return;
2234
2235 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2236 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002237}
2238
2239/*
2240 * Return the handle for a font name.
2241 * Returns NOFONT when failed.
2242 */
2243 static GuiFont
2244font_name2handle(char_u *name)
2245{
2246 if (STRCMP(name, "NONE") == 0)
2247 return NOFONT;
2248
2249 return gui_mch_get_font(name, TRUE);
2250}
2251
2252# ifdef FEAT_XFONTSET
2253/*
2254 * Return the handle for a fontset name.
2255 * Returns NOFONTSET when failed.
2256 */
2257 static GuiFontset
2258fontset_name2handle(char_u *name, int fixed_width)
2259{
2260 if (STRCMP(name, "NONE") == 0)
2261 return NOFONTSET;
2262
2263 return gui_mch_get_fontset(name, TRUE, fixed_width);
2264}
2265# endif
2266
2267/*
2268 * Get the font or fontset for one highlight group.
2269 */
2270 static void
2271hl_do_font(
2272 int idx,
2273 char_u *arg,
2274 int do_normal, // set normal font
2275 int do_menu UNUSED, // set menu font
2276 int do_tooltip UNUSED, // set tooltip font
2277 int free_font) // free current font/fontset
2278{
2279# ifdef FEAT_XFONTSET
2280 // If 'guifontset' is not empty, first try using the name as a
2281 // fontset. If that doesn't work, use it as a font name.
2282 if (*p_guifontset != NUL
2283# ifdef FONTSET_ALWAYS
2284 || do_menu
2285# endif
2286# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002287 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002288 || do_tooltip
2289# endif
2290 )
2291 {
2292 if (free_font)
2293 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2294 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2295# ifdef FONTSET_ALWAYS
2296 || do_menu
2297# endif
2298# ifdef FEAT_BEVAL_TIP
2299 || do_tooltip
2300# endif
2301 );
2302 }
2303 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2304 {
2305 // If it worked and it's the Normal group, use it as the normal
2306 // fontset. Same for the Menu group.
2307 if (do_normal)
2308 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002309# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002310 if (do_menu)
2311 {
2312# ifdef FONTSET_ALWAYS
2313 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2314# else
2315 // YIKES! This is a bug waiting to crash the program
2316 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2317# endif
2318 gui_mch_new_menu_font();
2319 }
2320# ifdef FEAT_BEVAL_GUI
2321 if (do_tooltip)
2322 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002323 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002324 // displaying a single font and a fontset.
2325 // If the XtNinternational resource is set to True at widget
2326 // creation, then a fontset is always used, otherwise an
2327 // XFontStruct is used.
2328 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2329 gui_mch_new_tooltip_font();
2330 }
2331# endif
2332# endif
2333 }
2334 else
2335# endif
2336 {
2337 if (free_font)
2338 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2339 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2340 // If it worked and it's the Normal group, use it as the
2341 // normal font. Same for the Menu group.
2342 if (HL_TABLE()[idx].sg_font != NOFONT)
2343 {
2344 if (do_normal)
2345 gui_init_font(arg, FALSE);
2346#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002347# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002348 if (do_menu)
2349 {
2350 gui.menu_font = HL_TABLE()[idx].sg_font;
2351 gui_mch_new_menu_font();
2352 }
2353# endif
2354#endif
2355 }
2356 }
2357}
2358
2359#endif // FEAT_GUI
2360
2361#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2362/*
2363 * Return the handle for a color name.
2364 * Returns INVALCOLOR when failed.
2365 */
2366 guicolor_T
2367color_name2handle(char_u *name)
2368{
2369 if (STRCMP(name, "NONE") == 0)
2370 return INVALCOLOR;
2371
2372 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2373 {
2374#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2375 if (gui.in_use)
2376#endif
2377#ifdef FEAT_GUI
2378 return gui.norm_pixel;
2379#endif
2380#ifdef FEAT_TERMGUICOLORS
2381 if (cterm_normal_fg_gui_color != INVALCOLOR)
2382 return cterm_normal_fg_gui_color;
2383 // Guess that the foreground is black or white.
2384 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2385#endif
2386 }
2387 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2388 {
2389#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2390 if (gui.in_use)
2391#endif
2392#ifdef FEAT_GUI
2393 return gui.back_pixel;
2394#endif
2395#ifdef FEAT_TERMGUICOLORS
2396 if (cterm_normal_bg_gui_color != INVALCOLOR)
2397 return cterm_normal_bg_gui_color;
2398 // Guess that the background is white or black.
2399 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2400#endif
2401 }
2402
2403 return GUI_GET_COLOR(name);
2404}
Drew Vogele30d1022021-10-24 20:35:07 +01002405
2406// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2407// values as used by the MS-Windows GDI api. It should be used only for
2408// MS-Windows GDI builds.
2409# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2410# undef RGB
2411# endif
2412# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002413# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002414# endif
2415
2416# ifdef VIMDLL
2417 static guicolor_T
2418gui_adjust_rgb(guicolor_T c)
2419{
2420 if (gui.in_use)
2421 return c;
2422 else
2423 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2424}
2425# else
2426# define gui_adjust_rgb(c) (c)
2427# endif
2428
2429 static int
2430hex_digit(int c)
2431{
Keith Thompson184f71c2024-01-04 21:19:04 +01002432 if (SAFE_isdigit(c))
Drew Vogele30d1022021-10-24 20:35:07 +01002433 return c - '0';
2434 c = TOLOWER_ASC(c);
2435 if (c >= 'a' && c <= 'f')
2436 return c - 'a' + 10;
2437 return 0x1ffffff;
2438}
2439
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002440 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002441decode_hex_color(char_u *hex)
2442{
2443 guicolor_T color;
2444
2445 if (hex[0] != '#' || STRLEN(hex) != 7)
2446 return INVALCOLOR;
2447
2448 // Name is in "#rrggbb" format
2449 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2450 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2451 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2452 if (color > 0xffffff)
2453 return INVALCOLOR;
2454 return gui_adjust_rgb(color);
2455}
2456
Bram Moolenaar2a521962021-10-25 10:30:14 +01002457#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002458// Returns the color currently mapped to the given name or INVALCOLOR if no
2459// such name exists in the color table. The convention is to use lowercase for
2460// all keys in the v:colornames dictionary. The value can be either a string in
2461// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002462 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002463colorname2rgb(char_u *name)
2464{
2465 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2466 char_u *lc_name;
2467 dictitem_T *colentry;
2468 char_u *colstr;
2469 varnumber_T colnum;
2470
2471 lc_name = strlow_save(name);
2472 if (lc_name == NULL)
2473 return INVALCOLOR;
2474
2475 colentry = dict_find(colornames_table, lc_name, -1);
2476 vim_free(lc_name);
2477 if (colentry == NULL)
2478 return INVALCOLOR;
2479
2480 if (colentry->di_tv.v_type == VAR_STRING)
2481 {
2482 colstr = tv_get_string_strict(&colentry->di_tv);
2483 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2484 {
2485 return decode_hex_color(colstr);
2486 }
2487 else
2488 {
2489 semsg(_(e_bad_color_string_str), colstr);
2490 return INVALCOLOR;
2491 }
2492 }
2493
2494 if (colentry->di_tv.v_type == VAR_NUMBER)
2495 {
2496 colnum = tv_get_number(&colentry->di_tv);
2497 return (guicolor_T)colnum;
2498 }
2499
2500 return INVALCOLOR;
2501}
2502
Drew Vogele30d1022021-10-24 20:35:07 +01002503#endif
2504
2505 guicolor_T
2506gui_get_color_cmn(char_u *name)
2507{
Drew Vogele30d1022021-10-24 20:35:07 +01002508 guicolor_T color;
Drew Vogele30d1022021-10-24 20:35:07 +01002509 // Only non X11 colors (not present in rgb.txt) and colors in
John Marriott34f00dd2024-04-08 23:28:12 +02002510 // color_name_tab[], useful when $VIMRUNTIME is not found,.
2511 // must be sorted by the 'value' field because it is used by bsearch()!
2512 static keyvalue_T rgb_tab[] = {
2513 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x00), "black"),
2514 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0xFF), "blue"),
2515 KEYVALUE_ENTRY(RGB(0xA5, 0x2A, 0x2A), "brown"),
2516 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0xFF), "cyan"),
2517 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x8B), "darkblue"),
2518 KEYVALUE_ENTRY(RGB(0x00, 0x8B, 0x8B), "darkcyan"),
2519 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgray"),
2520 KEYVALUE_ENTRY(RGB(0x00, 0x64, 0x00), "darkgreen"),
2521 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgrey"),
2522 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x8B), "darkmagenta"),
2523 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x00), "darkred"),
2524 KEYVALUE_ENTRY(RGB(0x8B, 0x8B, 0x00), "darkyellow"), // No X11
2525 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "gray"),
2526 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0x00), "green"),
2527 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "grey"),
2528 KEYVALUE_ENTRY(RGB(0x66, 0x66, 0x66), "grey40"),
2529 KEYVALUE_ENTRY(RGB(0x7F, 0x7F, 0x7F), "grey50"),
2530 KEYVALUE_ENTRY(RGB(0xE5, 0xE5, 0xE5), "grey90"),
2531 KEYVALUE_ENTRY(RGB(0xAD, 0xD8, 0xE6), "lightblue"),
2532 KEYVALUE_ENTRY(RGB(0xE0, 0xFF, 0xFF), "lightcyan"),
2533 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgray"),
2534 KEYVALUE_ENTRY(RGB(0x90, 0xEE, 0x90), "lightgreen"),
2535 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgrey"),
2536 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0xFF), "lightmagenta"), // No XX
2537 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0x8B), "lightred"), // No XX
2538 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xE0), "lightyellow"),
2539 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0xFF), "magenta"),
2540 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0x00), "red"),
2541 KEYVALUE_ENTRY(RGB(0x2E, 0x8B, 0x57), "seagreen"),
2542 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xFF), "white"),
2543 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0x00), "yellow")
Drew Vogele30d1022021-10-24 20:35:07 +01002544 };
John Marriott34f00dd2024-04-08 23:28:12 +02002545 keyvalue_T target;
2546 keyvalue_T *entry;
Drew Vogele30d1022021-10-24 20:35:07 +01002547
2548 color = decode_hex_color(name);
2549 if (color != INVALCOLOR)
2550 return color;
2551
John Marriott34f00dd2024-04-08 23:28:12 +02002552 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01002553 target.value.string = name;
2554 target.value.length = 0; // not used, see cmp_keyvalue_value_i()
2555 entry = (keyvalue_T *)bsearch(&target, &rgb_tab, ARRAY_LENGTH(rgb_tab),
2556 sizeof(rgb_tab[0]), cmp_keyvalue_value_i);
John Marriott34f00dd2024-04-08 23:28:12 +02002557 if (entry != NULL)
John Marriott34f00dd2024-04-08 23:28:12 +02002558 return gui_adjust_rgb((guicolor_T)entry->key);
Drew Vogele30d1022021-10-24 20:35:07 +01002559
2560#if defined(FEAT_EVAL)
2561 /*
2562 * Not a traditional color. Load additional color aliases and then consult the alias table.
2563 */
2564
2565 color = colorname2rgb(name);
2566 if (color == INVALCOLOR)
2567 {
2568 load_default_colors_lists();
2569 color = colorname2rgb(name);
2570 }
2571
2572 return color;
2573#else
2574 return INVALCOLOR;
2575#endif
2576}
2577
2578 guicolor_T
2579gui_get_rgb_color_cmn(int r, int g, int b)
2580{
2581 guicolor_T color = RGB(r, g, b);
2582
2583 if (color > 0xffffff)
2584 return INVALCOLOR;
2585 return gui_adjust_rgb(color);
2586}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002587#endif
2588
2589/*
2590 * Table with the specifications for an attribute number.
2591 * Note that this table is used by ALL buffers. This is required because the
2592 * GUI can redraw at any time for any buffer.
2593 */
2594static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2595
2596#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2597
2598static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2599
2600#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2601
2602#ifdef FEAT_GUI
2603static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2604
2605#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2606#endif
2607
2608/*
2609 * Return the attr number for a set of colors and font.
2610 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2611 * if the combination is new.
2612 * Return 0 for error (no more room).
2613 */
2614 static int
2615get_attr_entry(garray_T *table, attrentry_T *aep)
2616{
2617 int i;
2618 attrentry_T *taep;
2619 static int recursive = FALSE;
2620
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002621 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002622 table->ga_itemsize = sizeof(attrentry_T);
2623 table->ga_growsize = 7;
2624
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002625 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002626 for (i = 0; i < table->ga_len; ++i)
2627 {
2628 taep = &(((attrentry_T *)table->ga_data)[i]);
2629 if ( aep->ae_attr == taep->ae_attr
2630 && (
2631#ifdef FEAT_GUI
2632 (table == &gui_attr_table
2633 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2634 && aep->ae_u.gui.bg_color
2635 == taep->ae_u.gui.bg_color
2636 && aep->ae_u.gui.sp_color
2637 == taep->ae_u.gui.sp_color
2638 && aep->ae_u.gui.font == taep->ae_u.gui.font
2639# ifdef FEAT_XFONTSET
2640 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2641# endif
2642 ))
2643 ||
2644#endif
2645 (table == &term_attr_table
2646 && (aep->ae_u.term.start == NULL)
2647 == (taep->ae_u.term.start == NULL)
2648 && (aep->ae_u.term.start == NULL
2649 || STRCMP(aep->ae_u.term.start,
2650 taep->ae_u.term.start) == 0)
2651 && (aep->ae_u.term.stop == NULL)
2652 == (taep->ae_u.term.stop == NULL)
2653 && (aep->ae_u.term.stop == NULL
2654 || STRCMP(aep->ae_u.term.stop,
2655 taep->ae_u.term.stop) == 0))
2656 || (table == &cterm_attr_table
2657 && aep->ae_u.cterm.fg_color
2658 == taep->ae_u.cterm.fg_color
2659 && aep->ae_u.cterm.bg_color
2660 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002661 && aep->ae_u.cterm.ul_color
2662 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002663 && aep->ae_u.cterm.font
2664 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002665#ifdef FEAT_TERMGUICOLORS
2666 && aep->ae_u.cterm.fg_rgb
2667 == taep->ae_u.cterm.fg_rgb
2668 && aep->ae_u.cterm.bg_rgb
2669 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002670 && aep->ae_u.cterm.ul_rgb
2671 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002672#endif
2673 )))
2674
2675 return i + ATTR_OFF;
2676 }
2677
2678 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2679 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002680 // Running out of attribute entries! remove all attributes, and
2681 // compute new ones for all groups.
2682 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002683 if (recursive)
2684 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002685 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002686 return 0;
2687 }
2688 recursive = TRUE;
2689
2690 clear_hl_tables();
2691
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002692 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002693
2694 for (i = 0; i < highlight_ga.ga_len; ++i)
2695 set_hl_attr(i);
2696
2697 recursive = FALSE;
2698 }
2699
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002700 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002701 if (ga_grow(table, 1) == FAIL)
2702 return 0;
2703
2704 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002705 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002706 taep->ae_attr = aep->ae_attr;
2707#ifdef FEAT_GUI
2708 if (table == &gui_attr_table)
2709 {
2710 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2711 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2712 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2713 taep->ae_u.gui.font = aep->ae_u.gui.font;
2714# ifdef FEAT_XFONTSET
2715 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2716# endif
2717 }
2718#endif
2719 if (table == &term_attr_table)
2720 {
2721 if (aep->ae_u.term.start == NULL)
2722 taep->ae_u.term.start = NULL;
2723 else
2724 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2725 if (aep->ae_u.term.stop == NULL)
2726 taep->ae_u.term.stop = NULL;
2727 else
2728 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2729 }
2730 else if (table == &cterm_attr_table)
2731 {
2732 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2733 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002734 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002735 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002736#ifdef FEAT_TERMGUICOLORS
2737 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2738 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002739 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002740#endif
2741 }
2742 ++table->ga_len;
2743 return (table->ga_len - 1 + ATTR_OFF);
2744}
2745
2746#if defined(FEAT_TERMINAL) || defined(PROTO)
2747/*
2748 * Get an attribute index for a cterm entry.
2749 * Uses an existing entry when possible or adds one when needed.
2750 */
2751 int
2752get_cterm_attr_idx(int attr, int fg, int bg)
2753{
2754 attrentry_T at_en;
2755
Bram Moolenaara80faa82020-04-12 19:37:17 +02002756 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002757#ifdef FEAT_TERMGUICOLORS
2758 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2759 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002760 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002761#endif
2762 at_en.ae_attr = attr;
2763 at_en.ae_u.cterm.fg_color = fg;
2764 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002765 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002766 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002767 return get_attr_entry(&cterm_attr_table, &at_en);
2768}
2769#endif
2770
2771#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2772/*
2773 * Get an attribute index for a 'termguicolors' entry.
2774 * Uses an existing entry when possible or adds one when needed.
2775 */
2776 int
2777get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2778{
2779 attrentry_T at_en;
2780
Bram Moolenaara80faa82020-04-12 19:37:17 +02002781 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002782 at_en.ae_attr = attr;
2783 if (fg == INVALCOLOR && bg == INVALCOLOR)
2784 {
2785 // If both GUI colors are not set fall back to the cterm colors. Helps
2786 // if the GUI only has an attribute, such as undercurl.
2787 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2788 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2789 }
2790 else
2791 {
2792 at_en.ae_u.cterm.fg_rgb = fg;
2793 at_en.ae_u.cterm.bg_rgb = bg;
2794 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002795 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002796 return get_attr_entry(&cterm_attr_table, &at_en);
2797}
2798#endif
2799
2800#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2801/*
2802 * Get an attribute index for a cterm entry.
2803 * Uses an existing entry when possible or adds one when needed.
2804 */
2805 int
2806get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2807{
2808 attrentry_T at_en;
2809
Bram Moolenaara80faa82020-04-12 19:37:17 +02002810 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002811 at_en.ae_attr = attr;
2812 at_en.ae_u.gui.fg_color = fg;
2813 at_en.ae_u.gui.bg_color = bg;
2814 return get_attr_entry(&gui_attr_table, &at_en);
2815}
2816#endif
2817
2818/*
2819 * Clear all highlight tables.
2820 */
2821 void
2822clear_hl_tables(void)
2823{
2824 int i;
2825 attrentry_T *taep;
2826
2827#ifdef FEAT_GUI
2828 ga_clear(&gui_attr_table);
2829#endif
2830 for (i = 0; i < term_attr_table.ga_len; ++i)
2831 {
2832 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2833 vim_free(taep->ae_u.term.start);
2834 vim_free(taep->ae_u.term.stop);
2835 }
2836 ga_clear(&term_attr_table);
2837 ga_clear(&cterm_attr_table);
2838}
2839
2840/*
2841 * Combine special attributes (e.g., for spelling) with other attributes
2842 * (e.g., for syntax highlighting).
2843 * "prim_attr" overrules "char_attr".
2844 * This creates a new group when required.
2845 * Since we expect there to be few spelling mistakes we don't cache the
2846 * result.
2847 * Return the resulting attributes.
2848 */
2849 int
2850hl_combine_attr(int char_attr, int prim_attr)
2851{
2852 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002853 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002854 attrentry_T new_en;
2855
2856 if (char_attr == 0)
2857 return prim_attr;
2858 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2859 return ATTR_COMBINE(char_attr, prim_attr);
2860#ifdef FEAT_GUI
2861 if (gui.in_use)
2862 {
2863 if (char_attr > HL_ALL)
2864 char_aep = syn_gui_attr2entry(char_attr);
2865 if (char_aep != NULL)
2866 new_en = *char_aep;
2867 else
2868 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002869 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002870 new_en.ae_u.gui.fg_color = INVALCOLOR;
2871 new_en.ae_u.gui.bg_color = INVALCOLOR;
2872 new_en.ae_u.gui.sp_color = INVALCOLOR;
2873 if (char_attr <= HL_ALL)
2874 new_en.ae_attr = char_attr;
2875 }
2876
2877 if (prim_attr <= HL_ALL)
2878 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2879 else
2880 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002881 prim_aep = syn_gui_attr2entry(prim_attr);
2882 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002883 {
2884 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002885 prim_aep->ae_attr);
2886 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2887 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2888 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2889 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2890 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2891 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2892 if (prim_aep->ae_u.gui.font != NOFONT)
2893 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002894# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002895 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2896 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002897# endif
2898 }
2899 }
2900 return get_attr_entry(&gui_attr_table, &new_en);
2901 }
2902#endif
2903
2904 if (IS_CTERM)
2905 {
2906 if (char_attr > HL_ALL)
2907 char_aep = syn_cterm_attr2entry(char_attr);
2908 if (char_aep != NULL)
2909 new_en = *char_aep;
2910 else
2911 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002912 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002913#ifdef FEAT_TERMGUICOLORS
2914 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2915 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002916 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002917#endif
2918 if (char_attr <= HL_ALL)
2919 new_en.ae_attr = char_attr;
2920 }
2921
2922 if (prim_attr <= HL_ALL)
2923 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2924 else
2925 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002926 prim_aep = syn_cterm_attr2entry(prim_attr);
2927 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002928 {
2929 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002930 prim_aep->ae_attr);
2931 if (prim_aep->ae_u.cterm.fg_color > 0)
2932 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2933 if (prim_aep->ae_u.cterm.bg_color > 0)
2934 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2935 if (prim_aep->ae_u.cterm.ul_color > 0)
2936 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002937 if (prim_aep->ae_u.cterm.font > 0)
2938 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002939#ifdef FEAT_TERMGUICOLORS
2940 // If both fg and bg are not set fall back to cterm colors.
2941 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002942 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2943 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002944 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002945 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002946 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002947 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002948 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2949 }
2950 else
2951 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002952 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2953 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2954 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2955 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002956 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002957 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2958 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002959#endif
2960 }
2961 }
2962 return get_attr_entry(&cterm_attr_table, &new_en);
2963 }
2964
2965 if (char_attr > HL_ALL)
2966 char_aep = syn_term_attr2entry(char_attr);
2967 if (char_aep != NULL)
2968 new_en = *char_aep;
2969 else
2970 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002971 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002972 if (char_attr <= HL_ALL)
2973 new_en.ae_attr = char_attr;
2974 }
2975
2976 if (prim_attr <= HL_ALL)
2977 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2978 else
2979 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002980 prim_aep = syn_term_attr2entry(prim_attr);
2981 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002982 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002983 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2984 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002985 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002986 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2987 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002988 }
2989 }
2990 }
2991 return get_attr_entry(&term_attr_table, &new_en);
2992}
2993
2994#ifdef FEAT_GUI
2995 attrentry_T *
2996syn_gui_attr2entry(int attr)
2997{
2998 attr -= ATTR_OFF;
2999 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
3000 return NULL;
3001 return &(GUI_ATTR_ENTRY(attr));
3002}
3003#endif
3004
3005/*
3006 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
3007 * Only to be used when "attr" > HL_ALL.
3008 */
3009 int
3010syn_attr2attr(int attr)
3011{
3012 attrentry_T *aep;
3013
3014#ifdef FEAT_GUI
3015 if (gui.in_use)
3016 aep = syn_gui_attr2entry(attr);
3017 else
3018#endif
3019 if (IS_CTERM)
3020 aep = syn_cterm_attr2entry(attr);
3021 else
3022 aep = syn_term_attr2entry(attr);
3023
3024 if (aep == NULL) // highlighting not set
3025 return 0;
3026 return aep->ae_attr;
3027}
3028
3029
3030 attrentry_T *
3031syn_term_attr2entry(int attr)
3032{
3033 attr -= ATTR_OFF;
3034 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
3035 return NULL;
3036 return &(TERM_ATTR_ENTRY(attr));
3037}
3038
3039 attrentry_T *
3040syn_cterm_attr2entry(int attr)
3041{
3042 attr -= ATTR_OFF;
3043 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
3044 return NULL;
3045 return &(CTERM_ATTR_ENTRY(attr));
3046}
3047
3048#define LIST_ATTR 1
3049#define LIST_STRING 2
3050#define LIST_INT 3
3051
3052 static void
3053highlight_list_one(int id)
3054{
3055 hl_group_T *sgp;
3056 int didh = FALSE;
3057
3058 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
3059
3060 if (message_filtered(sgp->sg_name))
3061 return;
3062
Yee Cheng China7b81202025-02-23 09:32:47 +01003063 // Note: Keep this in sync with expand_highlight_group().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003064 didh = highlight_list_arg(id, didh, LIST_ATTR,
3065 sgp->sg_term, NULL, "term");
3066 didh = highlight_list_arg(id, didh, LIST_STRING,
3067 0, sgp->sg_start, "start");
3068 didh = highlight_list_arg(id, didh, LIST_STRING,
3069 0, sgp->sg_stop, "stop");
3070
3071 didh = highlight_list_arg(id, didh, LIST_ATTR,
3072 sgp->sg_cterm, NULL, "cterm");
3073 didh = highlight_list_arg(id, didh, LIST_INT,
3074 sgp->sg_cterm_fg, NULL, "ctermfg");
3075 didh = highlight_list_arg(id, didh, LIST_INT,
3076 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02003077 didh = highlight_list_arg(id, didh, LIST_INT,
3078 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01003079 didh = highlight_list_arg(id, didh, LIST_INT,
3080 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003081
3082#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3083 didh = highlight_list_arg(id, didh, LIST_ATTR,
3084 sgp->sg_gui, NULL, "gui");
3085 didh = highlight_list_arg(id, didh, LIST_STRING,
3086 0, sgp->sg_gui_fg_name, "guifg");
3087 didh = highlight_list_arg(id, didh, LIST_STRING,
3088 0, sgp->sg_gui_bg_name, "guibg");
3089 didh = highlight_list_arg(id, didh, LIST_STRING,
3090 0, sgp->sg_gui_sp_name, "guisp");
3091#endif
3092#ifdef FEAT_GUI
3093 didh = highlight_list_arg(id, didh, LIST_STRING,
3094 0, sgp->sg_font_name, "font");
3095#endif
3096
3097 if (sgp->sg_link && !got_int)
3098 {
3099 (void)syn_list_header(didh, 9999, id);
3100 didh = TRUE;
3101 msg_puts_attr("links to", HL_ATTR(HLF_D));
3102 msg_putchar(' ');
3103 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3104 }
3105
3106 if (!didh)
3107 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3108#ifdef FEAT_EVAL
3109 if (p_verbose > 0)
3110 last_set_msg(sgp->sg_script_ctx);
3111#endif
3112}
3113
Yee Cheng China7b81202025-02-23 09:32:47 +01003114 static char_u*
3115highlight_arg_to_string(
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003116 int type,
3117 int iarg,
3118 char_u *sarg,
Yee Cheng China7b81202025-02-23 09:32:47 +01003119 char_u *buf)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003120{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003121 if (type == LIST_INT)
3122 sprintf((char *)buf, "%d", iarg - 1);
3123 else if (type == LIST_STRING)
Yee Cheng China7b81202025-02-23 09:32:47 +01003124 return sarg;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003125 else // type == LIST_ATTR
3126 {
John Marriott34f00dd2024-04-08 23:28:12 +02003127 size_t buflen;
3128
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003129 buf[0] = NUL;
John Marriott34f00dd2024-04-08 23:28:12 +02003130 buflen = 0;
Yee Cheng China7b81202025-02-23 09:32:47 +01003131 for (int i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003132 {
John Marriott34f00dd2024-04-08 23:28:12 +02003133 if (iarg & highlight_index_tab[i]->key)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003134 {
John Marriott34f00dd2024-04-08 23:28:12 +02003135 if (buflen > 0)
3136 {
3137 STRCPY(buf + buflen, (char_u *)",");
3138 ++buflen;
3139 }
John Marriott8d4477e2024-11-02 15:59:01 +01003140 STRCPY(buf + buflen, highlight_index_tab[i]->value.string);
3141 buflen += highlight_index_tab[i]->value.length;
John Marriott34f00dd2024-04-08 23:28:12 +02003142 iarg &= ~highlight_index_tab[i]->key; // don't want "inverse"/"reverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003143 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003144 }
3145 }
Yee Cheng China7b81202025-02-23 09:32:47 +01003146 return buf;
3147}
3148
3149 static int
3150highlight_list_arg(
3151 int id,
3152 int didh,
3153 int type,
3154 int iarg,
3155 char_u *sarg,
3156 char *name)
3157{
3158 char_u buf[MAX_ATTR_LEN];
3159 char_u *ts;
3160
3161 if (got_int)
3162 return FALSE;
3163
3164 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3165 return didh;
3166
3167 ts = highlight_arg_to_string(type, iarg, sarg, buf);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003168
3169 (void)syn_list_header(didh,
3170 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3171 didh = TRUE;
3172 if (!got_int)
3173 {
3174 if (*name != NUL)
3175 {
3176 msg_puts_attr(name, HL_ATTR(HLF_D));
3177 msg_puts_attr("=", HL_ATTR(HLF_D));
3178 }
3179 msg_outtrans(ts);
3180 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003181 return didh;
3182}
3183
3184#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3185/*
3186 * Return "1" if highlight group "id" has attribute "flag".
3187 * Return NULL otherwise.
3188 */
3189 char_u *
3190highlight_has_attr(
3191 int id,
3192 int flag,
3193 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3194{
3195 int attr;
3196
3197 if (id <= 0 || id > highlight_ga.ga_len)
3198 return NULL;
3199
3200#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3201 if (modec == 'g')
3202 attr = HL_TABLE()[id - 1].sg_gui;
3203 else
3204#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003205 {
3206 if (modec == 'c')
3207 attr = HL_TABLE()[id - 1].sg_cterm;
3208 else
3209 attr = HL_TABLE()[id - 1].sg_term;
3210 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003211
3212 if (attr & flag)
3213 return (char_u *)"1";
3214 return NULL;
3215}
3216#endif
3217
3218#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3219/*
3220 * Return color name of highlight group "id".
3221 */
3222 char_u *
3223highlight_color(
3224 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003225 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003226 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3227{
3228 static char_u name[20];
3229 int n;
3230 int fg = FALSE;
3231 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003232 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003233 int font = FALSE;
3234
3235 if (id <= 0 || id > highlight_ga.ga_len)
3236 return NULL;
3237
3238 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3239 fg = TRUE;
3240 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3241 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3242 font = TRUE;
3243 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3244 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003245 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3246 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003247 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3248 return NULL;
3249 if (modec == 'g')
3250 {
3251# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3252# ifdef FEAT_GUI
3253 // return font name
3254 if (font)
3255 return HL_TABLE()[id - 1].sg_font_name;
3256# endif
3257
3258 // return #RRGGBB form (only possible when GUI is running)
3259 if ((USE_24BIT) && what[2] == '#')
3260 {
3261 guicolor_T color;
3262 long_u rgb;
3263 static char_u buf[10];
3264
3265 if (fg)
3266 color = HL_TABLE()[id - 1].sg_gui_fg;
3267 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003268 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003269 else
3270 color = HL_TABLE()[id - 1].sg_gui_bg;
3271 if (color == INVALCOLOR)
3272 return NULL;
3273 rgb = (long_u)GUI_MCH_GET_RGB(color);
3274 sprintf((char *)buf, "#%02x%02x%02x",
3275 (unsigned)(rgb >> 16),
3276 (unsigned)(rgb >> 8) & 255,
3277 (unsigned)rgb & 255);
3278 return buf;
3279 }
3280# endif
3281 if (fg)
3282 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3283 if (sp)
3284 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3285 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3286 }
PMuncha606f3a2023-11-15 15:35:49 +01003287 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003288 return NULL;
3289 if (modec == 'c')
3290 {
3291 if (fg)
3292 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003293 else if (ul)
3294 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003295 else if (font)
3296 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003297 else
3298 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3299 if (n < 0)
3300 return NULL;
3301 sprintf((char *)name, "%d", n);
3302 return name;
3303 }
3304 // term doesn't have color
3305 return NULL;
3306}
3307#endif
3308
3309#if (defined(FEAT_SYN_HL) \
3310 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3311 && defined(FEAT_PRINTER)) || defined(PROTO)
3312/*
3313 * Return color name of highlight group "id" as RGB value.
3314 */
3315 long_u
3316highlight_gui_color_rgb(
3317 int id,
3318 int fg) // TRUE = fg, FALSE = bg
3319{
3320 guicolor_T color;
3321
3322 if (id <= 0 || id > highlight_ga.ga_len)
3323 return 0L;
3324
3325 if (fg)
3326 color = HL_TABLE()[id - 1].sg_gui_fg;
3327 else
3328 color = HL_TABLE()[id - 1].sg_gui_bg;
3329
3330 if (color == INVALCOLOR)
3331 return 0L;
3332
3333 return GUI_MCH_GET_RGB(color);
3334}
3335#endif
3336
3337/*
3338 * Output the syntax list header.
3339 * Return TRUE when started a new line.
3340 */
3341 int
3342syn_list_header(
3343 int did_header, // did header already
3344 int outlen, // length of string that comes
3345 int id) // highlight group id
3346{
3347 int endcol = 19;
3348 int newline = TRUE;
3349 int name_col = 0;
3350
3351 if (!did_header)
3352 {
3353 msg_putchar('\n');
3354 if (got_int)
3355 return TRUE;
3356 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3357 name_col = msg_col;
3358 endcol = 15;
3359 }
3360 else if (msg_col + outlen + 1 >= Columns)
3361 {
3362 msg_putchar('\n');
3363 if (got_int)
3364 return TRUE;
3365 }
3366 else
3367 {
3368 if (msg_col >= endcol) // wrap around is like starting a new line
3369 newline = FALSE;
3370 }
3371
3372 if (msg_col >= endcol) // output at least one space
3373 endcol = msg_col + 1;
Christian Brabandt220474d2024-07-20 13:26:44 +02003374 if (Columns <= (long)endcol) // avoid hang for tiny window
3375 endcol = (int)(Columns - 1);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003376
3377 msg_advance(endcol);
3378
3379 // Show "xxx" with the attributes.
3380 if (!did_header)
3381 {
3382 if (endcol == Columns - 1 && endcol <= name_col)
3383 msg_putchar(' ');
3384 msg_puts_attr("xxx", syn_id2attr(id));
3385 msg_putchar(' ');
3386 }
3387
3388 return newline;
3389}
3390
3391/*
3392 * Set the attribute numbers for a highlight group.
3393 * Called after one of the attributes has changed.
3394 */
3395 static void
3396set_hl_attr(
3397 int idx) // index in array
3398{
3399 attrentry_T at_en;
3400 hl_group_T *sgp = HL_TABLE() + idx;
3401
3402 // The "Normal" group doesn't need an attribute number
3403 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3404 return;
3405
3406#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003407 // For the GUI mode: If there are other than "normal" highlighting
3408 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003409 if (sgp->sg_gui_fg == INVALCOLOR
3410 && sgp->sg_gui_bg == INVALCOLOR
3411 && sgp->sg_gui_sp == INVALCOLOR
3412 && sgp->sg_font == NOFONT
3413# ifdef FEAT_XFONTSET
3414 && sgp->sg_fontset == NOFONTSET
3415# endif
3416 )
3417 {
3418 sgp->sg_gui_attr = sgp->sg_gui;
3419 }
3420 else
3421 {
3422 at_en.ae_attr = sgp->sg_gui;
3423 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3424 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3425 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3426 at_en.ae_u.gui.font = sgp->sg_font;
3427# ifdef FEAT_XFONTSET
3428 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3429# endif
3430 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3431 }
3432#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003433 // For the term mode: If there are other than "normal" highlighting
3434 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003435 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3436 sgp->sg_term_attr = sgp->sg_term;
3437 else
3438 {
3439 at_en.ae_attr = sgp->sg_term;
3440 at_en.ae_u.term.start = sgp->sg_start;
3441 at_en.ae_u.term.stop = sgp->sg_stop;
3442 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3443 }
3444
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003445 // For the color term mode: If there are other than "normal"
3446 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003447 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3448 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003449# ifdef FEAT_TERMGUICOLORS
3450 && sgp->sg_gui_fg == INVALCOLOR
3451 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003452 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003453# endif
3454 )
3455 sgp->sg_cterm_attr = sgp->sg_cterm;
3456 else
3457 {
3458 at_en.ae_attr = sgp->sg_cterm;
3459 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3460 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003461 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003462 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003463# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003464 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3465 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003466 // Only use the underline/undercurl color when used, it may clear the
3467 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003468 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3469 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003470 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3471 else
3472 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003473 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3474 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3475 {
3476 // If both fg and bg are invalid fall back to the cterm colors.
3477 // Helps when the GUI only uses an attribute, e.g. undercurl.
3478 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3479 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3480 }
3481# endif
3482 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3483 }
3484}
3485
3486/*
3487 * Lookup a highlight group name and return its ID.
3488 * If it is not found, 0 is returned.
3489 */
3490 int
3491syn_name2id(char_u *name)
3492{
3493 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003494 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003495
3496 // Avoid using stricmp() too much, it's slow on some systems
3497 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3498 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003499 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003500 vim_strup(name_u);
3501 for (i = highlight_ga.ga_len; --i >= 0; )
3502 if (HL_TABLE()[i].sg_name_u != NULL
3503 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3504 break;
3505 return i + 1;
3506}
3507
3508/*
3509 * Lookup a highlight group name and return its attributes.
3510 * Return zero if not found.
3511 */
3512 int
3513syn_name2attr(char_u *name)
3514{
3515 int id = syn_name2id(name);
3516
3517 if (id != 0)
3518 return syn_id2attr(id);
3519 return 0;
3520}
3521
3522#if defined(FEAT_EVAL) || defined(PROTO)
3523/*
3524 * Return TRUE if highlight group "name" exists.
3525 */
3526 int
3527highlight_exists(char_u *name)
3528{
3529 return (syn_name2id(name) > 0);
3530}
3531
3532# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3533/*
3534 * Return the name of highlight group "id".
3535 * When not a valid ID return an empty string.
3536 */
3537 char_u *
3538syn_id2name(int id)
3539{
3540 if (id <= 0 || id > highlight_ga.ga_len)
3541 return (char_u *)"";
3542 return HL_TABLE()[id - 1].sg_name;
3543}
3544# endif
3545#endif
3546
3547/*
3548 * Like syn_name2id(), but take a pointer + length argument.
3549 */
3550 int
3551syn_namen2id(char_u *linep, int len)
3552{
3553 char_u *name;
3554 int id = 0;
3555
3556 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003557 if (name == NULL)
3558 return 0;
3559
3560 id = syn_name2id(name);
3561 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003562 return id;
3563}
3564
3565/*
3566 * Find highlight group name in the table and return its ID.
3567 * The argument is a pointer to the name and the length of the name.
3568 * If it doesn't exist yet, a new entry is created.
3569 * Return 0 for failure.
3570 */
3571 int
3572syn_check_group(char_u *pp, int len)
3573{
3574 int id;
3575 char_u *name;
3576
erw7f7f7aaf2021-12-07 21:29:20 +00003577 if (len > MAX_SYN_NAME)
3578 {
3579 emsg(_(e_highlight_group_name_too_long));
3580 return 0;
3581 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003582 name = vim_strnsave(pp, len);
3583 if (name == NULL)
3584 return 0;
3585
3586 id = syn_name2id(name);
3587 if (id == 0) // doesn't exist yet
3588 id = syn_add_group(name);
3589 else
3590 vim_free(name);
3591 return id;
3592}
3593
3594/*
3595 * Add new highlight group and return its ID.
3596 * "name" must be an allocated string, it will be consumed.
3597 * Return 0 for failure.
3598 */
3599 static int
3600syn_add_group(char_u *name)
3601{
3602 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003603 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003604
Gregory Andersd4376dc2023-08-20 19:14:03 +02003605 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003606 for (p = name; *p != NUL; ++p)
3607 {
3608 if (!vim_isprintc(*p))
3609 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003610 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003611 vim_free(name);
3612 return 0;
3613 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003614 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003615 {
3616 // This is an error, but since there previously was no check only
3617 // give a warning.
3618 msg_source(HL_ATTR(HLF_W));
3619 msg(_("W18: Invalid character in group name"));
3620 break;
3621 }
3622 }
3623
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003624 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003625 if (highlight_ga.ga_data == NULL)
3626 {
3627 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3628 highlight_ga.ga_growsize = 10;
3629 }
3630
3631 if (highlight_ga.ga_len >= MAX_HL_ID)
3632 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003633 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003634 vim_free(name);
3635 return 0;
3636 }
3637
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003638 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003639 if (ga_grow(&highlight_ga, 1) == FAIL)
3640 {
3641 vim_free(name);
3642 return 0;
3643 }
3644
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003645 name_up = vim_strsave_up(name);
3646 if (name_up == NULL)
3647 {
3648 vim_free(name);
3649 return 0;
3650 }
3651
Bram Moolenaara80faa82020-04-12 19:37:17 +02003652 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003653 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003654 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003655#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3656 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3657 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003658 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003659#endif
3660 ++highlight_ga.ga_len;
3661
3662 return highlight_ga.ga_len; // ID is index plus one
3663}
3664
3665/*
3666 * When, just after calling syn_add_group(), an error is discovered, this
3667 * function deletes the new name.
3668 */
3669 static void
3670syn_unadd_group(void)
3671{
3672 --highlight_ga.ga_len;
3673 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3674 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3675}
3676
3677/*
3678 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003679 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003680 */
3681 int
3682syn_id2attr(int hl_id)
3683{
3684 int attr;
3685 hl_group_T *sgp;
3686
3687 hl_id = syn_get_final_id(hl_id);
3688 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3689
3690#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003691 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003692 if (gui.in_use)
3693 attr = sgp->sg_gui_attr;
3694 else
3695#endif
3696 if (IS_CTERM)
3697 attr = sgp->sg_cterm_attr;
3698 else
3699 attr = sgp->sg_term_attr;
3700
3701 return attr;
3702}
3703
3704#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3705/*
3706 * Get the GUI colors and attributes for a group ID.
3707 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3708 */
3709 int
3710syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3711{
3712 hl_group_T *sgp;
3713
3714 hl_id = syn_get_final_id(hl_id);
3715 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3716
3717 *fgp = sgp->sg_gui_fg;
3718 *bgp = sgp->sg_gui_bg;
3719 return sgp->sg_gui;
3720}
3721#endif
3722
3723#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003724 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3725 && defined(FEAT_TERMGUICOLORS)) \
3726 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003727 void
3728syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3729{
3730 hl_group_T *sgp;
3731
3732 hl_id = syn_get_final_id(hl_id);
3733 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3734 *fgp = sgp->sg_cterm_fg - 1;
3735 *bgp = sgp->sg_cterm_bg - 1;
3736}
3737#endif
3738
3739/*
3740 * Translate a group ID to the final group ID (following links).
3741 */
3742 int
3743syn_get_final_id(int hl_id)
3744{
3745 int count;
3746 hl_group_T *sgp;
3747
3748 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3749 return 0; // Can be called from eval!!
3750
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003751 // Follow links until there is no more.
3752 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003753 for (count = 100; --count >= 0; )
3754 {
3755 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3756 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3757 break;
3758 hl_id = sgp->sg_link;
3759 }
3760
3761 return hl_id;
3762}
3763
3764#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3765/*
3766 * Call this function just after the GUI has started.
3767 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3768 * It finds the font and color handles for the highlighting groups.
3769 */
3770 void
3771highlight_gui_started(void)
3772{
3773 int idx;
3774
3775 // First get the colors from the "Normal" and "Menu" group, if set
3776 if (USE_24BIT)
3777 set_normal_colors();
3778
3779 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3780 gui_do_one_color(idx, FALSE, FALSE);
3781
3782 highlight_changed();
3783}
3784
3785 static void
3786gui_do_one_color(
3787 int idx,
3788 int do_menu UNUSED, // TRUE: might set the menu font
3789 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3790{
3791 int didit = FALSE;
3792
3793# ifdef FEAT_GUI
3794# ifdef FEAT_TERMGUICOLORS
3795 if (gui.in_use)
3796# endif
3797 if (HL_TABLE()[idx].sg_font_name != NULL)
3798 {
3799 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3800 do_tooltip, TRUE);
3801 didit = TRUE;
3802 }
3803# endif
3804 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3805 {
3806 HL_TABLE()[idx].sg_gui_fg =
3807 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3808 didit = TRUE;
3809 }
3810 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3811 {
3812 HL_TABLE()[idx].sg_gui_bg =
3813 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3814 didit = TRUE;
3815 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003816 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3817 {
3818 HL_TABLE()[idx].sg_gui_sp =
3819 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3820 didit = TRUE;
3821 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003822 if (didit) // need to get a new attr number
3823 set_hl_attr(idx);
3824}
3825#endif
3826
3827#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3828/*
3829 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3830 */
3831 static void
3832combine_stl_hlt(
3833 int id,
3834 int id_S,
3835 int id_alt,
3836 int hlcnt,
3837 int i,
3838 int hlf,
3839 int *table)
3840{
3841 hl_group_T *hlt = HL_TABLE();
3842
3843 if (id_alt == 0)
3844 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003845 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003846 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3847 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3848# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3849 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3850# endif
3851 }
3852 else
3853 mch_memmove(&hlt[hlcnt + i],
3854 &hlt[id_alt - 1],
3855 sizeof(hl_group_T));
3856 hlt[hlcnt + i].sg_link = 0;
3857
3858 hlt[hlcnt + i].sg_term ^=
3859 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3860 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3861 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3862 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3863 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3864 hlt[hlcnt + i].sg_cterm ^=
3865 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3866 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3867 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3868 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3869 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003870 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3871 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003872# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3873 hlt[hlcnt + i].sg_gui ^=
3874 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3875# endif
3876# ifdef FEAT_GUI
3877 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3878 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3879 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3880 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3881 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3882 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3883 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3884 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3885# ifdef FEAT_XFONTSET
3886 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3887 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3888# endif
3889# endif
3890 highlight_ga.ga_len = hlcnt + i + 1;
3891 set_hl_attr(hlcnt + i); // At long last we can apply
3892 table[i] = syn_id2attr(hlcnt + i + 1);
3893}
3894#endif
3895
3896/*
3897 * Translate the 'highlight' option into attributes in highlight_attr[] and
3898 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3899 * corresponding highlights to use on top of HLF_SNC is computed.
3900 * Called only when the 'highlight' option has been changed and upon first
3901 * screen redraw after any :highlight command.
3902 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3903 */
3904 int
3905highlight_changed(void)
3906{
3907 int hlf;
3908 int i;
3909 char_u *p;
3910 int attr;
3911 char_u *end;
3912 int id;
3913#ifdef USER_HIGHLIGHT
3914 char_u userhl[30]; // use 30 to avoid compiler warning
3915# ifdef FEAT_STL_OPT
3916 int id_S = -1;
3917 int id_SNC = 0;
3918# ifdef FEAT_TERMINAL
3919 int id_ST = 0;
3920 int id_STNC = 0;
3921# endif
3922 int hlcnt;
3923# endif
3924#endif
3925 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3926
3927 need_highlight_changed = FALSE;
3928
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003929#ifdef FEAT_TERMINAL
3930 term_update_colors_all();
3931 term_update_wincolor_all();
3932#endif
3933
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003934 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003935 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3936 highlight_attr[hlf] = 0;
3937
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003938 // First set all attributes to their default value.
3939 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003940 for (i = 0; i < 2; ++i)
3941 {
3942 if (i)
3943 p = p_hl;
3944 else
3945 p = get_highlight_default();
3946 if (p == NULL) // just in case
3947 continue;
3948
3949 while (*p)
3950 {
3951 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3952 if (hl_flags[hlf] == *p)
3953 break;
3954 ++p;
3955 if (hlf == (int)HLF_COUNT || *p == NUL)
3956 return FAIL;
3957
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003958 // Allow several hl_flags to be combined, like "bu" for
3959 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003960 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003961 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003962 {
3963 if (VIM_ISWHITE(*p)) // ignore white space
3964 continue;
3965
3966 if (attr > HL_ALL) // Combination with ':' is not allowed.
3967 return FAIL;
3968
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003969 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003970 switch (*p)
3971 {
3972 case 'b': attr |= HL_BOLD;
3973 break;
3974 case 'i': attr |= HL_ITALIC;
3975 break;
3976 case '-':
3977 case 'n': // no highlighting
3978 break;
3979 case 'r': attr |= HL_INVERSE;
3980 break;
3981 case 's': attr |= HL_STANDOUT;
3982 break;
3983 case 'u': attr |= HL_UNDERLINE;
3984 break;
3985 case 'c': attr |= HL_UNDERCURL;
3986 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003987 case '2': attr |= HL_UNDERDOUBLE;
3988 break;
3989 case 'd': attr |= HL_UNDERDOTTED;
3990 break;
3991 case '=': attr |= HL_UNDERDASHED;
3992 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003993 case 't': attr |= HL_STRIKETHROUGH;
3994 break;
3995 case ':': ++p; // highlight group name
3996 if (attr || *p == NUL) // no combinations
3997 return FAIL;
3998 end = vim_strchr(p, ',');
3999 if (end == NULL)
4000 end = p + STRLEN(p);
4001 id = syn_check_group(p, (int)(end - p));
4002 if (id == 0)
4003 return FAIL;
4004 attr = syn_id2attr(id);
4005 p = end - 1;
4006#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
4007 if (hlf == (int)HLF_SNC)
4008 id_SNC = syn_get_final_id(id);
4009# ifdef FEAT_TERMINAL
4010 else if (hlf == (int)HLF_ST)
4011 id_ST = syn_get_final_id(id);
4012 else if (hlf == (int)HLF_STNC)
4013 id_STNC = syn_get_final_id(id);
4014# endif
4015 else if (hlf == (int)HLF_S)
4016 id_S = syn_get_final_id(id);
4017#endif
4018 break;
4019 default: return FAIL;
4020 }
4021 }
4022 highlight_attr[hlf] = attr;
4023
4024 p = skip_to_option_part(p); // skip comma and spaces
4025 }
4026 }
4027
4028#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01004029 // Setup the user highlights
4030 //
4031 // Temporarily utilize 28 more hl entries:
4032 // 9 for User1-User9 combined with StatusLineNC
4033 // 9 for User1-User9 combined with StatusLineTerm
4034 // 9 for User1-User9 combined with StatusLineTermNC
4035 // 1 for StatusLine default
4036 // Have to be in there simultaneously in case of table overflows in
4037 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004038# ifdef FEAT_STL_OPT
4039 if (ga_grow(&highlight_ga, 28) == FAIL)
4040 return FAIL;
4041 hlcnt = highlight_ga.ga_len;
4042 if (id_S == -1)
4043 {
4044 // Make sure id_S is always valid to simplify code below. Use the last
4045 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02004046 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004047 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
4048 id_S = hlcnt + 19;
4049 }
4050# endif
4051 for (i = 0; i < 9; i++)
4052 {
4053 sprintf((char *)userhl, "User%d", i + 1);
4054 id = syn_name2id(userhl);
4055 if (id == 0)
4056 {
4057 highlight_user[i] = 0;
4058# ifdef FEAT_STL_OPT
4059 highlight_stlnc[i] = 0;
4060# ifdef FEAT_TERMINAL
4061 highlight_stlterm[i] = 0;
4062 highlight_stltermnc[i] = 0;
4063# endif
4064# endif
4065 }
4066 else
4067 {
4068 highlight_user[i] = syn_id2attr(id);
4069# ifdef FEAT_STL_OPT
4070 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
4071 HLF_SNC, highlight_stlnc);
4072# ifdef FEAT_TERMINAL
4073 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
4074 HLF_ST, highlight_stlterm);
4075 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
4076 HLF_STNC, highlight_stltermnc);
4077# endif
4078# endif
4079 }
4080 }
4081# ifdef FEAT_STL_OPT
4082 highlight_ga.ga_len = hlcnt;
4083# endif
4084
4085#endif // USER_HIGHLIGHT
4086
4087 return OK;
4088}
4089
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004090static void highlight_list(void);
4091static void highlight_list_two(int cnt, int attr);
4092
Yee Cheng China7b81202025-02-23 09:32:47 +01004093// context for :highlight <group> <arg> expansion
4094static int expand_hi_synid = 0; // ID for highlight group being completed
4095static int expand_hi_equal_col = 0; // column where the '=' is
4096static int expand_hi_include_orig = 0; // whether to fill the existing current value or not
4097static char_u *expand_hi_curvalue = NULL; // the existing current value
4098#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4099static dict_iterator_T expand_colornames_iter; // iterator for looping through v:colornames
4100#endif
4101
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004102/*
4103 * Handle command line completion for :highlight command.
4104 */
4105 void
4106set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
4107{
4108 char_u *p;
Yee Cheng China7b81202025-02-23 09:32:47 +01004109 int expand_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004110
4111 // Default: expand group names
4112 xp->xp_context = EXPAND_HIGHLIGHT;
4113 xp->xp_pattern = arg;
Yee Cheng China7b81202025-02-23 09:32:47 +01004114 include_none = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004115 include_link = 2;
4116 include_default = 1;
4117
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004118 if (*arg == NUL)
4119 return;
4120
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004121 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004122 p = skiptowhite(arg);
4123 if (*p == NUL)
4124 return;
4125
4126 // past "default" or group name
4127 include_default = 0;
4128 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004129 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004130 arg = skipwhite(p);
4131 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004132 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004133 }
4134 if (*p == NUL)
4135 return;
4136
4137 // past group name
4138 include_link = 0;
4139 if (arg[1] == 'i' && arg[0] == 'N')
Yee Cheng China7b81202025-02-23 09:32:47 +01004140 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004141 highlight_list();
Yee Cheng China7b81202025-02-23 09:32:47 +01004142 expand_group = FALSE;
4143 }
4144 if (STRNCMP("link", arg, p - arg) == 0)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004145 {
4146 xp->xp_pattern = skipwhite(p);
4147 p = skiptowhite(xp->xp_pattern);
4148 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004149 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004150 xp->xp_pattern = skipwhite(p);
4151 p = skiptowhite(xp->xp_pattern);
Yee Cheng China7b81202025-02-23 09:32:47 +01004152 include_none = 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004153 }
Yee Cheng China7b81202025-02-23 09:32:47 +01004154 expand_group = FALSE;
4155 }
4156 else if (STRNCMP("clear", arg, p - arg) == 0)
4157 {
4158 xp->xp_pattern = skipwhite(p);
4159 p = skiptowhite(xp->xp_pattern);
4160 expand_group = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004161 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004162 if (*p != NUL) // past group name(s)
Yee Cheng China7b81202025-02-23 09:32:47 +01004163 {
4164 if (expand_group)
4165 {
4166 // expansion will be done in expand_highlight_group()
4167 xp->xp_context = EXPAND_HIGHLIGHT_GROUP;
4168
4169 expand_hi_synid = syn_namen2id(arg, (int)(p - arg));
4170
4171 while (*p != NUL)
4172 {
4173 arg = skipwhite(p);
4174 p = skiptowhite(arg);
4175 }
4176
4177 p = vim_strchr(arg, '=');
4178 if (p == NULL)
4179 {
4180 // Didn't find a key=<value> pattern
4181 xp->xp_pattern = arg;
4182 expand_hi_equal_col = -1;
4183 expand_hi_include_orig = FALSE;
4184 }
4185 else
4186 {
4187 // Found key=<value> pattern, record the exact location
4188 expand_hi_equal_col = (int)(p - xp->xp_line);
4189
4190 // Only include the original value if the pattern is empty
4191 if (*(p + 1) == NUL)
4192 expand_hi_include_orig = TRUE;
4193 else
4194 expand_hi_include_orig = FALSE;
4195
4196 // Account for comma-separated values
4197 if (STRNCMP(arg, "term=", 5) == 0 ||
4198 STRNCMP(arg, "cterm=", 6) == 0 ||
4199 STRNCMP(arg, "gui=", 4) == 0)
4200 {
4201 char_u *comma = vim_strrchr(p + 1, ',');
4202 if (comma != NULL)
4203 p = comma;
4204 }
4205 xp->xp_pattern = p + 1;
4206 }
4207 }
4208 else
4209 {
4210 xp->xp_context = EXPAND_NOTHING;
4211 }
4212 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004213}
4214
4215/*
4216 * List highlighting matches in a nice way.
4217 */
4218 static void
4219highlight_list(void)
4220{
4221 int i;
4222
4223 for (i = 10; --i >= 0; )
4224 highlight_list_two(i, HL_ATTR(HLF_D));
4225 for (i = 40; --i >= 0; )
4226 highlight_list_two(99, 0);
4227}
4228
4229 static void
4230highlight_list_two(int cnt, int attr)
4231{
4232 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4233 msg_clr_eos();
4234 out_flush();
4235 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4236}
4237
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004238/*
4239 * Function given to ExpandGeneric() to obtain the list of group names.
4240 */
4241 char_u *
4242get_highlight_name(expand_T *xp UNUSED, int idx)
4243{
4244 return get_highlight_name_ext(xp, idx, TRUE);
4245}
4246
4247/*
4248 * Obtain a highlight group name.
4249 * When "skip_cleared" is TRUE don't return a cleared entry.
4250 */
4251 char_u *
4252get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4253{
4254 if (idx < 0)
4255 return NULL;
4256
4257 // Items are never removed from the table, skip the ones that were
4258 // cleared.
4259 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4260 return (char_u *)"";
4261
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004262 if (idx == highlight_ga.ga_len && include_none != 0)
Yee Cheng China7b81202025-02-23 09:32:47 +01004263 return (char_u *)"NONE";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004264 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4265 return (char_u *)"default";
4266 if (idx == highlight_ga.ga_len + include_none + include_default
4267 && include_link != 0)
4268 return (char_u *)"link";
4269 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4270 && include_link != 0)
4271 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004272 if (idx >= highlight_ga.ga_len)
4273 return NULL;
4274 return HL_TABLE()[idx].sg_name;
4275}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004276
Yee Cheng China7b81202025-02-23 09:32:47 +01004277 static char_u *
4278get_highlight_attr_name(expand_T *xp UNUSED, int idx)
4279{
4280 if (idx == 0)
4281 {
4282 // Fill with current value first
4283 if (expand_hi_curvalue != NULL)
4284 return expand_hi_curvalue;
4285 else
4286 return (char_u*)"";
4287 }
4288 if (idx < (int)ARRAY_LENGTH(highlight_index_tab) + 1)
4289 {
4290 char_u *value = highlight_index_tab[idx-1]->value.string;
4291 if (expand_hi_curvalue != NULL && STRCMP(expand_hi_curvalue, value) == 0)
4292 {
4293 // Already returned the current value above, just skip.
4294 return (char_u*)"";
4295 }
4296 return value;
4297 }
4298 return NULL;
4299}
4300
4301 static char_u *
4302get_highlight_cterm_color(expand_T *xp UNUSED, int idx)
4303{
4304 if (idx == 0)
4305 {
4306 // Fill with current value first
4307 if (expand_hi_curvalue != NULL)
4308 return expand_hi_curvalue;
4309 else
4310 return (char_u*)"";
4311 }
4312 // See highlight_set_cterm_color()
4313 else if (idx == 1)
4314 return (char_u*)"fg";
4315 else if (idx == 2)
4316 return (char_u*)"bg";
4317 if (idx < (int)ARRAY_LENGTH(color_name_tab) + 3)
4318 {
4319 char_u *value = color_name_tab[idx-3].value.string;
4320 return value;
4321 }
4322 return NULL;
4323}
4324
4325#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4326 static char_u *
4327get_highlight_gui_color(expand_T *xp UNUSED, int idx)
4328{
4329 if (idx == 0)
4330 {
4331 // Fill with current value first
4332 if (expand_hi_curvalue != NULL)
4333 return expand_hi_curvalue;
4334 else
4335 return (char_u*)"";
4336 }
4337 // See color_name2handle()
4338 else if (idx == 1)
4339 return (char_u*)"fg";
4340 else if (idx == 2)
4341 return (char_u*)"bg";
4342 else if (idx == 3)
4343 return (char_u*)"NONE";
4344
4345 // Complete from v:colornames. Don't do platform specific names for now.
4346 typval_T *tv_result;
4347 char_u *colorname = dict_iterate_next(&expand_colornames_iter, &tv_result);
4348 if (colorname != NULL)
4349 {
4350 // :hi command doesn't allow space, so don't suggest any malformed items
4351 if (vim_strchr(colorname, ' ') != NULL)
4352 return (char_u*)"";
4353
4354 if (expand_hi_curvalue != NULL && STRICMP(expand_hi_curvalue, colorname) == 0)
4355 {
4356 // Already returned the current value above, just skip.
4357 return (char_u*)"";
4358 }
4359 }
4360 return colorname;
4361}
4362#endif
4363
4364 static char_u *
4365get_highlight_group_key(expand_T *xp UNUSED, int idx)
4366{
4367 // Note: Keep this in sync with do_highlight.
4368 static char *(p_hi_group_key_values[]) =
4369 {
4370 "term=",
4371 "start=",
4372 "stop=",
4373 "cterm=",
4374 "ctermfg=",
4375 "ctermbg=",
4376 "ctermul=",
4377 "ctermfont=",
4378#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
4379 "gui=",
4380 "guifg=",
4381 "guibg=",
4382 "guisp=",
4383#endif
4384#ifdef FEAT_GUI
4385 "font=",
4386#endif
4387 "NONE",
4388 };
4389
4390 if (idx < (int)ARRAY_LENGTH(p_hi_group_key_values))
4391 return (char_u*)p_hi_group_key_values[idx];
4392 return NULL;
4393}
4394
4395/*
4396 * Command-line expansion for :hi {group-name} <args>...
4397 */
4398 int
4399expand_highlight_group(
4400 char_u *pat,
4401 expand_T *xp,
4402 regmatch_T *rmp,
4403 char_u ***matches,
4404 int *numMatches)
4405{
4406 if (expand_hi_equal_col != -1)
4407 {
4408 // List the values. First fill in the current value, then if possible colors
4409 // or attribute names.
4410 char_u *(*expandfunc)(expand_T *, int) = NULL;
4411 int type = 0;
4412 hl_group_T *sgp = NULL;
4413 int iarg = 0;
4414 char_u *sarg = NULL;
4415
4416 int unsortedItems = -1; // don't sort by default
4417
4418 if (expand_hi_synid != 0)
4419 sgp = &HL_TABLE()[expand_hi_synid - 1]; // index is ID minus one
4420
4421 // Note: Keep this in sync with highlight_list_one().
4422 char_u *name_end = xp->xp_line + expand_hi_equal_col;
4423 if (name_end - xp->xp_line >= 5
4424 && STRNCMP(name_end - 5, " term", 5) == 0)
4425 {
4426 expandfunc = get_highlight_attr_name;
4427 if (sgp)
4428 {
4429 type = LIST_ATTR;
4430 iarg = sgp->sg_term;
4431 }
4432 }
4433 else if (name_end - xp->xp_line >= 6
4434 && STRNCMP(name_end - 6, " cterm", 6) == 0)
4435 {
4436 expandfunc = get_highlight_attr_name;
4437 if (sgp)
4438 {
4439 type = LIST_ATTR;
4440 iarg = sgp->sg_cterm;
4441 }
4442 }
4443#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
4444 else if (name_end - xp->xp_line >= 4
4445 && STRNCMP(name_end - 4, " gui", 4) == 0)
4446 {
4447 expandfunc = get_highlight_attr_name;
4448 if (sgp)
4449 {
4450 type = LIST_ATTR;
4451 iarg = sgp->sg_gui;
4452 }
4453 }
4454#endif
4455 else if (name_end - xp->xp_line >= 8
4456 && STRNCMP(name_end - 8, " ctermfg", 8) == 0)
4457 {
4458 expandfunc = get_highlight_cterm_color;
4459 if (sgp)
4460 {
4461 type = LIST_INT;
4462 iarg = sgp->sg_cterm_fg;
4463 }
4464 }
4465 else if (name_end - xp->xp_line >= 8
4466 && STRNCMP(name_end - 8, " ctermbg", 8) == 0)
4467 {
4468 expandfunc = get_highlight_cterm_color;
4469 if (sgp)
4470 {
4471 type = LIST_INT;
4472 iarg = sgp->sg_cterm_bg;
4473 }
4474 }
4475 else if (name_end - xp->xp_line >= 8
4476 && STRNCMP(name_end - 8, " ctermul", 8) == 0)
4477 {
4478 expandfunc = get_highlight_cterm_color;
4479 if (sgp)
4480 {
4481 type = LIST_INT;
4482 iarg = sgp->sg_cterm_ul;
4483 }
4484 }
4485#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4486 else if (name_end - xp->xp_line >= 6
4487 && STRNCMP(name_end - 6, " guifg", 6) == 0)
4488 {
4489 expandfunc = get_highlight_gui_color;
4490 if (sgp)
4491 {
4492 type = LIST_STRING;
4493 sarg = sgp->sg_gui_fg_name;
4494 }
4495 }
4496 else if (name_end - xp->xp_line >= 6
4497 && STRNCMP(name_end - 6, " guibg", 6) == 0)
4498 {
4499 expandfunc = get_highlight_gui_color;
4500 if (sgp)
4501 {
4502 type = LIST_STRING;
4503 sarg = sgp->sg_gui_bg_name;
4504 }
4505 }
4506 else if (name_end - xp->xp_line >= 6
4507 && STRNCMP(name_end - 6, " guisp", 6) == 0)
4508 {
4509 expandfunc = get_highlight_gui_color;
4510 if (sgp)
4511 {
4512 type = LIST_STRING;
4513 sarg = sgp->sg_gui_sp_name;
4514 }
4515 }
4516#endif
4517
4518#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4519 if (expandfunc == get_highlight_gui_color)
4520 {
4521 // Top 4 items are special, after that sort all the color names
4522 unsortedItems = 4;
4523
4524 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
4525 typval_T colornames_val;
4526 colornames_val.v_type = VAR_DICT;
4527 colornames_val.vval.v_dict = colornames_table;
4528 dict_iterate_start(&colornames_val, &expand_colornames_iter);
4529 }
4530#endif
4531
4532 char_u buf[MAX_ATTR_LEN];
4533
4534 if (expand_hi_synid != 0 && type != 0 && expand_hi_include_orig)
4535 {
4536 // Retrieve the current value to go first in completion
4537 expand_hi_curvalue = highlight_arg_to_string(
4538 type, iarg, sarg, buf);
4539 }
4540 else
4541 expand_hi_curvalue = NULL;
4542
4543 if (expandfunc != NULL)
4544 {
4545 return ExpandGenericExt(
4546 pat,
4547 xp,
4548 rmp,
4549 matches,
4550 numMatches,
4551 expandfunc,
4552 FALSE,
4553 unsortedItems);
4554 }
4555
4556 return FAIL;
4557 }
4558
4559 // List all the key names
4560 return ExpandGenericExt(
4561 pat,
4562 xp,
4563 rmp,
4564 matches,
4565 numMatches,
4566 get_highlight_group_key,
4567 FALSE,
4568 -1);
4569}
4570
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004571#if defined(FEAT_GUI) || defined(PROTO)
4572/*
4573 * Free all the highlight group fonts.
4574 * Used when quitting for systems which need it.
4575 */
4576 void
4577free_highlight_fonts(void)
4578{
4579 int idx;
4580
4581 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4582 {
4583 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4584 HL_TABLE()[idx].sg_font = NOFONT;
4585# ifdef FEAT_XFONTSET
4586 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4587 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4588# endif
4589 }
4590
4591 gui_mch_free_font(gui.norm_font);
4592# ifdef FEAT_XFONTSET
4593 gui_mch_free_fontset(gui.fontset);
4594# endif
4595# ifndef FEAT_GUI_GTK
4596 gui_mch_free_font(gui.bold_font);
4597 gui_mch_free_font(gui.ital_font);
4598 gui_mch_free_font(gui.boldital_font);
4599# endif
4600}
4601#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004602
4603#if defined(FEAT_EVAL) || defined(PROTO)
4604/*
4605 * Convert each of the highlight attribute bits (bold, standout, underline,
4606 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4607 * the attribute name as the key.
4608 */
4609 static dict_T *
4610highlight_get_attr_dict(int hlattr)
4611{
4612 dict_T *dict;
4613 int i;
4614
4615 dict = dict_alloc();
4616 if (dict == NULL)
4617 return NULL;
4618
John Marriott34f00dd2024-04-08 23:28:12 +02004619 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004620 {
John Marriott34f00dd2024-04-08 23:28:12 +02004621 if (hlattr & highlight_index_tab[i]->key)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004622 {
John Marriott8d4477e2024-11-02 15:59:01 +01004623 dict_add_bool(dict, (char *)highlight_index_tab[i]->value.string,
4624 VVAL_TRUE);
4625 // don't want "inverse"/"reverse"
4626 hlattr &= ~highlight_index_tab[i]->key;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004627 }
4628 }
4629
4630 return dict;
4631}
4632
4633/*
4634 * Return the attributes of the highlight group at index 'hl_idx' as a
4635 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4636 * links recursively.
4637 */
4638 static dict_T *
4639highlight_get_info(int hl_idx, int resolve_link)
4640{
4641 dict_T *dict;
4642 hl_group_T *sgp;
4643 dict_T *attr_dict;
4644 int hlgid;
4645
4646 dict = dict_alloc();
4647 if (dict == NULL)
4648 return dict;
4649
4650 sgp = &HL_TABLE()[hl_idx];
4651 // highlight group id is 1-based
4652 hlgid = hl_idx + 1;
4653
4654 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4655 goto error;
4656 if (dict_add_number(dict, "id", hlgid) == FAIL)
4657 goto error;
4658
4659 if (sgp->sg_link && resolve_link)
4660 {
4661 // resolve the highlight group link recursively
4662 while (sgp->sg_link)
4663 {
4664 hlgid = sgp->sg_link;
4665 sgp = &HL_TABLE()[sgp->sg_link - 1];
4666 }
4667 }
4668
4669 if (sgp->sg_term != 0)
4670 {
4671 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4672 if (attr_dict != NULL)
4673 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4674 goto error;
4675 }
4676 if (sgp->sg_start != NULL)
4677 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4678 goto error;
4679 if (sgp->sg_stop != NULL)
4680 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4681 goto error;
4682 if (sgp->sg_cterm != 0)
4683 {
4684 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4685 if (attr_dict != NULL)
4686 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4687 goto error;
4688 }
4689 if (sgp->sg_cterm_fg != 0)
4690 if (dict_add_string(dict, "ctermfg",
4691 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4692 goto error;
4693 if (sgp->sg_cterm_bg != 0)
4694 if (dict_add_string(dict, "ctermbg",
4695 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4696 goto error;
4697 if (sgp->sg_cterm_ul != 0)
4698 if (dict_add_string(dict, "ctermul",
4699 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4700 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004701 if (sgp->sg_cterm_font != 0)
4702 if (dict_add_string(dict, "ctermfont",
4703 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4704 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004705 if (sgp->sg_gui != 0)
4706 {
4707 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4708 if (attr_dict != NULL)
4709 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4710 goto error;
4711 }
4712 if (sgp->sg_gui_fg_name != NULL)
4713 if (dict_add_string(dict, "guifg",
4714 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4715 goto error;
4716 if (sgp->sg_gui_bg_name != NULL)
4717 if (dict_add_string(dict, "guibg",
4718 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4719 goto error;
4720 if (sgp->sg_gui_sp_name != NULL)
4721 if (dict_add_string(dict, "guisp",
4722 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4723 goto error;
4724# ifdef FEAT_GUI
4725 if (sgp->sg_font_name != NULL)
4726 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4727 goto error;
4728# endif
4729 if (sgp->sg_link)
4730 {
4731 char_u *link;
4732
4733 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4734 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4735 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004736
4737 if (sgp->sg_deflink)
4738 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004739 }
4740 if (dict_len(dict) == 2)
4741 // If only 'name' is present, then the highlight group is cleared.
4742 dict_add_bool(dict, "cleared", VVAL_TRUE);
4743
4744 return dict;
4745
4746error:
4747 vim_free(dict);
4748 return NULL;
4749}
4750
4751/*
4752 * "hlget([name])" function
4753 * Return the attributes of a specific highlight group (if specified) or all
4754 * the highlight groups.
4755 */
4756 void
4757f_hlget(typval_T *argvars, typval_T *rettv)
4758{
4759 list_T *list;
4760 dict_T *dict;
4761 int i;
4762 char_u *hlarg = NULL;
4763 int resolve_link = FALSE;
4764
4765 if (rettv_list_alloc(rettv) == FAIL)
4766 return;
4767
4768 if (check_for_opt_string_arg(argvars, 0) == FAIL
4769 || (argvars[0].v_type != VAR_UNKNOWN
4770 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4771 return;
4772
4773 if (argvars[0].v_type != VAR_UNKNOWN)
4774 {
4775 // highlight group name supplied
4776 hlarg = tv_get_string_chk(&argvars[0]);
4777 if (hlarg == NULL)
4778 return;
4779
4780 if (argvars[1].v_type != VAR_UNKNOWN)
4781 {
4782 int error = FALSE;
4783
4784 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4785 if (error)
4786 return;
4787 }
4788 }
4789
4790 list = rettv->vval.v_list;
4791 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4792 {
4793 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4794 {
4795 dict = highlight_get_info(i, resolve_link);
4796 if (dict != NULL)
4797 list_append_dict(list, dict);
4798 }
4799 }
4800}
4801
4802/*
4803 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4804 * 'dict' or the value is not a string type. If the value is not a string type
4805 * or is NULL, then 'error' is set to TRUE.
4806 */
4807 static char_u *
4808hldict_get_string(dict_T *dict, char_u *key, int *error)
4809{
4810 dictitem_T *di;
4811
4812 *error = FALSE;
4813 di = dict_find(dict, key, -1);
4814 if (di == NULL)
4815 return NULL;
4816
4817 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4818 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004819 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004820 *error = TRUE;
4821 return NULL;
4822 }
4823
4824 return di->di_tv.vval.v_string;
4825}
4826
4827/*
4828 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4829 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4830 * Dictionary or is NULL.
4831 */
4832 static int
4833hldict_attr_to_str(
4834 dict_T *dict,
4835 char_u *key,
4836 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004837 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004838{
4839 dictitem_T *di;
4840 dict_T *attrdict;
4841 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004842 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004843
4844 attr_str[0] = NUL;
4845 di = dict_find(dict, key, -1);
4846 if (di == NULL)
4847 return TRUE;
4848
4849 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4850 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004851 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004852 return FALSE;
4853 }
4854
4855 attrdict = di->di_tv.vval.v_dict;
4856
4857 // If the attribute dict is empty, then return NONE to clear the attributes
4858 if (dict_len(attrdict) == 0)
4859 {
4860 vim_strcat(attr_str, (char_u *)"NONE", len);
4861 return TRUE;
4862 }
4863
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004864 p = attr_str;
John Marriott34f00dd2024-04-08 23:28:12 +02004865 for (i = 0; i < (int)ARRAY_LENGTH(highlight_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004866 {
John Marriott8d4477e2024-11-02 15:59:01 +01004867 if (dict_get_bool(attrdict, (char *)highlight_tab[i].value.string,
4868 VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004869 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004870 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4871 STRCPY(p, (char_u *)",");
John Marriott8d4477e2024-11-02 15:59:01 +01004872 if (p - attr_str + highlight_tab[i].value.length + 1 < len)
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004873 {
John Marriott8d4477e2024-11-02 15:59:01 +01004874 STRCPY(p, highlight_tab[i].value.string);
4875 p += highlight_tab[i].value.length;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004876 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004877 }
4878 }
4879
4880 return TRUE;
4881}
4882
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004883// Temporary buffer used to store the command string produced by hlset().
4884// IObuff cannot be used for this as the error messages produced by hlset()
4885// internally use IObuff.
4886#define HLSETBUFSZ 512
4887static char_u hlsetBuf[HLSETBUFSZ + 1];
4888
4889/*
4890 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4891 * "dptr", which points into "hlsetBuf".
4892 * Returns the updated pointer.
4893 */
4894 static char_u *
4895add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4896{
4897 size_t vallen;
4898
4899 // Do nothing if the value is not specified or is empty
4900 if (value == NULL || *value == NUL)
4901 return dptr;
4902
4903 vallen = STRLEN(value);
4904 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4905 {
4906 STRCPY(dptr, attr);
4907 dptr += attrlen;
4908 STRCPY(dptr, value);
4909 dptr += vallen;
4910 }
4911
4912 return dptr;
4913}
4914
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004915/*
4916 * Add or update a highlight group using 'dict' items. Returns TRUE if
4917 * successfully updated the highlight group.
4918 */
4919 static int
4920hlg_add_or_update(dict_T *dict)
4921{
4922 char_u *name;
4923 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004924 char_u term_attr[MAX_ATTR_LEN];
4925 char_u cterm_attr[MAX_ATTR_LEN];
4926 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004927 char_u *start;
4928 char_u *stop;
4929 char_u *ctermfg;
4930 char_u *ctermbg;
4931 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004932 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004933 char_u *guifg;
4934 char_u *guibg;
4935 char_u *guisp;
4936# ifdef FEAT_GUI
4937 char_u *font;
4938# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004939 int forceit = FALSE;
4940 int dodefault = FALSE;
4941 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004942 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004943
4944 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004945 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004946 return FALSE;
4947
Bram Moolenaard61efa52022-07-23 09:52:04 +01004948 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004949 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004950
Bram Moolenaard61efa52022-07-23 09:52:04 +01004951 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004952 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004953
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004954 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004955 {
4956 varnumber_T cleared;
4957
4958 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004959 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004960 if (cleared == TRUE)
4961 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004962 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4963 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004964 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004965 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004966 }
4967
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004968 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004969 {
4970 char_u *linksto;
4971
4972 // link highlight groups
4973 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004974 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004975 return FALSE;
4976
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004977 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004978 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004979 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004980
4981 done = TRUE;
4982 }
4983
4984 // If 'cleared' or 'linksto' are specified, then don't process the other
4985 // attributes.
4986 if (done)
4987 return TRUE;
4988
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004989 start = hldict_get_string(dict, (char_u *)"start", &error);
4990 if (error)
4991 return FALSE;
4992
4993 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4994 if (error)
4995 return FALSE;
4996
4997 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004998 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004999 return FALSE;
5000
5001 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005002 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005003 return FALSE;
5004
5005 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
5006 if (error)
5007 return FALSE;
5008
5009 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
5010 if (error)
5011 return FALSE;
5012
5013 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
5014 if (error)
5015 return FALSE;
5016
PMuncha606f3a2023-11-15 15:35:49 +01005017 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
5018 if (error)
5019 return FALSE;
5020
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005021 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005022 return FALSE;
5023
5024 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
5025 if (error)
5026 return FALSE;
5027
5028 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
5029 if (error)
5030 return FALSE;
5031
5032 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
5033 if (error)
5034 return FALSE;
5035
5036# ifdef FEAT_GUI
5037 font = hldict_get_string(dict, (char_u *)"font", &error);
5038 if (error)
5039 return FALSE;
5040# endif
5041
5042 // If none of the attributes are specified, then do nothing.
5043 if (term_attr[0] == NUL && start == NULL && stop == NULL
5044 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01005045 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005046# ifdef FEAT_GUI
5047 && font == NULL
5048# endif
5049 && guifg == NULL && guibg == NULL && guisp == NULL
5050 )
5051 return TRUE;
5052
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005053 hlsetBuf[0] = NUL;
5054 p = hlsetBuf;
5055 if (dodefault)
5056 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
5057 p = add_attr_and_value(p, (char_u *)"", 0, name);
5058 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
5059 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
5060 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
5061 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
5062 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
5063 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
5064 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01005065 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005066 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005067# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005068 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005069# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005070 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
5071 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01005072 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005073
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005074 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005075
5076 return TRUE;
5077}
5078
5079/*
5080 * "hlset([{highlight_attr}])" function
5081 * Add or modify highlight groups
5082 */
5083 void
5084f_hlset(typval_T *argvars, typval_T *rettv)
5085{
5086 listitem_T *li;
5087 dict_T *dict;
5088
5089 rettv->vval.v_number = -1;
5090
5091 if (check_for_list_arg(argvars, 0) == FAIL)
5092 return;
5093
5094 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
5095 {
5096 if (li->li_tv.v_type != VAR_DICT)
5097 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005098 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005099 return;
5100 }
5101
5102 dict = li->li_tv.vval.v_dict;
5103 if (!hlg_add_or_update(dict))
5104 return;
5105 }
5106
5107 rettv->vval.v_number = 0;
5108}
5109#endif