blob: 54e348d330bbb970e7db110c01a8b886fc7adbec [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)
John Marriott34f00dd2024-04-08 23:28:12 +02002546 return gui_adjust_rgb((guicolor_T)entry->key);
Drew Vogele30d1022021-10-24 20:35:07 +01002547
2548#if defined(FEAT_EVAL)
2549 /*
2550 * Not a traditional color. Load additional color aliases and then consult the alias table.
2551 */
2552
2553 color = colorname2rgb(name);
2554 if (color == INVALCOLOR)
2555 {
2556 load_default_colors_lists();
2557 color = colorname2rgb(name);
2558 }
2559
2560 return color;
2561#else
2562 return INVALCOLOR;
2563#endif
2564}
2565
2566 guicolor_T
2567gui_get_rgb_color_cmn(int r, int g, int b)
2568{
2569 guicolor_T color = RGB(r, g, b);
2570
2571 if (color > 0xffffff)
2572 return INVALCOLOR;
2573 return gui_adjust_rgb(color);
2574}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002575#endif
2576
2577/*
2578 * Table with the specifications for an attribute number.
2579 * Note that this table is used by ALL buffers. This is required because the
2580 * GUI can redraw at any time for any buffer.
2581 */
2582static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2583
2584#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2585
2586static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2587
2588#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2589
2590#ifdef FEAT_GUI
2591static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2592
2593#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2594#endif
2595
2596/*
2597 * Return the attr number for a set of colors and font.
2598 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2599 * if the combination is new.
2600 * Return 0 for error (no more room).
2601 */
2602 static int
2603get_attr_entry(garray_T *table, attrentry_T *aep)
2604{
2605 int i;
2606 attrentry_T *taep;
2607 static int recursive = FALSE;
2608
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002609 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002610 table->ga_itemsize = sizeof(attrentry_T);
2611 table->ga_growsize = 7;
2612
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002613 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002614 for (i = 0; i < table->ga_len; ++i)
2615 {
2616 taep = &(((attrentry_T *)table->ga_data)[i]);
2617 if ( aep->ae_attr == taep->ae_attr
2618 && (
2619#ifdef FEAT_GUI
2620 (table == &gui_attr_table
2621 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2622 && aep->ae_u.gui.bg_color
2623 == taep->ae_u.gui.bg_color
2624 && aep->ae_u.gui.sp_color
2625 == taep->ae_u.gui.sp_color
2626 && aep->ae_u.gui.font == taep->ae_u.gui.font
2627# ifdef FEAT_XFONTSET
2628 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2629# endif
2630 ))
2631 ||
2632#endif
2633 (table == &term_attr_table
2634 && (aep->ae_u.term.start == NULL)
2635 == (taep->ae_u.term.start == NULL)
2636 && (aep->ae_u.term.start == NULL
2637 || STRCMP(aep->ae_u.term.start,
2638 taep->ae_u.term.start) == 0)
2639 && (aep->ae_u.term.stop == NULL)
2640 == (taep->ae_u.term.stop == NULL)
2641 && (aep->ae_u.term.stop == NULL
2642 || STRCMP(aep->ae_u.term.stop,
2643 taep->ae_u.term.stop) == 0))
2644 || (table == &cterm_attr_table
2645 && aep->ae_u.cterm.fg_color
2646 == taep->ae_u.cterm.fg_color
2647 && aep->ae_u.cterm.bg_color
2648 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002649 && aep->ae_u.cterm.ul_color
2650 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002651 && aep->ae_u.cterm.font
2652 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002653#ifdef FEAT_TERMGUICOLORS
2654 && aep->ae_u.cterm.fg_rgb
2655 == taep->ae_u.cterm.fg_rgb
2656 && aep->ae_u.cterm.bg_rgb
2657 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002658 && aep->ae_u.cterm.ul_rgb
2659 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002660#endif
2661 )))
2662
2663 return i + ATTR_OFF;
2664 }
2665
2666 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2667 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002668 // Running out of attribute entries! remove all attributes, and
2669 // compute new ones for all groups.
2670 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002671 if (recursive)
2672 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002673 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002674 return 0;
2675 }
2676 recursive = TRUE;
2677
2678 clear_hl_tables();
2679
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002680 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002681
2682 for (i = 0; i < highlight_ga.ga_len; ++i)
2683 set_hl_attr(i);
2684
2685 recursive = FALSE;
2686 }
2687
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002688 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002689 if (ga_grow(table, 1) == FAIL)
2690 return 0;
2691
2692 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002693 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002694 taep->ae_attr = aep->ae_attr;
2695#ifdef FEAT_GUI
2696 if (table == &gui_attr_table)
2697 {
2698 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2699 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2700 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2701 taep->ae_u.gui.font = aep->ae_u.gui.font;
2702# ifdef FEAT_XFONTSET
2703 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2704# endif
2705 }
2706#endif
2707 if (table == &term_attr_table)
2708 {
2709 if (aep->ae_u.term.start == NULL)
2710 taep->ae_u.term.start = NULL;
2711 else
2712 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2713 if (aep->ae_u.term.stop == NULL)
2714 taep->ae_u.term.stop = NULL;
2715 else
2716 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2717 }
2718 else if (table == &cterm_attr_table)
2719 {
2720 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2721 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002722 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002723 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002724#ifdef FEAT_TERMGUICOLORS
2725 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2726 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002727 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002728#endif
2729 }
2730 ++table->ga_len;
2731 return (table->ga_len - 1 + ATTR_OFF);
2732}
2733
2734#if defined(FEAT_TERMINAL) || defined(PROTO)
2735/*
2736 * Get an attribute index for a cterm entry.
2737 * Uses an existing entry when possible or adds one when needed.
2738 */
2739 int
2740get_cterm_attr_idx(int attr, int fg, int bg)
2741{
2742 attrentry_T at_en;
2743
Bram Moolenaara80faa82020-04-12 19:37:17 +02002744 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002745#ifdef FEAT_TERMGUICOLORS
2746 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2747 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002748 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002749#endif
2750 at_en.ae_attr = attr;
2751 at_en.ae_u.cterm.fg_color = fg;
2752 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002753 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002754 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002755 return get_attr_entry(&cterm_attr_table, &at_en);
2756}
2757#endif
2758
2759#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2760/*
2761 * Get an attribute index for a 'termguicolors' entry.
2762 * Uses an existing entry when possible or adds one when needed.
2763 */
2764 int
2765get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2766{
2767 attrentry_T at_en;
2768
Bram Moolenaara80faa82020-04-12 19:37:17 +02002769 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002770 at_en.ae_attr = attr;
2771 if (fg == INVALCOLOR && bg == INVALCOLOR)
2772 {
2773 // If both GUI colors are not set fall back to the cterm colors. Helps
2774 // if the GUI only has an attribute, such as undercurl.
2775 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2776 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2777 }
2778 else
2779 {
2780 at_en.ae_u.cterm.fg_rgb = fg;
2781 at_en.ae_u.cterm.bg_rgb = bg;
2782 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002783 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002784 return get_attr_entry(&cterm_attr_table, &at_en);
2785}
2786#endif
2787
2788#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2789/*
2790 * Get an attribute index for a cterm entry.
2791 * Uses an existing entry when possible or adds one when needed.
2792 */
2793 int
2794get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2795{
2796 attrentry_T at_en;
2797
Bram Moolenaara80faa82020-04-12 19:37:17 +02002798 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002799 at_en.ae_attr = attr;
2800 at_en.ae_u.gui.fg_color = fg;
2801 at_en.ae_u.gui.bg_color = bg;
2802 return get_attr_entry(&gui_attr_table, &at_en);
2803}
2804#endif
2805
2806/*
2807 * Clear all highlight tables.
2808 */
2809 void
2810clear_hl_tables(void)
2811{
2812 int i;
2813 attrentry_T *taep;
2814
2815#ifdef FEAT_GUI
2816 ga_clear(&gui_attr_table);
2817#endif
2818 for (i = 0; i < term_attr_table.ga_len; ++i)
2819 {
2820 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2821 vim_free(taep->ae_u.term.start);
2822 vim_free(taep->ae_u.term.stop);
2823 }
2824 ga_clear(&term_attr_table);
2825 ga_clear(&cterm_attr_table);
2826}
2827
2828/*
2829 * Combine special attributes (e.g., for spelling) with other attributes
2830 * (e.g., for syntax highlighting).
2831 * "prim_attr" overrules "char_attr".
2832 * This creates a new group when required.
2833 * Since we expect there to be few spelling mistakes we don't cache the
2834 * result.
2835 * Return the resulting attributes.
2836 */
2837 int
2838hl_combine_attr(int char_attr, int prim_attr)
2839{
2840 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002841 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002842 attrentry_T new_en;
2843
2844 if (char_attr == 0)
2845 return prim_attr;
2846 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2847 return ATTR_COMBINE(char_attr, prim_attr);
2848#ifdef FEAT_GUI
2849 if (gui.in_use)
2850 {
2851 if (char_attr > HL_ALL)
2852 char_aep = syn_gui_attr2entry(char_attr);
2853 if (char_aep != NULL)
2854 new_en = *char_aep;
2855 else
2856 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002857 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002858 new_en.ae_u.gui.fg_color = INVALCOLOR;
2859 new_en.ae_u.gui.bg_color = INVALCOLOR;
2860 new_en.ae_u.gui.sp_color = INVALCOLOR;
2861 if (char_attr <= HL_ALL)
2862 new_en.ae_attr = char_attr;
2863 }
2864
2865 if (prim_attr <= HL_ALL)
2866 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2867 else
2868 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002869 prim_aep = syn_gui_attr2entry(prim_attr);
2870 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002871 {
2872 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002873 prim_aep->ae_attr);
2874 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2875 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2876 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2877 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2878 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2879 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2880 if (prim_aep->ae_u.gui.font != NOFONT)
2881 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002882# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002883 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2884 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002885# endif
2886 }
2887 }
2888 return get_attr_entry(&gui_attr_table, &new_en);
2889 }
2890#endif
2891
2892 if (IS_CTERM)
2893 {
2894 if (char_attr > HL_ALL)
2895 char_aep = syn_cterm_attr2entry(char_attr);
2896 if (char_aep != NULL)
2897 new_en = *char_aep;
2898 else
2899 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002900 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002901#ifdef FEAT_TERMGUICOLORS
2902 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2903 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002904 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002905#endif
2906 if (char_attr <= HL_ALL)
2907 new_en.ae_attr = char_attr;
2908 }
2909
2910 if (prim_attr <= HL_ALL)
2911 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2912 else
2913 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002914 prim_aep = syn_cterm_attr2entry(prim_attr);
2915 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002916 {
2917 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002918 prim_aep->ae_attr);
2919 if (prim_aep->ae_u.cterm.fg_color > 0)
2920 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2921 if (prim_aep->ae_u.cterm.bg_color > 0)
2922 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2923 if (prim_aep->ae_u.cterm.ul_color > 0)
2924 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002925 if (prim_aep->ae_u.cterm.font > 0)
2926 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002927#ifdef FEAT_TERMGUICOLORS
2928 // If both fg and bg are not set fall back to cterm colors.
2929 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002930 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2931 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002932 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002933 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002934 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002935 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002936 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2937 }
2938 else
2939 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002940 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2941 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2942 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2943 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002944 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002945 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2946 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002947#endif
2948 }
2949 }
2950 return get_attr_entry(&cterm_attr_table, &new_en);
2951 }
2952
2953 if (char_attr > HL_ALL)
2954 char_aep = syn_term_attr2entry(char_attr);
2955 if (char_aep != NULL)
2956 new_en = *char_aep;
2957 else
2958 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002959 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002960 if (char_attr <= HL_ALL)
2961 new_en.ae_attr = char_attr;
2962 }
2963
2964 if (prim_attr <= HL_ALL)
2965 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2966 else
2967 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002968 prim_aep = syn_term_attr2entry(prim_attr);
2969 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002970 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002971 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2972 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002973 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002974 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2975 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002976 }
2977 }
2978 }
2979 return get_attr_entry(&term_attr_table, &new_en);
2980}
2981
2982#ifdef FEAT_GUI
2983 attrentry_T *
2984syn_gui_attr2entry(int attr)
2985{
2986 attr -= ATTR_OFF;
2987 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2988 return NULL;
2989 return &(GUI_ATTR_ENTRY(attr));
2990}
2991#endif
2992
2993/*
2994 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2995 * Only to be used when "attr" > HL_ALL.
2996 */
2997 int
2998syn_attr2attr(int attr)
2999{
3000 attrentry_T *aep;
3001
3002#ifdef FEAT_GUI
3003 if (gui.in_use)
3004 aep = syn_gui_attr2entry(attr);
3005 else
3006#endif
3007 if (IS_CTERM)
3008 aep = syn_cterm_attr2entry(attr);
3009 else
3010 aep = syn_term_attr2entry(attr);
3011
3012 if (aep == NULL) // highlighting not set
3013 return 0;
3014 return aep->ae_attr;
3015}
3016
3017
3018 attrentry_T *
3019syn_term_attr2entry(int attr)
3020{
3021 attr -= ATTR_OFF;
3022 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
3023 return NULL;
3024 return &(TERM_ATTR_ENTRY(attr));
3025}
3026
3027 attrentry_T *
3028syn_cterm_attr2entry(int attr)
3029{
3030 attr -= ATTR_OFF;
3031 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
3032 return NULL;
3033 return &(CTERM_ATTR_ENTRY(attr));
3034}
3035
3036#define LIST_ATTR 1
3037#define LIST_STRING 2
3038#define LIST_INT 3
3039
3040 static void
3041highlight_list_one(int id)
3042{
3043 hl_group_T *sgp;
3044 int didh = FALSE;
3045
3046 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
3047
3048 if (message_filtered(sgp->sg_name))
3049 return;
3050
3051 didh = highlight_list_arg(id, didh, LIST_ATTR,
3052 sgp->sg_term, NULL, "term");
3053 didh = highlight_list_arg(id, didh, LIST_STRING,
3054 0, sgp->sg_start, "start");
3055 didh = highlight_list_arg(id, didh, LIST_STRING,
3056 0, sgp->sg_stop, "stop");
3057
3058 didh = highlight_list_arg(id, didh, LIST_ATTR,
3059 sgp->sg_cterm, NULL, "cterm");
3060 didh = highlight_list_arg(id, didh, LIST_INT,
3061 sgp->sg_cterm_fg, NULL, "ctermfg");
3062 didh = highlight_list_arg(id, didh, LIST_INT,
3063 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02003064 didh = highlight_list_arg(id, didh, LIST_INT,
3065 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01003066 didh = highlight_list_arg(id, didh, LIST_INT,
3067 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003068
3069#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3070 didh = highlight_list_arg(id, didh, LIST_ATTR,
3071 sgp->sg_gui, NULL, "gui");
3072 didh = highlight_list_arg(id, didh, LIST_STRING,
3073 0, sgp->sg_gui_fg_name, "guifg");
3074 didh = highlight_list_arg(id, didh, LIST_STRING,
3075 0, sgp->sg_gui_bg_name, "guibg");
3076 didh = highlight_list_arg(id, didh, LIST_STRING,
3077 0, sgp->sg_gui_sp_name, "guisp");
3078#endif
3079#ifdef FEAT_GUI
3080 didh = highlight_list_arg(id, didh, LIST_STRING,
3081 0, sgp->sg_font_name, "font");
3082#endif
3083
3084 if (sgp->sg_link && !got_int)
3085 {
3086 (void)syn_list_header(didh, 9999, id);
3087 didh = TRUE;
3088 msg_puts_attr("links to", HL_ATTR(HLF_D));
3089 msg_putchar(' ');
3090 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3091 }
3092
3093 if (!didh)
3094 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3095#ifdef FEAT_EVAL
3096 if (p_verbose > 0)
3097 last_set_msg(sgp->sg_script_ctx);
3098#endif
3099}
3100
3101 static int
3102highlight_list_arg(
3103 int id,
3104 int didh,
3105 int type,
3106 int iarg,
3107 char_u *sarg,
3108 char *name)
3109{
Bram Moolenaar84f54632022-06-29 18:39:11 +01003110 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003111 char_u *ts;
3112 int i;
3113
3114 if (got_int)
3115 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003116
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003117 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3118 return didh;
3119
3120 ts = buf;
3121 if (type == LIST_INT)
3122 sprintf((char *)buf, "%d", iarg - 1);
3123 else if (type == LIST_STRING)
3124 ts = sarg;
3125 else // type == LIST_ATTR
3126 {
John Marriott34f00dd2024-04-08 23:28:12 +02003127 size_t buflen;
3128
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003129 buf[0] = NUL;
John Marriott34f00dd2024-04-08 23:28:12 +02003130 buflen = 0;
3131 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003132 {
John Marriott34f00dd2024-04-08 23:28:12 +02003133 if (iarg & highlight_index_tab[i]->key)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003134 {
John Marriott34f00dd2024-04-08 23:28:12 +02003135 if (buflen > 0)
3136 {
3137 STRCPY(buf + buflen, (char_u *)",");
3138 ++buflen;
3139 }
3140 STRCPY(buf + buflen, (char_u *)highlight_index_tab[i]->value);
3141 buflen += highlight_index_tab[i]->length;
3142 iarg &= ~highlight_index_tab[i]->key; // don't want "inverse"/"reverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003143 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003144 }
3145 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003146
3147 (void)syn_list_header(didh,
3148 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3149 didh = TRUE;
3150 if (!got_int)
3151 {
3152 if (*name != NUL)
3153 {
3154 msg_puts_attr(name, HL_ATTR(HLF_D));
3155 msg_puts_attr("=", HL_ATTR(HLF_D));
3156 }
3157 msg_outtrans(ts);
3158 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003159 return didh;
3160}
3161
3162#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3163/*
3164 * Return "1" if highlight group "id" has attribute "flag".
3165 * Return NULL otherwise.
3166 */
3167 char_u *
3168highlight_has_attr(
3169 int id,
3170 int flag,
3171 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3172{
3173 int attr;
3174
3175 if (id <= 0 || id > highlight_ga.ga_len)
3176 return NULL;
3177
3178#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3179 if (modec == 'g')
3180 attr = HL_TABLE()[id - 1].sg_gui;
3181 else
3182#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003183 {
3184 if (modec == 'c')
3185 attr = HL_TABLE()[id - 1].sg_cterm;
3186 else
3187 attr = HL_TABLE()[id - 1].sg_term;
3188 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003189
3190 if (attr & flag)
3191 return (char_u *)"1";
3192 return NULL;
3193}
3194#endif
3195
3196#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3197/*
3198 * Return color name of highlight group "id".
3199 */
3200 char_u *
3201highlight_color(
3202 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003203 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003204 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3205{
3206 static char_u name[20];
3207 int n;
3208 int fg = FALSE;
3209 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003210 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003211 int font = FALSE;
3212
3213 if (id <= 0 || id > highlight_ga.ga_len)
3214 return NULL;
3215
3216 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3217 fg = TRUE;
3218 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3219 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3220 font = TRUE;
3221 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3222 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003223 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3224 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003225 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3226 return NULL;
3227 if (modec == 'g')
3228 {
3229# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3230# ifdef FEAT_GUI
3231 // return font name
3232 if (font)
3233 return HL_TABLE()[id - 1].sg_font_name;
3234# endif
3235
3236 // return #RRGGBB form (only possible when GUI is running)
3237 if ((USE_24BIT) && what[2] == '#')
3238 {
3239 guicolor_T color;
3240 long_u rgb;
3241 static char_u buf[10];
3242
3243 if (fg)
3244 color = HL_TABLE()[id - 1].sg_gui_fg;
3245 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003246 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003247 else
3248 color = HL_TABLE()[id - 1].sg_gui_bg;
3249 if (color == INVALCOLOR)
3250 return NULL;
3251 rgb = (long_u)GUI_MCH_GET_RGB(color);
3252 sprintf((char *)buf, "#%02x%02x%02x",
3253 (unsigned)(rgb >> 16),
3254 (unsigned)(rgb >> 8) & 255,
3255 (unsigned)rgb & 255);
3256 return buf;
3257 }
3258# endif
3259 if (fg)
3260 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3261 if (sp)
3262 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3263 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3264 }
PMuncha606f3a2023-11-15 15:35:49 +01003265 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003266 return NULL;
3267 if (modec == 'c')
3268 {
3269 if (fg)
3270 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003271 else if (ul)
3272 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003273 else if (font)
3274 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003275 else
3276 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3277 if (n < 0)
3278 return NULL;
3279 sprintf((char *)name, "%d", n);
3280 return name;
3281 }
3282 // term doesn't have color
3283 return NULL;
3284}
3285#endif
3286
3287#if (defined(FEAT_SYN_HL) \
3288 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3289 && defined(FEAT_PRINTER)) || defined(PROTO)
3290/*
3291 * Return color name of highlight group "id" as RGB value.
3292 */
3293 long_u
3294highlight_gui_color_rgb(
3295 int id,
3296 int fg) // TRUE = fg, FALSE = bg
3297{
3298 guicolor_T color;
3299
3300 if (id <= 0 || id > highlight_ga.ga_len)
3301 return 0L;
3302
3303 if (fg)
3304 color = HL_TABLE()[id - 1].sg_gui_fg;
3305 else
3306 color = HL_TABLE()[id - 1].sg_gui_bg;
3307
3308 if (color == INVALCOLOR)
3309 return 0L;
3310
3311 return GUI_MCH_GET_RGB(color);
3312}
3313#endif
3314
3315/*
3316 * Output the syntax list header.
3317 * Return TRUE when started a new line.
3318 */
3319 int
3320syn_list_header(
3321 int did_header, // did header already
3322 int outlen, // length of string that comes
3323 int id) // highlight group id
3324{
3325 int endcol = 19;
3326 int newline = TRUE;
3327 int name_col = 0;
3328
3329 if (!did_header)
3330 {
3331 msg_putchar('\n');
3332 if (got_int)
3333 return TRUE;
3334 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3335 name_col = msg_col;
3336 endcol = 15;
3337 }
3338 else if (msg_col + outlen + 1 >= Columns)
3339 {
3340 msg_putchar('\n');
3341 if (got_int)
3342 return TRUE;
3343 }
3344 else
3345 {
3346 if (msg_col >= endcol) // wrap around is like starting a new line
3347 newline = FALSE;
3348 }
3349
3350 if (msg_col >= endcol) // output at least one space
3351 endcol = msg_col + 1;
3352 if (Columns <= endcol) // avoid hang for tiny window
3353 endcol = Columns - 1;
3354
3355 msg_advance(endcol);
3356
3357 // Show "xxx" with the attributes.
3358 if (!did_header)
3359 {
3360 if (endcol == Columns - 1 && endcol <= name_col)
3361 msg_putchar(' ');
3362 msg_puts_attr("xxx", syn_id2attr(id));
3363 msg_putchar(' ');
3364 }
3365
3366 return newline;
3367}
3368
3369/*
3370 * Set the attribute numbers for a highlight group.
3371 * Called after one of the attributes has changed.
3372 */
3373 static void
3374set_hl_attr(
3375 int idx) // index in array
3376{
3377 attrentry_T at_en;
3378 hl_group_T *sgp = HL_TABLE() + idx;
3379
3380 // The "Normal" group doesn't need an attribute number
3381 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3382 return;
3383
3384#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003385 // For the GUI mode: If there are other than "normal" highlighting
3386 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003387 if (sgp->sg_gui_fg == INVALCOLOR
3388 && sgp->sg_gui_bg == INVALCOLOR
3389 && sgp->sg_gui_sp == INVALCOLOR
3390 && sgp->sg_font == NOFONT
3391# ifdef FEAT_XFONTSET
3392 && sgp->sg_fontset == NOFONTSET
3393# endif
3394 )
3395 {
3396 sgp->sg_gui_attr = sgp->sg_gui;
3397 }
3398 else
3399 {
3400 at_en.ae_attr = sgp->sg_gui;
3401 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3402 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3403 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3404 at_en.ae_u.gui.font = sgp->sg_font;
3405# ifdef FEAT_XFONTSET
3406 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3407# endif
3408 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3409 }
3410#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003411 // For the term mode: If there are other than "normal" highlighting
3412 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003413 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3414 sgp->sg_term_attr = sgp->sg_term;
3415 else
3416 {
3417 at_en.ae_attr = sgp->sg_term;
3418 at_en.ae_u.term.start = sgp->sg_start;
3419 at_en.ae_u.term.stop = sgp->sg_stop;
3420 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3421 }
3422
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003423 // For the color term mode: If there are other than "normal"
3424 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003425 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3426 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003427# ifdef FEAT_TERMGUICOLORS
3428 && sgp->sg_gui_fg == INVALCOLOR
3429 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003430 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003431# endif
3432 )
3433 sgp->sg_cterm_attr = sgp->sg_cterm;
3434 else
3435 {
3436 at_en.ae_attr = sgp->sg_cterm;
3437 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3438 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003439 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003440 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003441# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003442 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3443 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003444 // Only use the underline/undercurl color when used, it may clear the
3445 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003446 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3447 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003448 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3449 else
3450 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003451 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3452 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3453 {
3454 // If both fg and bg are invalid fall back to the cterm colors.
3455 // Helps when the GUI only uses an attribute, e.g. undercurl.
3456 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3457 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3458 }
3459# endif
3460 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3461 }
3462}
3463
3464/*
3465 * Lookup a highlight group name and return its ID.
3466 * If it is not found, 0 is returned.
3467 */
3468 int
3469syn_name2id(char_u *name)
3470{
3471 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003472 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003473
3474 // Avoid using stricmp() too much, it's slow on some systems
3475 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3476 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003477 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003478 vim_strup(name_u);
3479 for (i = highlight_ga.ga_len; --i >= 0; )
3480 if (HL_TABLE()[i].sg_name_u != NULL
3481 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3482 break;
3483 return i + 1;
3484}
3485
3486/*
3487 * Lookup a highlight group name and return its attributes.
3488 * Return zero if not found.
3489 */
3490 int
3491syn_name2attr(char_u *name)
3492{
3493 int id = syn_name2id(name);
3494
3495 if (id != 0)
3496 return syn_id2attr(id);
3497 return 0;
3498}
3499
3500#if defined(FEAT_EVAL) || defined(PROTO)
3501/*
3502 * Return TRUE if highlight group "name" exists.
3503 */
3504 int
3505highlight_exists(char_u *name)
3506{
3507 return (syn_name2id(name) > 0);
3508}
3509
3510# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3511/*
3512 * Return the name of highlight group "id".
3513 * When not a valid ID return an empty string.
3514 */
3515 char_u *
3516syn_id2name(int id)
3517{
3518 if (id <= 0 || id > highlight_ga.ga_len)
3519 return (char_u *)"";
3520 return HL_TABLE()[id - 1].sg_name;
3521}
3522# endif
3523#endif
3524
3525/*
3526 * Like syn_name2id(), but take a pointer + length argument.
3527 */
3528 int
3529syn_namen2id(char_u *linep, int len)
3530{
3531 char_u *name;
3532 int id = 0;
3533
3534 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003535 if (name == NULL)
3536 return 0;
3537
3538 id = syn_name2id(name);
3539 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003540 return id;
3541}
3542
3543/*
3544 * Find highlight group name in the table and return its ID.
3545 * The argument is a pointer to the name and the length of the name.
3546 * If it doesn't exist yet, a new entry is created.
3547 * Return 0 for failure.
3548 */
3549 int
3550syn_check_group(char_u *pp, int len)
3551{
3552 int id;
3553 char_u *name;
3554
erw7f7f7aaf2021-12-07 21:29:20 +00003555 if (len > MAX_SYN_NAME)
3556 {
3557 emsg(_(e_highlight_group_name_too_long));
3558 return 0;
3559 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003560 name = vim_strnsave(pp, len);
3561 if (name == NULL)
3562 return 0;
3563
3564 id = syn_name2id(name);
3565 if (id == 0) // doesn't exist yet
3566 id = syn_add_group(name);
3567 else
3568 vim_free(name);
3569 return id;
3570}
3571
3572/*
3573 * Add new highlight group and return its ID.
3574 * "name" must be an allocated string, it will be consumed.
3575 * Return 0 for failure.
3576 */
3577 static int
3578syn_add_group(char_u *name)
3579{
3580 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003581 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003582
Gregory Andersd4376dc2023-08-20 19:14:03 +02003583 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003584 for (p = name; *p != NUL; ++p)
3585 {
3586 if (!vim_isprintc(*p))
3587 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003588 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003589 vim_free(name);
3590 return 0;
3591 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003592 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003593 {
3594 // This is an error, but since there previously was no check only
3595 // give a warning.
3596 msg_source(HL_ATTR(HLF_W));
3597 msg(_("W18: Invalid character in group name"));
3598 break;
3599 }
3600 }
3601
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003602 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003603 if (highlight_ga.ga_data == NULL)
3604 {
3605 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3606 highlight_ga.ga_growsize = 10;
3607 }
3608
3609 if (highlight_ga.ga_len >= MAX_HL_ID)
3610 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003611 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003612 vim_free(name);
3613 return 0;
3614 }
3615
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003616 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003617 if (ga_grow(&highlight_ga, 1) == FAIL)
3618 {
3619 vim_free(name);
3620 return 0;
3621 }
3622
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003623 name_up = vim_strsave_up(name);
3624 if (name_up == NULL)
3625 {
3626 vim_free(name);
3627 return 0;
3628 }
3629
Bram Moolenaara80faa82020-04-12 19:37:17 +02003630 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003631 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003632 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003633#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3634 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3635 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003636 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003637#endif
3638 ++highlight_ga.ga_len;
3639
3640 return highlight_ga.ga_len; // ID is index plus one
3641}
3642
3643/*
3644 * When, just after calling syn_add_group(), an error is discovered, this
3645 * function deletes the new name.
3646 */
3647 static void
3648syn_unadd_group(void)
3649{
3650 --highlight_ga.ga_len;
3651 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3652 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3653}
3654
3655/*
3656 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003657 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003658 */
3659 int
3660syn_id2attr(int hl_id)
3661{
3662 int attr;
3663 hl_group_T *sgp;
3664
3665 hl_id = syn_get_final_id(hl_id);
3666 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3667
3668#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003669 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003670 if (gui.in_use)
3671 attr = sgp->sg_gui_attr;
3672 else
3673#endif
3674 if (IS_CTERM)
3675 attr = sgp->sg_cterm_attr;
3676 else
3677 attr = sgp->sg_term_attr;
3678
3679 return attr;
3680}
3681
3682#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3683/*
3684 * Get the GUI colors and attributes for a group ID.
3685 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3686 */
3687 int
3688syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3689{
3690 hl_group_T *sgp;
3691
3692 hl_id = syn_get_final_id(hl_id);
3693 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3694
3695 *fgp = sgp->sg_gui_fg;
3696 *bgp = sgp->sg_gui_bg;
3697 return sgp->sg_gui;
3698}
3699#endif
3700
3701#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003702 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3703 && defined(FEAT_TERMGUICOLORS)) \
3704 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003705 void
3706syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3707{
3708 hl_group_T *sgp;
3709
3710 hl_id = syn_get_final_id(hl_id);
3711 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3712 *fgp = sgp->sg_cterm_fg - 1;
3713 *bgp = sgp->sg_cterm_bg - 1;
3714}
3715#endif
3716
3717/*
3718 * Translate a group ID to the final group ID (following links).
3719 */
3720 int
3721syn_get_final_id(int hl_id)
3722{
3723 int count;
3724 hl_group_T *sgp;
3725
3726 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3727 return 0; // Can be called from eval!!
3728
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003729 // Follow links until there is no more.
3730 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003731 for (count = 100; --count >= 0; )
3732 {
3733 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3734 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3735 break;
3736 hl_id = sgp->sg_link;
3737 }
3738
3739 return hl_id;
3740}
3741
3742#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3743/*
3744 * Call this function just after the GUI has started.
3745 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3746 * It finds the font and color handles for the highlighting groups.
3747 */
3748 void
3749highlight_gui_started(void)
3750{
3751 int idx;
3752
3753 // First get the colors from the "Normal" and "Menu" group, if set
3754 if (USE_24BIT)
3755 set_normal_colors();
3756
3757 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3758 gui_do_one_color(idx, FALSE, FALSE);
3759
3760 highlight_changed();
3761}
3762
3763 static void
3764gui_do_one_color(
3765 int idx,
3766 int do_menu UNUSED, // TRUE: might set the menu font
3767 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3768{
3769 int didit = FALSE;
3770
3771# ifdef FEAT_GUI
3772# ifdef FEAT_TERMGUICOLORS
3773 if (gui.in_use)
3774# endif
3775 if (HL_TABLE()[idx].sg_font_name != NULL)
3776 {
3777 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3778 do_tooltip, TRUE);
3779 didit = TRUE;
3780 }
3781# endif
3782 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3783 {
3784 HL_TABLE()[idx].sg_gui_fg =
3785 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3786 didit = TRUE;
3787 }
3788 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3789 {
3790 HL_TABLE()[idx].sg_gui_bg =
3791 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3792 didit = TRUE;
3793 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003794 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3795 {
3796 HL_TABLE()[idx].sg_gui_sp =
3797 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3798 didit = TRUE;
3799 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003800 if (didit) // need to get a new attr number
3801 set_hl_attr(idx);
3802}
3803#endif
3804
3805#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3806/*
3807 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3808 */
3809 static void
3810combine_stl_hlt(
3811 int id,
3812 int id_S,
3813 int id_alt,
3814 int hlcnt,
3815 int i,
3816 int hlf,
3817 int *table)
3818{
3819 hl_group_T *hlt = HL_TABLE();
3820
3821 if (id_alt == 0)
3822 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003823 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003824 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3825 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3826# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3827 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3828# endif
3829 }
3830 else
3831 mch_memmove(&hlt[hlcnt + i],
3832 &hlt[id_alt - 1],
3833 sizeof(hl_group_T));
3834 hlt[hlcnt + i].sg_link = 0;
3835
3836 hlt[hlcnt + i].sg_term ^=
3837 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3838 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3839 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3840 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3841 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3842 hlt[hlcnt + i].sg_cterm ^=
3843 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3844 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3845 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3846 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3847 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003848 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3849 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003850# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3851 hlt[hlcnt + i].sg_gui ^=
3852 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3853# endif
3854# ifdef FEAT_GUI
3855 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3856 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3857 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3858 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3859 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3860 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3861 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3862 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3863# ifdef FEAT_XFONTSET
3864 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3865 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3866# endif
3867# endif
3868 highlight_ga.ga_len = hlcnt + i + 1;
3869 set_hl_attr(hlcnt + i); // At long last we can apply
3870 table[i] = syn_id2attr(hlcnt + i + 1);
3871}
3872#endif
3873
3874/*
3875 * Translate the 'highlight' option into attributes in highlight_attr[] and
3876 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3877 * corresponding highlights to use on top of HLF_SNC is computed.
3878 * Called only when the 'highlight' option has been changed and upon first
3879 * screen redraw after any :highlight command.
3880 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3881 */
3882 int
3883highlight_changed(void)
3884{
3885 int hlf;
3886 int i;
3887 char_u *p;
3888 int attr;
3889 char_u *end;
3890 int id;
3891#ifdef USER_HIGHLIGHT
3892 char_u userhl[30]; // use 30 to avoid compiler warning
3893# ifdef FEAT_STL_OPT
3894 int id_S = -1;
3895 int id_SNC = 0;
3896# ifdef FEAT_TERMINAL
3897 int id_ST = 0;
3898 int id_STNC = 0;
3899# endif
3900 int hlcnt;
3901# endif
3902#endif
3903 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3904
3905 need_highlight_changed = FALSE;
3906
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003907#ifdef FEAT_TERMINAL
3908 term_update_colors_all();
3909 term_update_wincolor_all();
3910#endif
3911
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003912 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003913 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3914 highlight_attr[hlf] = 0;
3915
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003916 // First set all attributes to their default value.
3917 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003918 for (i = 0; i < 2; ++i)
3919 {
3920 if (i)
3921 p = p_hl;
3922 else
3923 p = get_highlight_default();
3924 if (p == NULL) // just in case
3925 continue;
3926
3927 while (*p)
3928 {
3929 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3930 if (hl_flags[hlf] == *p)
3931 break;
3932 ++p;
3933 if (hlf == (int)HLF_COUNT || *p == NUL)
3934 return FAIL;
3935
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003936 // Allow several hl_flags to be combined, like "bu" for
3937 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003938 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003939 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003940 {
3941 if (VIM_ISWHITE(*p)) // ignore white space
3942 continue;
3943
3944 if (attr > HL_ALL) // Combination with ':' is not allowed.
3945 return FAIL;
3946
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003947 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003948 switch (*p)
3949 {
3950 case 'b': attr |= HL_BOLD;
3951 break;
3952 case 'i': attr |= HL_ITALIC;
3953 break;
3954 case '-':
3955 case 'n': // no highlighting
3956 break;
3957 case 'r': attr |= HL_INVERSE;
3958 break;
3959 case 's': attr |= HL_STANDOUT;
3960 break;
3961 case 'u': attr |= HL_UNDERLINE;
3962 break;
3963 case 'c': attr |= HL_UNDERCURL;
3964 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003965 case '2': attr |= HL_UNDERDOUBLE;
3966 break;
3967 case 'd': attr |= HL_UNDERDOTTED;
3968 break;
3969 case '=': attr |= HL_UNDERDASHED;
3970 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003971 case 't': attr |= HL_STRIKETHROUGH;
3972 break;
3973 case ':': ++p; // highlight group name
3974 if (attr || *p == NUL) // no combinations
3975 return FAIL;
3976 end = vim_strchr(p, ',');
3977 if (end == NULL)
3978 end = p + STRLEN(p);
3979 id = syn_check_group(p, (int)(end - p));
3980 if (id == 0)
3981 return FAIL;
3982 attr = syn_id2attr(id);
3983 p = end - 1;
3984#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3985 if (hlf == (int)HLF_SNC)
3986 id_SNC = syn_get_final_id(id);
3987# ifdef FEAT_TERMINAL
3988 else if (hlf == (int)HLF_ST)
3989 id_ST = syn_get_final_id(id);
3990 else if (hlf == (int)HLF_STNC)
3991 id_STNC = syn_get_final_id(id);
3992# endif
3993 else if (hlf == (int)HLF_S)
3994 id_S = syn_get_final_id(id);
3995#endif
3996 break;
3997 default: return FAIL;
3998 }
3999 }
4000 highlight_attr[hlf] = attr;
4001
4002 p = skip_to_option_part(p); // skip comma and spaces
4003 }
4004 }
4005
4006#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01004007 // Setup the user highlights
4008 //
4009 // Temporarily utilize 28 more hl entries:
4010 // 9 for User1-User9 combined with StatusLineNC
4011 // 9 for User1-User9 combined with StatusLineTerm
4012 // 9 for User1-User9 combined with StatusLineTermNC
4013 // 1 for StatusLine default
4014 // Have to be in there simultaneously in case of table overflows in
4015 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004016# ifdef FEAT_STL_OPT
4017 if (ga_grow(&highlight_ga, 28) == FAIL)
4018 return FAIL;
4019 hlcnt = highlight_ga.ga_len;
4020 if (id_S == -1)
4021 {
4022 // Make sure id_S is always valid to simplify code below. Use the last
4023 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02004024 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004025 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
4026 id_S = hlcnt + 19;
4027 }
4028# endif
4029 for (i = 0; i < 9; i++)
4030 {
4031 sprintf((char *)userhl, "User%d", i + 1);
4032 id = syn_name2id(userhl);
4033 if (id == 0)
4034 {
4035 highlight_user[i] = 0;
4036# ifdef FEAT_STL_OPT
4037 highlight_stlnc[i] = 0;
4038# ifdef FEAT_TERMINAL
4039 highlight_stlterm[i] = 0;
4040 highlight_stltermnc[i] = 0;
4041# endif
4042# endif
4043 }
4044 else
4045 {
4046 highlight_user[i] = syn_id2attr(id);
4047# ifdef FEAT_STL_OPT
4048 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
4049 HLF_SNC, highlight_stlnc);
4050# ifdef FEAT_TERMINAL
4051 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
4052 HLF_ST, highlight_stlterm);
4053 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
4054 HLF_STNC, highlight_stltermnc);
4055# endif
4056# endif
4057 }
4058 }
4059# ifdef FEAT_STL_OPT
4060 highlight_ga.ga_len = hlcnt;
4061# endif
4062
4063#endif // USER_HIGHLIGHT
4064
4065 return OK;
4066}
4067
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004068static void highlight_list(void);
4069static void highlight_list_two(int cnt, int attr);
4070
4071/*
4072 * Handle command line completion for :highlight command.
4073 */
4074 void
4075set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
4076{
4077 char_u *p;
4078
4079 // Default: expand group names
4080 xp->xp_context = EXPAND_HIGHLIGHT;
4081 xp->xp_pattern = arg;
4082 include_link = 2;
4083 include_default = 1;
4084
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004085 if (*arg == NUL)
4086 return;
4087
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004088 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004089 p = skiptowhite(arg);
4090 if (*p == NUL)
4091 return;
4092
4093 // past "default" or group name
4094 include_default = 0;
4095 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004096 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004097 arg = skipwhite(p);
4098 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004099 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004100 }
4101 if (*p == NUL)
4102 return;
4103
4104 // past group name
4105 include_link = 0;
4106 if (arg[1] == 'i' && arg[0] == 'N')
4107 highlight_list();
4108 if (STRNCMP("link", arg, p - arg) == 0
4109 || STRNCMP("clear", arg, p - arg) == 0)
4110 {
4111 xp->xp_pattern = skipwhite(p);
4112 p = skiptowhite(xp->xp_pattern);
4113 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004114 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004115 xp->xp_pattern = skipwhite(p);
4116 p = skiptowhite(xp->xp_pattern);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004117 }
4118 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004119 if (*p != NUL) // past group name(s)
4120 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004121}
4122
4123/*
4124 * List highlighting matches in a nice way.
4125 */
4126 static void
4127highlight_list(void)
4128{
4129 int i;
4130
4131 for (i = 10; --i >= 0; )
4132 highlight_list_two(i, HL_ATTR(HLF_D));
4133 for (i = 40; --i >= 0; )
4134 highlight_list_two(99, 0);
4135}
4136
4137 static void
4138highlight_list_two(int cnt, int attr)
4139{
4140 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4141 msg_clr_eos();
4142 out_flush();
4143 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4144}
4145
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004146/*
4147 * Function given to ExpandGeneric() to obtain the list of group names.
4148 */
4149 char_u *
4150get_highlight_name(expand_T *xp UNUSED, int idx)
4151{
4152 return get_highlight_name_ext(xp, idx, TRUE);
4153}
4154
4155/*
4156 * Obtain a highlight group name.
4157 * When "skip_cleared" is TRUE don't return a cleared entry.
4158 */
4159 char_u *
4160get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4161{
4162 if (idx < 0)
4163 return NULL;
4164
4165 // Items are never removed from the table, skip the ones that were
4166 // cleared.
4167 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4168 return (char_u *)"";
4169
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004170 if (idx == highlight_ga.ga_len && include_none != 0)
4171 return (char_u *)"none";
4172 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4173 return (char_u *)"default";
4174 if (idx == highlight_ga.ga_len + include_none + include_default
4175 && include_link != 0)
4176 return (char_u *)"link";
4177 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4178 && include_link != 0)
4179 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004180 if (idx >= highlight_ga.ga_len)
4181 return NULL;
4182 return HL_TABLE()[idx].sg_name;
4183}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004184
4185#if defined(FEAT_GUI) || defined(PROTO)
4186/*
4187 * Free all the highlight group fonts.
4188 * Used when quitting for systems which need it.
4189 */
4190 void
4191free_highlight_fonts(void)
4192{
4193 int idx;
4194
4195 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4196 {
4197 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4198 HL_TABLE()[idx].sg_font = NOFONT;
4199# ifdef FEAT_XFONTSET
4200 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4201 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4202# endif
4203 }
4204
4205 gui_mch_free_font(gui.norm_font);
4206# ifdef FEAT_XFONTSET
4207 gui_mch_free_fontset(gui.fontset);
4208# endif
4209# ifndef FEAT_GUI_GTK
4210 gui_mch_free_font(gui.bold_font);
4211 gui_mch_free_font(gui.ital_font);
4212 gui_mch_free_font(gui.boldital_font);
4213# endif
4214}
4215#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004216
4217#if defined(FEAT_EVAL) || defined(PROTO)
4218/*
4219 * Convert each of the highlight attribute bits (bold, standout, underline,
4220 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4221 * the attribute name as the key.
4222 */
4223 static dict_T *
4224highlight_get_attr_dict(int hlattr)
4225{
4226 dict_T *dict;
4227 int i;
4228
4229 dict = dict_alloc();
4230 if (dict == NULL)
4231 return NULL;
4232
John Marriott34f00dd2024-04-08 23:28:12 +02004233 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004234 {
John Marriott34f00dd2024-04-08 23:28:12 +02004235 if (hlattr & highlight_index_tab[i]->key)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004236 {
John Marriott34f00dd2024-04-08 23:28:12 +02004237 dict_add_bool(dict, highlight_index_tab[i]->value, VVAL_TRUE);
4238 hlattr &= ~highlight_index_tab[i]->key; // don't want "inverse"/"reverse"
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004239 }
4240 }
4241
4242 return dict;
4243}
4244
4245/*
4246 * Return the attributes of the highlight group at index 'hl_idx' as a
4247 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4248 * links recursively.
4249 */
4250 static dict_T *
4251highlight_get_info(int hl_idx, int resolve_link)
4252{
4253 dict_T *dict;
4254 hl_group_T *sgp;
4255 dict_T *attr_dict;
4256 int hlgid;
4257
4258 dict = dict_alloc();
4259 if (dict == NULL)
4260 return dict;
4261
4262 sgp = &HL_TABLE()[hl_idx];
4263 // highlight group id is 1-based
4264 hlgid = hl_idx + 1;
4265
4266 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4267 goto error;
4268 if (dict_add_number(dict, "id", hlgid) == FAIL)
4269 goto error;
4270
4271 if (sgp->sg_link && resolve_link)
4272 {
4273 // resolve the highlight group link recursively
4274 while (sgp->sg_link)
4275 {
4276 hlgid = sgp->sg_link;
4277 sgp = &HL_TABLE()[sgp->sg_link - 1];
4278 }
4279 }
4280
4281 if (sgp->sg_term != 0)
4282 {
4283 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4284 if (attr_dict != NULL)
4285 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4286 goto error;
4287 }
4288 if (sgp->sg_start != NULL)
4289 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4290 goto error;
4291 if (sgp->sg_stop != NULL)
4292 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4293 goto error;
4294 if (sgp->sg_cterm != 0)
4295 {
4296 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4297 if (attr_dict != NULL)
4298 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4299 goto error;
4300 }
4301 if (sgp->sg_cterm_fg != 0)
4302 if (dict_add_string(dict, "ctermfg",
4303 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4304 goto error;
4305 if (sgp->sg_cterm_bg != 0)
4306 if (dict_add_string(dict, "ctermbg",
4307 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4308 goto error;
4309 if (sgp->sg_cterm_ul != 0)
4310 if (dict_add_string(dict, "ctermul",
4311 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4312 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004313 if (sgp->sg_cterm_font != 0)
4314 if (dict_add_string(dict, "ctermfont",
4315 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4316 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004317 if (sgp->sg_gui != 0)
4318 {
4319 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4320 if (attr_dict != NULL)
4321 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4322 goto error;
4323 }
4324 if (sgp->sg_gui_fg_name != NULL)
4325 if (dict_add_string(dict, "guifg",
4326 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4327 goto error;
4328 if (sgp->sg_gui_bg_name != NULL)
4329 if (dict_add_string(dict, "guibg",
4330 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4331 goto error;
4332 if (sgp->sg_gui_sp_name != NULL)
4333 if (dict_add_string(dict, "guisp",
4334 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4335 goto error;
4336# ifdef FEAT_GUI
4337 if (sgp->sg_font_name != NULL)
4338 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4339 goto error;
4340# endif
4341 if (sgp->sg_link)
4342 {
4343 char_u *link;
4344
4345 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4346 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4347 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004348
4349 if (sgp->sg_deflink)
4350 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004351 }
4352 if (dict_len(dict) == 2)
4353 // If only 'name' is present, then the highlight group is cleared.
4354 dict_add_bool(dict, "cleared", VVAL_TRUE);
4355
4356 return dict;
4357
4358error:
4359 vim_free(dict);
4360 return NULL;
4361}
4362
4363/*
4364 * "hlget([name])" function
4365 * Return the attributes of a specific highlight group (if specified) or all
4366 * the highlight groups.
4367 */
4368 void
4369f_hlget(typval_T *argvars, typval_T *rettv)
4370{
4371 list_T *list;
4372 dict_T *dict;
4373 int i;
4374 char_u *hlarg = NULL;
4375 int resolve_link = FALSE;
4376
4377 if (rettv_list_alloc(rettv) == FAIL)
4378 return;
4379
4380 if (check_for_opt_string_arg(argvars, 0) == FAIL
4381 || (argvars[0].v_type != VAR_UNKNOWN
4382 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4383 return;
4384
4385 if (argvars[0].v_type != VAR_UNKNOWN)
4386 {
4387 // highlight group name supplied
4388 hlarg = tv_get_string_chk(&argvars[0]);
4389 if (hlarg == NULL)
4390 return;
4391
4392 if (argvars[1].v_type != VAR_UNKNOWN)
4393 {
4394 int error = FALSE;
4395
4396 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4397 if (error)
4398 return;
4399 }
4400 }
4401
4402 list = rettv->vval.v_list;
4403 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4404 {
4405 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4406 {
4407 dict = highlight_get_info(i, resolve_link);
4408 if (dict != NULL)
4409 list_append_dict(list, dict);
4410 }
4411 }
4412}
4413
4414/*
4415 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4416 * 'dict' or the value is not a string type. If the value is not a string type
4417 * or is NULL, then 'error' is set to TRUE.
4418 */
4419 static char_u *
4420hldict_get_string(dict_T *dict, char_u *key, int *error)
4421{
4422 dictitem_T *di;
4423
4424 *error = FALSE;
4425 di = dict_find(dict, key, -1);
4426 if (di == NULL)
4427 return NULL;
4428
4429 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4430 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004431 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004432 *error = TRUE;
4433 return NULL;
4434 }
4435
4436 return di->di_tv.vval.v_string;
4437}
4438
4439/*
4440 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4441 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4442 * Dictionary or is NULL.
4443 */
4444 static int
4445hldict_attr_to_str(
4446 dict_T *dict,
4447 char_u *key,
4448 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004449 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004450{
4451 dictitem_T *di;
4452 dict_T *attrdict;
4453 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004454 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004455
4456 attr_str[0] = NUL;
4457 di = dict_find(dict, key, -1);
4458 if (di == NULL)
4459 return TRUE;
4460
4461 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4462 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004463 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004464 return FALSE;
4465 }
4466
4467 attrdict = di->di_tv.vval.v_dict;
4468
4469 // If the attribute dict is empty, then return NONE to clear the attributes
4470 if (dict_len(attrdict) == 0)
4471 {
4472 vim_strcat(attr_str, (char_u *)"NONE", len);
4473 return TRUE;
4474 }
4475
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004476 p = attr_str;
John Marriott34f00dd2024-04-08 23:28:12 +02004477 for (i = 0; i < (int)ARRAY_LENGTH(highlight_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004478 {
John Marriott34f00dd2024-04-08 23:28:12 +02004479 if (dict_get_bool(attrdict, highlight_tab[i].value, VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004480 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004481 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4482 STRCPY(p, (char_u *)",");
John Marriott34f00dd2024-04-08 23:28:12 +02004483 if (p - attr_str + highlight_tab[i].length + 1 < len)
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004484 {
John Marriott34f00dd2024-04-08 23:28:12 +02004485 STRCPY(p, highlight_tab[i].value);
4486 p += highlight_tab[i].length;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004487 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004488 }
4489 }
4490
4491 return TRUE;
4492}
4493
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004494// Temporary buffer used to store the command string produced by hlset().
4495// IObuff cannot be used for this as the error messages produced by hlset()
4496// internally use IObuff.
4497#define HLSETBUFSZ 512
4498static char_u hlsetBuf[HLSETBUFSZ + 1];
4499
4500/*
4501 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4502 * "dptr", which points into "hlsetBuf".
4503 * Returns the updated pointer.
4504 */
4505 static char_u *
4506add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4507{
4508 size_t vallen;
4509
4510 // Do nothing if the value is not specified or is empty
4511 if (value == NULL || *value == NUL)
4512 return dptr;
4513
4514 vallen = STRLEN(value);
4515 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4516 {
4517 STRCPY(dptr, attr);
4518 dptr += attrlen;
4519 STRCPY(dptr, value);
4520 dptr += vallen;
4521 }
4522
4523 return dptr;
4524}
4525
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004526/*
4527 * Add or update a highlight group using 'dict' items. Returns TRUE if
4528 * successfully updated the highlight group.
4529 */
4530 static int
4531hlg_add_or_update(dict_T *dict)
4532{
4533 char_u *name;
4534 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004535 char_u term_attr[MAX_ATTR_LEN];
4536 char_u cterm_attr[MAX_ATTR_LEN];
4537 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004538 char_u *start;
4539 char_u *stop;
4540 char_u *ctermfg;
4541 char_u *ctermbg;
4542 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004543 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004544 char_u *guifg;
4545 char_u *guibg;
4546 char_u *guisp;
4547# ifdef FEAT_GUI
4548 char_u *font;
4549# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004550 int forceit = FALSE;
4551 int dodefault = FALSE;
4552 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004553 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004554
4555 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004556 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004557 return FALSE;
4558
Bram Moolenaard61efa52022-07-23 09:52:04 +01004559 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004560 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004561
Bram Moolenaard61efa52022-07-23 09:52:04 +01004562 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004563 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004564
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004565 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004566 {
4567 varnumber_T cleared;
4568
4569 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004570 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004571 if (cleared == TRUE)
4572 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004573 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4574 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004575 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004576 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004577 }
4578
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004579 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004580 {
4581 char_u *linksto;
4582
4583 // link highlight groups
4584 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004585 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004586 return FALSE;
4587
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004588 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004589 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004590 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004591
4592 done = TRUE;
4593 }
4594
4595 // If 'cleared' or 'linksto' are specified, then don't process the other
4596 // attributes.
4597 if (done)
4598 return TRUE;
4599
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004600 start = hldict_get_string(dict, (char_u *)"start", &error);
4601 if (error)
4602 return FALSE;
4603
4604 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4605 if (error)
4606 return FALSE;
4607
4608 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004609 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004610 return FALSE;
4611
4612 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004613 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004614 return FALSE;
4615
4616 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4617 if (error)
4618 return FALSE;
4619
4620 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4621 if (error)
4622 return FALSE;
4623
4624 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4625 if (error)
4626 return FALSE;
4627
PMuncha606f3a2023-11-15 15:35:49 +01004628 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
4629 if (error)
4630 return FALSE;
4631
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004632 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004633 return FALSE;
4634
4635 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4636 if (error)
4637 return FALSE;
4638
4639 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4640 if (error)
4641 return FALSE;
4642
4643 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4644 if (error)
4645 return FALSE;
4646
4647# ifdef FEAT_GUI
4648 font = hldict_get_string(dict, (char_u *)"font", &error);
4649 if (error)
4650 return FALSE;
4651# endif
4652
4653 // If none of the attributes are specified, then do nothing.
4654 if (term_attr[0] == NUL && start == NULL && stop == NULL
4655 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01004656 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004657# ifdef FEAT_GUI
4658 && font == NULL
4659# endif
4660 && guifg == NULL && guibg == NULL && guisp == NULL
4661 )
4662 return TRUE;
4663
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004664 hlsetBuf[0] = NUL;
4665 p = hlsetBuf;
4666 if (dodefault)
4667 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4668 p = add_attr_and_value(p, (char_u *)"", 0, name);
4669 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4670 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4671 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4672 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4673 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4674 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4675 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01004676 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004677 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004678# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004679 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004680# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004681 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4682 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004683 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004684
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004685 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004686
4687 return TRUE;
4688}
4689
4690/*
4691 * "hlset([{highlight_attr}])" function
4692 * Add or modify highlight groups
4693 */
4694 void
4695f_hlset(typval_T *argvars, typval_T *rettv)
4696{
4697 listitem_T *li;
4698 dict_T *dict;
4699
4700 rettv->vval.v_number = -1;
4701
4702 if (check_for_list_arg(argvars, 0) == FAIL)
4703 return;
4704
4705 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4706 {
4707 if (li->li_tv.v_type != VAR_DICT)
4708 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004709 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004710 return;
4711 }
4712
4713 dict = li->li_tv.vval.v_dict;
4714 if (!hlg_add_or_update(dict))
4715 return;
4716 }
4717
4718 rettv->vval.v_number = 0;
4719}
4720#endif