blob: 8c1ad8049ef05b12cf451b2f10c9ee89ecb8d448 [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
1694 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1695 // or "guibg").
1696 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1697 ++linep;
1698 vim_free(key);
1699 key = vim_strnsave_up(key_start, linep - key_start);
1700 if (key == NULL)
1701 {
1702 error = TRUE;
1703 break;
1704 }
1705 linep = skipwhite(linep);
1706
1707 if (STRCMP(key, "NONE") == 0)
1708 {
1709 if (!init || HL_TABLE()[idx].sg_set == 0)
1710 {
1711 if (!init)
1712 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1713 highlight_clear(idx);
1714 }
1715 continue;
1716 }
1717
1718 // Check for the equal sign.
1719 if (*linep != '=')
1720 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001721 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001722 error = TRUE;
1723 break;
1724 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001725 ++linep;
1726
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001727 // Isolate the argument.
1728 linep = skipwhite(linep);
1729 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001730 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001731 arg_start = ++linep;
1732 linep = vim_strchr(linep, '\'');
1733 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001734 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001735 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001736 error = TRUE;
1737 break;
1738 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001739 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001740 else
1741 {
1742 arg_start = linep;
1743 linep = skiptowhite(linep);
1744 }
1745 if (linep == arg_start)
1746 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001747 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001748 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001749 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001750 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001751 vim_free(arg);
1752 arg = vim_strnsave(arg_start, linep - arg_start);
1753 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001754 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001755 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001756 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001757 }
1758 if (*linep == '\'')
1759 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001760
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001761 // Store the argument.
1762 if (STRCMP(key, "TERM") == 0
1763 || STRCMP(key, "CTERM") == 0
1764 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001765 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001766 if (!highlight_set_termgui_attr(idx, key, arg, init))
1767 {
1768 error = TRUE;
1769 break;
1770 }
1771 }
1772 else if (STRCMP(key, "FONT") == 0)
1773 {
1774 // in non-GUI fonts are simply ignored
1775#ifdef FEAT_GUI
1776 if (highlight_set_font(idx, arg, is_normal_group,
1777 is_menu_group, is_tooltip_group))
1778 did_change = TRUE;
1779#endif
1780 }
1781 else if (STRCMP(key, "CTERMFG") == 0
1782 || STRCMP(key, "CTERMBG") == 0
1783 || STRCMP(key, "CTERMUL") == 0)
1784 {
1785 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1786 is_normal_group, init))
1787 {
1788 error = TRUE;
1789 break;
1790 }
1791 }
PMuncha606f3a2023-11-15 15:35:49 +01001792 else if (STRCMP(key, "CTERMFONT") == 0)
1793 {
1794 if (!highlight_set_cterm_font(idx, arg, init))
1795 {
1796 error = TRUE;
1797 break;
1798 }
1799 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001800 else if (STRCMP(key, "GUIFG") == 0)
1801 {
1802#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1803 if (highlight_set_guifg(idx, arg, is_menu_group,
1804 is_scrollbar_group, is_tooltip_group,
1805 &do_colors, init))
1806 did_change = TRUE;
1807#endif
1808 }
1809 else if (STRCMP(key, "GUIBG") == 0)
1810 {
1811#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1812 if (highlight_set_guibg(idx, arg, is_menu_group,
1813 is_scrollbar_group, is_tooltip_group,
1814 &do_colors, init))
1815 did_change = TRUE;
1816#endif
1817 }
1818 else if (STRCMP(key, "GUISP") == 0)
1819 {
1820#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1821 if (highlight_set_guisp(idx, arg, init))
1822 did_change = TRUE;
1823#endif
1824 }
1825 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1826 {
1827 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1828 {
1829 error = TRUE;
1830 break;
1831 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001832 }
1833 else
1834 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001835 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001836 error = TRUE;
1837 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001838 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001839 HL_TABLE()[idx].sg_cleared = FALSE;
1840
1841 // When highlighting has been given for a group, don't link it.
1842 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1843 HL_TABLE()[idx].sg_link = 0;
1844
1845 // Continue with next argument.
1846 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001847 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001848
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001849 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001850 if (error && idx == highlight_ga.ga_len)
1851 syn_unadd_group();
1852 else
1853 {
1854 if (is_normal_group)
1855 {
1856 HL_TABLE()[idx].sg_term_attr = 0;
1857 HL_TABLE()[idx].sg_cterm_attr = 0;
1858#ifdef FEAT_GUI
1859 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001860 // Need to update all groups, because they might be using "bg"
1861 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001862#endif
1863#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1864 if (USE_24BIT)
1865 {
1866 highlight_gui_started();
1867 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001868 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001869 }
1870#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001871#ifdef FEAT_VTP
1872 control_console_color_rgb();
1873#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001874 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001875#ifdef FEAT_GUI_X11
1876# ifdef FEAT_MENU
1877 else if (is_menu_group)
1878 {
1879 if (gui.in_use && do_colors)
1880 gui_mch_new_menu_colors();
1881 }
1882# endif
1883 else if (is_scrollbar_group)
1884 {
1885 if (gui.in_use && do_colors)
1886 gui_new_scrollbar_colors();
1887 else
1888 set_hl_attr(idx);
1889 }
1890# ifdef FEAT_BEVAL_GUI
1891 else if (is_tooltip_group)
1892 {
1893 if (gui.in_use && do_colors)
1894 gui_mch_new_tooltip_colors();
1895 }
1896# endif
1897#endif
1898 else
1899 set_hl_attr(idx);
1900#ifdef FEAT_EVAL
1901 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001902 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001903#endif
1904 }
1905
1906 vim_free(key);
1907 vim_free(arg);
1908
1909 // Only call highlight_changed() once, after a sequence of highlight
1910 // commands, and only if an attribute actually changed.
1911 if ((did_change
1912 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1913#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1914 && !did_highlight_changed
1915#endif
1916 )
1917 {
1918 // Do not trigger a redraw when highlighting is changed while
1919 // redrawing. This may happen when evaluating 'statusline' changes the
1920 // StatusLine group.
1921 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001922 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001923 need_highlight_changed = TRUE;
1924 }
1925}
1926
1927#if defined(EXITFREE) || defined(PROTO)
1928 void
1929free_highlight(void)
1930{
1931 int i;
1932
1933 for (i = 0; i < highlight_ga.ga_len; ++i)
1934 {
1935 highlight_clear(i);
1936 vim_free(HL_TABLE()[i].sg_name);
1937 vim_free(HL_TABLE()[i].sg_name_u);
1938 }
1939 ga_clear(&highlight_ga);
1940}
1941#endif
1942
1943/*
1944 * Reset the cterm colors to what they were before Vim was started, if
1945 * possible. Otherwise reset them to zero.
1946 */
1947 void
1948restore_cterm_colors(void)
1949{
1950#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1951 // Since t_me has been set, this probably means that the user
1952 // wants to use this as default colors. Need to reset default
1953 // background/foreground colors.
1954 mch_set_normal_colors();
1955#else
1956# ifdef VIMDLL
1957 if (!gui.in_use)
1958 {
1959 mch_set_normal_colors();
1960 return;
1961 }
1962# endif
1963 cterm_normal_fg_color = 0;
1964 cterm_normal_fg_bold = 0;
1965 cterm_normal_bg_color = 0;
1966# ifdef FEAT_TERMGUICOLORS
1967 cterm_normal_fg_gui_color = INVALCOLOR;
1968 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001969 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001970# endif
1971#endif
1972}
1973
1974/*
1975 * Return TRUE if highlight group "idx" has any settings.
1976 * When "check_link" is TRUE also check for an existing link.
1977 */
1978 static int
1979hl_has_settings(int idx, int check_link)
1980{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001981 return HL_TABLE()[idx].sg_cleared == 0
1982 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001983 || HL_TABLE()[idx].sg_cterm_attr != 0
1984 || HL_TABLE()[idx].sg_cterm_fg != 0
1985 || HL_TABLE()[idx].sg_cterm_bg != 0
PMuncha606f3a2023-11-15 15:35:49 +01001986 || HL_TABLE()[idx].sg_cterm_font != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001987#ifdef FEAT_GUI
1988 || HL_TABLE()[idx].sg_gui_attr != 0
1989 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1990 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1991 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1992 || HL_TABLE()[idx].sg_font_name != NULL
1993#endif
1994 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1995}
1996
1997/*
1998 * Clear highlighting for one group.
1999 */
2000 static void
2001highlight_clear(int idx)
2002{
2003 HL_TABLE()[idx].sg_cleared = TRUE;
2004
2005 HL_TABLE()[idx].sg_term = 0;
2006 VIM_CLEAR(HL_TABLE()[idx].sg_start);
2007 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
2008 HL_TABLE()[idx].sg_term_attr = 0;
2009 HL_TABLE()[idx].sg_cterm = 0;
2010 HL_TABLE()[idx].sg_cterm_bold = FALSE;
2011 HL_TABLE()[idx].sg_cterm_fg = 0;
2012 HL_TABLE()[idx].sg_cterm_bg = 0;
2013 HL_TABLE()[idx].sg_cterm_attr = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002014 HL_TABLE()[idx].sg_cterm_font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002015#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2016 HL_TABLE()[idx].sg_gui = 0;
2017 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
2018 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
2019 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
2020#endif
2021#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2022 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
2023 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002024 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002025#endif
2026#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002027 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2028 HL_TABLE()[idx].sg_font = NOFONT;
2029# ifdef FEAT_XFONTSET
2030 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2031 HL_TABLE()[idx].sg_fontset = NOFONTSET;
2032# endif
2033 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
2034 HL_TABLE()[idx].sg_gui_attr = 0;
2035#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02002036 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02002037 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02002038#ifdef FEAT_EVAL
2039 // Since we set the default link, set the location to where the default
2040 // link was set.
2041 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002042#endif
2043}
2044
2045#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2046/*
2047 * Set the normal foreground and background colors according to the "Normal"
2048 * highlighting group. For X11 also set "Menu", "Scrollbar", and
2049 * "Tooltip" colors.
2050 */
2051 void
2052set_normal_colors(void)
2053{
2054# ifdef FEAT_GUI
2055# ifdef FEAT_TERMGUICOLORS
2056 if (gui.in_use)
2057# endif
2058 {
2059 if (set_group_colors((char_u *)"Normal",
2060 &gui.norm_pixel, &gui.back_pixel,
2061 FALSE, TRUE, FALSE))
2062 {
2063 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002064 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002065 }
2066# ifdef FEAT_GUI_X11
2067 if (set_group_colors((char_u *)"Menu",
2068 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
2069 TRUE, FALSE, FALSE))
2070 {
2071# ifdef FEAT_MENU
2072 gui_mch_new_menu_colors();
2073# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002074 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002075 }
2076# ifdef FEAT_BEVAL_GUI
2077 if (set_group_colors((char_u *)"Tooltip",
2078 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
2079 FALSE, FALSE, TRUE))
2080 {
2081# ifdef FEAT_TOOLBAR
2082 gui_mch_new_tooltip_colors();
2083# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002084 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002085 }
2086# endif
2087 if (set_group_colors((char_u *)"Scrollbar",
2088 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
2089 FALSE, FALSE, FALSE))
2090 {
2091 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002092 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002093 }
2094# endif
2095 }
2096# endif
2097# ifdef FEAT_TERMGUICOLORS
2098# ifdef FEAT_GUI
2099 else
2100# endif
2101 {
2102 int idx;
2103
2104 idx = syn_name2id((char_u *)"Normal") - 1;
2105 if (idx >= 0)
2106 {
2107 gui_do_one_color(idx, FALSE, FALSE);
2108
2109 // If the normal fg or bg color changed a complete redraw is
2110 // required.
2111 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
2112 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
2113 {
2114 // if the GUI color is INVALCOLOR then we use the default cterm
2115 // color
2116 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
2117 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002118 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002119 }
2120 }
2121 }
2122# endif
2123}
2124#endif
2125
2126#if defined(FEAT_GUI) || defined(PROTO)
2127/*
2128 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2129 */
2130 static int
2131set_group_colors(
2132 char_u *name,
2133 guicolor_T *fgp,
2134 guicolor_T *bgp,
2135 int do_menu,
2136 int use_norm,
2137 int do_tooltip)
2138{
2139 int idx;
2140
2141 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002142 if (idx < 0)
2143 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002144
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002145 gui_do_one_color(idx, do_menu, do_tooltip);
2146
2147 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2148 *fgp = HL_TABLE()[idx].sg_gui_fg;
2149 else if (use_norm)
2150 *fgp = gui.def_norm_pixel;
2151 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2152 *bgp = HL_TABLE()[idx].sg_gui_bg;
2153 else if (use_norm)
2154 *bgp = gui.def_back_pixel;
2155 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002156}
2157
2158/*
2159 * Get the font of the "Normal" group.
2160 * Returns "" when it's not found or not set.
2161 */
2162 char_u *
2163hl_get_font_name(void)
2164{
2165 int id;
2166 char_u *s;
2167
2168 id = syn_name2id((char_u *)"Normal");
2169 if (id > 0)
2170 {
2171 s = HL_TABLE()[id - 1].sg_font_name;
2172 if (s != NULL)
2173 return s;
2174 }
2175 return (char_u *)"";
2176}
2177
2178/*
2179 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2180 * actually chosen to be used.
2181 */
2182 void
2183hl_set_font_name(char_u *font_name)
2184{
2185 int id;
2186
2187 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002188 if (id <= 0)
2189 return;
2190
2191 vim_free(HL_TABLE()[id - 1].sg_font_name);
2192 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002193}
2194
2195/*
2196 * Set background color for "Normal" group. Called by gui_set_bg_color()
2197 * when the color is known.
2198 */
2199 void
2200hl_set_bg_color_name(
2201 char_u *name) // must have been allocated
2202{
2203 int id;
2204
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002205 if (name == NULL)
2206 return;
2207
2208 id = syn_name2id((char_u *)"Normal");
2209 if (id <= 0)
2210 return;
2211
2212 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2213 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002214}
2215
2216/*
2217 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2218 * when the color is known.
2219 */
2220 void
2221hl_set_fg_color_name(
2222 char_u *name) // must have been allocated
2223{
2224 int id;
2225
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002226 if (name == NULL)
2227 return;
2228
2229 id = syn_name2id((char_u *)"Normal");
2230 if (id <= 0)
2231 return;
2232
2233 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2234 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002235}
2236
2237/*
2238 * Return the handle for a font name.
2239 * Returns NOFONT when failed.
2240 */
2241 static GuiFont
2242font_name2handle(char_u *name)
2243{
2244 if (STRCMP(name, "NONE") == 0)
2245 return NOFONT;
2246
2247 return gui_mch_get_font(name, TRUE);
2248}
2249
2250# ifdef FEAT_XFONTSET
2251/*
2252 * Return the handle for a fontset name.
2253 * Returns NOFONTSET when failed.
2254 */
2255 static GuiFontset
2256fontset_name2handle(char_u *name, int fixed_width)
2257{
2258 if (STRCMP(name, "NONE") == 0)
2259 return NOFONTSET;
2260
2261 return gui_mch_get_fontset(name, TRUE, fixed_width);
2262}
2263# endif
2264
2265/*
2266 * Get the font or fontset for one highlight group.
2267 */
2268 static void
2269hl_do_font(
2270 int idx,
2271 char_u *arg,
2272 int do_normal, // set normal font
2273 int do_menu UNUSED, // set menu font
2274 int do_tooltip UNUSED, // set tooltip font
2275 int free_font) // free current font/fontset
2276{
2277# ifdef FEAT_XFONTSET
2278 // If 'guifontset' is not empty, first try using the name as a
2279 // fontset. If that doesn't work, use it as a font name.
2280 if (*p_guifontset != NUL
2281# ifdef FONTSET_ALWAYS
2282 || do_menu
2283# endif
2284# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002285 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002286 || do_tooltip
2287# endif
2288 )
2289 {
2290 if (free_font)
2291 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2292 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2293# ifdef FONTSET_ALWAYS
2294 || do_menu
2295# endif
2296# ifdef FEAT_BEVAL_TIP
2297 || do_tooltip
2298# endif
2299 );
2300 }
2301 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2302 {
2303 // If it worked and it's the Normal group, use it as the normal
2304 // fontset. Same for the Menu group.
2305 if (do_normal)
2306 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002307# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002308 if (do_menu)
2309 {
2310# ifdef FONTSET_ALWAYS
2311 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2312# else
2313 // YIKES! This is a bug waiting to crash the program
2314 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2315# endif
2316 gui_mch_new_menu_font();
2317 }
2318# ifdef FEAT_BEVAL_GUI
2319 if (do_tooltip)
2320 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002321 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002322 // displaying a single font and a fontset.
2323 // If the XtNinternational resource is set to True at widget
2324 // creation, then a fontset is always used, otherwise an
2325 // XFontStruct is used.
2326 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2327 gui_mch_new_tooltip_font();
2328 }
2329# endif
2330# endif
2331 }
2332 else
2333# endif
2334 {
2335 if (free_font)
2336 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2337 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2338 // If it worked and it's the Normal group, use it as the
2339 // normal font. Same for the Menu group.
2340 if (HL_TABLE()[idx].sg_font != NOFONT)
2341 {
2342 if (do_normal)
2343 gui_init_font(arg, FALSE);
2344#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002345# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002346 if (do_menu)
2347 {
2348 gui.menu_font = HL_TABLE()[idx].sg_font;
2349 gui_mch_new_menu_font();
2350 }
2351# endif
2352#endif
2353 }
2354 }
2355}
2356
2357#endif // FEAT_GUI
2358
2359#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2360/*
2361 * Return the handle for a color name.
2362 * Returns INVALCOLOR when failed.
2363 */
2364 guicolor_T
2365color_name2handle(char_u *name)
2366{
2367 if (STRCMP(name, "NONE") == 0)
2368 return INVALCOLOR;
2369
2370 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2371 {
2372#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2373 if (gui.in_use)
2374#endif
2375#ifdef FEAT_GUI
2376 return gui.norm_pixel;
2377#endif
2378#ifdef FEAT_TERMGUICOLORS
2379 if (cterm_normal_fg_gui_color != INVALCOLOR)
2380 return cterm_normal_fg_gui_color;
2381 // Guess that the foreground is black or white.
2382 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2383#endif
2384 }
2385 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2386 {
2387#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2388 if (gui.in_use)
2389#endif
2390#ifdef FEAT_GUI
2391 return gui.back_pixel;
2392#endif
2393#ifdef FEAT_TERMGUICOLORS
2394 if (cterm_normal_bg_gui_color != INVALCOLOR)
2395 return cterm_normal_bg_gui_color;
2396 // Guess that the background is white or black.
2397 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2398#endif
2399 }
2400
2401 return GUI_GET_COLOR(name);
2402}
Drew Vogele30d1022021-10-24 20:35:07 +01002403
2404// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2405// values as used by the MS-Windows GDI api. It should be used only for
2406// MS-Windows GDI builds.
2407# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2408# undef RGB
2409# endif
2410# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002411# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002412# endif
2413
2414# ifdef VIMDLL
2415 static guicolor_T
2416gui_adjust_rgb(guicolor_T c)
2417{
2418 if (gui.in_use)
2419 return c;
2420 else
2421 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2422}
2423# else
2424# define gui_adjust_rgb(c) (c)
2425# endif
2426
2427 static int
2428hex_digit(int c)
2429{
Keith Thompson184f71c2024-01-04 21:19:04 +01002430 if (SAFE_isdigit(c))
Drew Vogele30d1022021-10-24 20:35:07 +01002431 return c - '0';
2432 c = TOLOWER_ASC(c);
2433 if (c >= 'a' && c <= 'f')
2434 return c - 'a' + 10;
2435 return 0x1ffffff;
2436}
2437
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002438 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002439decode_hex_color(char_u *hex)
2440{
2441 guicolor_T color;
2442
2443 if (hex[0] != '#' || STRLEN(hex) != 7)
2444 return INVALCOLOR;
2445
2446 // Name is in "#rrggbb" format
2447 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2448 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2449 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2450 if (color > 0xffffff)
2451 return INVALCOLOR;
2452 return gui_adjust_rgb(color);
2453}
2454
Bram Moolenaar2a521962021-10-25 10:30:14 +01002455#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002456// Returns the color currently mapped to the given name or INVALCOLOR if no
2457// such name exists in the color table. The convention is to use lowercase for
2458// all keys in the v:colornames dictionary. The value can be either a string in
2459// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002460 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002461colorname2rgb(char_u *name)
2462{
2463 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2464 char_u *lc_name;
2465 dictitem_T *colentry;
2466 char_u *colstr;
2467 varnumber_T colnum;
2468
2469 lc_name = strlow_save(name);
2470 if (lc_name == NULL)
2471 return INVALCOLOR;
2472
2473 colentry = dict_find(colornames_table, lc_name, -1);
2474 vim_free(lc_name);
2475 if (colentry == NULL)
2476 return INVALCOLOR;
2477
2478 if (colentry->di_tv.v_type == VAR_STRING)
2479 {
2480 colstr = tv_get_string_strict(&colentry->di_tv);
2481 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2482 {
2483 return decode_hex_color(colstr);
2484 }
2485 else
2486 {
2487 semsg(_(e_bad_color_string_str), colstr);
2488 return INVALCOLOR;
2489 }
2490 }
2491
2492 if (colentry->di_tv.v_type == VAR_NUMBER)
2493 {
2494 colnum = tv_get_number(&colentry->di_tv);
2495 return (guicolor_T)colnum;
2496 }
2497
2498 return INVALCOLOR;
2499}
2500
Drew Vogele30d1022021-10-24 20:35:07 +01002501#endif
2502
2503 guicolor_T
2504gui_get_color_cmn(char_u *name)
2505{
Drew Vogele30d1022021-10-24 20:35:07 +01002506 guicolor_T color;
Drew Vogele30d1022021-10-24 20:35:07 +01002507 // Only non X11 colors (not present in rgb.txt) and colors in
John Marriott34f00dd2024-04-08 23:28:12 +02002508 // color_name_tab[], useful when $VIMRUNTIME is not found,.
2509 // must be sorted by the 'value' field because it is used by bsearch()!
2510 static keyvalue_T rgb_tab[] = {
2511 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x00), "black"),
2512 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0xFF), "blue"),
2513 KEYVALUE_ENTRY(RGB(0xA5, 0x2A, 0x2A), "brown"),
2514 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0xFF), "cyan"),
2515 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x8B), "darkblue"),
2516 KEYVALUE_ENTRY(RGB(0x00, 0x8B, 0x8B), "darkcyan"),
2517 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgray"),
2518 KEYVALUE_ENTRY(RGB(0x00, 0x64, 0x00), "darkgreen"),
2519 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgrey"),
2520 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x8B), "darkmagenta"),
2521 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x00), "darkred"),
2522 KEYVALUE_ENTRY(RGB(0x8B, 0x8B, 0x00), "darkyellow"), // No X11
2523 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "gray"),
2524 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0x00), "green"),
2525 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "grey"),
2526 KEYVALUE_ENTRY(RGB(0x66, 0x66, 0x66), "grey40"),
2527 KEYVALUE_ENTRY(RGB(0x7F, 0x7F, 0x7F), "grey50"),
2528 KEYVALUE_ENTRY(RGB(0xE5, 0xE5, 0xE5), "grey90"),
2529 KEYVALUE_ENTRY(RGB(0xAD, 0xD8, 0xE6), "lightblue"),
2530 KEYVALUE_ENTRY(RGB(0xE0, 0xFF, 0xFF), "lightcyan"),
2531 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgray"),
2532 KEYVALUE_ENTRY(RGB(0x90, 0xEE, 0x90), "lightgreen"),
2533 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgrey"),
2534 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0xFF), "lightmagenta"), // No XX
2535 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0x8B), "lightred"), // No XX
2536 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xE0), "lightyellow"),
2537 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0xFF), "magenta"),
2538 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0x00), "red"),
2539 KEYVALUE_ENTRY(RGB(0x2E, 0x8B, 0x57), "seagreen"),
2540 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xFF), "white"),
2541 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0x00), "yellow")
Drew Vogele30d1022021-10-24 20:35:07 +01002542 };
John Marriott34f00dd2024-04-08 23:28:12 +02002543 keyvalue_T target;
2544 keyvalue_T *entry;
Drew Vogele30d1022021-10-24 20:35:07 +01002545
2546 color = decode_hex_color(name);
2547 if (color != INVALCOLOR)
2548 return color;
2549
John Marriott34f00dd2024-04-08 23:28:12 +02002550 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01002551 target.value.string = name;
2552 target.value.length = 0; // not used, see cmp_keyvalue_value_i()
2553 entry = (keyvalue_T *)bsearch(&target, &rgb_tab, ARRAY_LENGTH(rgb_tab),
2554 sizeof(rgb_tab[0]), cmp_keyvalue_value_i);
John Marriott34f00dd2024-04-08 23:28:12 +02002555 if (entry != NULL)
John Marriott34f00dd2024-04-08 23:28:12 +02002556 return gui_adjust_rgb((guicolor_T)entry->key);
Drew Vogele30d1022021-10-24 20:35:07 +01002557
2558#if defined(FEAT_EVAL)
2559 /*
2560 * Not a traditional color. Load additional color aliases and then consult the alias table.
2561 */
2562
2563 color = colorname2rgb(name);
2564 if (color == INVALCOLOR)
2565 {
2566 load_default_colors_lists();
2567 color = colorname2rgb(name);
2568 }
2569
2570 return color;
2571#else
2572 return INVALCOLOR;
2573#endif
2574}
2575
2576 guicolor_T
2577gui_get_rgb_color_cmn(int r, int g, int b)
2578{
2579 guicolor_T color = RGB(r, g, b);
2580
2581 if (color > 0xffffff)
2582 return INVALCOLOR;
2583 return gui_adjust_rgb(color);
2584}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002585#endif
2586
2587/*
2588 * Table with the specifications for an attribute number.
2589 * Note that this table is used by ALL buffers. This is required because the
2590 * GUI can redraw at any time for any buffer.
2591 */
2592static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2593
2594#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2595
2596static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2597
2598#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2599
2600#ifdef FEAT_GUI
2601static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2602
2603#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2604#endif
2605
2606/*
2607 * Return the attr number for a set of colors and font.
2608 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2609 * if the combination is new.
2610 * Return 0 for error (no more room).
2611 */
2612 static int
2613get_attr_entry(garray_T *table, attrentry_T *aep)
2614{
2615 int i;
2616 attrentry_T *taep;
2617 static int recursive = FALSE;
2618
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002619 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002620 table->ga_itemsize = sizeof(attrentry_T);
2621 table->ga_growsize = 7;
2622
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002623 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002624 for (i = 0; i < table->ga_len; ++i)
2625 {
2626 taep = &(((attrentry_T *)table->ga_data)[i]);
2627 if ( aep->ae_attr == taep->ae_attr
2628 && (
2629#ifdef FEAT_GUI
2630 (table == &gui_attr_table
2631 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2632 && aep->ae_u.gui.bg_color
2633 == taep->ae_u.gui.bg_color
2634 && aep->ae_u.gui.sp_color
2635 == taep->ae_u.gui.sp_color
2636 && aep->ae_u.gui.font == taep->ae_u.gui.font
2637# ifdef FEAT_XFONTSET
2638 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2639# endif
2640 ))
2641 ||
2642#endif
2643 (table == &term_attr_table
2644 && (aep->ae_u.term.start == NULL)
2645 == (taep->ae_u.term.start == NULL)
2646 && (aep->ae_u.term.start == NULL
2647 || STRCMP(aep->ae_u.term.start,
2648 taep->ae_u.term.start) == 0)
2649 && (aep->ae_u.term.stop == NULL)
2650 == (taep->ae_u.term.stop == NULL)
2651 && (aep->ae_u.term.stop == NULL
2652 || STRCMP(aep->ae_u.term.stop,
2653 taep->ae_u.term.stop) == 0))
2654 || (table == &cterm_attr_table
2655 && aep->ae_u.cterm.fg_color
2656 == taep->ae_u.cterm.fg_color
2657 && aep->ae_u.cterm.bg_color
2658 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002659 && aep->ae_u.cterm.ul_color
2660 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002661 && aep->ae_u.cterm.font
2662 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002663#ifdef FEAT_TERMGUICOLORS
2664 && aep->ae_u.cterm.fg_rgb
2665 == taep->ae_u.cterm.fg_rgb
2666 && aep->ae_u.cterm.bg_rgb
2667 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002668 && aep->ae_u.cterm.ul_rgb
2669 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002670#endif
2671 )))
2672
2673 return i + ATTR_OFF;
2674 }
2675
2676 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2677 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002678 // Running out of attribute entries! remove all attributes, and
2679 // compute new ones for all groups.
2680 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002681 if (recursive)
2682 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002683 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002684 return 0;
2685 }
2686 recursive = TRUE;
2687
2688 clear_hl_tables();
2689
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002690 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002691
2692 for (i = 0; i < highlight_ga.ga_len; ++i)
2693 set_hl_attr(i);
2694
2695 recursive = FALSE;
2696 }
2697
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002698 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002699 if (ga_grow(table, 1) == FAIL)
2700 return 0;
2701
2702 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002703 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002704 taep->ae_attr = aep->ae_attr;
2705#ifdef FEAT_GUI
2706 if (table == &gui_attr_table)
2707 {
2708 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2709 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2710 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2711 taep->ae_u.gui.font = aep->ae_u.gui.font;
2712# ifdef FEAT_XFONTSET
2713 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2714# endif
2715 }
2716#endif
2717 if (table == &term_attr_table)
2718 {
2719 if (aep->ae_u.term.start == NULL)
2720 taep->ae_u.term.start = NULL;
2721 else
2722 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2723 if (aep->ae_u.term.stop == NULL)
2724 taep->ae_u.term.stop = NULL;
2725 else
2726 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2727 }
2728 else if (table == &cterm_attr_table)
2729 {
2730 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2731 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002732 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002733 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002734#ifdef FEAT_TERMGUICOLORS
2735 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2736 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002737 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002738#endif
2739 }
2740 ++table->ga_len;
2741 return (table->ga_len - 1 + ATTR_OFF);
2742}
2743
2744#if defined(FEAT_TERMINAL) || defined(PROTO)
2745/*
2746 * Get an attribute index for a cterm entry.
2747 * Uses an existing entry when possible or adds one when needed.
2748 */
2749 int
2750get_cterm_attr_idx(int attr, int fg, int bg)
2751{
2752 attrentry_T at_en;
2753
Bram Moolenaara80faa82020-04-12 19:37:17 +02002754 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002755#ifdef FEAT_TERMGUICOLORS
2756 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2757 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002758 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002759#endif
2760 at_en.ae_attr = attr;
2761 at_en.ae_u.cterm.fg_color = fg;
2762 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002763 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002764 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002765 return get_attr_entry(&cterm_attr_table, &at_en);
2766}
2767#endif
2768
2769#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2770/*
2771 * Get an attribute index for a 'termguicolors' entry.
2772 * Uses an existing entry when possible or adds one when needed.
2773 */
2774 int
2775get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2776{
2777 attrentry_T at_en;
2778
Bram Moolenaara80faa82020-04-12 19:37:17 +02002779 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002780 at_en.ae_attr = attr;
2781 if (fg == INVALCOLOR && bg == INVALCOLOR)
2782 {
2783 // If both GUI colors are not set fall back to the cterm colors. Helps
2784 // if the GUI only has an attribute, such as undercurl.
2785 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2786 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2787 }
2788 else
2789 {
2790 at_en.ae_u.cterm.fg_rgb = fg;
2791 at_en.ae_u.cterm.bg_rgb = bg;
2792 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002793 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002794 return get_attr_entry(&cterm_attr_table, &at_en);
2795}
2796#endif
2797
2798#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2799/*
2800 * Get an attribute index for a cterm entry.
2801 * Uses an existing entry when possible or adds one when needed.
2802 */
2803 int
2804get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2805{
2806 attrentry_T at_en;
2807
Bram Moolenaara80faa82020-04-12 19:37:17 +02002808 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002809 at_en.ae_attr = attr;
2810 at_en.ae_u.gui.fg_color = fg;
2811 at_en.ae_u.gui.bg_color = bg;
2812 return get_attr_entry(&gui_attr_table, &at_en);
2813}
2814#endif
2815
2816/*
2817 * Clear all highlight tables.
2818 */
2819 void
2820clear_hl_tables(void)
2821{
2822 int i;
2823 attrentry_T *taep;
2824
2825#ifdef FEAT_GUI
2826 ga_clear(&gui_attr_table);
2827#endif
2828 for (i = 0; i < term_attr_table.ga_len; ++i)
2829 {
2830 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2831 vim_free(taep->ae_u.term.start);
2832 vim_free(taep->ae_u.term.stop);
2833 }
2834 ga_clear(&term_attr_table);
2835 ga_clear(&cterm_attr_table);
2836}
2837
2838/*
2839 * Combine special attributes (e.g., for spelling) with other attributes
2840 * (e.g., for syntax highlighting).
2841 * "prim_attr" overrules "char_attr".
2842 * This creates a new group when required.
2843 * Since we expect there to be few spelling mistakes we don't cache the
2844 * result.
2845 * Return the resulting attributes.
2846 */
2847 int
2848hl_combine_attr(int char_attr, int prim_attr)
2849{
2850 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002851 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002852 attrentry_T new_en;
2853
2854 if (char_attr == 0)
2855 return prim_attr;
2856 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2857 return ATTR_COMBINE(char_attr, prim_attr);
2858#ifdef FEAT_GUI
2859 if (gui.in_use)
2860 {
2861 if (char_attr > HL_ALL)
2862 char_aep = syn_gui_attr2entry(char_attr);
2863 if (char_aep != NULL)
2864 new_en = *char_aep;
2865 else
2866 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002867 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002868 new_en.ae_u.gui.fg_color = INVALCOLOR;
2869 new_en.ae_u.gui.bg_color = INVALCOLOR;
2870 new_en.ae_u.gui.sp_color = INVALCOLOR;
2871 if (char_attr <= HL_ALL)
2872 new_en.ae_attr = char_attr;
2873 }
2874
2875 if (prim_attr <= HL_ALL)
2876 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2877 else
2878 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002879 prim_aep = syn_gui_attr2entry(prim_attr);
2880 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002881 {
2882 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002883 prim_aep->ae_attr);
2884 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2885 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2886 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2887 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2888 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2889 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2890 if (prim_aep->ae_u.gui.font != NOFONT)
2891 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002892# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002893 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2894 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002895# endif
2896 }
2897 }
2898 return get_attr_entry(&gui_attr_table, &new_en);
2899 }
2900#endif
2901
2902 if (IS_CTERM)
2903 {
2904 if (char_attr > HL_ALL)
2905 char_aep = syn_cterm_attr2entry(char_attr);
2906 if (char_aep != NULL)
2907 new_en = *char_aep;
2908 else
2909 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002910 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002911#ifdef FEAT_TERMGUICOLORS
2912 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2913 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002914 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002915#endif
2916 if (char_attr <= HL_ALL)
2917 new_en.ae_attr = char_attr;
2918 }
2919
2920 if (prim_attr <= HL_ALL)
2921 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2922 else
2923 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002924 prim_aep = syn_cterm_attr2entry(prim_attr);
2925 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002926 {
2927 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002928 prim_aep->ae_attr);
2929 if (prim_aep->ae_u.cterm.fg_color > 0)
2930 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2931 if (prim_aep->ae_u.cterm.bg_color > 0)
2932 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2933 if (prim_aep->ae_u.cterm.ul_color > 0)
2934 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002935 if (prim_aep->ae_u.cterm.font > 0)
2936 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002937#ifdef FEAT_TERMGUICOLORS
2938 // If both fg and bg are not set fall back to cterm colors.
2939 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002940 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2941 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002942 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002943 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002944 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002945 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002946 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2947 }
2948 else
2949 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002950 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2951 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2952 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2953 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002954 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002955 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2956 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002957#endif
2958 }
2959 }
2960 return get_attr_entry(&cterm_attr_table, &new_en);
2961 }
2962
2963 if (char_attr > HL_ALL)
2964 char_aep = syn_term_attr2entry(char_attr);
2965 if (char_aep != NULL)
2966 new_en = *char_aep;
2967 else
2968 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002969 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002970 if (char_attr <= HL_ALL)
2971 new_en.ae_attr = char_attr;
2972 }
2973
2974 if (prim_attr <= HL_ALL)
2975 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2976 else
2977 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002978 prim_aep = syn_term_attr2entry(prim_attr);
2979 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002980 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002981 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2982 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002983 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002984 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2985 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002986 }
2987 }
2988 }
2989 return get_attr_entry(&term_attr_table, &new_en);
2990}
2991
2992#ifdef FEAT_GUI
2993 attrentry_T *
2994syn_gui_attr2entry(int attr)
2995{
2996 attr -= ATTR_OFF;
2997 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2998 return NULL;
2999 return &(GUI_ATTR_ENTRY(attr));
3000}
3001#endif
3002
3003/*
3004 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
3005 * Only to be used when "attr" > HL_ALL.
3006 */
3007 int
3008syn_attr2attr(int attr)
3009{
3010 attrentry_T *aep;
3011
3012#ifdef FEAT_GUI
3013 if (gui.in_use)
3014 aep = syn_gui_attr2entry(attr);
3015 else
3016#endif
3017 if (IS_CTERM)
3018 aep = syn_cterm_attr2entry(attr);
3019 else
3020 aep = syn_term_attr2entry(attr);
3021
3022 if (aep == NULL) // highlighting not set
3023 return 0;
3024 return aep->ae_attr;
3025}
3026
3027
3028 attrentry_T *
3029syn_term_attr2entry(int attr)
3030{
3031 attr -= ATTR_OFF;
3032 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
3033 return NULL;
3034 return &(TERM_ATTR_ENTRY(attr));
3035}
3036
3037 attrentry_T *
3038syn_cterm_attr2entry(int attr)
3039{
3040 attr -= ATTR_OFF;
3041 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
3042 return NULL;
3043 return &(CTERM_ATTR_ENTRY(attr));
3044}
3045
3046#define LIST_ATTR 1
3047#define LIST_STRING 2
3048#define LIST_INT 3
3049
3050 static void
3051highlight_list_one(int id)
3052{
3053 hl_group_T *sgp;
3054 int didh = FALSE;
3055
3056 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
3057
3058 if (message_filtered(sgp->sg_name))
3059 return;
3060
3061 didh = highlight_list_arg(id, didh, LIST_ATTR,
3062 sgp->sg_term, NULL, "term");
3063 didh = highlight_list_arg(id, didh, LIST_STRING,
3064 0, sgp->sg_start, "start");
3065 didh = highlight_list_arg(id, didh, LIST_STRING,
3066 0, sgp->sg_stop, "stop");
3067
3068 didh = highlight_list_arg(id, didh, LIST_ATTR,
3069 sgp->sg_cterm, NULL, "cterm");
3070 didh = highlight_list_arg(id, didh, LIST_INT,
3071 sgp->sg_cterm_fg, NULL, "ctermfg");
3072 didh = highlight_list_arg(id, didh, LIST_INT,
3073 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02003074 didh = highlight_list_arg(id, didh, LIST_INT,
3075 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01003076 didh = highlight_list_arg(id, didh, LIST_INT,
3077 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003078
3079#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3080 didh = highlight_list_arg(id, didh, LIST_ATTR,
3081 sgp->sg_gui, NULL, "gui");
3082 didh = highlight_list_arg(id, didh, LIST_STRING,
3083 0, sgp->sg_gui_fg_name, "guifg");
3084 didh = highlight_list_arg(id, didh, LIST_STRING,
3085 0, sgp->sg_gui_bg_name, "guibg");
3086 didh = highlight_list_arg(id, didh, LIST_STRING,
3087 0, sgp->sg_gui_sp_name, "guisp");
3088#endif
3089#ifdef FEAT_GUI
3090 didh = highlight_list_arg(id, didh, LIST_STRING,
3091 0, sgp->sg_font_name, "font");
3092#endif
3093
3094 if (sgp->sg_link && !got_int)
3095 {
3096 (void)syn_list_header(didh, 9999, id);
3097 didh = TRUE;
3098 msg_puts_attr("links to", HL_ATTR(HLF_D));
3099 msg_putchar(' ');
3100 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3101 }
3102
3103 if (!didh)
3104 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3105#ifdef FEAT_EVAL
3106 if (p_verbose > 0)
3107 last_set_msg(sgp->sg_script_ctx);
3108#endif
3109}
3110
3111 static int
3112highlight_list_arg(
3113 int id,
3114 int didh,
3115 int type,
3116 int iarg,
3117 char_u *sarg,
3118 char *name)
3119{
Bram Moolenaar84f54632022-06-29 18:39:11 +01003120 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003121 char_u *ts;
3122 int i;
3123
3124 if (got_int)
3125 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003126
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003127 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3128 return didh;
3129
3130 ts = buf;
3131 if (type == LIST_INT)
3132 sprintf((char *)buf, "%d", iarg - 1);
3133 else if (type == LIST_STRING)
3134 ts = sarg;
3135 else // type == LIST_ATTR
3136 {
John Marriott34f00dd2024-04-08 23:28:12 +02003137 size_t buflen;
3138
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003139 buf[0] = NUL;
John Marriott34f00dd2024-04-08 23:28:12 +02003140 buflen = 0;
3141 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003142 {
John Marriott34f00dd2024-04-08 23:28:12 +02003143 if (iarg & highlight_index_tab[i]->key)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003144 {
John Marriott34f00dd2024-04-08 23:28:12 +02003145 if (buflen > 0)
3146 {
3147 STRCPY(buf + buflen, (char_u *)",");
3148 ++buflen;
3149 }
John Marriott8d4477e2024-11-02 15:59:01 +01003150 STRCPY(buf + buflen, highlight_index_tab[i]->value.string);
3151 buflen += highlight_index_tab[i]->value.length;
John Marriott34f00dd2024-04-08 23:28:12 +02003152 iarg &= ~highlight_index_tab[i]->key; // don't want "inverse"/"reverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003153 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003154 }
3155 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003156
3157 (void)syn_list_header(didh,
3158 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3159 didh = TRUE;
3160 if (!got_int)
3161 {
3162 if (*name != NUL)
3163 {
3164 msg_puts_attr(name, HL_ATTR(HLF_D));
3165 msg_puts_attr("=", HL_ATTR(HLF_D));
3166 }
3167 msg_outtrans(ts);
3168 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003169 return didh;
3170}
3171
3172#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3173/*
3174 * Return "1" if highlight group "id" has attribute "flag".
3175 * Return NULL otherwise.
3176 */
3177 char_u *
3178highlight_has_attr(
3179 int id,
3180 int flag,
3181 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3182{
3183 int attr;
3184
3185 if (id <= 0 || id > highlight_ga.ga_len)
3186 return NULL;
3187
3188#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3189 if (modec == 'g')
3190 attr = HL_TABLE()[id - 1].sg_gui;
3191 else
3192#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003193 {
3194 if (modec == 'c')
3195 attr = HL_TABLE()[id - 1].sg_cterm;
3196 else
3197 attr = HL_TABLE()[id - 1].sg_term;
3198 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003199
3200 if (attr & flag)
3201 return (char_u *)"1";
3202 return NULL;
3203}
3204#endif
3205
3206#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3207/*
3208 * Return color name of highlight group "id".
3209 */
3210 char_u *
3211highlight_color(
3212 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003213 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003214 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3215{
3216 static char_u name[20];
3217 int n;
3218 int fg = FALSE;
3219 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003220 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003221 int font = FALSE;
3222
3223 if (id <= 0 || id > highlight_ga.ga_len)
3224 return NULL;
3225
3226 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3227 fg = TRUE;
3228 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3229 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3230 font = TRUE;
3231 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3232 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003233 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3234 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003235 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3236 return NULL;
3237 if (modec == 'g')
3238 {
3239# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3240# ifdef FEAT_GUI
3241 // return font name
3242 if (font)
3243 return HL_TABLE()[id - 1].sg_font_name;
3244# endif
3245
3246 // return #RRGGBB form (only possible when GUI is running)
3247 if ((USE_24BIT) && what[2] == '#')
3248 {
3249 guicolor_T color;
3250 long_u rgb;
3251 static char_u buf[10];
3252
3253 if (fg)
3254 color = HL_TABLE()[id - 1].sg_gui_fg;
3255 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003256 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003257 else
3258 color = HL_TABLE()[id - 1].sg_gui_bg;
3259 if (color == INVALCOLOR)
3260 return NULL;
3261 rgb = (long_u)GUI_MCH_GET_RGB(color);
3262 sprintf((char *)buf, "#%02x%02x%02x",
3263 (unsigned)(rgb >> 16),
3264 (unsigned)(rgb >> 8) & 255,
3265 (unsigned)rgb & 255);
3266 return buf;
3267 }
3268# endif
3269 if (fg)
3270 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3271 if (sp)
3272 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3273 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3274 }
PMuncha606f3a2023-11-15 15:35:49 +01003275 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003276 return NULL;
3277 if (modec == 'c')
3278 {
3279 if (fg)
3280 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003281 else if (ul)
3282 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003283 else if (font)
3284 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003285 else
3286 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3287 if (n < 0)
3288 return NULL;
3289 sprintf((char *)name, "%d", n);
3290 return name;
3291 }
3292 // term doesn't have color
3293 return NULL;
3294}
3295#endif
3296
3297#if (defined(FEAT_SYN_HL) \
3298 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3299 && defined(FEAT_PRINTER)) || defined(PROTO)
3300/*
3301 * Return color name of highlight group "id" as RGB value.
3302 */
3303 long_u
3304highlight_gui_color_rgb(
3305 int id,
3306 int fg) // TRUE = fg, FALSE = bg
3307{
3308 guicolor_T color;
3309
3310 if (id <= 0 || id > highlight_ga.ga_len)
3311 return 0L;
3312
3313 if (fg)
3314 color = HL_TABLE()[id - 1].sg_gui_fg;
3315 else
3316 color = HL_TABLE()[id - 1].sg_gui_bg;
3317
3318 if (color == INVALCOLOR)
3319 return 0L;
3320
3321 return GUI_MCH_GET_RGB(color);
3322}
3323#endif
3324
3325/*
3326 * Output the syntax list header.
3327 * Return TRUE when started a new line.
3328 */
3329 int
3330syn_list_header(
3331 int did_header, // did header already
3332 int outlen, // length of string that comes
3333 int id) // highlight group id
3334{
3335 int endcol = 19;
3336 int newline = TRUE;
3337 int name_col = 0;
3338
3339 if (!did_header)
3340 {
3341 msg_putchar('\n');
3342 if (got_int)
3343 return TRUE;
3344 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3345 name_col = msg_col;
3346 endcol = 15;
3347 }
3348 else if (msg_col + outlen + 1 >= Columns)
3349 {
3350 msg_putchar('\n');
3351 if (got_int)
3352 return TRUE;
3353 }
3354 else
3355 {
3356 if (msg_col >= endcol) // wrap around is like starting a new line
3357 newline = FALSE;
3358 }
3359
3360 if (msg_col >= endcol) // output at least one space
3361 endcol = msg_col + 1;
Christian Brabandt220474d2024-07-20 13:26:44 +02003362 if (Columns <= (long)endcol) // avoid hang for tiny window
3363 endcol = (int)(Columns - 1);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003364
3365 msg_advance(endcol);
3366
3367 // Show "xxx" with the attributes.
3368 if (!did_header)
3369 {
3370 if (endcol == Columns - 1 && endcol <= name_col)
3371 msg_putchar(' ');
3372 msg_puts_attr("xxx", syn_id2attr(id));
3373 msg_putchar(' ');
3374 }
3375
3376 return newline;
3377}
3378
3379/*
3380 * Set the attribute numbers for a highlight group.
3381 * Called after one of the attributes has changed.
3382 */
3383 static void
3384set_hl_attr(
3385 int idx) // index in array
3386{
3387 attrentry_T at_en;
3388 hl_group_T *sgp = HL_TABLE() + idx;
3389
3390 // The "Normal" group doesn't need an attribute number
3391 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3392 return;
3393
3394#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003395 // For the GUI mode: If there are other than "normal" highlighting
3396 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003397 if (sgp->sg_gui_fg == INVALCOLOR
3398 && sgp->sg_gui_bg == INVALCOLOR
3399 && sgp->sg_gui_sp == INVALCOLOR
3400 && sgp->sg_font == NOFONT
3401# ifdef FEAT_XFONTSET
3402 && sgp->sg_fontset == NOFONTSET
3403# endif
3404 )
3405 {
3406 sgp->sg_gui_attr = sgp->sg_gui;
3407 }
3408 else
3409 {
3410 at_en.ae_attr = sgp->sg_gui;
3411 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3412 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3413 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3414 at_en.ae_u.gui.font = sgp->sg_font;
3415# ifdef FEAT_XFONTSET
3416 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3417# endif
3418 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3419 }
3420#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003421 // For the term mode: If there are other than "normal" highlighting
3422 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003423 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3424 sgp->sg_term_attr = sgp->sg_term;
3425 else
3426 {
3427 at_en.ae_attr = sgp->sg_term;
3428 at_en.ae_u.term.start = sgp->sg_start;
3429 at_en.ae_u.term.stop = sgp->sg_stop;
3430 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3431 }
3432
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003433 // For the color term mode: If there are other than "normal"
3434 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003435 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3436 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003437# ifdef FEAT_TERMGUICOLORS
3438 && sgp->sg_gui_fg == INVALCOLOR
3439 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003440 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003441# endif
3442 )
3443 sgp->sg_cterm_attr = sgp->sg_cterm;
3444 else
3445 {
3446 at_en.ae_attr = sgp->sg_cterm;
3447 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3448 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003449 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003450 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003451# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003452 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3453 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003454 // Only use the underline/undercurl color when used, it may clear the
3455 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003456 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3457 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003458 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3459 else
3460 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003461 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3462 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3463 {
3464 // If both fg and bg are invalid fall back to the cterm colors.
3465 // Helps when the GUI only uses an attribute, e.g. undercurl.
3466 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3467 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3468 }
3469# endif
3470 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3471 }
3472}
3473
3474/*
3475 * Lookup a highlight group name and return its ID.
3476 * If it is not found, 0 is returned.
3477 */
3478 int
3479syn_name2id(char_u *name)
3480{
3481 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003482 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003483
3484 // Avoid using stricmp() too much, it's slow on some systems
3485 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3486 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003487 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003488 vim_strup(name_u);
3489 for (i = highlight_ga.ga_len; --i >= 0; )
3490 if (HL_TABLE()[i].sg_name_u != NULL
3491 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3492 break;
3493 return i + 1;
3494}
3495
3496/*
3497 * Lookup a highlight group name and return its attributes.
3498 * Return zero if not found.
3499 */
3500 int
3501syn_name2attr(char_u *name)
3502{
3503 int id = syn_name2id(name);
3504
3505 if (id != 0)
3506 return syn_id2attr(id);
3507 return 0;
3508}
3509
3510#if defined(FEAT_EVAL) || defined(PROTO)
3511/*
3512 * Return TRUE if highlight group "name" exists.
3513 */
3514 int
3515highlight_exists(char_u *name)
3516{
3517 return (syn_name2id(name) > 0);
3518}
3519
3520# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3521/*
3522 * Return the name of highlight group "id".
3523 * When not a valid ID return an empty string.
3524 */
3525 char_u *
3526syn_id2name(int id)
3527{
3528 if (id <= 0 || id > highlight_ga.ga_len)
3529 return (char_u *)"";
3530 return HL_TABLE()[id - 1].sg_name;
3531}
3532# endif
3533#endif
3534
3535/*
3536 * Like syn_name2id(), but take a pointer + length argument.
3537 */
3538 int
3539syn_namen2id(char_u *linep, int len)
3540{
3541 char_u *name;
3542 int id = 0;
3543
3544 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003545 if (name == NULL)
3546 return 0;
3547
3548 id = syn_name2id(name);
3549 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003550 return id;
3551}
3552
3553/*
3554 * Find highlight group name in the table and return its ID.
3555 * The argument is a pointer to the name and the length of the name.
3556 * If it doesn't exist yet, a new entry is created.
3557 * Return 0 for failure.
3558 */
3559 int
3560syn_check_group(char_u *pp, int len)
3561{
3562 int id;
3563 char_u *name;
3564
erw7f7f7aaf2021-12-07 21:29:20 +00003565 if (len > MAX_SYN_NAME)
3566 {
3567 emsg(_(e_highlight_group_name_too_long));
3568 return 0;
3569 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003570 name = vim_strnsave(pp, len);
3571 if (name == NULL)
3572 return 0;
3573
3574 id = syn_name2id(name);
3575 if (id == 0) // doesn't exist yet
3576 id = syn_add_group(name);
3577 else
3578 vim_free(name);
3579 return id;
3580}
3581
3582/*
3583 * Add new highlight group and return its ID.
3584 * "name" must be an allocated string, it will be consumed.
3585 * Return 0 for failure.
3586 */
3587 static int
3588syn_add_group(char_u *name)
3589{
3590 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003591 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003592
Gregory Andersd4376dc2023-08-20 19:14:03 +02003593 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003594 for (p = name; *p != NUL; ++p)
3595 {
3596 if (!vim_isprintc(*p))
3597 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003598 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003599 vim_free(name);
3600 return 0;
3601 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003602 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003603 {
3604 // This is an error, but since there previously was no check only
3605 // give a warning.
3606 msg_source(HL_ATTR(HLF_W));
3607 msg(_("W18: Invalid character in group name"));
3608 break;
3609 }
3610 }
3611
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003612 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003613 if (highlight_ga.ga_data == NULL)
3614 {
3615 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3616 highlight_ga.ga_growsize = 10;
3617 }
3618
3619 if (highlight_ga.ga_len >= MAX_HL_ID)
3620 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003621 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003622 vim_free(name);
3623 return 0;
3624 }
3625
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003626 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003627 if (ga_grow(&highlight_ga, 1) == FAIL)
3628 {
3629 vim_free(name);
3630 return 0;
3631 }
3632
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003633 name_up = vim_strsave_up(name);
3634 if (name_up == NULL)
3635 {
3636 vim_free(name);
3637 return 0;
3638 }
3639
Bram Moolenaara80faa82020-04-12 19:37:17 +02003640 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003641 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003642 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003643#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3644 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3645 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003646 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003647#endif
3648 ++highlight_ga.ga_len;
3649
3650 return highlight_ga.ga_len; // ID is index plus one
3651}
3652
3653/*
3654 * When, just after calling syn_add_group(), an error is discovered, this
3655 * function deletes the new name.
3656 */
3657 static void
3658syn_unadd_group(void)
3659{
3660 --highlight_ga.ga_len;
3661 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3662 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3663}
3664
3665/*
3666 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003667 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003668 */
3669 int
3670syn_id2attr(int hl_id)
3671{
3672 int attr;
3673 hl_group_T *sgp;
3674
3675 hl_id = syn_get_final_id(hl_id);
3676 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3677
3678#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003679 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003680 if (gui.in_use)
3681 attr = sgp->sg_gui_attr;
3682 else
3683#endif
3684 if (IS_CTERM)
3685 attr = sgp->sg_cterm_attr;
3686 else
3687 attr = sgp->sg_term_attr;
3688
3689 return attr;
3690}
3691
3692#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3693/*
3694 * Get the GUI colors and attributes for a group ID.
3695 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3696 */
3697 int
3698syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3699{
3700 hl_group_T *sgp;
3701
3702 hl_id = syn_get_final_id(hl_id);
3703 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3704
3705 *fgp = sgp->sg_gui_fg;
3706 *bgp = sgp->sg_gui_bg;
3707 return sgp->sg_gui;
3708}
3709#endif
3710
3711#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003712 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3713 && defined(FEAT_TERMGUICOLORS)) \
3714 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003715 void
3716syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3717{
3718 hl_group_T *sgp;
3719
3720 hl_id = syn_get_final_id(hl_id);
3721 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3722 *fgp = sgp->sg_cterm_fg - 1;
3723 *bgp = sgp->sg_cterm_bg - 1;
3724}
3725#endif
3726
3727/*
3728 * Translate a group ID to the final group ID (following links).
3729 */
3730 int
3731syn_get_final_id(int hl_id)
3732{
3733 int count;
3734 hl_group_T *sgp;
3735
3736 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3737 return 0; // Can be called from eval!!
3738
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003739 // Follow links until there is no more.
3740 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003741 for (count = 100; --count >= 0; )
3742 {
3743 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3744 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3745 break;
3746 hl_id = sgp->sg_link;
3747 }
3748
3749 return hl_id;
3750}
3751
3752#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3753/*
3754 * Call this function just after the GUI has started.
3755 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3756 * It finds the font and color handles for the highlighting groups.
3757 */
3758 void
3759highlight_gui_started(void)
3760{
3761 int idx;
3762
3763 // First get the colors from the "Normal" and "Menu" group, if set
3764 if (USE_24BIT)
3765 set_normal_colors();
3766
3767 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3768 gui_do_one_color(idx, FALSE, FALSE);
3769
3770 highlight_changed();
3771}
3772
3773 static void
3774gui_do_one_color(
3775 int idx,
3776 int do_menu UNUSED, // TRUE: might set the menu font
3777 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3778{
3779 int didit = FALSE;
3780
3781# ifdef FEAT_GUI
3782# ifdef FEAT_TERMGUICOLORS
3783 if (gui.in_use)
3784# endif
3785 if (HL_TABLE()[idx].sg_font_name != NULL)
3786 {
3787 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3788 do_tooltip, TRUE);
3789 didit = TRUE;
3790 }
3791# endif
3792 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3793 {
3794 HL_TABLE()[idx].sg_gui_fg =
3795 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3796 didit = TRUE;
3797 }
3798 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3799 {
3800 HL_TABLE()[idx].sg_gui_bg =
3801 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3802 didit = TRUE;
3803 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003804 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3805 {
3806 HL_TABLE()[idx].sg_gui_sp =
3807 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3808 didit = TRUE;
3809 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003810 if (didit) // need to get a new attr number
3811 set_hl_attr(idx);
3812}
3813#endif
3814
3815#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3816/*
3817 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3818 */
3819 static void
3820combine_stl_hlt(
3821 int id,
3822 int id_S,
3823 int id_alt,
3824 int hlcnt,
3825 int i,
3826 int hlf,
3827 int *table)
3828{
3829 hl_group_T *hlt = HL_TABLE();
3830
3831 if (id_alt == 0)
3832 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003833 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003834 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3835 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3836# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3837 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3838# endif
3839 }
3840 else
3841 mch_memmove(&hlt[hlcnt + i],
3842 &hlt[id_alt - 1],
3843 sizeof(hl_group_T));
3844 hlt[hlcnt + i].sg_link = 0;
3845
3846 hlt[hlcnt + i].sg_term ^=
3847 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3848 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3849 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3850 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3851 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3852 hlt[hlcnt + i].sg_cterm ^=
3853 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3854 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3855 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3856 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3857 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003858 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3859 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003860# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3861 hlt[hlcnt + i].sg_gui ^=
3862 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3863# endif
3864# ifdef FEAT_GUI
3865 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3866 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3867 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3868 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3869 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3870 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3871 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3872 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3873# ifdef FEAT_XFONTSET
3874 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3875 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3876# endif
3877# endif
3878 highlight_ga.ga_len = hlcnt + i + 1;
3879 set_hl_attr(hlcnt + i); // At long last we can apply
3880 table[i] = syn_id2attr(hlcnt + i + 1);
3881}
3882#endif
3883
3884/*
3885 * Translate the 'highlight' option into attributes in highlight_attr[] and
3886 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3887 * corresponding highlights to use on top of HLF_SNC is computed.
3888 * Called only when the 'highlight' option has been changed and upon first
3889 * screen redraw after any :highlight command.
3890 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3891 */
3892 int
3893highlight_changed(void)
3894{
3895 int hlf;
3896 int i;
3897 char_u *p;
3898 int attr;
3899 char_u *end;
3900 int id;
3901#ifdef USER_HIGHLIGHT
3902 char_u userhl[30]; // use 30 to avoid compiler warning
3903# ifdef FEAT_STL_OPT
3904 int id_S = -1;
3905 int id_SNC = 0;
3906# ifdef FEAT_TERMINAL
3907 int id_ST = 0;
3908 int id_STNC = 0;
3909# endif
3910 int hlcnt;
3911# endif
3912#endif
3913 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3914
3915 need_highlight_changed = FALSE;
3916
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003917#ifdef FEAT_TERMINAL
3918 term_update_colors_all();
3919 term_update_wincolor_all();
3920#endif
3921
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003922 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003923 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3924 highlight_attr[hlf] = 0;
3925
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003926 // First set all attributes to their default value.
3927 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003928 for (i = 0; i < 2; ++i)
3929 {
3930 if (i)
3931 p = p_hl;
3932 else
3933 p = get_highlight_default();
3934 if (p == NULL) // just in case
3935 continue;
3936
3937 while (*p)
3938 {
3939 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3940 if (hl_flags[hlf] == *p)
3941 break;
3942 ++p;
3943 if (hlf == (int)HLF_COUNT || *p == NUL)
3944 return FAIL;
3945
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003946 // Allow several hl_flags to be combined, like "bu" for
3947 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003948 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003949 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003950 {
3951 if (VIM_ISWHITE(*p)) // ignore white space
3952 continue;
3953
3954 if (attr > HL_ALL) // Combination with ':' is not allowed.
3955 return FAIL;
3956
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003957 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003958 switch (*p)
3959 {
3960 case 'b': attr |= HL_BOLD;
3961 break;
3962 case 'i': attr |= HL_ITALIC;
3963 break;
3964 case '-':
3965 case 'n': // no highlighting
3966 break;
3967 case 'r': attr |= HL_INVERSE;
3968 break;
3969 case 's': attr |= HL_STANDOUT;
3970 break;
3971 case 'u': attr |= HL_UNDERLINE;
3972 break;
3973 case 'c': attr |= HL_UNDERCURL;
3974 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003975 case '2': attr |= HL_UNDERDOUBLE;
3976 break;
3977 case 'd': attr |= HL_UNDERDOTTED;
3978 break;
3979 case '=': attr |= HL_UNDERDASHED;
3980 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003981 case 't': attr |= HL_STRIKETHROUGH;
3982 break;
3983 case ':': ++p; // highlight group name
3984 if (attr || *p == NUL) // no combinations
3985 return FAIL;
3986 end = vim_strchr(p, ',');
3987 if (end == NULL)
3988 end = p + STRLEN(p);
3989 id = syn_check_group(p, (int)(end - p));
3990 if (id == 0)
3991 return FAIL;
3992 attr = syn_id2attr(id);
3993 p = end - 1;
3994#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3995 if (hlf == (int)HLF_SNC)
3996 id_SNC = syn_get_final_id(id);
3997# ifdef FEAT_TERMINAL
3998 else if (hlf == (int)HLF_ST)
3999 id_ST = syn_get_final_id(id);
4000 else if (hlf == (int)HLF_STNC)
4001 id_STNC = syn_get_final_id(id);
4002# endif
4003 else if (hlf == (int)HLF_S)
4004 id_S = syn_get_final_id(id);
4005#endif
4006 break;
4007 default: return FAIL;
4008 }
4009 }
4010 highlight_attr[hlf] = attr;
4011
4012 p = skip_to_option_part(p); // skip comma and spaces
4013 }
4014 }
4015
4016#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01004017 // Setup the user highlights
4018 //
4019 // Temporarily utilize 28 more hl entries:
4020 // 9 for User1-User9 combined with StatusLineNC
4021 // 9 for User1-User9 combined with StatusLineTerm
4022 // 9 for User1-User9 combined with StatusLineTermNC
4023 // 1 for StatusLine default
4024 // Have to be in there simultaneously in case of table overflows in
4025 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004026# ifdef FEAT_STL_OPT
4027 if (ga_grow(&highlight_ga, 28) == FAIL)
4028 return FAIL;
4029 hlcnt = highlight_ga.ga_len;
4030 if (id_S == -1)
4031 {
4032 // Make sure id_S is always valid to simplify code below. Use the last
4033 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02004034 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004035 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
4036 id_S = hlcnt + 19;
4037 }
4038# endif
4039 for (i = 0; i < 9; i++)
4040 {
4041 sprintf((char *)userhl, "User%d", i + 1);
4042 id = syn_name2id(userhl);
4043 if (id == 0)
4044 {
4045 highlight_user[i] = 0;
4046# ifdef FEAT_STL_OPT
4047 highlight_stlnc[i] = 0;
4048# ifdef FEAT_TERMINAL
4049 highlight_stlterm[i] = 0;
4050 highlight_stltermnc[i] = 0;
4051# endif
4052# endif
4053 }
4054 else
4055 {
4056 highlight_user[i] = syn_id2attr(id);
4057# ifdef FEAT_STL_OPT
4058 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
4059 HLF_SNC, highlight_stlnc);
4060# ifdef FEAT_TERMINAL
4061 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
4062 HLF_ST, highlight_stlterm);
4063 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
4064 HLF_STNC, highlight_stltermnc);
4065# endif
4066# endif
4067 }
4068 }
4069# ifdef FEAT_STL_OPT
4070 highlight_ga.ga_len = hlcnt;
4071# endif
4072
4073#endif // USER_HIGHLIGHT
4074
4075 return OK;
4076}
4077
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004078static void highlight_list(void);
4079static void highlight_list_two(int cnt, int attr);
4080
4081/*
4082 * Handle command line completion for :highlight command.
4083 */
4084 void
4085set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
4086{
4087 char_u *p;
4088
4089 // Default: expand group names
4090 xp->xp_context = EXPAND_HIGHLIGHT;
4091 xp->xp_pattern = arg;
4092 include_link = 2;
4093 include_default = 1;
4094
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004095 if (*arg == NUL)
4096 return;
4097
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004098 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004099 p = skiptowhite(arg);
4100 if (*p == NUL)
4101 return;
4102
4103 // past "default" or group name
4104 include_default = 0;
4105 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004106 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004107 arg = skipwhite(p);
4108 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004109 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004110 }
4111 if (*p == NUL)
4112 return;
4113
4114 // past group name
4115 include_link = 0;
4116 if (arg[1] == 'i' && arg[0] == 'N')
4117 highlight_list();
4118 if (STRNCMP("link", arg, p - arg) == 0
4119 || STRNCMP("clear", arg, p - arg) == 0)
4120 {
4121 xp->xp_pattern = skipwhite(p);
4122 p = skiptowhite(xp->xp_pattern);
4123 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004124 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004125 xp->xp_pattern = skipwhite(p);
4126 p = skiptowhite(xp->xp_pattern);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004127 }
4128 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004129 if (*p != NUL) // past group name(s)
4130 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004131}
4132
4133/*
4134 * List highlighting matches in a nice way.
4135 */
4136 static void
4137highlight_list(void)
4138{
4139 int i;
4140
4141 for (i = 10; --i >= 0; )
4142 highlight_list_two(i, HL_ATTR(HLF_D));
4143 for (i = 40; --i >= 0; )
4144 highlight_list_two(99, 0);
4145}
4146
4147 static void
4148highlight_list_two(int cnt, int attr)
4149{
4150 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4151 msg_clr_eos();
4152 out_flush();
4153 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4154}
4155
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004156/*
4157 * Function given to ExpandGeneric() to obtain the list of group names.
4158 */
4159 char_u *
4160get_highlight_name(expand_T *xp UNUSED, int idx)
4161{
4162 return get_highlight_name_ext(xp, idx, TRUE);
4163}
4164
4165/*
4166 * Obtain a highlight group name.
4167 * When "skip_cleared" is TRUE don't return a cleared entry.
4168 */
4169 char_u *
4170get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4171{
4172 if (idx < 0)
4173 return NULL;
4174
4175 // Items are never removed from the table, skip the ones that were
4176 // cleared.
4177 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4178 return (char_u *)"";
4179
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004180 if (idx == highlight_ga.ga_len && include_none != 0)
4181 return (char_u *)"none";
4182 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4183 return (char_u *)"default";
4184 if (idx == highlight_ga.ga_len + include_none + include_default
4185 && include_link != 0)
4186 return (char_u *)"link";
4187 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4188 && include_link != 0)
4189 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004190 if (idx >= highlight_ga.ga_len)
4191 return NULL;
4192 return HL_TABLE()[idx].sg_name;
4193}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004194
4195#if defined(FEAT_GUI) || defined(PROTO)
4196/*
4197 * Free all the highlight group fonts.
4198 * Used when quitting for systems which need it.
4199 */
4200 void
4201free_highlight_fonts(void)
4202{
4203 int idx;
4204
4205 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4206 {
4207 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4208 HL_TABLE()[idx].sg_font = NOFONT;
4209# ifdef FEAT_XFONTSET
4210 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4211 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4212# endif
4213 }
4214
4215 gui_mch_free_font(gui.norm_font);
4216# ifdef FEAT_XFONTSET
4217 gui_mch_free_fontset(gui.fontset);
4218# endif
4219# ifndef FEAT_GUI_GTK
4220 gui_mch_free_font(gui.bold_font);
4221 gui_mch_free_font(gui.ital_font);
4222 gui_mch_free_font(gui.boldital_font);
4223# endif
4224}
4225#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004226
4227#if defined(FEAT_EVAL) || defined(PROTO)
4228/*
4229 * Convert each of the highlight attribute bits (bold, standout, underline,
4230 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4231 * the attribute name as the key.
4232 */
4233 static dict_T *
4234highlight_get_attr_dict(int hlattr)
4235{
4236 dict_T *dict;
4237 int i;
4238
4239 dict = dict_alloc();
4240 if (dict == NULL)
4241 return NULL;
4242
John Marriott34f00dd2024-04-08 23:28:12 +02004243 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004244 {
John Marriott34f00dd2024-04-08 23:28:12 +02004245 if (hlattr & highlight_index_tab[i]->key)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004246 {
John Marriott8d4477e2024-11-02 15:59:01 +01004247 dict_add_bool(dict, (char *)highlight_index_tab[i]->value.string,
4248 VVAL_TRUE);
4249 // don't want "inverse"/"reverse"
4250 hlattr &= ~highlight_index_tab[i]->key;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004251 }
4252 }
4253
4254 return dict;
4255}
4256
4257/*
4258 * Return the attributes of the highlight group at index 'hl_idx' as a
4259 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4260 * links recursively.
4261 */
4262 static dict_T *
4263highlight_get_info(int hl_idx, int resolve_link)
4264{
4265 dict_T *dict;
4266 hl_group_T *sgp;
4267 dict_T *attr_dict;
4268 int hlgid;
4269
4270 dict = dict_alloc();
4271 if (dict == NULL)
4272 return dict;
4273
4274 sgp = &HL_TABLE()[hl_idx];
4275 // highlight group id is 1-based
4276 hlgid = hl_idx + 1;
4277
4278 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4279 goto error;
4280 if (dict_add_number(dict, "id", hlgid) == FAIL)
4281 goto error;
4282
4283 if (sgp->sg_link && resolve_link)
4284 {
4285 // resolve the highlight group link recursively
4286 while (sgp->sg_link)
4287 {
4288 hlgid = sgp->sg_link;
4289 sgp = &HL_TABLE()[sgp->sg_link - 1];
4290 }
4291 }
4292
4293 if (sgp->sg_term != 0)
4294 {
4295 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4296 if (attr_dict != NULL)
4297 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4298 goto error;
4299 }
4300 if (sgp->sg_start != NULL)
4301 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4302 goto error;
4303 if (sgp->sg_stop != NULL)
4304 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4305 goto error;
4306 if (sgp->sg_cterm != 0)
4307 {
4308 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4309 if (attr_dict != NULL)
4310 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4311 goto error;
4312 }
4313 if (sgp->sg_cterm_fg != 0)
4314 if (dict_add_string(dict, "ctermfg",
4315 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4316 goto error;
4317 if (sgp->sg_cterm_bg != 0)
4318 if (dict_add_string(dict, "ctermbg",
4319 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4320 goto error;
4321 if (sgp->sg_cterm_ul != 0)
4322 if (dict_add_string(dict, "ctermul",
4323 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4324 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004325 if (sgp->sg_cterm_font != 0)
4326 if (dict_add_string(dict, "ctermfont",
4327 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4328 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004329 if (sgp->sg_gui != 0)
4330 {
4331 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4332 if (attr_dict != NULL)
4333 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4334 goto error;
4335 }
4336 if (sgp->sg_gui_fg_name != NULL)
4337 if (dict_add_string(dict, "guifg",
4338 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4339 goto error;
4340 if (sgp->sg_gui_bg_name != NULL)
4341 if (dict_add_string(dict, "guibg",
4342 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4343 goto error;
4344 if (sgp->sg_gui_sp_name != NULL)
4345 if (dict_add_string(dict, "guisp",
4346 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4347 goto error;
4348# ifdef FEAT_GUI
4349 if (sgp->sg_font_name != NULL)
4350 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4351 goto error;
4352# endif
4353 if (sgp->sg_link)
4354 {
4355 char_u *link;
4356
4357 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4358 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4359 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004360
4361 if (sgp->sg_deflink)
4362 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004363 }
4364 if (dict_len(dict) == 2)
4365 // If only 'name' is present, then the highlight group is cleared.
4366 dict_add_bool(dict, "cleared", VVAL_TRUE);
4367
4368 return dict;
4369
4370error:
4371 vim_free(dict);
4372 return NULL;
4373}
4374
4375/*
4376 * "hlget([name])" function
4377 * Return the attributes of a specific highlight group (if specified) or all
4378 * the highlight groups.
4379 */
4380 void
4381f_hlget(typval_T *argvars, typval_T *rettv)
4382{
4383 list_T *list;
4384 dict_T *dict;
4385 int i;
4386 char_u *hlarg = NULL;
4387 int resolve_link = FALSE;
4388
4389 if (rettv_list_alloc(rettv) == FAIL)
4390 return;
4391
4392 if (check_for_opt_string_arg(argvars, 0) == FAIL
4393 || (argvars[0].v_type != VAR_UNKNOWN
4394 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4395 return;
4396
4397 if (argvars[0].v_type != VAR_UNKNOWN)
4398 {
4399 // highlight group name supplied
4400 hlarg = tv_get_string_chk(&argvars[0]);
4401 if (hlarg == NULL)
4402 return;
4403
4404 if (argvars[1].v_type != VAR_UNKNOWN)
4405 {
4406 int error = FALSE;
4407
4408 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4409 if (error)
4410 return;
4411 }
4412 }
4413
4414 list = rettv->vval.v_list;
4415 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4416 {
4417 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4418 {
4419 dict = highlight_get_info(i, resolve_link);
4420 if (dict != NULL)
4421 list_append_dict(list, dict);
4422 }
4423 }
4424}
4425
4426/*
4427 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4428 * 'dict' or the value is not a string type. If the value is not a string type
4429 * or is NULL, then 'error' is set to TRUE.
4430 */
4431 static char_u *
4432hldict_get_string(dict_T *dict, char_u *key, int *error)
4433{
4434 dictitem_T *di;
4435
4436 *error = FALSE;
4437 di = dict_find(dict, key, -1);
4438 if (di == NULL)
4439 return NULL;
4440
4441 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4442 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004443 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004444 *error = TRUE;
4445 return NULL;
4446 }
4447
4448 return di->di_tv.vval.v_string;
4449}
4450
4451/*
4452 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4453 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4454 * Dictionary or is NULL.
4455 */
4456 static int
4457hldict_attr_to_str(
4458 dict_T *dict,
4459 char_u *key,
4460 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004461 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004462{
4463 dictitem_T *di;
4464 dict_T *attrdict;
4465 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004466 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004467
4468 attr_str[0] = NUL;
4469 di = dict_find(dict, key, -1);
4470 if (di == NULL)
4471 return TRUE;
4472
4473 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4474 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004475 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004476 return FALSE;
4477 }
4478
4479 attrdict = di->di_tv.vval.v_dict;
4480
4481 // If the attribute dict is empty, then return NONE to clear the attributes
4482 if (dict_len(attrdict) == 0)
4483 {
4484 vim_strcat(attr_str, (char_u *)"NONE", len);
4485 return TRUE;
4486 }
4487
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004488 p = attr_str;
John Marriott34f00dd2024-04-08 23:28:12 +02004489 for (i = 0; i < (int)ARRAY_LENGTH(highlight_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004490 {
John Marriott8d4477e2024-11-02 15:59:01 +01004491 if (dict_get_bool(attrdict, (char *)highlight_tab[i].value.string,
4492 VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004493 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004494 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4495 STRCPY(p, (char_u *)",");
John Marriott8d4477e2024-11-02 15:59:01 +01004496 if (p - attr_str + highlight_tab[i].value.length + 1 < len)
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004497 {
John Marriott8d4477e2024-11-02 15:59:01 +01004498 STRCPY(p, highlight_tab[i].value.string);
4499 p += highlight_tab[i].value.length;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004500 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004501 }
4502 }
4503
4504 return TRUE;
4505}
4506
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004507// Temporary buffer used to store the command string produced by hlset().
4508// IObuff cannot be used for this as the error messages produced by hlset()
4509// internally use IObuff.
4510#define HLSETBUFSZ 512
4511static char_u hlsetBuf[HLSETBUFSZ + 1];
4512
4513/*
4514 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4515 * "dptr", which points into "hlsetBuf".
4516 * Returns the updated pointer.
4517 */
4518 static char_u *
4519add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4520{
4521 size_t vallen;
4522
4523 // Do nothing if the value is not specified or is empty
4524 if (value == NULL || *value == NUL)
4525 return dptr;
4526
4527 vallen = STRLEN(value);
4528 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4529 {
4530 STRCPY(dptr, attr);
4531 dptr += attrlen;
4532 STRCPY(dptr, value);
4533 dptr += vallen;
4534 }
4535
4536 return dptr;
4537}
4538
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004539/*
4540 * Add or update a highlight group using 'dict' items. Returns TRUE if
4541 * successfully updated the highlight group.
4542 */
4543 static int
4544hlg_add_or_update(dict_T *dict)
4545{
4546 char_u *name;
4547 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004548 char_u term_attr[MAX_ATTR_LEN];
4549 char_u cterm_attr[MAX_ATTR_LEN];
4550 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004551 char_u *start;
4552 char_u *stop;
4553 char_u *ctermfg;
4554 char_u *ctermbg;
4555 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004556 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004557 char_u *guifg;
4558 char_u *guibg;
4559 char_u *guisp;
4560# ifdef FEAT_GUI
4561 char_u *font;
4562# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004563 int forceit = FALSE;
4564 int dodefault = FALSE;
4565 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004566 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004567
4568 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004569 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004570 return FALSE;
4571
Bram Moolenaard61efa52022-07-23 09:52:04 +01004572 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004573 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004574
Bram Moolenaard61efa52022-07-23 09:52:04 +01004575 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004576 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004577
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004578 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004579 {
4580 varnumber_T cleared;
4581
4582 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004583 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004584 if (cleared == TRUE)
4585 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004586 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4587 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004588 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004589 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004590 }
4591
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004592 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004593 {
4594 char_u *linksto;
4595
4596 // link highlight groups
4597 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004598 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004599 return FALSE;
4600
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004601 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004602 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004603 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004604
4605 done = TRUE;
4606 }
4607
4608 // If 'cleared' or 'linksto' are specified, then don't process the other
4609 // attributes.
4610 if (done)
4611 return TRUE;
4612
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004613 start = hldict_get_string(dict, (char_u *)"start", &error);
4614 if (error)
4615 return FALSE;
4616
4617 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4618 if (error)
4619 return FALSE;
4620
4621 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004622 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004623 return FALSE;
4624
4625 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004626 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004627 return FALSE;
4628
4629 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4630 if (error)
4631 return FALSE;
4632
4633 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4634 if (error)
4635 return FALSE;
4636
4637 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4638 if (error)
4639 return FALSE;
4640
PMuncha606f3a2023-11-15 15:35:49 +01004641 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
4642 if (error)
4643 return FALSE;
4644
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004645 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004646 return FALSE;
4647
4648 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4649 if (error)
4650 return FALSE;
4651
4652 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4653 if (error)
4654 return FALSE;
4655
4656 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4657 if (error)
4658 return FALSE;
4659
4660# ifdef FEAT_GUI
4661 font = hldict_get_string(dict, (char_u *)"font", &error);
4662 if (error)
4663 return FALSE;
4664# endif
4665
4666 // If none of the attributes are specified, then do nothing.
4667 if (term_attr[0] == NUL && start == NULL && stop == NULL
4668 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01004669 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004670# ifdef FEAT_GUI
4671 && font == NULL
4672# endif
4673 && guifg == NULL && guibg == NULL && guisp == NULL
4674 )
4675 return TRUE;
4676
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004677 hlsetBuf[0] = NUL;
4678 p = hlsetBuf;
4679 if (dodefault)
4680 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4681 p = add_attr_and_value(p, (char_u *)"", 0, name);
4682 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4683 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4684 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4685 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4686 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4687 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4688 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01004689 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004690 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004691# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004692 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004693# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004694 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4695 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004696 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004697
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004698 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004699
4700 return TRUE;
4701}
4702
4703/*
4704 * "hlset([{highlight_attr}])" function
4705 * Add or modify highlight groups
4706 */
4707 void
4708f_hlset(typval_T *argvars, typval_T *rettv)
4709{
4710 listitem_T *li;
4711 dict_T *dict;
4712
4713 rettv->vval.v_number = -1;
4714
4715 if (check_for_list_arg(argvars, 0) == FAIL)
4716 return;
4717
4718 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4719 {
4720 if (li->li_tv.v_type != VAR_DICT)
4721 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004722 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004723 return;
4724 }
4725
4726 dict = li->li_tv.vval.v_dict;
4727 if (!hlg_add_or_update(dict))
4728 return;
4729 }
4730
4731 rettv->vval.v_number = 0;
4732}
4733#endif