blob: 1a4c76d9439e3c2bb259ecc9bd303403f4d2f209 [file] [log] [blame]
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +020011 * Highlighting stuff.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020012 */
13
14#include "vim.h"
15
16#define SG_TERM 1 // term has been set
17#define SG_CTERM 2 // cterm has been set
18#define SG_GUI 4 // gui has been set
19#define SG_LINK 8 // link has been set
20
erw7f7f7aaf2021-12-07 21:29:20 +000021#define MAX_SYN_NAME 200
22
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020023/*
24 * The "term", "cterm" and "gui" arguments can be any combination of the
25 * following names, separated by commas (but no spaces!).
26 */
John Marriott34f00dd2024-04-08 23:28:12 +020027// must be sorted by the 'value' field because it is used by bsearch()!
28// note: inverse and reverse use the same key
29static keyvalue_T highlight_tab[] = {
30 KEYVALUE_ENTRY(HL_BOLD, "bold"), // index 0
31 KEYVALUE_ENTRY(HL_INVERSE, "inverse"), // index 1
32 KEYVALUE_ENTRY(HL_ITALIC, "italic"), // index 2
33 KEYVALUE_ENTRY(HL_NOCOMBINE, "nocombine"), // index 3
34 KEYVALUE_ENTRY(HL_NORMAL, "NONE"), // index 4
35 KEYVALUE_ENTRY(HL_INVERSE, "reverse"), // index 5
36 KEYVALUE_ENTRY(HL_STANDOUT, "standout"), // index 6
37 KEYVALUE_ENTRY(HL_STRIKETHROUGH, "strikethrough"), // index 7
38 KEYVALUE_ENTRY(HL_UNDERCURL, "undercurl"), // index 8
39 KEYVALUE_ENTRY(HL_UNDERDASHED, "underdashed"), // index 9
40 KEYVALUE_ENTRY(HL_UNDERDOTTED, "underdotted"), // index 10
41 KEYVALUE_ENTRY(HL_UNDERDOUBLE, "underdouble"), // index 11
42 KEYVALUE_ENTRY(HL_UNDERLINE, "underline") // index 12
43};
44
45// this table is used to display highlight names in the "correct" sequence.
46// keep this in sync with highlight_tab[].
47static keyvalue_T *highlight_index_tab[] = {
48 &highlight_tab[0], // HL_BOLD
49 &highlight_tab[6], // HL_STANDOUT
50 &highlight_tab[12], // HL_UNDERLINE
51 &highlight_tab[8], // HL_UNDERCURL
52 &highlight_tab[11], // HL_UNDERDOUBLE
53 &highlight_tab[10], // HL_UNDERDOTTED
54 &highlight_tab[9], // HL_UNDERDASHED
55 &highlight_tab[2], // HL_ITALIC
56 &highlight_tab[5], // HL_REVERSE
57 &highlight_tab[1], // HL_INVERSE
58 &highlight_tab[3], // HL_NOCOMBINE
59 &highlight_tab[7], // HL_STRIKETHROUGH
60 &highlight_tab[4] // HL_NORMAL
61};
62
Bram Moolenaar84f54632022-06-29 18:39:11 +010063// length of all attribute names, plus commas, together (and a bit more)
64#define MAX_ATTR_LEN 120
65
kylo252ae6f1d82022-02-16 19:24:07 +000066#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? (attr_b) : (attr_a)) | (attr_b))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020067
John Marriott34f00dd2024-04-08 23:28:12 +020068enum {
69 BLACK = 0,
70 DARKBLUE,
71 DARKGREEN,
72 DARKCYAN,
73 DARKRED,
74 DARKMAGENTA,
75 BROWN,
76 DARKYELLOW,
77 GRAY,
78 GREY,
79 LIGHTGRAY,
80 LIGHTGREY,
81 DARKGRAY,
82 DARKGREY,
83 BLUE,
84 LIGHTBLUE,
85 GREEN,
86 LIGHTGREEN,
87 CYAN,
88 LIGHTCYAN,
89 RED,
90 LIGHTRED,
91 MAGENTA,
92 LIGHTMAGENTA,
93 YELLOW,
94 LIGHTYELLOW,
95 WHITE,
96 NONE
97};
98
99// must be sorted by the 'value' field because it is used by bsearch()!
100static keyvalue_T color_name_tab[] = {
101 KEYVALUE_ENTRY(BLACK, "Black"),
102 KEYVALUE_ENTRY(BLUE, "Blue"),
103 KEYVALUE_ENTRY(BROWN, "Brown"),
104 KEYVALUE_ENTRY(CYAN, "Cyan"),
105 KEYVALUE_ENTRY(DARKBLUE, "DarkBlue"),
106 KEYVALUE_ENTRY(DARKCYAN, "DarkCyan"),
107 KEYVALUE_ENTRY(DARKGRAY, "DarkGray"),
108 KEYVALUE_ENTRY(DARKGREEN, "DarkGreen"),
109 KEYVALUE_ENTRY(DARKGREY, "DarkGrey"),
110 KEYVALUE_ENTRY(DARKMAGENTA, "DarkMagenta"),
111 KEYVALUE_ENTRY(DARKRED, "DarkRed"),
112 KEYVALUE_ENTRY(DARKYELLOW, "DarkYellow"),
113 KEYVALUE_ENTRY(GRAY, "Gray"),
114 KEYVALUE_ENTRY(GREEN, "Green"),
115 KEYVALUE_ENTRY(GREY, "Grey"),
116 KEYVALUE_ENTRY(LIGHTBLUE, "LightBlue"),
117 KEYVALUE_ENTRY(LIGHTCYAN, "LightCyan"),
118 KEYVALUE_ENTRY(LIGHTGRAY, "LightGray"),
119 KEYVALUE_ENTRY(LIGHTGREEN, "LightGreen"),
120 KEYVALUE_ENTRY(LIGHTGREY, "LightGrey"),
121 KEYVALUE_ENTRY(LIGHTMAGENTA, "LightMagenta"),
122 KEYVALUE_ENTRY(LIGHTRED, "LightRed"),
123 KEYVALUE_ENTRY(LIGHTYELLOW, "LightYellow"),
124 KEYVALUE_ENTRY(MAGENTA, "Magenta"),
125 KEYVALUE_ENTRY(NONE, "NONE"),
126 KEYVALUE_ENTRY(RED, "Red"),
127 KEYVALUE_ENTRY(WHITE, "White"),
128 KEYVALUE_ENTRY(YELLOW, "Yellow")
129};
130
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200131/*
132 * Structure that stores information about a highlight group.
133 * The ID of a highlight group is also called group ID. It is the index in
134 * the highlight_ga array PLUS ONE.
135 */
136typedef struct
137{
138 char_u *sg_name; // highlight group name
139 char_u *sg_name_u; // uppercase of sg_name
140 int sg_cleared; // "hi clear" was used
141// for normal terminals
142 int sg_term; // "term=" highlighting attributes
143 char_u *sg_start; // terminal string for start highl
144 char_u *sg_stop; // terminal string for stop highl
145 int sg_term_attr; // Screen attr for term mode
146// for color terminals
147 int sg_cterm; // "cterm=" highlighting attr
148 int sg_cterm_bold; // bold attr was set for light color
149 int sg_cterm_fg; // terminal fg color number + 1
150 int sg_cterm_bg; // terminal bg color number + 1
Bram Moolenaare023e882020-05-31 16:42:30 +0200151 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200152 int sg_cterm_attr; // Screen attr for color term mode
PMuncha606f3a2023-11-15 15:35:49 +0100153 int sg_cterm_font; // terminal alternative font (0 for normal)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200154// for when using the GUI
155#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
156 guicolor_T sg_gui_fg; // GUI foreground color handle
157 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +0200158 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200159#endif
160#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200161 GuiFont sg_font; // GUI font handle
162#ifdef FEAT_XFONTSET
163 GuiFontset sg_fontset; // GUI fontset handle
164#endif
165 char_u *sg_font_name; // GUI font or fontset name
166 int sg_gui_attr; // Screen attr for GUI mode
167#endif
168#if defined(FEAT_GUI) || defined(FEAT_EVAL)
169// Store the sp color name for the GUI or synIDattr()
170 int sg_gui; // "gui=" highlighting attributes
171 char_u *sg_gui_fg_name;// GUI foreground color name
172 char_u *sg_gui_bg_name;// GUI background color name
173 char_u *sg_gui_sp_name;// GUI special color name
174#endif
175 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +0200176 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200177 int sg_set; // combination of SG_* flags
178#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +0200179 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200180 sctx_T sg_script_ctx; // script in which the group was last set
181#endif
182} hl_group_T;
183
184// highlight groups for 'highlight' option
185static garray_T highlight_ga;
186#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
187
188/*
189 * An attribute number is the index in attr_table plus ATTR_OFF.
190 */
191#define ATTR_OFF (HL_ALL + 1)
192
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200193static void syn_unadd_group(void);
194static void set_hl_attr(int idx);
195static void highlight_list_one(int id);
196static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
197static int syn_add_group(char_u *name);
198static int hl_has_settings(int idx, int check_link);
199static void highlight_clear(int idx);
200
201#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
202static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
203#endif
204#ifdef FEAT_GUI
205static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
206static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
207#endif
208
209/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200210 * The default highlight groups. These are compiled-in for fast startup and
211 * they still work when the runtime files can't be found.
212 * When making changes here, also change runtime/colors/default.vim!
213 * The #ifdefs are needed to reduce the amount of static data. Helps to make
214 * the 16 bit DOS (museum) version compile.
215 */
216#if defined(FEAT_GUI) || defined(FEAT_EVAL)
217# define CENT(a, b) b
218#else
219# define CENT(a, b) a
220#endif
221static char *(highlight_init_both[]) = {
222 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
223 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
224 CENT("IncSearch term=reverse cterm=reverse",
225 "IncSearch term=reverse cterm=reverse gui=reverse"),
226 CENT("ModeMsg term=bold cterm=bold",
227 "ModeMsg term=bold cterm=bold gui=bold"),
228 CENT("NonText term=bold ctermfg=Blue",
229 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
230 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
231 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
232 CENT("StatusLineNC term=reverse cterm=reverse",
233 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
234 "default link EndOfBuffer NonText",
235 CENT("VertSplit term=reverse cterm=reverse",
236 "VertSplit term=reverse cterm=reverse gui=reverse"),
237#ifdef FEAT_CLIPBOARD
238 CENT("VisualNOS term=underline,bold cterm=underline,bold",
239 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
240#endif
241#ifdef FEAT_DIFF
242 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
243 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
244#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200245 CENT("PmenuSbar ctermbg=Grey",
246 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200247 CENT("TabLineSel term=bold cterm=bold",
248 "TabLineSel term=bold cterm=bold gui=bold"),
249 CENT("TabLineFill term=reverse cterm=reverse",
250 "TabLineFill term=reverse cterm=reverse gui=reverse"),
251#ifdef FEAT_GUI
252 "Cursor guibg=fg guifg=bg",
253 "lCursor guibg=fg guifg=bg", // should be different, but what?
254#endif
255 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000256 "default link CursorLineSign SignColumn",
257 "default link CursorLineFold FoldColumn",
LemonBoya4399382022-04-09 21:04:08 +0100258 "default link CurSearch Search",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000259 "default link PmenuKind Pmenu",
260 "default link PmenuKindSel PmenuSel",
glepnir40c1c332024-06-11 19:37:04 +0200261 "default link PmenuMatch Pmenu",
262 "default link PmenuMatchSel PmenuSel",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000263 "default link PmenuExtra Pmenu",
264 "default link PmenuExtraSel PmenuSel",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200265 CENT("Normal cterm=NONE", "Normal gui=NONE"),
266 NULL
267};
268
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200269// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200270static char *(highlight_init_light[]) = {
271 CENT("Directory term=bold ctermfg=DarkBlue",
272 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
273 CENT("LineNr term=underline ctermfg=Brown",
274 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200275 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
276 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200277 CENT("MoreMsg term=bold ctermfg=DarkGreen",
278 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
279 CENT("Question term=standout ctermfg=DarkGreen",
280 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
281 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
282 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
283#ifdef FEAT_SPELL
284 CENT("SpellBad term=reverse ctermbg=LightRed",
285 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
286 CENT("SpellCap term=reverse ctermbg=LightBlue",
287 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
288 CENT("SpellRare term=reverse ctermbg=LightMagenta",
289 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
290 CENT("SpellLocal term=underline ctermbg=Cyan",
291 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
292#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200293 CENT("PmenuThumb ctermbg=Black",
294 "PmenuThumb ctermbg=Black guibg=Black"),
295 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
296 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
297 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
298 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200299 CENT("SpecialKey term=bold ctermfg=DarkBlue",
300 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
301 CENT("Title term=bold ctermfg=DarkMagenta",
302 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
303 CENT("WarningMsg term=standout ctermfg=DarkRed",
304 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200305 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
306 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200307#ifdef FEAT_FOLDING
308 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
309 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
310 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
311 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
312#endif
313#ifdef FEAT_SIGNS
314 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
315 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
316#endif
Maxim Kim59bafc82024-02-01 21:07:51 +0100317 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100318 "Visual ctermbg=Grey ctermfg=Black guibg=LightGrey guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200319#ifdef FEAT_DIFF
320 CENT("DiffAdd term=bold ctermbg=LightBlue",
321 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
322 CENT("DiffChange term=bold ctermbg=LightMagenta",
323 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
324 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
325 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
326#endif
327 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
328 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
329#ifdef FEAT_SYN_HL
330 CENT("CursorColumn term=reverse ctermbg=LightGrey",
331 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
332 CENT("CursorLine term=underline cterm=underline",
333 "CursorLine term=underline cterm=underline guibg=Grey90"),
334 CENT("ColorColumn term=reverse ctermbg=LightRed",
335 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
336#endif
337#ifdef FEAT_CONCEAL
338 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
339 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
340#endif
341 CENT("MatchParen term=reverse ctermbg=Cyan",
342 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
343#ifdef FEAT_TERMINAL
344 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
345 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
346 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
347 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
348#endif
349#ifdef FEAT_MENU
350 CENT("ToolbarLine term=underline ctermbg=LightGrey",
351 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
352 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
353 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
354#endif
355 NULL
356};
357
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200358// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200359static char *(highlight_init_dark[]) = {
360 CENT("Directory term=bold ctermfg=LightCyan",
361 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
362 CENT("LineNr term=underline ctermfg=Yellow",
363 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200364 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
365 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200366 CENT("MoreMsg term=bold ctermfg=LightGreen",
367 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
368 CENT("Question term=standout ctermfg=LightGreen",
369 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
370 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
371 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
372 CENT("SpecialKey term=bold ctermfg=LightBlue",
373 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
374#ifdef FEAT_SPELL
375 CENT("SpellBad term=reverse ctermbg=Red",
376 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
377 CENT("SpellCap term=reverse ctermbg=Blue",
378 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
379 CENT("SpellRare term=reverse ctermbg=Magenta",
380 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
381 CENT("SpellLocal term=underline ctermbg=Cyan",
382 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
383#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200384 CENT("PmenuThumb ctermbg=White",
385 "PmenuThumb ctermbg=White guibg=White"),
386 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
387 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
388 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
389 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200390 CENT("Title term=bold ctermfg=LightMagenta",
391 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
392 CENT("WarningMsg term=standout ctermfg=LightRed",
393 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200394 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
395 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200396#ifdef FEAT_FOLDING
397 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
398 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
399 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
400 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
401#endif
402#ifdef FEAT_SIGNS
403 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
404 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
405#endif
Christian Brabandte6d8b462024-01-28 23:33:29 +0100406 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100407 "Visual ctermbg=Grey ctermfg=Black guibg=#575757 guifg=LightGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200408#ifdef FEAT_DIFF
409 CENT("DiffAdd term=bold ctermbg=DarkBlue",
410 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
411 CENT("DiffChange term=bold ctermbg=DarkMagenta",
412 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
413 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
414 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
415#endif
416 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
417 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
418#ifdef FEAT_SYN_HL
419 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
420 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
421 CENT("CursorLine term=underline cterm=underline",
422 "CursorLine term=underline cterm=underline guibg=Grey40"),
423 CENT("ColorColumn term=reverse ctermbg=DarkRed",
424 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
425#endif
426 CENT("MatchParen term=reverse ctermbg=DarkCyan",
427 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
428#ifdef FEAT_CONCEAL
429 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
430 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
431#endif
432#ifdef FEAT_TERMINAL
433 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
434 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
435 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
436 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
437#endif
438#ifdef FEAT_MENU
439 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
440 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
441 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
442 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
443#endif
444 NULL
445};
446
Dominique Pelle748b3082022-01-08 12:41:16 +0000447#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200448/*
449 * Returns the number of highlight groups.
450 */
451 int
452highlight_num_groups(void)
453{
454 return highlight_ga.ga_len;
455}
456
457/*
458 * Returns the name of a highlight group.
459 */
460 char_u *
461highlight_group_name(int id)
462{
463 return HL_TABLE()[id].sg_name;
464}
465
466/*
467 * Returns the ID of the link to a highlight group.
468 */
469 int
470highlight_link_id(int id)
471{
472 return HL_TABLE()[id].sg_link;
473}
Dominique Pelle748b3082022-01-08 12:41:16 +0000474#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200475
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200476 void
477init_highlight(
478 int both, // include groups where 'bg' doesn't matter
479 int reset) // clear group first
480{
481 int i;
482 char **pp;
483 static int had_both = FALSE;
484#ifdef FEAT_EVAL
485 char_u *p;
486
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100487 // Try finding the color scheme file. Used when a color file was loaded
488 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200489 p = get_var_value((char_u *)"g:colors_name");
490 if (p != NULL)
491 {
492 // The value of g:colors_name could be freed when sourcing the script,
493 // making "p" invalid, so copy it.
494 char_u *copy_p = vim_strsave(p);
495 int r;
496
497 if (copy_p != NULL)
498 {
499 r = load_colors(copy_p);
500 vim_free(copy_p);
501 if (r == OK)
502 return;
503 }
504 }
505
506#endif
507
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100508 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200509 if (both)
510 {
511 had_both = TRUE;
512 pp = highlight_init_both;
513 for (i = 0; pp[i] != NULL; ++i)
514 do_highlight((char_u *)pp[i], reset, TRUE);
515 }
516 else if (!had_both)
517 // Don't do anything before the call with both == TRUE from main().
518 // Not everything has been setup then, and that call will overrule
519 // everything anyway.
520 return;
521
522 if (*p_bg == 'l')
523 pp = highlight_init_light;
524 else
525 pp = highlight_init_dark;
526 for (i = 0; pp[i] != NULL; ++i)
527 do_highlight((char_u *)pp[i], reset, TRUE);
528
Maxim Kim59bafc82024-02-01 21:07:51 +0100529 // Reverse looks ugly, but grey may not work for less than 8 colors. Thus
530 // let it depend on the number of colors available.
531 if (t_colors < 8)
532 do_highlight((char_u *)"Visual term=reverse cterm=reverse ctermbg=NONE ctermfg=NONE",
533 FALSE, TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200534 // With 8 colors brown is equal to yellow, need to use black for Search fg
535 // to avoid Statement highlighted text disappears.
536 // Clear the attributes, needed when changing the t_Co value.
Christian Brabandte6d8b462024-01-28 23:33:29 +0100537 if (t_colors <= 8)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200538 {
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200539 if (*p_bg == 'l')
540 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
541 }
542
543#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100544 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200545 if (get_var_value((char_u *)"g:syntax_on") != NULL)
546 {
547 static int recursive = 0;
548
549 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000550 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200551 else
552 {
553 ++recursive;
554 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
555 --recursive;
556 }
557 }
558#endif
559}
560
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000561#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
562/*
563 * Load a default color list. Intended to support legacy color names but allows
564 * the user to override the color values. Only loaded once.
565 */
566 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000567load_default_colors_lists(void)
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000568{
569 // Lacking a default color list isn't the end of the world but it is likely
570 // an inconvenience so users should know when it is missing.
571 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
572 msg("failed to load colors/lists/default.vim");
573}
574#endif
575
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200576/*
577 * Load color file "name".
578 * Return OK for success, FAIL for failure.
579 */
580 int
581load_colors(char_u *name)
582{
583 char_u *buf;
584 int retval = FAIL;
585 static int recursive = FALSE;
586
587 // When being called recursively, this is probably because setting
588 // 'background' caused the highlighting to be reloaded. This means it is
589 // working, thus we should return OK.
590 if (recursive)
591 return OK;
592
593 recursive = TRUE;
594 buf = alloc(STRLEN(name) + 12);
595 if (buf != NULL)
596 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100597#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100598 load_default_colors_lists();
599#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200600 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
601 curbuf->b_fname, FALSE, curbuf);
602 sprintf((char *)buf, "colors/%s.vim", name);
603 retval = source_runtime(buf, DIP_START + DIP_OPT);
604 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100605 if (retval == OK)
606 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
607 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200608 }
609 recursive = FALSE;
610
611 return retval;
612}
613
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200614static int color_numbers_16[28] = {0, 1, 2, 3,
615 4, 5, 6, 6,
616 7, 7, 7, 7,
617 8, 8,
618 9, 9, 10, 10,
619 11, 11, 12, 12, 13,
620 13, 14, 14, 15, -1};
621// for xterm with 88 colors...
622static int color_numbers_88[28] = {0, 4, 2, 6,
623 1, 5, 32, 72,
624 84, 84, 7, 7,
625 82, 82,
626 12, 43, 10, 61,
627 14, 63, 9, 74, 13,
628 75, 11, 78, 15, -1};
629// for xterm with 256 colors...
630static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200631 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200632 248, 248, 7, 7,
633 242, 242,
634 12, 81, 10, 121,
635 14, 159, 9, 224, 13,
636 225, 11, 229, 15, -1};
637// for terminals with less than 16 colors...
638static int color_numbers_8[28] = {0, 4, 2, 6,
639 1, 5, 3, 3,
640 7, 7, 7, 7,
641 0+8, 0+8,
642 4+8, 4+8, 2+8, 2+8,
643 6+8, 6+8, 1+8, 1+8, 5+8,
644 5+8, 3+8, 3+8, 7+8, -1};
645
646/*
647 * Lookup the "cterm" value to be used for color with index "idx" in
John Marriott34f00dd2024-04-08 23:28:12 +0200648 * color_name_tab[].
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200649 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
650 * colors, otherwise it will be unchanged.
651 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100652 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200653lookup_color(int idx, int foreground, int *boldp)
654{
655 int color = color_numbers_16[idx];
656 char_u *p;
657
658 // Use the _16 table to check if it's a valid color name.
659 if (color < 0)
660 return -1;
661
662 if (t_colors == 8)
663 {
664 // t_Co is 8: use the 8 colors table
665#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100666 // On qnx, the 8 & 16 color arrays are the same
667 if (STRNCMP(T_NAME, "qansi", 5) == 0)
668 color = color_numbers_16[idx];
669 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200670#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100671 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200672 if (foreground)
673 {
674 // set/reset bold attribute to get light foreground
675 // colors (on some terminals, e.g. "linux")
676 if (color & 8)
677 *boldp = TRUE;
678 else
679 *boldp = FALSE;
680 }
681 color &= 7; // truncate to 8 colors
682 }
683 else if (t_colors == 16 || t_colors == 88
684 || t_colors >= 256)
685 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100686 // Guess: if the termcap entry ends in 'm', it is
687 // probably an xterm-like terminal. Use the changed
688 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200689 if (*T_CAF != NUL)
690 p = T_CAF;
691 else
692 p = T_CSF;
693 if (*p != NUL && (t_colors > 256
694 || *(p + STRLEN(p) - 1) == 'm'))
695 {
696 if (t_colors == 88)
697 color = color_numbers_88[idx];
698 else if (t_colors >= 256)
699 color = color_numbers_256[idx];
700 else
701 color = color_numbers_8[idx];
702 }
703#ifdef FEAT_TERMRESPONSE
704 if (t_colors >= 256 && color == 15 && is_mac_terminal)
705 // Terminal.app has a bug: 15 is light grey. Use white
706 // from the color cube instead.
707 color = 231;
708#endif
709 }
710 return color;
711}
712
713/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100714 * Link highlight group 'from_hg' to 'to_hg'.
715 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000716 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100717 * 'init' is set to TRUE when initializing all the highlight groups.
718 */
719 static void
720highlight_group_link(
721 char_u *from_hg,
722 int from_len,
723 char_u *to_hg,
724 int to_len,
725 int dodefault,
726 int forceit,
727 int init)
728{
729 int from_id;
730 int to_id;
731 hl_group_T *hlgroup = NULL;
732
733 from_id = syn_check_group(from_hg, from_len);
734 if (STRNCMP(to_hg, "NONE", 4) == 0)
735 to_id = 0;
736 else
737 to_id = syn_check_group(to_hg, to_len);
738
739 if (from_id > 0)
740 {
741 hlgroup = &HL_TABLE()[from_id - 1];
742 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
743 {
744 hlgroup->sg_deflink = to_id;
745#ifdef FEAT_EVAL
746 hlgroup->sg_deflink_sctx = current_sctx;
747 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
748#endif
749 }
750 }
751
752 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
753 {
754 // Don't allow a link when there already is some highlighting
755 // for the group, unless '!' is used
756 if (to_id > 0 && !forceit && !init
757 && hl_has_settings(from_id - 1, dodefault))
758 {
759 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000760 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100761 }
762 else if (hlgroup->sg_link != to_id
763#ifdef FEAT_EVAL
764 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
765#endif
766 || hlgroup->sg_cleared)
767 {
768 if (!init)
769 hlgroup->sg_set |= SG_LINK;
770 hlgroup->sg_link = to_id;
771#ifdef FEAT_EVAL
772 hlgroup->sg_script_ctx = current_sctx;
773 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
774#endif
775 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100776 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100777
778 // Only call highlight_changed() once after multiple changes.
779 need_highlight_changed = TRUE;
780 }
781 }
782
783}
784
785/*
786 * Reset all highlighting to the defaults. Removes all highlighting for the
787 * groups added by the user.
788 */
789 static void
790highlight_reset_all(void)
791{
792 int idx;
793
794#ifdef FEAT_GUI
795 // First, we do not destroy the old values, but allocate the new
796 // ones and update the display. THEN we destroy the old values.
797 // If we destroy the old values first, then the old values
798 // (such as GuiFont's or GuiFontset's) will still be displayed but
799 // invalid because they were free'd.
800 if (gui.in_use)
801 {
802# ifdef FEAT_BEVAL_TIP
803 gui_init_tooltip_font();
804# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100805# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100806 gui_init_menu_font();
807# endif
808 }
809# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
810 gui_mch_def_colors();
811# endif
812# ifdef FEAT_GUI_X11
813# ifdef FEAT_MENU
814
815 // This only needs to be done when there is no Menu highlight
816 // group defined by default, which IS currently the case.
817 gui_mch_new_menu_colors();
818# endif
819 if (gui.in_use)
820 {
821 gui_new_scrollbar_colors();
822# ifdef FEAT_BEVAL_GUI
823 gui_mch_new_tooltip_colors();
824# endif
825# ifdef FEAT_MENU
826 gui_mch_new_menu_font();
827# endif
828 }
829# endif
830
831 // Ok, we're done allocating the new default graphics items.
832 // The screen should already be refreshed at this point.
833 // It is now Ok to clear out the old data.
834#endif
835#ifdef FEAT_EVAL
836 do_unlet((char_u *)"g:colors_name", TRUE);
837#endif
838 restore_cterm_colors();
839
840 // Clear all default highlight groups and load the defaults.
841 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
842 highlight_clear(idx);
843 init_highlight(TRUE, TRUE);
844#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
845 if (USE_24BIT)
846 highlight_gui_started();
847 else
848#endif
849 highlight_changed();
850 redraw_later_clear();
851}
852
853/*
854 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
855 * index 'idx'.
856 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
857 * 'arg' is the list of attribute names separated by comma.
858 * 'init' is set to TRUE when initializing all the highlight groups.
859 * Returns TRUE if the attributes are set.
860 */
861 static int
862highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
863{
864 int attr;
Mike Williams72a156b2024-04-09 22:04:54 +0200865 size_t off;
John Marriott34f00dd2024-04-08 23:28:12 +0200866 keyvalue_T target;
867 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100868
869 attr = 0;
870 off = 0;
John Marriott34f00dd2024-04-08 23:28:12 +0200871 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +0100872 target.value.length = 0; // not used, see cmp_keyvalue_value_ni()
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100873 while (arg[off] != NUL)
874 {
John Marriott8d4477e2024-11-02 15:59:01 +0100875 target.value.string = arg + off;
876 entry = (keyvalue_T *)bsearch(&target, &highlight_tab,
877 ARRAY_LENGTH(highlight_tab), sizeof(highlight_tab[0]),
878 cmp_keyvalue_value_ni);
John Marriott34f00dd2024-04-08 23:28:12 +0200879 if (entry == NULL)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100880 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000881 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100882 return FALSE;
883 }
John Marriott34f00dd2024-04-08 23:28:12 +0200884
885 attr |= entry->key;
John Marriott8d4477e2024-11-02 15:59:01 +0100886 off += entry->value.length;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100887 if (arg[off] == ',') // another one follows
888 ++off;
889 }
890 if (*key == 'T')
891 {
892 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
893 {
894 if (!init)
895 HL_TABLE()[idx].sg_set |= SG_TERM;
896 HL_TABLE()[idx].sg_term = attr;
897 }
898 }
899 else if (*key == 'C')
900 {
901 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
902 {
903 if (!init)
904 HL_TABLE()[idx].sg_set |= SG_CTERM;
905 HL_TABLE()[idx].sg_cterm = attr;
906 HL_TABLE()[idx].sg_cterm_bold = FALSE;
907 }
908 }
909#if defined(FEAT_GUI) || defined(FEAT_EVAL)
910 else
911 {
912 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
913 {
914 if (!init)
915 HL_TABLE()[idx].sg_set |= SG_GUI;
916 HL_TABLE()[idx].sg_gui = attr;
917 }
918 }
919#endif
920
921 return TRUE;
922}
923
924#ifdef FEAT_GUI
925/*
926 * Set the font for the highlight group at 'idx'.
927 * 'arg' is the font name.
928 * Returns TRUE if the font is changed.
929 */
930 static int
931highlight_set_font(
932 int idx,
933 char_u *arg,
934 int is_normal_group,
935 int is_menu_group,
936 int is_tooltip_group)
937{
938 int did_change = FALSE;
939
940 // in non-GUI fonts are simply ignored
941 if (HL_TABLE()[idx].sg_font_name != NULL
942 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
943 {
944 // Font name didn't change, ignore.
945 }
946 else if (!gui.shell_created)
947 {
948 // GUI not started yet, always accept the name.
949 vim_free(HL_TABLE()[idx].sg_font_name);
950 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
951 did_change = TRUE;
952 }
953 else
954 {
955 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
956# ifdef FEAT_XFONTSET
957 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
958# endif
959 // First, save the current font/fontset.
960 // Then try to allocate the font/fontset.
961 // If the allocation fails, HL_TABLE()[idx].sg_font OR
962 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
963
964 HL_TABLE()[idx].sg_font = NOFONT;
965# ifdef FEAT_XFONTSET
966 HL_TABLE()[idx].sg_fontset = NOFONTSET;
967# endif
968 hl_do_font(idx, arg, is_normal_group, is_menu_group,
969 is_tooltip_group, FALSE);
970
971# ifdef FEAT_XFONTSET
972 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
973 {
974 // New fontset was accepted. Free the old one, if there
975 // was one.
976 gui_mch_free_fontset(temp_sg_fontset);
977 vim_free(HL_TABLE()[idx].sg_font_name);
978 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
979 did_change = TRUE;
980 }
981 else
982 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
983# endif
984 if (HL_TABLE()[idx].sg_font != NOFONT)
985 {
986 // New font was accepted. Free the old one, if there was
987 // one.
988 gui_mch_free_font(temp_sg_font);
989 vim_free(HL_TABLE()[idx].sg_font_name);
990 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
991 did_change = TRUE;
992 }
993 else
994 HL_TABLE()[idx].sg_font = temp_sg_font;
995 }
996
997 return did_change;
998}
999#endif
1000
1001/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001002 * Set the cterm foreground color for the Normal highlight group to "color" and
1003 * the bold attribute to "bold".
1004 */
1005 static void
1006hl_set_ctermfg_normal_group(int color, int bold)
1007{
1008 cterm_normal_fg_color = color + 1;
1009 cterm_normal_fg_bold = bold;
1010#ifdef FEAT_GUI
1011 // Don't do this if the GUI is used.
1012 if (!gui.in_use && !gui.starting)
1013#endif
1014 {
1015 set_must_redraw(UPD_CLEAR);
1016 if (termcap_active && color >= 0)
1017 term_fg_color(color);
1018 }
1019}
1020
1021/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001022 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001023 */
1024 static void
1025highlight_set_ctermfg(int idx, int color, int is_normal_group)
1026{
1027 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1028 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001029 hl_set_ctermfg_normal_group(color,
1030 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001031}
1032
1033/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001034 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001035 */
1036 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001037hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001038{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001039 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001040#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001041 // Don't mess with 'background' if the GUI is used.
1042 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001043#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001044 {
1045 set_must_redraw(UPD_CLEAR);
1046 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001047 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001048 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001049
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001050 if (termcap_active)
1051 term_bg_color(color);
1052 if (t_colors < 16)
1053 dark = (color == 0 || color == 4);
1054 // Limit the heuristic to the standard 16 colors
1055 else if (color < 16)
1056 dark = (color < 7 || color == 8);
1057 // Set the 'background' option if the value is
1058 // wrong.
1059 if (dark != -1
1060 && dark != (*p_bg == 'd')
1061 && !option_was_set((char_u *)"bg"))
1062 {
1063 set_option_value_give_err((char_u *)"bg",
1064 0L, (char_u *)(dark ? "dark" : "light"), 0);
1065 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001066 }
1067 }
1068 }
1069}
1070
1071/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001072 * Set the cterm background color for the highlight group at 'idx' to 'color'.
1073 */
1074 static void
1075highlight_set_ctermbg(int idx, int color, int is_normal_group)
1076{
1077 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1078 if (is_normal_group)
1079 hl_set_ctermbg_normal_group(color);
1080}
1081
1082/*
1083 * Set the cterm underline color for the Normal highlight group to "color".
1084 */
1085 static void
1086hl_set_ctermul_normal_group(int color)
1087{
1088 cterm_normal_ul_color = color + 1;
1089#ifdef FEAT_GUI
1090 // Don't do this if the GUI is used.
1091 if (!gui.in_use && !gui.starting)
1092#endif
1093 {
1094 set_must_redraw(UPD_CLEAR);
1095 if (termcap_active && color >= 0)
1096 term_ul_color(color);
1097 }
1098}
1099
1100/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001101 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001102 */
1103 static void
1104highlight_set_ctermul(int idx, int color, int is_normal_group)
1105{
1106 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1107 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001108 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001109}
1110
1111/*
PMuncha606f3a2023-11-15 15:35:49 +01001112 * Set the cterm font for the highlight group at 'idx'.
1113 * 'arg' is the color name or the numeric value as a string.
1114 * 'init' is set to TRUE when initializing highlighting.
1115 * Called for the ":highlight" command and the "hlset()" function.
1116 *
1117 * Returns TRUE if the font is set.
1118 */
1119 static int
1120highlight_set_cterm_font(
1121 int idx,
1122 char_u *arg,
1123 int init)
1124{
1125 int font;
1126
1127 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1128 return FALSE;
1129
1130 if (!init)
1131 HL_TABLE()[idx].sg_set |= SG_CTERM;
1132
1133 if (VIM_ISDIGIT(*arg))
1134 font = atoi((char *)arg);
1135 else if (STRICMP(arg, "NONE") == 0)
1136 font = -1;
1137 else
1138 return FALSE;
1139
1140 HL_TABLE()[idx].sg_cterm_font = font + 1;
1141 return TRUE;
1142}
1143
1144/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001145 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1146 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1147 * 'keystart' is the color name/value.
1148 * 'arg' is the color name or the numeric value as a string.
1149 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1150 * 'init' is set to TRUE when initializing highlighting.
1151 * Called for the ":highlight" command and the "hlset()" function.
1152 *
1153 * Returns TRUE if the color is set.
1154 */
1155 static int
1156highlight_set_cterm_color(
1157 int idx,
1158 char_u *key,
1159 char_u *key_start,
1160 char_u *arg,
1161 int is_normal_group,
1162 int init)
1163{
1164 int color;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001165
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001166 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1167 return FALSE;
1168
1169 if (!init)
1170 HL_TABLE()[idx].sg_set |= SG_CTERM;
1171
1172 // When setting the foreground color, and previously the "bold"
1173 // flag was set for a light color, reset it now
1174 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001175 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001176 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1177 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1178 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001179
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001180 if (VIM_ISDIGIT(*arg))
1181 color = atoi((char *)arg);
1182 else if (STRICMP(arg, "fg") == 0)
1183 {
1184 if (cterm_normal_fg_color)
1185 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001186 else
1187 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001188 emsg(_(e_fg_color_unknown));
1189 return FALSE;
1190 }
1191 }
1192 else if (STRICMP(arg, "bg") == 0)
1193 {
1194 if (cterm_normal_bg_color > 0)
1195 color = cterm_normal_bg_color - 1;
1196 else
1197 {
1198 emsg(_(e_bg_color_unknown));
1199 return FALSE;
1200 }
1201 }
1202 else if (STRICMP(arg, "ul") == 0)
1203 {
1204 if (cterm_normal_ul_color > 0)
1205 color = cterm_normal_ul_color - 1;
1206 else
1207 {
1208 emsg(_(e_ul_color_unknown));
1209 return FALSE;
1210 }
1211 }
1212 else
1213 {
1214 int bold = MAYBE;
John Marriott34f00dd2024-04-08 23:28:12 +02001215 keyvalue_T target;
1216 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001217
John Marriott34f00dd2024-04-08 23:28:12 +02001218 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01001219 target.value.string = arg;
1220 target.value.length = 0; // not used, see cmp_keyvalue_value_i()
1221 entry = (keyvalue_T *)bsearch(&target, &color_name_tab,
1222 ARRAY_LENGTH(color_name_tab), sizeof(color_name_tab[0]),
1223 cmp_keyvalue_value_i);
John Marriott34f00dd2024-04-08 23:28:12 +02001224 if (entry == NULL)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001225 {
1226 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1227 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001228 }
1229
John Marriott34f00dd2024-04-08 23:28:12 +02001230 color = lookup_color(entry->key, key[5] == 'F', &bold);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001231
1232 // set/reset bold attribute to get light foreground
1233 // colors (on some terminals, e.g. "linux")
1234 if (bold == TRUE)
1235 {
1236 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1237 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1238 }
1239 else if (bold == FALSE)
1240 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001241 }
1242
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001243 // Add one to the argument, to avoid zero. Zero is used for
1244 // "NONE", then "color" is -1.
1245 if (key[5] == 'F')
1246 highlight_set_ctermfg(idx, color, is_normal_group);
1247 else if (key[5] == 'B')
1248 highlight_set_ctermbg(idx, color, is_normal_group);
1249 else // ctermul
1250 highlight_set_ctermul(idx, color, is_normal_group);
1251
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001252 return TRUE;
1253}
1254
1255#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1256/*
1257 * Set the GUI foreground color for the highlight group at 'idx'.
1258 * Returns TRUE if the color is set.
1259 */
1260 static int
1261highlight_set_guifg(
1262 int idx,
1263 char_u *arg,
1264 int is_menu_group UNUSED,
1265 int is_scrollbar_group UNUSED,
1266 int is_tooltip_group UNUSED,
1267 int *do_colors UNUSED,
1268 int init)
1269{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001270# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001271 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001272# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001273 char_u **namep;
1274 int did_change = FALSE;
1275
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001276 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1277 return FALSE;
1278
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001279 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001280 if (!init)
1281 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001282
1283# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001284 // In GUI guifg colors are only used when recognized
1285 i = color_name2handle(arg);
1286 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1287 {
1288 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001289# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001290 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1291 {
1292 vim_free(*namep);
1293 if (STRCMP(arg, "NONE") != 0)
1294 *namep = vim_strsave(arg);
1295 else
1296 *namep = NULL;
1297 did_change = TRUE;
1298 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001299# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1300# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001301 if (is_menu_group && gui.menu_fg_pixel != i)
1302 {
1303 gui.menu_fg_pixel = i;
1304 *do_colors = TRUE;
1305 }
1306 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1307 {
1308 gui.scroll_fg_pixel = i;
1309 *do_colors = TRUE;
1310 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001311# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001312 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1313 {
1314 gui.tooltip_fg_pixel = i;
1315 *do_colors = TRUE;
1316 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001317# endif
1318# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001319 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001320# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001321
1322 return did_change;
1323}
1324
1325/*
1326 * Set the GUI background color for the highlight group at 'idx'.
1327 * Returns TRUE if the color is set.
1328 */
1329 static int
1330highlight_set_guibg(
1331 int idx,
1332 char_u *arg,
1333 int is_menu_group UNUSED,
1334 int is_scrollbar_group UNUSED,
1335 int is_tooltip_group UNUSED,
1336 int *do_colors UNUSED,
1337 int init)
1338{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001339# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001340 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001341# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001342 char_u **namep;
1343 int did_change = FALSE;
1344
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001345 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1346 return FALSE;
1347
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001348 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001349 if (!init)
1350 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001351
1352# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001353 // In GUI guibg colors are only used when recognized
1354 i = color_name2handle(arg);
1355 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1356 {
1357 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001358# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001359 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1360 {
1361 vim_free(*namep);
1362 if (STRCMP(arg, "NONE") != 0)
1363 *namep = vim_strsave(arg);
1364 else
1365 *namep = NULL;
1366 did_change = TRUE;
1367 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001368# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1369# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001370 if (is_menu_group && gui.menu_bg_pixel != i)
1371 {
1372 gui.menu_bg_pixel = i;
1373 *do_colors = TRUE;
1374 }
1375 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1376 {
1377 gui.scroll_bg_pixel = i;
1378 *do_colors = TRUE;
1379 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001380# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001381 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1382 {
1383 gui.tooltip_bg_pixel = i;
1384 *do_colors = TRUE;
1385 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001386# endif
1387# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001388 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001389# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001390
1391 return did_change;
1392}
1393
1394/*
1395 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1396 * Returns TRUE if the color is set.
1397 */
1398 static int
1399highlight_set_guisp(int idx, char_u *arg, int init)
1400{
1401# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1402 int i;
1403# endif
1404 int did_change = FALSE;
1405 char_u **namep;
1406
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001407 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1408 return FALSE;
1409
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001410 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001411 if (!init)
1412 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001413
1414# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001415 // In GUI guisp colors are only used when recognized
1416 i = color_name2handle(arg);
1417 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1418 {
1419 HL_TABLE()[idx].sg_gui_sp = i;
1420# endif
1421 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001422 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001423 vim_free(*namep);
1424 if (STRCMP(arg, "NONE") != 0)
1425 *namep = vim_strsave(arg);
1426 else
1427 *namep = NULL;
1428 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001429 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001430# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001431 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001432# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001433
1434 return did_change;
1435}
1436#endif
1437
1438/*
1439 * Set the start/stop terminal codes for a highlight group.
1440 * Returns TRUE if the terminal code is set.
1441 */
1442 static int
1443highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1444{
1445 int off;
1446 char_u buf[100];
1447 int len;
1448 char_u *tname;
1449 char_u *p;
1450
1451 if (!init)
1452 HL_TABLE()[idx].sg_set |= SG_TERM;
1453
1454 // The "start" and "stop" arguments can be a literal escape
1455 // sequence, or a comma separated list of terminal codes.
1456 if (STRNCMP(arg, "t_", 2) == 0)
1457 {
1458 off = 0;
1459 buf[0] = 0;
1460 while (arg[off] != NUL)
1461 {
1462 // Isolate one termcap name
1463 for (len = 0; arg[off + len] &&
1464 arg[off + len] != ','; ++len)
1465 ;
1466 tname = vim_strnsave(arg + off, len);
1467 if (tname == NULL) // out of memory
1468 return FALSE;
1469 // lookup the escape sequence for the item
1470 p = get_term_code(tname);
1471 vim_free(tname);
1472 if (p == NULL) // ignore non-existing things
1473 p = (char_u *)"";
1474
1475 // Append it to the already found stuff
1476 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1477 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001478 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001479 return FALSE;
1480 }
1481 STRCAT(buf, p);
1482
1483 // Advance to the next item
1484 off += len;
1485 if (arg[off] == ',') // another one follows
1486 ++off;
1487 }
1488 }
1489 else
1490 {
1491 // Copy characters from arg[] to buf[], translating <> codes.
1492 for (p = arg, off = 0; off < 100 - 6 && *p; )
1493 {
zeertzjqdb088872022-05-02 22:53:45 +01001494 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001495 if (len > 0) // recognized special char
1496 off += len;
1497 else // copy as normal char
1498 buf[off++] = *p++;
1499 }
1500 buf[off] = NUL;
1501 }
1502
1503 if (STRCMP(buf, "NONE") == 0) // resetting the value
1504 p = NULL;
1505 else
1506 p = vim_strsave(buf);
1507 if (key[2] == 'A')
1508 {
1509 vim_free(HL_TABLE()[idx].sg_start);
1510 HL_TABLE()[idx].sg_start = p;
1511 }
1512 else
1513 {
1514 vim_free(HL_TABLE()[idx].sg_stop);
1515 HL_TABLE()[idx].sg_stop = p;
1516 }
1517 return TRUE;
1518}
1519
1520/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001521 * Handle the ":highlight .." command.
1522 * When using ":hi clear" this is called recursively for each group with
1523 * "forceit" and "init" both TRUE.
1524 */
1525 void
1526do_highlight(
1527 char_u *line,
1528 int forceit,
1529 int init) // TRUE when called for initializing
1530{
1531 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001532 char_u *linep;
1533 char_u *key_start;
1534 char_u *arg_start;
1535 char_u *key = NULL, *arg = NULL;
1536 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001537 int id;
1538 int idx;
1539 hl_group_T item_before;
1540 int did_change = FALSE;
1541 int dodefault = FALSE;
1542 int doclear = FALSE;
1543 int dolink = FALSE;
1544 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001545 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001546#ifdef FEAT_GUI_X11
1547 int is_menu_group = FALSE; // "Menu" group
1548 int is_scrollbar_group = FALSE; // "Scrollbar" group
1549 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001550#else
1551# define is_menu_group 0
1552# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001553# define is_scrollbar_group 0
1554#endif
1555#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1556 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001557#endif
1558#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1559 int did_highlight_changed = FALSE;
1560#endif
1561
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001562 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001563 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001564 {
1565 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1566 // TODO: only call when the group has attributes set
1567 highlight_list_one((int)i);
1568 return;
1569 }
1570
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001571 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001572 name_end = skiptowhite(line);
1573 linep = skipwhite(name_end);
1574
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001575 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001576 if (STRNCMP(line, "default", name_end - line) == 0)
1577 {
1578 dodefault = TRUE;
1579 line = linep;
1580 name_end = skiptowhite(line);
1581 linep = skipwhite(name_end);
1582 }
1583
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001584 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001585 if (STRNCMP(line, "clear", name_end - line) == 0)
1586 doclear = TRUE;
1587 if (STRNCMP(line, "link", name_end - line) == 0)
1588 dolink = TRUE;
1589
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001590 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001591 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001592 {
1593 id = syn_namen2id(line, (int)(name_end - line));
1594 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001595 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001596 else
1597 highlight_list_one(id);
1598 return;
1599 }
1600
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001601 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001602 if (dolink)
1603 {
1604 char_u *from_start = linep;
1605 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001606 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001607 char_u *to_start;
1608 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001609 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001610
1611 from_end = skiptowhite(from_start);
1612 to_start = skipwhite(from_end);
1613 to_end = skiptowhite(to_start);
1614
Bram Moolenaar1966c242020-04-20 22:42:32 +02001615 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001616 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001617 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001618 return;
1619 }
1620
Bram Moolenaar1966c242020-04-20 22:42:32 +02001621 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001622 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001623 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001624 return;
1625 }
1626
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001627 from_len = (int)(from_end - from_start);
1628 to_len = (int)(to_end - to_start);
1629 highlight_group_link(from_start, from_len, to_start, to_len,
1630 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001631 return;
1632 }
1633
1634 if (doclear)
1635 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001636 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001637 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001638 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001639 // ":highlight clear" without group name
1640 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001641 return;
1642 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001643 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001644 name_end = skiptowhite(line);
1645 linep = skipwhite(name_end);
1646 }
1647
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001648 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001649 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001650 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001651 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001652 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001653
1654 // Return if "default" was used and the group already has settings.
1655 if (dodefault && hl_has_settings(idx, TRUE))
1656 return;
1657
1658 // Make a copy so we can check if any attribute actually changed.
1659 item_before = HL_TABLE()[idx];
1660
1661 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1662 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001663#ifdef FEAT_GUI_X11
1664 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1665 is_menu_group = TRUE;
1666 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1667 is_scrollbar_group = TRUE;
1668 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1669 is_tooltip_group = TRUE;
1670#endif
1671
1672 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1673 if (doclear || (forceit && init))
1674 {
1675 highlight_clear(idx);
1676 if (!doclear)
1677 HL_TABLE()[idx].sg_set = 0;
1678 }
1679
1680 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001681 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001682 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001683 key_start = linep;
1684 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001685 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001686 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001687 error = TRUE;
1688 break;
1689 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001690
1691 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1692 // or "guibg").
1693 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1694 ++linep;
1695 vim_free(key);
1696 key = vim_strnsave_up(key_start, linep - key_start);
1697 if (key == NULL)
1698 {
1699 error = TRUE;
1700 break;
1701 }
1702 linep = skipwhite(linep);
1703
1704 if (STRCMP(key, "NONE") == 0)
1705 {
1706 if (!init || HL_TABLE()[idx].sg_set == 0)
1707 {
1708 if (!init)
1709 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1710 highlight_clear(idx);
1711 }
1712 continue;
1713 }
1714
1715 // Check for the equal sign.
1716 if (*linep != '=')
1717 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001718 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001719 error = TRUE;
1720 break;
1721 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001722 ++linep;
1723
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001724 // Isolate the argument.
1725 linep = skipwhite(linep);
1726 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001727 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001728 arg_start = ++linep;
1729 linep = vim_strchr(linep, '\'');
1730 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001731 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001732 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001733 error = TRUE;
1734 break;
1735 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001736 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001737 else
1738 {
1739 arg_start = linep;
1740 linep = skiptowhite(linep);
1741 }
1742 if (linep == arg_start)
1743 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001744 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001745 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001746 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001747 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001748 vim_free(arg);
1749 arg = vim_strnsave(arg_start, linep - arg_start);
1750 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001751 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001752 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001753 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001754 }
1755 if (*linep == '\'')
1756 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001757
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001758 // Store the argument.
1759 if (STRCMP(key, "TERM") == 0
1760 || STRCMP(key, "CTERM") == 0
1761 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001762 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001763 if (!highlight_set_termgui_attr(idx, key, arg, init))
1764 {
1765 error = TRUE;
1766 break;
1767 }
1768 }
1769 else if (STRCMP(key, "FONT") == 0)
1770 {
1771 // in non-GUI fonts are simply ignored
1772#ifdef FEAT_GUI
1773 if (highlight_set_font(idx, arg, is_normal_group,
1774 is_menu_group, is_tooltip_group))
1775 did_change = TRUE;
1776#endif
1777 }
1778 else if (STRCMP(key, "CTERMFG") == 0
1779 || STRCMP(key, "CTERMBG") == 0
1780 || STRCMP(key, "CTERMUL") == 0)
1781 {
1782 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1783 is_normal_group, init))
1784 {
1785 error = TRUE;
1786 break;
1787 }
1788 }
PMuncha606f3a2023-11-15 15:35:49 +01001789 else if (STRCMP(key, "CTERMFONT") == 0)
1790 {
1791 if (!highlight_set_cterm_font(idx, arg, init))
1792 {
1793 error = TRUE;
1794 break;
1795 }
1796 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001797 else if (STRCMP(key, "GUIFG") == 0)
1798 {
1799#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1800 if (highlight_set_guifg(idx, arg, is_menu_group,
1801 is_scrollbar_group, is_tooltip_group,
1802 &do_colors, init))
1803 did_change = TRUE;
1804#endif
1805 }
1806 else if (STRCMP(key, "GUIBG") == 0)
1807 {
1808#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1809 if (highlight_set_guibg(idx, arg, is_menu_group,
1810 is_scrollbar_group, is_tooltip_group,
1811 &do_colors, init))
1812 did_change = TRUE;
1813#endif
1814 }
1815 else if (STRCMP(key, "GUISP") == 0)
1816 {
1817#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1818 if (highlight_set_guisp(idx, arg, init))
1819 did_change = TRUE;
1820#endif
1821 }
1822 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1823 {
1824 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1825 {
1826 error = TRUE;
1827 break;
1828 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001829 }
1830 else
1831 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001832 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001833 error = TRUE;
1834 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001835 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001836 HL_TABLE()[idx].sg_cleared = FALSE;
1837
1838 // When highlighting has been given for a group, don't link it.
1839 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1840 HL_TABLE()[idx].sg_link = 0;
1841
1842 // Continue with next argument.
1843 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001844 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001845
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001846 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001847 if (error && idx == highlight_ga.ga_len)
1848 syn_unadd_group();
1849 else
1850 {
1851 if (is_normal_group)
1852 {
1853 HL_TABLE()[idx].sg_term_attr = 0;
1854 HL_TABLE()[idx].sg_cterm_attr = 0;
1855#ifdef FEAT_GUI
1856 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001857 // Need to update all groups, because they might be using "bg"
1858 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001859#endif
1860#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1861 if (USE_24BIT)
1862 {
1863 highlight_gui_started();
1864 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001865 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001866 }
1867#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001868#ifdef FEAT_VTP
1869 control_console_color_rgb();
1870#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001871 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001872#ifdef FEAT_GUI_X11
1873# ifdef FEAT_MENU
1874 else if (is_menu_group)
1875 {
1876 if (gui.in_use && do_colors)
1877 gui_mch_new_menu_colors();
1878 }
1879# endif
1880 else if (is_scrollbar_group)
1881 {
1882 if (gui.in_use && do_colors)
1883 gui_new_scrollbar_colors();
1884 else
1885 set_hl_attr(idx);
1886 }
1887# ifdef FEAT_BEVAL_GUI
1888 else if (is_tooltip_group)
1889 {
1890 if (gui.in_use && do_colors)
1891 gui_mch_new_tooltip_colors();
1892 }
1893# endif
1894#endif
1895 else
1896 set_hl_attr(idx);
1897#ifdef FEAT_EVAL
1898 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001899 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001900#endif
1901 }
1902
1903 vim_free(key);
1904 vim_free(arg);
1905
1906 // Only call highlight_changed() once, after a sequence of highlight
1907 // commands, and only if an attribute actually changed.
1908 if ((did_change
1909 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1910#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1911 && !did_highlight_changed
1912#endif
1913 )
1914 {
1915 // Do not trigger a redraw when highlighting is changed while
1916 // redrawing. This may happen when evaluating 'statusline' changes the
1917 // StatusLine group.
1918 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001919 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001920 need_highlight_changed = TRUE;
1921 }
1922}
1923
1924#if defined(EXITFREE) || defined(PROTO)
1925 void
1926free_highlight(void)
1927{
1928 int i;
1929
1930 for (i = 0; i < highlight_ga.ga_len; ++i)
1931 {
1932 highlight_clear(i);
1933 vim_free(HL_TABLE()[i].sg_name);
1934 vim_free(HL_TABLE()[i].sg_name_u);
1935 }
1936 ga_clear(&highlight_ga);
1937}
1938#endif
1939
1940/*
1941 * Reset the cterm colors to what they were before Vim was started, if
1942 * possible. Otherwise reset them to zero.
1943 */
1944 void
1945restore_cterm_colors(void)
1946{
1947#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1948 // Since t_me has been set, this probably means that the user
1949 // wants to use this as default colors. Need to reset default
1950 // background/foreground colors.
1951 mch_set_normal_colors();
1952#else
1953# ifdef VIMDLL
1954 if (!gui.in_use)
1955 {
1956 mch_set_normal_colors();
1957 return;
1958 }
1959# endif
1960 cterm_normal_fg_color = 0;
1961 cterm_normal_fg_bold = 0;
1962 cterm_normal_bg_color = 0;
1963# ifdef FEAT_TERMGUICOLORS
1964 cterm_normal_fg_gui_color = INVALCOLOR;
1965 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001966 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001967# endif
1968#endif
1969}
1970
1971/*
1972 * Return TRUE if highlight group "idx" has any settings.
1973 * When "check_link" is TRUE also check for an existing link.
1974 */
1975 static int
1976hl_has_settings(int idx, int check_link)
1977{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001978 return HL_TABLE()[idx].sg_cleared == 0
1979 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001980 || HL_TABLE()[idx].sg_cterm_attr != 0
1981 || HL_TABLE()[idx].sg_cterm_fg != 0
1982 || HL_TABLE()[idx].sg_cterm_bg != 0
PMuncha606f3a2023-11-15 15:35:49 +01001983 || HL_TABLE()[idx].sg_cterm_font != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001984#ifdef FEAT_GUI
1985 || HL_TABLE()[idx].sg_gui_attr != 0
1986 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1987 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1988 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1989 || HL_TABLE()[idx].sg_font_name != NULL
1990#endif
1991 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1992}
1993
1994/*
1995 * Clear highlighting for one group.
1996 */
1997 static void
1998highlight_clear(int idx)
1999{
2000 HL_TABLE()[idx].sg_cleared = TRUE;
2001
2002 HL_TABLE()[idx].sg_term = 0;
2003 VIM_CLEAR(HL_TABLE()[idx].sg_start);
2004 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
2005 HL_TABLE()[idx].sg_term_attr = 0;
2006 HL_TABLE()[idx].sg_cterm = 0;
2007 HL_TABLE()[idx].sg_cterm_bold = FALSE;
2008 HL_TABLE()[idx].sg_cterm_fg = 0;
2009 HL_TABLE()[idx].sg_cterm_bg = 0;
2010 HL_TABLE()[idx].sg_cterm_attr = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002011 HL_TABLE()[idx].sg_cterm_font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002012#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2013 HL_TABLE()[idx].sg_gui = 0;
2014 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
2015 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
2016 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
2017#endif
2018#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2019 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
2020 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002021 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002022#endif
2023#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002024 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2025 HL_TABLE()[idx].sg_font = NOFONT;
2026# ifdef FEAT_XFONTSET
2027 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2028 HL_TABLE()[idx].sg_fontset = NOFONTSET;
2029# endif
2030 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
2031 HL_TABLE()[idx].sg_gui_attr = 0;
2032#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02002033 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02002034 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02002035#ifdef FEAT_EVAL
2036 // Since we set the default link, set the location to where the default
2037 // link was set.
2038 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002039#endif
2040}
2041
2042#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2043/*
2044 * Set the normal foreground and background colors according to the "Normal"
2045 * highlighting group. For X11 also set "Menu", "Scrollbar", and
2046 * "Tooltip" colors.
2047 */
2048 void
2049set_normal_colors(void)
2050{
2051# ifdef FEAT_GUI
2052# ifdef FEAT_TERMGUICOLORS
2053 if (gui.in_use)
2054# endif
2055 {
2056 if (set_group_colors((char_u *)"Normal",
2057 &gui.norm_pixel, &gui.back_pixel,
2058 FALSE, TRUE, FALSE))
2059 {
2060 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002061 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002062 }
2063# ifdef FEAT_GUI_X11
2064 if (set_group_colors((char_u *)"Menu",
2065 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
2066 TRUE, FALSE, FALSE))
2067 {
2068# ifdef FEAT_MENU
2069 gui_mch_new_menu_colors();
2070# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002071 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002072 }
2073# ifdef FEAT_BEVAL_GUI
2074 if (set_group_colors((char_u *)"Tooltip",
2075 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
2076 FALSE, FALSE, TRUE))
2077 {
2078# ifdef FEAT_TOOLBAR
2079 gui_mch_new_tooltip_colors();
2080# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002081 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002082 }
2083# endif
2084 if (set_group_colors((char_u *)"Scrollbar",
2085 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
2086 FALSE, FALSE, FALSE))
2087 {
2088 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002089 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002090 }
2091# endif
2092 }
2093# endif
2094# ifdef FEAT_TERMGUICOLORS
2095# ifdef FEAT_GUI
2096 else
2097# endif
2098 {
2099 int idx;
2100
2101 idx = syn_name2id((char_u *)"Normal") - 1;
2102 if (idx >= 0)
2103 {
2104 gui_do_one_color(idx, FALSE, FALSE);
2105
2106 // If the normal fg or bg color changed a complete redraw is
2107 // required.
2108 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
2109 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
2110 {
2111 // if the GUI color is INVALCOLOR then we use the default cterm
2112 // color
2113 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
2114 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002115 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002116 }
2117 }
2118 }
2119# endif
2120}
2121#endif
2122
2123#if defined(FEAT_GUI) || defined(PROTO)
2124/*
2125 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2126 */
2127 static int
2128set_group_colors(
2129 char_u *name,
2130 guicolor_T *fgp,
2131 guicolor_T *bgp,
2132 int do_menu,
2133 int use_norm,
2134 int do_tooltip)
2135{
2136 int idx;
2137
2138 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002139 if (idx < 0)
2140 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002141
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002142 gui_do_one_color(idx, do_menu, do_tooltip);
2143
2144 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2145 *fgp = HL_TABLE()[idx].sg_gui_fg;
2146 else if (use_norm)
2147 *fgp = gui.def_norm_pixel;
2148 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2149 *bgp = HL_TABLE()[idx].sg_gui_bg;
2150 else if (use_norm)
2151 *bgp = gui.def_back_pixel;
2152 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002153}
2154
2155/*
2156 * Get the font of the "Normal" group.
2157 * Returns "" when it's not found or not set.
2158 */
2159 char_u *
2160hl_get_font_name(void)
2161{
2162 int id;
2163 char_u *s;
2164
2165 id = syn_name2id((char_u *)"Normal");
2166 if (id > 0)
2167 {
2168 s = HL_TABLE()[id - 1].sg_font_name;
2169 if (s != NULL)
2170 return s;
2171 }
2172 return (char_u *)"";
2173}
2174
2175/*
2176 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2177 * actually chosen to be used.
2178 */
2179 void
2180hl_set_font_name(char_u *font_name)
2181{
2182 int id;
2183
2184 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002185 if (id <= 0)
2186 return;
2187
2188 vim_free(HL_TABLE()[id - 1].sg_font_name);
2189 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002190}
2191
2192/*
2193 * Set background color for "Normal" group. Called by gui_set_bg_color()
2194 * when the color is known.
2195 */
2196 void
2197hl_set_bg_color_name(
2198 char_u *name) // must have been allocated
2199{
2200 int id;
2201
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002202 if (name == NULL)
2203 return;
2204
2205 id = syn_name2id((char_u *)"Normal");
2206 if (id <= 0)
2207 return;
2208
2209 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2210 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002211}
2212
2213/*
2214 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2215 * when the color is known.
2216 */
2217 void
2218hl_set_fg_color_name(
2219 char_u *name) // must have been allocated
2220{
2221 int id;
2222
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002223 if (name == NULL)
2224 return;
2225
2226 id = syn_name2id((char_u *)"Normal");
2227 if (id <= 0)
2228 return;
2229
2230 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2231 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002232}
2233
2234/*
2235 * Return the handle for a font name.
2236 * Returns NOFONT when failed.
2237 */
2238 static GuiFont
2239font_name2handle(char_u *name)
2240{
2241 if (STRCMP(name, "NONE") == 0)
2242 return NOFONT;
2243
2244 return gui_mch_get_font(name, TRUE);
2245}
2246
2247# ifdef FEAT_XFONTSET
2248/*
2249 * Return the handle for a fontset name.
2250 * Returns NOFONTSET when failed.
2251 */
2252 static GuiFontset
2253fontset_name2handle(char_u *name, int fixed_width)
2254{
2255 if (STRCMP(name, "NONE") == 0)
2256 return NOFONTSET;
2257
2258 return gui_mch_get_fontset(name, TRUE, fixed_width);
2259}
2260# endif
2261
2262/*
2263 * Get the font or fontset for one highlight group.
2264 */
2265 static void
2266hl_do_font(
2267 int idx,
2268 char_u *arg,
2269 int do_normal, // set normal font
2270 int do_menu UNUSED, // set menu font
2271 int do_tooltip UNUSED, // set tooltip font
2272 int free_font) // free current font/fontset
2273{
2274# ifdef FEAT_XFONTSET
2275 // If 'guifontset' is not empty, first try using the name as a
2276 // fontset. If that doesn't work, use it as a font name.
2277 if (*p_guifontset != NUL
2278# ifdef FONTSET_ALWAYS
2279 || do_menu
2280# endif
2281# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002282 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002283 || do_tooltip
2284# endif
2285 )
2286 {
2287 if (free_font)
2288 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2289 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2290# ifdef FONTSET_ALWAYS
2291 || do_menu
2292# endif
2293# ifdef FEAT_BEVAL_TIP
2294 || do_tooltip
2295# endif
2296 );
2297 }
2298 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2299 {
2300 // If it worked and it's the Normal group, use it as the normal
2301 // fontset. Same for the Menu group.
2302 if (do_normal)
2303 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002304# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002305 if (do_menu)
2306 {
2307# ifdef FONTSET_ALWAYS
2308 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2309# else
2310 // YIKES! This is a bug waiting to crash the program
2311 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2312# endif
2313 gui_mch_new_menu_font();
2314 }
2315# ifdef FEAT_BEVAL_GUI
2316 if (do_tooltip)
2317 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002318 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002319 // displaying a single font and a fontset.
2320 // If the XtNinternational resource is set to True at widget
2321 // creation, then a fontset is always used, otherwise an
2322 // XFontStruct is used.
2323 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2324 gui_mch_new_tooltip_font();
2325 }
2326# endif
2327# endif
2328 }
2329 else
2330# endif
2331 {
2332 if (free_font)
2333 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2334 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2335 // If it worked and it's the Normal group, use it as the
2336 // normal font. Same for the Menu group.
2337 if (HL_TABLE()[idx].sg_font != NOFONT)
2338 {
2339 if (do_normal)
2340 gui_init_font(arg, FALSE);
2341#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002342# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002343 if (do_menu)
2344 {
2345 gui.menu_font = HL_TABLE()[idx].sg_font;
2346 gui_mch_new_menu_font();
2347 }
2348# endif
2349#endif
2350 }
2351 }
2352}
2353
2354#endif // FEAT_GUI
2355
2356#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2357/*
2358 * Return the handle for a color name.
2359 * Returns INVALCOLOR when failed.
2360 */
2361 guicolor_T
2362color_name2handle(char_u *name)
2363{
2364 if (STRCMP(name, "NONE") == 0)
2365 return INVALCOLOR;
2366
2367 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2368 {
2369#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2370 if (gui.in_use)
2371#endif
2372#ifdef FEAT_GUI
2373 return gui.norm_pixel;
2374#endif
2375#ifdef FEAT_TERMGUICOLORS
2376 if (cterm_normal_fg_gui_color != INVALCOLOR)
2377 return cterm_normal_fg_gui_color;
2378 // Guess that the foreground is black or white.
2379 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2380#endif
2381 }
2382 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2383 {
2384#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2385 if (gui.in_use)
2386#endif
2387#ifdef FEAT_GUI
2388 return gui.back_pixel;
2389#endif
2390#ifdef FEAT_TERMGUICOLORS
2391 if (cterm_normal_bg_gui_color != INVALCOLOR)
2392 return cterm_normal_bg_gui_color;
2393 // Guess that the background is white or black.
2394 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2395#endif
2396 }
2397
2398 return GUI_GET_COLOR(name);
2399}
Drew Vogele30d1022021-10-24 20:35:07 +01002400
2401// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2402// values as used by the MS-Windows GDI api. It should be used only for
2403// MS-Windows GDI builds.
2404# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2405# undef RGB
2406# endif
2407# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002408# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002409# endif
2410
2411# ifdef VIMDLL
2412 static guicolor_T
2413gui_adjust_rgb(guicolor_T c)
2414{
2415 if (gui.in_use)
2416 return c;
2417 else
2418 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2419}
2420# else
2421# define gui_adjust_rgb(c) (c)
2422# endif
2423
2424 static int
2425hex_digit(int c)
2426{
Keith Thompson184f71c2024-01-04 21:19:04 +01002427 if (SAFE_isdigit(c))
Drew Vogele30d1022021-10-24 20:35:07 +01002428 return c - '0';
2429 c = TOLOWER_ASC(c);
2430 if (c >= 'a' && c <= 'f')
2431 return c - 'a' + 10;
2432 return 0x1ffffff;
2433}
2434
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002435 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002436decode_hex_color(char_u *hex)
2437{
2438 guicolor_T color;
2439
2440 if (hex[0] != '#' || STRLEN(hex) != 7)
2441 return INVALCOLOR;
2442
2443 // Name is in "#rrggbb" format
2444 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2445 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2446 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2447 if (color > 0xffffff)
2448 return INVALCOLOR;
2449 return gui_adjust_rgb(color);
2450}
2451
Bram Moolenaar2a521962021-10-25 10:30:14 +01002452#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002453// Returns the color currently mapped to the given name or INVALCOLOR if no
2454// such name exists in the color table. The convention is to use lowercase for
2455// all keys in the v:colornames dictionary. The value can be either a string in
2456// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002457 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002458colorname2rgb(char_u *name)
2459{
2460 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2461 char_u *lc_name;
2462 dictitem_T *colentry;
2463 char_u *colstr;
2464 varnumber_T colnum;
2465
2466 lc_name = strlow_save(name);
2467 if (lc_name == NULL)
2468 return INVALCOLOR;
2469
2470 colentry = dict_find(colornames_table, lc_name, -1);
2471 vim_free(lc_name);
2472 if (colentry == NULL)
2473 return INVALCOLOR;
2474
2475 if (colentry->di_tv.v_type == VAR_STRING)
2476 {
2477 colstr = tv_get_string_strict(&colentry->di_tv);
2478 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2479 {
2480 return decode_hex_color(colstr);
2481 }
2482 else
2483 {
2484 semsg(_(e_bad_color_string_str), colstr);
2485 return INVALCOLOR;
2486 }
2487 }
2488
2489 if (colentry->di_tv.v_type == VAR_NUMBER)
2490 {
2491 colnum = tv_get_number(&colentry->di_tv);
2492 return (guicolor_T)colnum;
2493 }
2494
2495 return INVALCOLOR;
2496}
2497
Drew Vogele30d1022021-10-24 20:35:07 +01002498#endif
2499
2500 guicolor_T
2501gui_get_color_cmn(char_u *name)
2502{
Drew Vogele30d1022021-10-24 20:35:07 +01002503 guicolor_T color;
Drew Vogele30d1022021-10-24 20:35:07 +01002504 // Only non X11 colors (not present in rgb.txt) and colors in
John Marriott34f00dd2024-04-08 23:28:12 +02002505 // color_name_tab[], useful when $VIMRUNTIME is not found,.
2506 // must be sorted by the 'value' field because it is used by bsearch()!
2507 static keyvalue_T rgb_tab[] = {
2508 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x00), "black"),
2509 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0xFF), "blue"),
2510 KEYVALUE_ENTRY(RGB(0xA5, 0x2A, 0x2A), "brown"),
2511 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0xFF), "cyan"),
2512 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x8B), "darkblue"),
2513 KEYVALUE_ENTRY(RGB(0x00, 0x8B, 0x8B), "darkcyan"),
2514 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgray"),
2515 KEYVALUE_ENTRY(RGB(0x00, 0x64, 0x00), "darkgreen"),
2516 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgrey"),
2517 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x8B), "darkmagenta"),
2518 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x00), "darkred"),
2519 KEYVALUE_ENTRY(RGB(0x8B, 0x8B, 0x00), "darkyellow"), // No X11
2520 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "gray"),
2521 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0x00), "green"),
2522 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "grey"),
2523 KEYVALUE_ENTRY(RGB(0x66, 0x66, 0x66), "grey40"),
2524 KEYVALUE_ENTRY(RGB(0x7F, 0x7F, 0x7F), "grey50"),
2525 KEYVALUE_ENTRY(RGB(0xE5, 0xE5, 0xE5), "grey90"),
2526 KEYVALUE_ENTRY(RGB(0xAD, 0xD8, 0xE6), "lightblue"),
2527 KEYVALUE_ENTRY(RGB(0xE0, 0xFF, 0xFF), "lightcyan"),
2528 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgray"),
2529 KEYVALUE_ENTRY(RGB(0x90, 0xEE, 0x90), "lightgreen"),
2530 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgrey"),
2531 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0xFF), "lightmagenta"), // No XX
2532 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0x8B), "lightred"), // No XX
2533 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xE0), "lightyellow"),
2534 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0xFF), "magenta"),
2535 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0x00), "red"),
2536 KEYVALUE_ENTRY(RGB(0x2E, 0x8B, 0x57), "seagreen"),
2537 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xFF), "white"),
2538 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0x00), "yellow")
Drew Vogele30d1022021-10-24 20:35:07 +01002539 };
John Marriott34f00dd2024-04-08 23:28:12 +02002540 keyvalue_T target;
2541 keyvalue_T *entry;
Drew Vogele30d1022021-10-24 20:35:07 +01002542
2543 color = decode_hex_color(name);
2544 if (color != INVALCOLOR)
2545 return color;
2546
John Marriott34f00dd2024-04-08 23:28:12 +02002547 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01002548 target.value.string = name;
2549 target.value.length = 0; // not used, see cmp_keyvalue_value_i()
2550 entry = (keyvalue_T *)bsearch(&target, &rgb_tab, ARRAY_LENGTH(rgb_tab),
2551 sizeof(rgb_tab[0]), cmp_keyvalue_value_i);
John Marriott34f00dd2024-04-08 23:28:12 +02002552 if (entry != NULL)
John Marriott34f00dd2024-04-08 23:28:12 +02002553 return gui_adjust_rgb((guicolor_T)entry->key);
Drew Vogele30d1022021-10-24 20:35:07 +01002554
2555#if defined(FEAT_EVAL)
2556 /*
2557 * Not a traditional color. Load additional color aliases and then consult the alias table.
2558 */
2559
2560 color = colorname2rgb(name);
2561 if (color == INVALCOLOR)
2562 {
2563 load_default_colors_lists();
2564 color = colorname2rgb(name);
2565 }
2566
2567 return color;
2568#else
2569 return INVALCOLOR;
2570#endif
2571}
2572
2573 guicolor_T
2574gui_get_rgb_color_cmn(int r, int g, int b)
2575{
2576 guicolor_T color = RGB(r, g, b);
2577
2578 if (color > 0xffffff)
2579 return INVALCOLOR;
2580 return gui_adjust_rgb(color);
2581}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002582#endif
2583
2584/*
2585 * Table with the specifications for an attribute number.
2586 * Note that this table is used by ALL buffers. This is required because the
2587 * GUI can redraw at any time for any buffer.
2588 */
2589static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2590
2591#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2592
2593static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2594
2595#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2596
2597#ifdef FEAT_GUI
2598static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2599
2600#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2601#endif
2602
2603/*
2604 * Return the attr number for a set of colors and font.
2605 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2606 * if the combination is new.
2607 * Return 0 for error (no more room).
2608 */
2609 static int
2610get_attr_entry(garray_T *table, attrentry_T *aep)
2611{
2612 int i;
2613 attrentry_T *taep;
2614 static int recursive = FALSE;
2615
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002616 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002617 table->ga_itemsize = sizeof(attrentry_T);
2618 table->ga_growsize = 7;
2619
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002620 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002621 for (i = 0; i < table->ga_len; ++i)
2622 {
2623 taep = &(((attrentry_T *)table->ga_data)[i]);
2624 if ( aep->ae_attr == taep->ae_attr
2625 && (
2626#ifdef FEAT_GUI
2627 (table == &gui_attr_table
2628 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2629 && aep->ae_u.gui.bg_color
2630 == taep->ae_u.gui.bg_color
2631 && aep->ae_u.gui.sp_color
2632 == taep->ae_u.gui.sp_color
2633 && aep->ae_u.gui.font == taep->ae_u.gui.font
2634# ifdef FEAT_XFONTSET
2635 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2636# endif
2637 ))
2638 ||
2639#endif
2640 (table == &term_attr_table
2641 && (aep->ae_u.term.start == NULL)
2642 == (taep->ae_u.term.start == NULL)
2643 && (aep->ae_u.term.start == NULL
2644 || STRCMP(aep->ae_u.term.start,
2645 taep->ae_u.term.start) == 0)
2646 && (aep->ae_u.term.stop == NULL)
2647 == (taep->ae_u.term.stop == NULL)
2648 && (aep->ae_u.term.stop == NULL
2649 || STRCMP(aep->ae_u.term.stop,
2650 taep->ae_u.term.stop) == 0))
2651 || (table == &cterm_attr_table
2652 && aep->ae_u.cterm.fg_color
2653 == taep->ae_u.cterm.fg_color
2654 && aep->ae_u.cterm.bg_color
2655 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002656 && aep->ae_u.cterm.ul_color
2657 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002658 && aep->ae_u.cterm.font
2659 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002660#ifdef FEAT_TERMGUICOLORS
2661 && aep->ae_u.cterm.fg_rgb
2662 == taep->ae_u.cterm.fg_rgb
2663 && aep->ae_u.cterm.bg_rgb
2664 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002665 && aep->ae_u.cterm.ul_rgb
2666 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002667#endif
2668 )))
2669
2670 return i + ATTR_OFF;
2671 }
2672
2673 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2674 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002675 // Running out of attribute entries! remove all attributes, and
2676 // compute new ones for all groups.
2677 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002678 if (recursive)
2679 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002680 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002681 return 0;
2682 }
2683 recursive = TRUE;
2684
2685 clear_hl_tables();
2686
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002687 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002688
2689 for (i = 0; i < highlight_ga.ga_len; ++i)
2690 set_hl_attr(i);
2691
2692 recursive = FALSE;
2693 }
2694
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002695 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002696 if (ga_grow(table, 1) == FAIL)
2697 return 0;
2698
2699 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002700 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002701 taep->ae_attr = aep->ae_attr;
2702#ifdef FEAT_GUI
2703 if (table == &gui_attr_table)
2704 {
2705 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2706 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2707 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2708 taep->ae_u.gui.font = aep->ae_u.gui.font;
2709# ifdef FEAT_XFONTSET
2710 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2711# endif
2712 }
2713#endif
2714 if (table == &term_attr_table)
2715 {
2716 if (aep->ae_u.term.start == NULL)
2717 taep->ae_u.term.start = NULL;
2718 else
2719 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2720 if (aep->ae_u.term.stop == NULL)
2721 taep->ae_u.term.stop = NULL;
2722 else
2723 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2724 }
2725 else if (table == &cterm_attr_table)
2726 {
2727 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2728 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002729 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002730 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002731#ifdef FEAT_TERMGUICOLORS
2732 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2733 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002734 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002735#endif
2736 }
2737 ++table->ga_len;
2738 return (table->ga_len - 1 + ATTR_OFF);
2739}
2740
2741#if defined(FEAT_TERMINAL) || defined(PROTO)
2742/*
2743 * Get an attribute index for a cterm entry.
2744 * Uses an existing entry when possible or adds one when needed.
2745 */
2746 int
2747get_cterm_attr_idx(int attr, int fg, int bg)
2748{
2749 attrentry_T at_en;
2750
Bram Moolenaara80faa82020-04-12 19:37:17 +02002751 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002752#ifdef FEAT_TERMGUICOLORS
2753 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2754 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002755 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002756#endif
2757 at_en.ae_attr = attr;
2758 at_en.ae_u.cterm.fg_color = fg;
2759 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002760 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002761 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002762 return get_attr_entry(&cterm_attr_table, &at_en);
2763}
2764#endif
2765
2766#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2767/*
2768 * Get an attribute index for a 'termguicolors' entry.
2769 * Uses an existing entry when possible or adds one when needed.
2770 */
2771 int
2772get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2773{
2774 attrentry_T at_en;
2775
Bram Moolenaara80faa82020-04-12 19:37:17 +02002776 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002777 at_en.ae_attr = attr;
2778 if (fg == INVALCOLOR && bg == INVALCOLOR)
2779 {
2780 // If both GUI colors are not set fall back to the cterm colors. Helps
2781 // if the GUI only has an attribute, such as undercurl.
2782 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2783 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2784 }
2785 else
2786 {
2787 at_en.ae_u.cterm.fg_rgb = fg;
2788 at_en.ae_u.cterm.bg_rgb = bg;
2789 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002790 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002791 return get_attr_entry(&cterm_attr_table, &at_en);
2792}
2793#endif
2794
2795#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2796/*
2797 * Get an attribute index for a cterm entry.
2798 * Uses an existing entry when possible or adds one when needed.
2799 */
2800 int
2801get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2802{
2803 attrentry_T at_en;
2804
Bram Moolenaara80faa82020-04-12 19:37:17 +02002805 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002806 at_en.ae_attr = attr;
2807 at_en.ae_u.gui.fg_color = fg;
2808 at_en.ae_u.gui.bg_color = bg;
2809 return get_attr_entry(&gui_attr_table, &at_en);
2810}
2811#endif
2812
2813/*
2814 * Clear all highlight tables.
2815 */
2816 void
2817clear_hl_tables(void)
2818{
2819 int i;
2820 attrentry_T *taep;
2821
2822#ifdef FEAT_GUI
2823 ga_clear(&gui_attr_table);
2824#endif
2825 for (i = 0; i < term_attr_table.ga_len; ++i)
2826 {
2827 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2828 vim_free(taep->ae_u.term.start);
2829 vim_free(taep->ae_u.term.stop);
2830 }
2831 ga_clear(&term_attr_table);
2832 ga_clear(&cterm_attr_table);
2833}
2834
2835/*
2836 * Combine special attributes (e.g., for spelling) with other attributes
2837 * (e.g., for syntax highlighting).
2838 * "prim_attr" overrules "char_attr".
2839 * This creates a new group when required.
2840 * Since we expect there to be few spelling mistakes we don't cache the
2841 * result.
2842 * Return the resulting attributes.
2843 */
2844 int
2845hl_combine_attr(int char_attr, int prim_attr)
2846{
2847 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002848 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002849 attrentry_T new_en;
2850
2851 if (char_attr == 0)
2852 return prim_attr;
2853 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2854 return ATTR_COMBINE(char_attr, prim_attr);
2855#ifdef FEAT_GUI
2856 if (gui.in_use)
2857 {
2858 if (char_attr > HL_ALL)
2859 char_aep = syn_gui_attr2entry(char_attr);
2860 if (char_aep != NULL)
2861 new_en = *char_aep;
2862 else
2863 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002864 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002865 new_en.ae_u.gui.fg_color = INVALCOLOR;
2866 new_en.ae_u.gui.bg_color = INVALCOLOR;
2867 new_en.ae_u.gui.sp_color = INVALCOLOR;
2868 if (char_attr <= HL_ALL)
2869 new_en.ae_attr = char_attr;
2870 }
2871
2872 if (prim_attr <= HL_ALL)
2873 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2874 else
2875 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002876 prim_aep = syn_gui_attr2entry(prim_attr);
2877 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002878 {
2879 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002880 prim_aep->ae_attr);
2881 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2882 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2883 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2884 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2885 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2886 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2887 if (prim_aep->ae_u.gui.font != NOFONT)
2888 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002889# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002890 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2891 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002892# endif
2893 }
2894 }
2895 return get_attr_entry(&gui_attr_table, &new_en);
2896 }
2897#endif
2898
2899 if (IS_CTERM)
2900 {
2901 if (char_attr > HL_ALL)
2902 char_aep = syn_cterm_attr2entry(char_attr);
2903 if (char_aep != NULL)
2904 new_en = *char_aep;
2905 else
2906 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002907 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002908#ifdef FEAT_TERMGUICOLORS
2909 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2910 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002911 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002912#endif
2913 if (char_attr <= HL_ALL)
2914 new_en.ae_attr = char_attr;
2915 }
2916
2917 if (prim_attr <= HL_ALL)
2918 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2919 else
2920 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002921 prim_aep = syn_cterm_attr2entry(prim_attr);
2922 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002923 {
2924 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002925 prim_aep->ae_attr);
2926 if (prim_aep->ae_u.cterm.fg_color > 0)
2927 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2928 if (prim_aep->ae_u.cterm.bg_color > 0)
2929 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2930 if (prim_aep->ae_u.cterm.ul_color > 0)
2931 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002932 if (prim_aep->ae_u.cterm.font > 0)
2933 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002934#ifdef FEAT_TERMGUICOLORS
2935 // If both fg and bg are not set fall back to cterm colors.
2936 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002937 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2938 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002939 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002940 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002941 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002942 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002943 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2944 }
2945 else
2946 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002947 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2948 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2949 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2950 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002951 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002952 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2953 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002954#endif
2955 }
2956 }
2957 return get_attr_entry(&cterm_attr_table, &new_en);
2958 }
2959
2960 if (char_attr > HL_ALL)
2961 char_aep = syn_term_attr2entry(char_attr);
2962 if (char_aep != NULL)
2963 new_en = *char_aep;
2964 else
2965 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002966 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002967 if (char_attr <= HL_ALL)
2968 new_en.ae_attr = char_attr;
2969 }
2970
2971 if (prim_attr <= HL_ALL)
2972 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2973 else
2974 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002975 prim_aep = syn_term_attr2entry(prim_attr);
2976 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002977 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002978 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2979 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002980 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002981 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2982 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002983 }
2984 }
2985 }
2986 return get_attr_entry(&term_attr_table, &new_en);
2987}
2988
2989#ifdef FEAT_GUI
2990 attrentry_T *
2991syn_gui_attr2entry(int attr)
2992{
2993 attr -= ATTR_OFF;
2994 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2995 return NULL;
2996 return &(GUI_ATTR_ENTRY(attr));
2997}
2998#endif
2999
3000/*
3001 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
3002 * Only to be used when "attr" > HL_ALL.
3003 */
3004 int
3005syn_attr2attr(int attr)
3006{
3007 attrentry_T *aep;
3008
3009#ifdef FEAT_GUI
3010 if (gui.in_use)
3011 aep = syn_gui_attr2entry(attr);
3012 else
3013#endif
3014 if (IS_CTERM)
3015 aep = syn_cterm_attr2entry(attr);
3016 else
3017 aep = syn_term_attr2entry(attr);
3018
3019 if (aep == NULL) // highlighting not set
3020 return 0;
3021 return aep->ae_attr;
3022}
3023
3024
3025 attrentry_T *
3026syn_term_attr2entry(int attr)
3027{
3028 attr -= ATTR_OFF;
3029 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
3030 return NULL;
3031 return &(TERM_ATTR_ENTRY(attr));
3032}
3033
3034 attrentry_T *
3035syn_cterm_attr2entry(int attr)
3036{
3037 attr -= ATTR_OFF;
3038 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
3039 return NULL;
3040 return &(CTERM_ATTR_ENTRY(attr));
3041}
3042
3043#define LIST_ATTR 1
3044#define LIST_STRING 2
3045#define LIST_INT 3
3046
3047 static void
3048highlight_list_one(int id)
3049{
3050 hl_group_T *sgp;
3051 int didh = FALSE;
3052
3053 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
3054
3055 if (message_filtered(sgp->sg_name))
3056 return;
3057
3058 didh = highlight_list_arg(id, didh, LIST_ATTR,
3059 sgp->sg_term, NULL, "term");
3060 didh = highlight_list_arg(id, didh, LIST_STRING,
3061 0, sgp->sg_start, "start");
3062 didh = highlight_list_arg(id, didh, LIST_STRING,
3063 0, sgp->sg_stop, "stop");
3064
3065 didh = highlight_list_arg(id, didh, LIST_ATTR,
3066 sgp->sg_cterm, NULL, "cterm");
3067 didh = highlight_list_arg(id, didh, LIST_INT,
3068 sgp->sg_cterm_fg, NULL, "ctermfg");
3069 didh = highlight_list_arg(id, didh, LIST_INT,
3070 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02003071 didh = highlight_list_arg(id, didh, LIST_INT,
3072 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01003073 didh = highlight_list_arg(id, didh, LIST_INT,
3074 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003075
3076#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3077 didh = highlight_list_arg(id, didh, LIST_ATTR,
3078 sgp->sg_gui, NULL, "gui");
3079 didh = highlight_list_arg(id, didh, LIST_STRING,
3080 0, sgp->sg_gui_fg_name, "guifg");
3081 didh = highlight_list_arg(id, didh, LIST_STRING,
3082 0, sgp->sg_gui_bg_name, "guibg");
3083 didh = highlight_list_arg(id, didh, LIST_STRING,
3084 0, sgp->sg_gui_sp_name, "guisp");
3085#endif
3086#ifdef FEAT_GUI
3087 didh = highlight_list_arg(id, didh, LIST_STRING,
3088 0, sgp->sg_font_name, "font");
3089#endif
3090
3091 if (sgp->sg_link && !got_int)
3092 {
3093 (void)syn_list_header(didh, 9999, id);
3094 didh = TRUE;
3095 msg_puts_attr("links to", HL_ATTR(HLF_D));
3096 msg_putchar(' ');
3097 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3098 }
3099
3100 if (!didh)
3101 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3102#ifdef FEAT_EVAL
3103 if (p_verbose > 0)
3104 last_set_msg(sgp->sg_script_ctx);
3105#endif
3106}
3107
3108 static int
3109highlight_list_arg(
3110 int id,
3111 int didh,
3112 int type,
3113 int iarg,
3114 char_u *sarg,
3115 char *name)
3116{
Bram Moolenaar84f54632022-06-29 18:39:11 +01003117 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003118 char_u *ts;
3119 int i;
3120
3121 if (got_int)
3122 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003123
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003124 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3125 return didh;
3126
3127 ts = buf;
3128 if (type == LIST_INT)
3129 sprintf((char *)buf, "%d", iarg - 1);
3130 else if (type == LIST_STRING)
3131 ts = sarg;
3132 else // type == LIST_ATTR
3133 {
John Marriott34f00dd2024-04-08 23:28:12 +02003134 size_t buflen;
3135
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003136 buf[0] = NUL;
John Marriott34f00dd2024-04-08 23:28:12 +02003137 buflen = 0;
3138 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003139 {
John Marriott34f00dd2024-04-08 23:28:12 +02003140 if (iarg & highlight_index_tab[i]->key)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003141 {
John Marriott34f00dd2024-04-08 23:28:12 +02003142 if (buflen > 0)
3143 {
3144 STRCPY(buf + buflen, (char_u *)",");
3145 ++buflen;
3146 }
John Marriott8d4477e2024-11-02 15:59:01 +01003147 STRCPY(buf + buflen, highlight_index_tab[i]->value.string);
3148 buflen += highlight_index_tab[i]->value.length;
John Marriott34f00dd2024-04-08 23:28:12 +02003149 iarg &= ~highlight_index_tab[i]->key; // don't want "inverse"/"reverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003150 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003151 }
3152 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003153
3154 (void)syn_list_header(didh,
3155 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3156 didh = TRUE;
3157 if (!got_int)
3158 {
3159 if (*name != NUL)
3160 {
3161 msg_puts_attr(name, HL_ATTR(HLF_D));
3162 msg_puts_attr("=", HL_ATTR(HLF_D));
3163 }
3164 msg_outtrans(ts);
3165 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003166 return didh;
3167}
3168
3169#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3170/*
3171 * Return "1" if highlight group "id" has attribute "flag".
3172 * Return NULL otherwise.
3173 */
3174 char_u *
3175highlight_has_attr(
3176 int id,
3177 int flag,
3178 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3179{
3180 int attr;
3181
3182 if (id <= 0 || id > highlight_ga.ga_len)
3183 return NULL;
3184
3185#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3186 if (modec == 'g')
3187 attr = HL_TABLE()[id - 1].sg_gui;
3188 else
3189#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003190 {
3191 if (modec == 'c')
3192 attr = HL_TABLE()[id - 1].sg_cterm;
3193 else
3194 attr = HL_TABLE()[id - 1].sg_term;
3195 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003196
3197 if (attr & flag)
3198 return (char_u *)"1";
3199 return NULL;
3200}
3201#endif
3202
3203#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3204/*
3205 * Return color name of highlight group "id".
3206 */
3207 char_u *
3208highlight_color(
3209 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003210 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003211 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3212{
3213 static char_u name[20];
3214 int n;
3215 int fg = FALSE;
3216 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003217 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003218 int font = FALSE;
3219
3220 if (id <= 0 || id > highlight_ga.ga_len)
3221 return NULL;
3222
3223 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3224 fg = TRUE;
3225 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3226 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3227 font = TRUE;
3228 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3229 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003230 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3231 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003232 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3233 return NULL;
3234 if (modec == 'g')
3235 {
3236# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3237# ifdef FEAT_GUI
3238 // return font name
3239 if (font)
3240 return HL_TABLE()[id - 1].sg_font_name;
3241# endif
3242
3243 // return #RRGGBB form (only possible when GUI is running)
3244 if ((USE_24BIT) && what[2] == '#')
3245 {
3246 guicolor_T color;
3247 long_u rgb;
3248 static char_u buf[10];
3249
3250 if (fg)
3251 color = HL_TABLE()[id - 1].sg_gui_fg;
3252 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003253 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003254 else
3255 color = HL_TABLE()[id - 1].sg_gui_bg;
3256 if (color == INVALCOLOR)
3257 return NULL;
3258 rgb = (long_u)GUI_MCH_GET_RGB(color);
3259 sprintf((char *)buf, "#%02x%02x%02x",
3260 (unsigned)(rgb >> 16),
3261 (unsigned)(rgb >> 8) & 255,
3262 (unsigned)rgb & 255);
3263 return buf;
3264 }
3265# endif
3266 if (fg)
3267 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3268 if (sp)
3269 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3270 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3271 }
PMuncha606f3a2023-11-15 15:35:49 +01003272 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003273 return NULL;
3274 if (modec == 'c')
3275 {
3276 if (fg)
3277 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003278 else if (ul)
3279 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003280 else if (font)
3281 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003282 else
3283 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3284 if (n < 0)
3285 return NULL;
3286 sprintf((char *)name, "%d", n);
3287 return name;
3288 }
3289 // term doesn't have color
3290 return NULL;
3291}
3292#endif
3293
3294#if (defined(FEAT_SYN_HL) \
3295 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3296 && defined(FEAT_PRINTER)) || defined(PROTO)
3297/*
3298 * Return color name of highlight group "id" as RGB value.
3299 */
3300 long_u
3301highlight_gui_color_rgb(
3302 int id,
3303 int fg) // TRUE = fg, FALSE = bg
3304{
3305 guicolor_T color;
3306
3307 if (id <= 0 || id > highlight_ga.ga_len)
3308 return 0L;
3309
3310 if (fg)
3311 color = HL_TABLE()[id - 1].sg_gui_fg;
3312 else
3313 color = HL_TABLE()[id - 1].sg_gui_bg;
3314
3315 if (color == INVALCOLOR)
3316 return 0L;
3317
3318 return GUI_MCH_GET_RGB(color);
3319}
3320#endif
3321
3322/*
3323 * Output the syntax list header.
3324 * Return TRUE when started a new line.
3325 */
3326 int
3327syn_list_header(
3328 int did_header, // did header already
3329 int outlen, // length of string that comes
3330 int id) // highlight group id
3331{
3332 int endcol = 19;
3333 int newline = TRUE;
3334 int name_col = 0;
3335
3336 if (!did_header)
3337 {
3338 msg_putchar('\n');
3339 if (got_int)
3340 return TRUE;
3341 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3342 name_col = msg_col;
3343 endcol = 15;
3344 }
3345 else if (msg_col + outlen + 1 >= Columns)
3346 {
3347 msg_putchar('\n');
3348 if (got_int)
3349 return TRUE;
3350 }
3351 else
3352 {
3353 if (msg_col >= endcol) // wrap around is like starting a new line
3354 newline = FALSE;
3355 }
3356
3357 if (msg_col >= endcol) // output at least one space
3358 endcol = msg_col + 1;
Christian Brabandt220474d2024-07-20 13:26:44 +02003359 if (Columns <= (long)endcol) // avoid hang for tiny window
3360 endcol = (int)(Columns - 1);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003361
3362 msg_advance(endcol);
3363
3364 // Show "xxx" with the attributes.
3365 if (!did_header)
3366 {
3367 if (endcol == Columns - 1 && endcol <= name_col)
3368 msg_putchar(' ');
3369 msg_puts_attr("xxx", syn_id2attr(id));
3370 msg_putchar(' ');
3371 }
3372
3373 return newline;
3374}
3375
3376/*
3377 * Set the attribute numbers for a highlight group.
3378 * Called after one of the attributes has changed.
3379 */
3380 static void
3381set_hl_attr(
3382 int idx) // index in array
3383{
3384 attrentry_T at_en;
3385 hl_group_T *sgp = HL_TABLE() + idx;
3386
3387 // The "Normal" group doesn't need an attribute number
3388 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3389 return;
3390
3391#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003392 // For the GUI mode: If there are other than "normal" highlighting
3393 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003394 if (sgp->sg_gui_fg == INVALCOLOR
3395 && sgp->sg_gui_bg == INVALCOLOR
3396 && sgp->sg_gui_sp == INVALCOLOR
3397 && sgp->sg_font == NOFONT
3398# ifdef FEAT_XFONTSET
3399 && sgp->sg_fontset == NOFONTSET
3400# endif
3401 )
3402 {
3403 sgp->sg_gui_attr = sgp->sg_gui;
3404 }
3405 else
3406 {
3407 at_en.ae_attr = sgp->sg_gui;
3408 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3409 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3410 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3411 at_en.ae_u.gui.font = sgp->sg_font;
3412# ifdef FEAT_XFONTSET
3413 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3414# endif
3415 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3416 }
3417#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003418 // For the term mode: If there are other than "normal" highlighting
3419 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003420 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3421 sgp->sg_term_attr = sgp->sg_term;
3422 else
3423 {
3424 at_en.ae_attr = sgp->sg_term;
3425 at_en.ae_u.term.start = sgp->sg_start;
3426 at_en.ae_u.term.stop = sgp->sg_stop;
3427 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3428 }
3429
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003430 // For the color term mode: If there are other than "normal"
3431 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003432 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3433 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003434# ifdef FEAT_TERMGUICOLORS
3435 && sgp->sg_gui_fg == INVALCOLOR
3436 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003437 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003438# endif
3439 )
3440 sgp->sg_cterm_attr = sgp->sg_cterm;
3441 else
3442 {
3443 at_en.ae_attr = sgp->sg_cterm;
3444 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3445 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003446 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003447 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003448# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003449 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3450 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003451 // Only use the underline/undercurl color when used, it may clear the
3452 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003453 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3454 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003455 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3456 else
3457 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003458 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3459 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3460 {
3461 // If both fg and bg are invalid fall back to the cterm colors.
3462 // Helps when the GUI only uses an attribute, e.g. undercurl.
3463 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3464 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3465 }
3466# endif
3467 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3468 }
3469}
3470
3471/*
3472 * Lookup a highlight group name and return its ID.
3473 * If it is not found, 0 is returned.
3474 */
3475 int
3476syn_name2id(char_u *name)
3477{
3478 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003479 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003480
3481 // Avoid using stricmp() too much, it's slow on some systems
3482 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3483 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003484 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003485 vim_strup(name_u);
3486 for (i = highlight_ga.ga_len; --i >= 0; )
3487 if (HL_TABLE()[i].sg_name_u != NULL
3488 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3489 break;
3490 return i + 1;
3491}
3492
3493/*
3494 * Lookup a highlight group name and return its attributes.
3495 * Return zero if not found.
3496 */
3497 int
3498syn_name2attr(char_u *name)
3499{
3500 int id = syn_name2id(name);
3501
3502 if (id != 0)
3503 return syn_id2attr(id);
3504 return 0;
3505}
3506
3507#if defined(FEAT_EVAL) || defined(PROTO)
3508/*
3509 * Return TRUE if highlight group "name" exists.
3510 */
3511 int
3512highlight_exists(char_u *name)
3513{
3514 return (syn_name2id(name) > 0);
3515}
3516
3517# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3518/*
3519 * Return the name of highlight group "id".
3520 * When not a valid ID return an empty string.
3521 */
3522 char_u *
3523syn_id2name(int id)
3524{
3525 if (id <= 0 || id > highlight_ga.ga_len)
3526 return (char_u *)"";
3527 return HL_TABLE()[id - 1].sg_name;
3528}
3529# endif
3530#endif
3531
3532/*
3533 * Like syn_name2id(), but take a pointer + length argument.
3534 */
3535 int
3536syn_namen2id(char_u *linep, int len)
3537{
3538 char_u *name;
3539 int id = 0;
3540
3541 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003542 if (name == NULL)
3543 return 0;
3544
3545 id = syn_name2id(name);
3546 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003547 return id;
3548}
3549
3550/*
3551 * Find highlight group name in the table and return its ID.
3552 * The argument is a pointer to the name and the length of the name.
3553 * If it doesn't exist yet, a new entry is created.
3554 * Return 0 for failure.
3555 */
3556 int
3557syn_check_group(char_u *pp, int len)
3558{
3559 int id;
3560 char_u *name;
3561
erw7f7f7aaf2021-12-07 21:29:20 +00003562 if (len > MAX_SYN_NAME)
3563 {
3564 emsg(_(e_highlight_group_name_too_long));
3565 return 0;
3566 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003567 name = vim_strnsave(pp, len);
3568 if (name == NULL)
3569 return 0;
3570
3571 id = syn_name2id(name);
3572 if (id == 0) // doesn't exist yet
3573 id = syn_add_group(name);
3574 else
3575 vim_free(name);
3576 return id;
3577}
3578
3579/*
3580 * Add new highlight group and return its ID.
3581 * "name" must be an allocated string, it will be consumed.
3582 * Return 0 for failure.
3583 */
3584 static int
3585syn_add_group(char_u *name)
3586{
3587 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003588 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003589
Gregory Andersd4376dc2023-08-20 19:14:03 +02003590 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003591 for (p = name; *p != NUL; ++p)
3592 {
3593 if (!vim_isprintc(*p))
3594 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003595 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003596 vim_free(name);
3597 return 0;
3598 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003599 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003600 {
3601 // This is an error, but since there previously was no check only
3602 // give a warning.
3603 msg_source(HL_ATTR(HLF_W));
3604 msg(_("W18: Invalid character in group name"));
3605 break;
3606 }
3607 }
3608
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003609 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003610 if (highlight_ga.ga_data == NULL)
3611 {
3612 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3613 highlight_ga.ga_growsize = 10;
3614 }
3615
3616 if (highlight_ga.ga_len >= MAX_HL_ID)
3617 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003618 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003619 vim_free(name);
3620 return 0;
3621 }
3622
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003623 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003624 if (ga_grow(&highlight_ga, 1) == FAIL)
3625 {
3626 vim_free(name);
3627 return 0;
3628 }
3629
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003630 name_up = vim_strsave_up(name);
3631 if (name_up == NULL)
3632 {
3633 vim_free(name);
3634 return 0;
3635 }
3636
Bram Moolenaara80faa82020-04-12 19:37:17 +02003637 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003638 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003639 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003640#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3641 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3642 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003643 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003644#endif
3645 ++highlight_ga.ga_len;
3646
3647 return highlight_ga.ga_len; // ID is index plus one
3648}
3649
3650/*
3651 * When, just after calling syn_add_group(), an error is discovered, this
3652 * function deletes the new name.
3653 */
3654 static void
3655syn_unadd_group(void)
3656{
3657 --highlight_ga.ga_len;
3658 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3659 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3660}
3661
3662/*
3663 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003664 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003665 */
3666 int
3667syn_id2attr(int hl_id)
3668{
3669 int attr;
3670 hl_group_T *sgp;
3671
3672 hl_id = syn_get_final_id(hl_id);
3673 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3674
3675#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003676 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003677 if (gui.in_use)
3678 attr = sgp->sg_gui_attr;
3679 else
3680#endif
3681 if (IS_CTERM)
3682 attr = sgp->sg_cterm_attr;
3683 else
3684 attr = sgp->sg_term_attr;
3685
3686 return attr;
3687}
3688
3689#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3690/*
3691 * Get the GUI colors and attributes for a group ID.
3692 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3693 */
3694 int
3695syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3696{
3697 hl_group_T *sgp;
3698
3699 hl_id = syn_get_final_id(hl_id);
3700 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3701
3702 *fgp = sgp->sg_gui_fg;
3703 *bgp = sgp->sg_gui_bg;
3704 return sgp->sg_gui;
3705}
3706#endif
3707
3708#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003709 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3710 && defined(FEAT_TERMGUICOLORS)) \
3711 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003712 void
3713syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3714{
3715 hl_group_T *sgp;
3716
3717 hl_id = syn_get_final_id(hl_id);
3718 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3719 *fgp = sgp->sg_cterm_fg - 1;
3720 *bgp = sgp->sg_cterm_bg - 1;
3721}
3722#endif
3723
3724/*
3725 * Translate a group ID to the final group ID (following links).
3726 */
3727 int
3728syn_get_final_id(int hl_id)
3729{
3730 int count;
3731 hl_group_T *sgp;
3732
3733 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3734 return 0; // Can be called from eval!!
3735
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003736 // Follow links until there is no more.
3737 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003738 for (count = 100; --count >= 0; )
3739 {
3740 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3741 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3742 break;
3743 hl_id = sgp->sg_link;
3744 }
3745
3746 return hl_id;
3747}
3748
3749#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3750/*
3751 * Call this function just after the GUI has started.
3752 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3753 * It finds the font and color handles for the highlighting groups.
3754 */
3755 void
3756highlight_gui_started(void)
3757{
3758 int idx;
3759
3760 // First get the colors from the "Normal" and "Menu" group, if set
3761 if (USE_24BIT)
3762 set_normal_colors();
3763
3764 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3765 gui_do_one_color(idx, FALSE, FALSE);
3766
3767 highlight_changed();
3768}
3769
3770 static void
3771gui_do_one_color(
3772 int idx,
3773 int do_menu UNUSED, // TRUE: might set the menu font
3774 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3775{
3776 int didit = FALSE;
3777
3778# ifdef FEAT_GUI
3779# ifdef FEAT_TERMGUICOLORS
3780 if (gui.in_use)
3781# endif
3782 if (HL_TABLE()[idx].sg_font_name != NULL)
3783 {
3784 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3785 do_tooltip, TRUE);
3786 didit = TRUE;
3787 }
3788# endif
3789 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3790 {
3791 HL_TABLE()[idx].sg_gui_fg =
3792 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3793 didit = TRUE;
3794 }
3795 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3796 {
3797 HL_TABLE()[idx].sg_gui_bg =
3798 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3799 didit = TRUE;
3800 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003801 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3802 {
3803 HL_TABLE()[idx].sg_gui_sp =
3804 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3805 didit = TRUE;
3806 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003807 if (didit) // need to get a new attr number
3808 set_hl_attr(idx);
3809}
3810#endif
3811
3812#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3813/*
3814 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3815 */
3816 static void
3817combine_stl_hlt(
3818 int id,
3819 int id_S,
3820 int id_alt,
3821 int hlcnt,
3822 int i,
3823 int hlf,
3824 int *table)
3825{
3826 hl_group_T *hlt = HL_TABLE();
3827
3828 if (id_alt == 0)
3829 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003830 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003831 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3832 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3833# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3834 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3835# endif
3836 }
3837 else
3838 mch_memmove(&hlt[hlcnt + i],
3839 &hlt[id_alt - 1],
3840 sizeof(hl_group_T));
3841 hlt[hlcnt + i].sg_link = 0;
3842
3843 hlt[hlcnt + i].sg_term ^=
3844 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3845 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3846 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3847 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3848 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3849 hlt[hlcnt + i].sg_cterm ^=
3850 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3851 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3852 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3853 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3854 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003855 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3856 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003857# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3858 hlt[hlcnt + i].sg_gui ^=
3859 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3860# endif
3861# ifdef FEAT_GUI
3862 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3863 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3864 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3865 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3866 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3867 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3868 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3869 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3870# ifdef FEAT_XFONTSET
3871 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3872 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3873# endif
3874# endif
3875 highlight_ga.ga_len = hlcnt + i + 1;
3876 set_hl_attr(hlcnt + i); // At long last we can apply
3877 table[i] = syn_id2attr(hlcnt + i + 1);
3878}
3879#endif
3880
3881/*
3882 * Translate the 'highlight' option into attributes in highlight_attr[] and
3883 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3884 * corresponding highlights to use on top of HLF_SNC is computed.
3885 * Called only when the 'highlight' option has been changed and upon first
3886 * screen redraw after any :highlight command.
3887 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3888 */
3889 int
3890highlight_changed(void)
3891{
3892 int hlf;
3893 int i;
3894 char_u *p;
3895 int attr;
3896 char_u *end;
3897 int id;
3898#ifdef USER_HIGHLIGHT
3899 char_u userhl[30]; // use 30 to avoid compiler warning
3900# ifdef FEAT_STL_OPT
3901 int id_S = -1;
3902 int id_SNC = 0;
3903# ifdef FEAT_TERMINAL
3904 int id_ST = 0;
3905 int id_STNC = 0;
3906# endif
3907 int hlcnt;
3908# endif
3909#endif
3910 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3911
3912 need_highlight_changed = FALSE;
3913
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003914#ifdef FEAT_TERMINAL
3915 term_update_colors_all();
3916 term_update_wincolor_all();
3917#endif
3918
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003919 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003920 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3921 highlight_attr[hlf] = 0;
3922
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003923 // First set all attributes to their default value.
3924 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003925 for (i = 0; i < 2; ++i)
3926 {
3927 if (i)
3928 p = p_hl;
3929 else
3930 p = get_highlight_default();
3931 if (p == NULL) // just in case
3932 continue;
3933
3934 while (*p)
3935 {
3936 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3937 if (hl_flags[hlf] == *p)
3938 break;
3939 ++p;
3940 if (hlf == (int)HLF_COUNT || *p == NUL)
3941 return FAIL;
3942
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003943 // Allow several hl_flags to be combined, like "bu" for
3944 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003945 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003946 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003947 {
3948 if (VIM_ISWHITE(*p)) // ignore white space
3949 continue;
3950
3951 if (attr > HL_ALL) // Combination with ':' is not allowed.
3952 return FAIL;
3953
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003954 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003955 switch (*p)
3956 {
3957 case 'b': attr |= HL_BOLD;
3958 break;
3959 case 'i': attr |= HL_ITALIC;
3960 break;
3961 case '-':
3962 case 'n': // no highlighting
3963 break;
3964 case 'r': attr |= HL_INVERSE;
3965 break;
3966 case 's': attr |= HL_STANDOUT;
3967 break;
3968 case 'u': attr |= HL_UNDERLINE;
3969 break;
3970 case 'c': attr |= HL_UNDERCURL;
3971 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003972 case '2': attr |= HL_UNDERDOUBLE;
3973 break;
3974 case 'd': attr |= HL_UNDERDOTTED;
3975 break;
3976 case '=': attr |= HL_UNDERDASHED;
3977 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003978 case 't': attr |= HL_STRIKETHROUGH;
3979 break;
3980 case ':': ++p; // highlight group name
3981 if (attr || *p == NUL) // no combinations
3982 return FAIL;
3983 end = vim_strchr(p, ',');
3984 if (end == NULL)
3985 end = p + STRLEN(p);
3986 id = syn_check_group(p, (int)(end - p));
3987 if (id == 0)
3988 return FAIL;
3989 attr = syn_id2attr(id);
3990 p = end - 1;
3991#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3992 if (hlf == (int)HLF_SNC)
3993 id_SNC = syn_get_final_id(id);
3994# ifdef FEAT_TERMINAL
3995 else if (hlf == (int)HLF_ST)
3996 id_ST = syn_get_final_id(id);
3997 else if (hlf == (int)HLF_STNC)
3998 id_STNC = syn_get_final_id(id);
3999# endif
4000 else if (hlf == (int)HLF_S)
4001 id_S = syn_get_final_id(id);
4002#endif
4003 break;
4004 default: return FAIL;
4005 }
4006 }
4007 highlight_attr[hlf] = attr;
4008
4009 p = skip_to_option_part(p); // skip comma and spaces
4010 }
4011 }
4012
4013#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01004014 // Setup the user highlights
4015 //
4016 // Temporarily utilize 28 more hl entries:
4017 // 9 for User1-User9 combined with StatusLineNC
4018 // 9 for User1-User9 combined with StatusLineTerm
4019 // 9 for User1-User9 combined with StatusLineTermNC
4020 // 1 for StatusLine default
4021 // Have to be in there simultaneously in case of table overflows in
4022 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004023# ifdef FEAT_STL_OPT
4024 if (ga_grow(&highlight_ga, 28) == FAIL)
4025 return FAIL;
4026 hlcnt = highlight_ga.ga_len;
4027 if (id_S == -1)
4028 {
4029 // Make sure id_S is always valid to simplify code below. Use the last
4030 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02004031 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004032 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
4033 id_S = hlcnt + 19;
4034 }
4035# endif
4036 for (i = 0; i < 9; i++)
4037 {
4038 sprintf((char *)userhl, "User%d", i + 1);
4039 id = syn_name2id(userhl);
4040 if (id == 0)
4041 {
4042 highlight_user[i] = 0;
4043# ifdef FEAT_STL_OPT
4044 highlight_stlnc[i] = 0;
4045# ifdef FEAT_TERMINAL
4046 highlight_stlterm[i] = 0;
4047 highlight_stltermnc[i] = 0;
4048# endif
4049# endif
4050 }
4051 else
4052 {
4053 highlight_user[i] = syn_id2attr(id);
4054# ifdef FEAT_STL_OPT
4055 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
4056 HLF_SNC, highlight_stlnc);
4057# ifdef FEAT_TERMINAL
4058 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
4059 HLF_ST, highlight_stlterm);
4060 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
4061 HLF_STNC, highlight_stltermnc);
4062# endif
4063# endif
4064 }
4065 }
4066# ifdef FEAT_STL_OPT
4067 highlight_ga.ga_len = hlcnt;
4068# endif
4069
4070#endif // USER_HIGHLIGHT
4071
4072 return OK;
4073}
4074
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004075static void highlight_list(void);
4076static void highlight_list_two(int cnt, int attr);
4077
4078/*
4079 * Handle command line completion for :highlight command.
4080 */
4081 void
4082set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
4083{
4084 char_u *p;
4085
4086 // Default: expand group names
4087 xp->xp_context = EXPAND_HIGHLIGHT;
4088 xp->xp_pattern = arg;
4089 include_link = 2;
4090 include_default = 1;
4091
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004092 if (*arg == NUL)
4093 return;
4094
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004095 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004096 p = skiptowhite(arg);
4097 if (*p == NUL)
4098 return;
4099
4100 // past "default" or group name
4101 include_default = 0;
4102 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004103 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004104 arg = skipwhite(p);
4105 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004106 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004107 }
4108 if (*p == NUL)
4109 return;
4110
4111 // past group name
4112 include_link = 0;
4113 if (arg[1] == 'i' && arg[0] == 'N')
4114 highlight_list();
4115 if (STRNCMP("link", arg, p - arg) == 0
4116 || STRNCMP("clear", arg, p - arg) == 0)
4117 {
4118 xp->xp_pattern = skipwhite(p);
4119 p = skiptowhite(xp->xp_pattern);
4120 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004121 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004122 xp->xp_pattern = skipwhite(p);
4123 p = skiptowhite(xp->xp_pattern);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004124 }
4125 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004126 if (*p != NUL) // past group name(s)
4127 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004128}
4129
4130/*
4131 * List highlighting matches in a nice way.
4132 */
4133 static void
4134highlight_list(void)
4135{
4136 int i;
4137
4138 for (i = 10; --i >= 0; )
4139 highlight_list_two(i, HL_ATTR(HLF_D));
4140 for (i = 40; --i >= 0; )
4141 highlight_list_two(99, 0);
4142}
4143
4144 static void
4145highlight_list_two(int cnt, int attr)
4146{
4147 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4148 msg_clr_eos();
4149 out_flush();
4150 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4151}
4152
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004153/*
4154 * Function given to ExpandGeneric() to obtain the list of group names.
4155 */
4156 char_u *
4157get_highlight_name(expand_T *xp UNUSED, int idx)
4158{
4159 return get_highlight_name_ext(xp, idx, TRUE);
4160}
4161
4162/*
4163 * Obtain a highlight group name.
4164 * When "skip_cleared" is TRUE don't return a cleared entry.
4165 */
4166 char_u *
4167get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4168{
4169 if (idx < 0)
4170 return NULL;
4171
4172 // Items are never removed from the table, skip the ones that were
4173 // cleared.
4174 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4175 return (char_u *)"";
4176
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004177 if (idx == highlight_ga.ga_len && include_none != 0)
4178 return (char_u *)"none";
4179 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4180 return (char_u *)"default";
4181 if (idx == highlight_ga.ga_len + include_none + include_default
4182 && include_link != 0)
4183 return (char_u *)"link";
4184 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4185 && include_link != 0)
4186 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004187 if (idx >= highlight_ga.ga_len)
4188 return NULL;
4189 return HL_TABLE()[idx].sg_name;
4190}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004191
4192#if defined(FEAT_GUI) || defined(PROTO)
4193/*
4194 * Free all the highlight group fonts.
4195 * Used when quitting for systems which need it.
4196 */
4197 void
4198free_highlight_fonts(void)
4199{
4200 int idx;
4201
4202 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4203 {
4204 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4205 HL_TABLE()[idx].sg_font = NOFONT;
4206# ifdef FEAT_XFONTSET
4207 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4208 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4209# endif
4210 }
4211
4212 gui_mch_free_font(gui.norm_font);
4213# ifdef FEAT_XFONTSET
4214 gui_mch_free_fontset(gui.fontset);
4215# endif
4216# ifndef FEAT_GUI_GTK
4217 gui_mch_free_font(gui.bold_font);
4218 gui_mch_free_font(gui.ital_font);
4219 gui_mch_free_font(gui.boldital_font);
4220# endif
4221}
4222#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004223
4224#if defined(FEAT_EVAL) || defined(PROTO)
4225/*
4226 * Convert each of the highlight attribute bits (bold, standout, underline,
4227 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4228 * the attribute name as the key.
4229 */
4230 static dict_T *
4231highlight_get_attr_dict(int hlattr)
4232{
4233 dict_T *dict;
4234 int i;
4235
4236 dict = dict_alloc();
4237 if (dict == NULL)
4238 return NULL;
4239
John Marriott34f00dd2024-04-08 23:28:12 +02004240 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004241 {
John Marriott34f00dd2024-04-08 23:28:12 +02004242 if (hlattr & highlight_index_tab[i]->key)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004243 {
John Marriott8d4477e2024-11-02 15:59:01 +01004244 dict_add_bool(dict, (char *)highlight_index_tab[i]->value.string,
4245 VVAL_TRUE);
4246 // don't want "inverse"/"reverse"
4247 hlattr &= ~highlight_index_tab[i]->key;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004248 }
4249 }
4250
4251 return dict;
4252}
4253
4254/*
4255 * Return the attributes of the highlight group at index 'hl_idx' as a
4256 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4257 * links recursively.
4258 */
4259 static dict_T *
4260highlight_get_info(int hl_idx, int resolve_link)
4261{
4262 dict_T *dict;
4263 hl_group_T *sgp;
4264 dict_T *attr_dict;
4265 int hlgid;
4266
4267 dict = dict_alloc();
4268 if (dict == NULL)
4269 return dict;
4270
4271 sgp = &HL_TABLE()[hl_idx];
4272 // highlight group id is 1-based
4273 hlgid = hl_idx + 1;
4274
4275 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4276 goto error;
4277 if (dict_add_number(dict, "id", hlgid) == FAIL)
4278 goto error;
4279
4280 if (sgp->sg_link && resolve_link)
4281 {
4282 // resolve the highlight group link recursively
4283 while (sgp->sg_link)
4284 {
4285 hlgid = sgp->sg_link;
4286 sgp = &HL_TABLE()[sgp->sg_link - 1];
4287 }
4288 }
4289
4290 if (sgp->sg_term != 0)
4291 {
4292 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4293 if (attr_dict != NULL)
4294 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4295 goto error;
4296 }
4297 if (sgp->sg_start != NULL)
4298 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4299 goto error;
4300 if (sgp->sg_stop != NULL)
4301 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4302 goto error;
4303 if (sgp->sg_cterm != 0)
4304 {
4305 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4306 if (attr_dict != NULL)
4307 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4308 goto error;
4309 }
4310 if (sgp->sg_cterm_fg != 0)
4311 if (dict_add_string(dict, "ctermfg",
4312 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4313 goto error;
4314 if (sgp->sg_cterm_bg != 0)
4315 if (dict_add_string(dict, "ctermbg",
4316 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4317 goto error;
4318 if (sgp->sg_cterm_ul != 0)
4319 if (dict_add_string(dict, "ctermul",
4320 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4321 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004322 if (sgp->sg_cterm_font != 0)
4323 if (dict_add_string(dict, "ctermfont",
4324 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4325 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004326 if (sgp->sg_gui != 0)
4327 {
4328 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4329 if (attr_dict != NULL)
4330 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4331 goto error;
4332 }
4333 if (sgp->sg_gui_fg_name != NULL)
4334 if (dict_add_string(dict, "guifg",
4335 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4336 goto error;
4337 if (sgp->sg_gui_bg_name != NULL)
4338 if (dict_add_string(dict, "guibg",
4339 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4340 goto error;
4341 if (sgp->sg_gui_sp_name != NULL)
4342 if (dict_add_string(dict, "guisp",
4343 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4344 goto error;
4345# ifdef FEAT_GUI
4346 if (sgp->sg_font_name != NULL)
4347 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4348 goto error;
4349# endif
4350 if (sgp->sg_link)
4351 {
4352 char_u *link;
4353
4354 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4355 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4356 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004357
4358 if (sgp->sg_deflink)
4359 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004360 }
4361 if (dict_len(dict) == 2)
4362 // If only 'name' is present, then the highlight group is cleared.
4363 dict_add_bool(dict, "cleared", VVAL_TRUE);
4364
4365 return dict;
4366
4367error:
4368 vim_free(dict);
4369 return NULL;
4370}
4371
4372/*
4373 * "hlget([name])" function
4374 * Return the attributes of a specific highlight group (if specified) or all
4375 * the highlight groups.
4376 */
4377 void
4378f_hlget(typval_T *argvars, typval_T *rettv)
4379{
4380 list_T *list;
4381 dict_T *dict;
4382 int i;
4383 char_u *hlarg = NULL;
4384 int resolve_link = FALSE;
4385
4386 if (rettv_list_alloc(rettv) == FAIL)
4387 return;
4388
4389 if (check_for_opt_string_arg(argvars, 0) == FAIL
4390 || (argvars[0].v_type != VAR_UNKNOWN
4391 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4392 return;
4393
4394 if (argvars[0].v_type != VAR_UNKNOWN)
4395 {
4396 // highlight group name supplied
4397 hlarg = tv_get_string_chk(&argvars[0]);
4398 if (hlarg == NULL)
4399 return;
4400
4401 if (argvars[1].v_type != VAR_UNKNOWN)
4402 {
4403 int error = FALSE;
4404
4405 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4406 if (error)
4407 return;
4408 }
4409 }
4410
4411 list = rettv->vval.v_list;
4412 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4413 {
4414 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4415 {
4416 dict = highlight_get_info(i, resolve_link);
4417 if (dict != NULL)
4418 list_append_dict(list, dict);
4419 }
4420 }
4421}
4422
4423/*
4424 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4425 * 'dict' or the value is not a string type. If the value is not a string type
4426 * or is NULL, then 'error' is set to TRUE.
4427 */
4428 static char_u *
4429hldict_get_string(dict_T *dict, char_u *key, int *error)
4430{
4431 dictitem_T *di;
4432
4433 *error = FALSE;
4434 di = dict_find(dict, key, -1);
4435 if (di == NULL)
4436 return NULL;
4437
4438 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4439 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004440 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004441 *error = TRUE;
4442 return NULL;
4443 }
4444
4445 return di->di_tv.vval.v_string;
4446}
4447
4448/*
4449 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4450 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4451 * Dictionary or is NULL.
4452 */
4453 static int
4454hldict_attr_to_str(
4455 dict_T *dict,
4456 char_u *key,
4457 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004458 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004459{
4460 dictitem_T *di;
4461 dict_T *attrdict;
4462 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004463 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004464
4465 attr_str[0] = NUL;
4466 di = dict_find(dict, key, -1);
4467 if (di == NULL)
4468 return TRUE;
4469
4470 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4471 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004472 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004473 return FALSE;
4474 }
4475
4476 attrdict = di->di_tv.vval.v_dict;
4477
4478 // If the attribute dict is empty, then return NONE to clear the attributes
4479 if (dict_len(attrdict) == 0)
4480 {
4481 vim_strcat(attr_str, (char_u *)"NONE", len);
4482 return TRUE;
4483 }
4484
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004485 p = attr_str;
John Marriott34f00dd2024-04-08 23:28:12 +02004486 for (i = 0; i < (int)ARRAY_LENGTH(highlight_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004487 {
John Marriott8d4477e2024-11-02 15:59:01 +01004488 if (dict_get_bool(attrdict, (char *)highlight_tab[i].value.string,
4489 VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004490 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004491 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4492 STRCPY(p, (char_u *)",");
John Marriott8d4477e2024-11-02 15:59:01 +01004493 if (p - attr_str + highlight_tab[i].value.length + 1 < len)
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004494 {
John Marriott8d4477e2024-11-02 15:59:01 +01004495 STRCPY(p, highlight_tab[i].value.string);
4496 p += highlight_tab[i].value.length;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004497 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004498 }
4499 }
4500
4501 return TRUE;
4502}
4503
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004504// Temporary buffer used to store the command string produced by hlset().
4505// IObuff cannot be used for this as the error messages produced by hlset()
4506// internally use IObuff.
4507#define HLSETBUFSZ 512
4508static char_u hlsetBuf[HLSETBUFSZ + 1];
4509
4510/*
4511 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4512 * "dptr", which points into "hlsetBuf".
4513 * Returns the updated pointer.
4514 */
4515 static char_u *
4516add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4517{
4518 size_t vallen;
4519
4520 // Do nothing if the value is not specified or is empty
4521 if (value == NULL || *value == NUL)
4522 return dptr;
4523
4524 vallen = STRLEN(value);
4525 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4526 {
4527 STRCPY(dptr, attr);
4528 dptr += attrlen;
4529 STRCPY(dptr, value);
4530 dptr += vallen;
4531 }
4532
4533 return dptr;
4534}
4535
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004536/*
4537 * Add or update a highlight group using 'dict' items. Returns TRUE if
4538 * successfully updated the highlight group.
4539 */
4540 static int
4541hlg_add_or_update(dict_T *dict)
4542{
4543 char_u *name;
4544 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004545 char_u term_attr[MAX_ATTR_LEN];
4546 char_u cterm_attr[MAX_ATTR_LEN];
4547 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004548 char_u *start;
4549 char_u *stop;
4550 char_u *ctermfg;
4551 char_u *ctermbg;
4552 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004553 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004554 char_u *guifg;
4555 char_u *guibg;
4556 char_u *guisp;
4557# ifdef FEAT_GUI
4558 char_u *font;
4559# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004560 int forceit = FALSE;
4561 int dodefault = FALSE;
4562 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004563 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004564
4565 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004566 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004567 return FALSE;
4568
Bram Moolenaard61efa52022-07-23 09:52:04 +01004569 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004570 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004571
Bram Moolenaard61efa52022-07-23 09:52:04 +01004572 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004573 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004574
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004575 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004576 {
4577 varnumber_T cleared;
4578
4579 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004580 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004581 if (cleared == TRUE)
4582 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004583 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4584 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004585 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004586 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004587 }
4588
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004589 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004590 {
4591 char_u *linksto;
4592
4593 // link highlight groups
4594 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004595 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004596 return FALSE;
4597
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004598 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004599 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004600 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004601
4602 done = TRUE;
4603 }
4604
4605 // If 'cleared' or 'linksto' are specified, then don't process the other
4606 // attributes.
4607 if (done)
4608 return TRUE;
4609
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004610 start = hldict_get_string(dict, (char_u *)"start", &error);
4611 if (error)
4612 return FALSE;
4613
4614 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4615 if (error)
4616 return FALSE;
4617
4618 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004619 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004620 return FALSE;
4621
4622 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004623 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004624 return FALSE;
4625
4626 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4627 if (error)
4628 return FALSE;
4629
4630 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4631 if (error)
4632 return FALSE;
4633
4634 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4635 if (error)
4636 return FALSE;
4637
PMuncha606f3a2023-11-15 15:35:49 +01004638 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
4639 if (error)
4640 return FALSE;
4641
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004642 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004643 return FALSE;
4644
4645 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4646 if (error)
4647 return FALSE;
4648
4649 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4650 if (error)
4651 return FALSE;
4652
4653 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4654 if (error)
4655 return FALSE;
4656
4657# ifdef FEAT_GUI
4658 font = hldict_get_string(dict, (char_u *)"font", &error);
4659 if (error)
4660 return FALSE;
4661# endif
4662
4663 // If none of the attributes are specified, then do nothing.
4664 if (term_attr[0] == NUL && start == NULL && stop == NULL
4665 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01004666 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004667# ifdef FEAT_GUI
4668 && font == NULL
4669# endif
4670 && guifg == NULL && guibg == NULL && guisp == NULL
4671 )
4672 return TRUE;
4673
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004674 hlsetBuf[0] = NUL;
4675 p = hlsetBuf;
4676 if (dodefault)
4677 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4678 p = add_attr_and_value(p, (char_u *)"", 0, name);
4679 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4680 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4681 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4682 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4683 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4684 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4685 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01004686 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004687 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004688# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004689 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004690# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004691 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4692 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004693 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004694
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004695 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004696
4697 return TRUE;
4698}
4699
4700/*
4701 * "hlset([{highlight_attr}])" function
4702 * Add or modify highlight groups
4703 */
4704 void
4705f_hlset(typval_T *argvars, typval_T *rettv)
4706{
4707 listitem_T *li;
4708 dict_T *dict;
4709
4710 rettv->vval.v_number = -1;
4711
4712 if (check_for_list_arg(argvars, 0) == FAIL)
4713 return;
4714
4715 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4716 {
4717 if (li->li_tv.v_type != VAR_DICT)
4718 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004719 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004720 return;
4721 }
4722
4723 dict = li->li_tv.vval.v_dict;
4724 if (!hlg_add_or_update(dict))
4725 return;
4726 }
4727
4728 rettv->vval.v_number = 0;
4729}
4730#endif