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