blob: 65670350bd07ceb70f4578305a231714810d6394 [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"),
Yee Cheng Chin9943d472025-03-26 19:41:02 +0100244 "default link DiffTextAdd DiffText",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200245#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200246 CENT("PmenuSbar ctermbg=Grey",
247 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200248 CENT("TabLineSel term=bold cterm=bold",
249 "TabLineSel term=bold cterm=bold gui=bold"),
250 CENT("TabLineFill term=reverse cterm=reverse",
251 "TabLineFill term=reverse cterm=reverse gui=reverse"),
252#ifdef FEAT_GUI
253 "Cursor guibg=fg guifg=bg",
254 "lCursor guibg=fg guifg=bg", // should be different, but what?
255#endif
256 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000257 "default link CursorLineSign SignColumn",
258 "default link CursorLineFold FoldColumn",
LemonBoya4399382022-04-09 21:04:08 +0100259 "default link CurSearch Search",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000260 "default link PmenuKind Pmenu",
261 "default link PmenuKindSel PmenuSel",
glepnir40c1c332024-06-11 19:37:04 +0200262 "default link PmenuMatch Pmenu",
263 "default link PmenuMatchSel PmenuSel",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000264 "default link PmenuExtra Pmenu",
265 "default link PmenuExtraSel PmenuSel",
Yee Cheng Chine700dde2025-02-20 21:58:21 +0100266 "default link PopupSelected PmenuSel",
267 "default link MessageWindow WarningMsg",
268 "default link PopupNotification WarningMsg",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200269 CENT("Normal cterm=NONE", "Normal gui=NONE"),
270 NULL
271};
272
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200273// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200274static char *(highlight_init_light[]) = {
275 CENT("Directory term=bold ctermfg=DarkBlue",
276 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
277 CENT("LineNr term=underline ctermfg=Brown",
278 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200279 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
280 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200281 CENT("MoreMsg term=bold ctermfg=DarkGreen",
282 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
283 CENT("Question term=standout ctermfg=DarkGreen",
284 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
285 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
286 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
287#ifdef FEAT_SPELL
288 CENT("SpellBad term=reverse ctermbg=LightRed",
289 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
290 CENT("SpellCap term=reverse ctermbg=LightBlue",
291 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
292 CENT("SpellRare term=reverse ctermbg=LightMagenta",
293 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
294 CENT("SpellLocal term=underline ctermbg=Cyan",
295 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
296#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200297 CENT("PmenuThumb ctermbg=Black",
298 "PmenuThumb ctermbg=Black guibg=Black"),
299 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
300 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
301 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
302 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200303 CENT("SpecialKey term=bold ctermfg=DarkBlue",
304 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
305 CENT("Title term=bold ctermfg=DarkMagenta",
306 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
307 CENT("WarningMsg term=standout ctermfg=DarkRed",
308 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200309 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
310 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200311#ifdef FEAT_FOLDING
312 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
313 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
314 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
315 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
316#endif
317#ifdef FEAT_SIGNS
318 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
319 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
320#endif
Maxim Kim59bafc82024-02-01 21:07:51 +0100321 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100322 "Visual ctermbg=Grey ctermfg=Black guibg=LightGrey guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200323#ifdef FEAT_DIFF
324 CENT("DiffAdd term=bold ctermbg=LightBlue",
325 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
326 CENT("DiffChange term=bold ctermbg=LightMagenta",
327 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
328 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
329 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
330#endif
331 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
332 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
333#ifdef FEAT_SYN_HL
334 CENT("CursorColumn term=reverse ctermbg=LightGrey",
335 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
336 CENT("CursorLine term=underline cterm=underline",
337 "CursorLine term=underline cterm=underline guibg=Grey90"),
338 CENT("ColorColumn term=reverse ctermbg=LightRed",
339 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
340#endif
341#ifdef FEAT_CONCEAL
342 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
343 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
344#endif
345 CENT("MatchParen term=reverse ctermbg=Cyan",
346 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
347#ifdef FEAT_TERMINAL
348 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
349 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
350 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
351 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
352#endif
353#ifdef FEAT_MENU
354 CENT("ToolbarLine term=underline ctermbg=LightGrey",
355 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
356 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
357 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
358#endif
359 NULL
360};
361
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200362// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200363static char *(highlight_init_dark[]) = {
364 CENT("Directory term=bold ctermfg=LightCyan",
365 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
366 CENT("LineNr term=underline ctermfg=Yellow",
367 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200368 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
369 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200370 CENT("MoreMsg term=bold ctermfg=LightGreen",
371 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
372 CENT("Question term=standout ctermfg=LightGreen",
373 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
374 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
375 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
376 CENT("SpecialKey term=bold ctermfg=LightBlue",
377 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
378#ifdef FEAT_SPELL
379 CENT("SpellBad term=reverse ctermbg=Red",
380 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
381 CENT("SpellCap term=reverse ctermbg=Blue",
382 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
383 CENT("SpellRare term=reverse ctermbg=Magenta",
384 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
385 CENT("SpellLocal term=underline ctermbg=Cyan",
386 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
387#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200388 CENT("PmenuThumb ctermbg=White",
389 "PmenuThumb ctermbg=White guibg=White"),
390 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
391 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
392 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
393 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200394 CENT("Title term=bold ctermfg=LightMagenta",
395 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
396 CENT("WarningMsg term=standout ctermfg=LightRed",
397 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200398 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
399 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200400#ifdef FEAT_FOLDING
401 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
402 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
403 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
404 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
405#endif
406#ifdef FEAT_SIGNS
407 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
408 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
409#endif
Christian Brabandte6d8b462024-01-28 23:33:29 +0100410 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100411 "Visual ctermbg=Grey ctermfg=Black guibg=#575757 guifg=LightGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200412#ifdef FEAT_DIFF
413 CENT("DiffAdd term=bold ctermbg=DarkBlue",
414 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
415 CENT("DiffChange term=bold ctermbg=DarkMagenta",
416 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
417 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
418 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
419#endif
420 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
421 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
422#ifdef FEAT_SYN_HL
423 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
424 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
425 CENT("CursorLine term=underline cterm=underline",
426 "CursorLine term=underline cterm=underline guibg=Grey40"),
427 CENT("ColorColumn term=reverse ctermbg=DarkRed",
428 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
429#endif
430 CENT("MatchParen term=reverse ctermbg=DarkCyan",
431 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
432#ifdef FEAT_CONCEAL
433 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
434 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
435#endif
436#ifdef FEAT_TERMINAL
437 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
438 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
439 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
440 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
441#endif
442#ifdef FEAT_MENU
443 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
444 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
445 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
446 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
447#endif
448 NULL
449};
450
Dominique Pelle748b3082022-01-08 12:41:16 +0000451#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200452/*
453 * Returns the number of highlight groups.
454 */
455 int
456highlight_num_groups(void)
457{
458 return highlight_ga.ga_len;
459}
460
461/*
462 * Returns the name of a highlight group.
463 */
464 char_u *
465highlight_group_name(int id)
466{
467 return HL_TABLE()[id].sg_name;
468}
469
470/*
471 * Returns the ID of the link to a highlight group.
472 */
473 int
474highlight_link_id(int id)
475{
476 return HL_TABLE()[id].sg_link;
477}
Dominique Pelle748b3082022-01-08 12:41:16 +0000478#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200479
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200480 void
481init_highlight(
482 int both, // include groups where 'bg' doesn't matter
483 int reset) // clear group first
484{
485 int i;
486 char **pp;
487 static int had_both = FALSE;
488#ifdef FEAT_EVAL
489 char_u *p;
490
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100491 // Try finding the color scheme file. Used when a color file was loaded
492 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200493 p = get_var_value((char_u *)"g:colors_name");
494 if (p != NULL)
495 {
496 // The value of g:colors_name could be freed when sourcing the script,
497 // making "p" invalid, so copy it.
498 char_u *copy_p = vim_strsave(p);
499 int r;
500
501 if (copy_p != NULL)
502 {
503 r = load_colors(copy_p);
504 vim_free(copy_p);
505 if (r == OK)
506 return;
507 }
508 }
509
510#endif
511
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100512 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200513 if (both)
514 {
515 had_both = TRUE;
516 pp = highlight_init_both;
517 for (i = 0; pp[i] != NULL; ++i)
518 do_highlight((char_u *)pp[i], reset, TRUE);
519 }
520 else if (!had_both)
521 // Don't do anything before the call with both == TRUE from main().
522 // Not everything has been setup then, and that call will overrule
523 // everything anyway.
524 return;
525
526 if (*p_bg == 'l')
527 pp = highlight_init_light;
528 else
529 pp = highlight_init_dark;
530 for (i = 0; pp[i] != NULL; ++i)
531 do_highlight((char_u *)pp[i], reset, TRUE);
532
Maxim Kim59bafc82024-02-01 21:07:51 +0100533 // Reverse looks ugly, but grey may not work for less than 8 colors. Thus
534 // let it depend on the number of colors available.
535 if (t_colors < 8)
536 do_highlight((char_u *)"Visual term=reverse cterm=reverse ctermbg=NONE ctermfg=NONE",
537 FALSE, TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200538 // With 8 colors brown is equal to yellow, need to use black for Search fg
539 // to avoid Statement highlighted text disappears.
540 // Clear the attributes, needed when changing the t_Co value.
Christian Brabandte6d8b462024-01-28 23:33:29 +0100541 if (t_colors <= 8)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200542 {
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200543 if (*p_bg == 'l')
544 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
545 }
546
547#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100548 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200549 if (get_var_value((char_u *)"g:syntax_on") != NULL)
550 {
551 static int recursive = 0;
552
553 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000554 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200555 else
556 {
557 ++recursive;
558 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
559 --recursive;
560 }
561 }
562#endif
563}
564
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000565#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
566/*
567 * Load a default color list. Intended to support legacy color names but allows
568 * the user to override the color values. Only loaded once.
569 */
570 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000571load_default_colors_lists(void)
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000572{
573 // Lacking a default color list isn't the end of the world but it is likely
574 // an inconvenience so users should know when it is missing.
575 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
576 msg("failed to load colors/lists/default.vim");
577}
578#endif
579
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200580/*
581 * Load color file "name".
582 * Return OK for success, FAIL for failure.
583 */
584 int
585load_colors(char_u *name)
586{
587 char_u *buf;
588 int retval = FAIL;
589 static int recursive = FALSE;
590
591 // When being called recursively, this is probably because setting
592 // 'background' caused the highlighting to be reloaded. This means it is
593 // working, thus we should return OK.
594 if (recursive)
595 return OK;
596
597 recursive = TRUE;
598 buf = alloc(STRLEN(name) + 12);
599 if (buf != NULL)
600 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100601#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100602 load_default_colors_lists();
603#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200604 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
605 curbuf->b_fname, FALSE, curbuf);
606 sprintf((char *)buf, "colors/%s.vim", name);
607 retval = source_runtime(buf, DIP_START + DIP_OPT);
608 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100609 if (retval == OK)
610 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
611 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200612 }
613 recursive = FALSE;
614
615 return retval;
616}
617
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200618static int color_numbers_16[28] = {0, 1, 2, 3,
619 4, 5, 6, 6,
620 7, 7, 7, 7,
621 8, 8,
622 9, 9, 10, 10,
623 11, 11, 12, 12, 13,
624 13, 14, 14, 15, -1};
625// for xterm with 88 colors...
626static int color_numbers_88[28] = {0, 4, 2, 6,
627 1, 5, 32, 72,
628 84, 84, 7, 7,
629 82, 82,
630 12, 43, 10, 61,
631 14, 63, 9, 74, 13,
632 75, 11, 78, 15, -1};
633// for xterm with 256 colors...
634static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200635 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200636 248, 248, 7, 7,
637 242, 242,
638 12, 81, 10, 121,
639 14, 159, 9, 224, 13,
640 225, 11, 229, 15, -1};
641// for terminals with less than 16 colors...
642static int color_numbers_8[28] = {0, 4, 2, 6,
643 1, 5, 3, 3,
644 7, 7, 7, 7,
645 0+8, 0+8,
646 4+8, 4+8, 2+8, 2+8,
647 6+8, 6+8, 1+8, 1+8, 5+8,
648 5+8, 3+8, 3+8, 7+8, -1};
649
650/*
651 * Lookup the "cterm" value to be used for color with index "idx" in
John Marriott34f00dd2024-04-08 23:28:12 +0200652 * color_name_tab[].
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200653 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
654 * colors, otherwise it will be unchanged.
655 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100656 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200657lookup_color(int idx, int foreground, int *boldp)
658{
659 int color = color_numbers_16[idx];
660 char_u *p;
661
662 // Use the _16 table to check if it's a valid color name.
663 if (color < 0)
664 return -1;
665
666 if (t_colors == 8)
667 {
668 // t_Co is 8: use the 8 colors table
669#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100670 // On qnx, the 8 & 16 color arrays are the same
671 if (STRNCMP(T_NAME, "qansi", 5) == 0)
672 color = color_numbers_16[idx];
673 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200674#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100675 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200676 if (foreground)
677 {
678 // set/reset bold attribute to get light foreground
679 // colors (on some terminals, e.g. "linux")
680 if (color & 8)
681 *boldp = TRUE;
682 else
683 *boldp = FALSE;
684 }
685 color &= 7; // truncate to 8 colors
686 }
687 else if (t_colors == 16 || t_colors == 88
688 || t_colors >= 256)
689 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100690 // Guess: if the termcap entry ends in 'm', it is
691 // probably an xterm-like terminal. Use the changed
692 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200693 if (*T_CAF != NUL)
694 p = T_CAF;
695 else
696 p = T_CSF;
697 if (*p != NUL && (t_colors > 256
698 || *(p + STRLEN(p) - 1) == 'm'))
699 {
700 if (t_colors == 88)
701 color = color_numbers_88[idx];
702 else if (t_colors >= 256)
703 color = color_numbers_256[idx];
704 else
705 color = color_numbers_8[idx];
706 }
707#ifdef FEAT_TERMRESPONSE
708 if (t_colors >= 256 && color == 15 && is_mac_terminal)
709 // Terminal.app has a bug: 15 is light grey. Use white
710 // from the color cube instead.
711 color = 231;
712#endif
713 }
714 return color;
715}
716
717/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100718 * Link highlight group 'from_hg' to 'to_hg'.
719 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000720 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100721 * 'init' is set to TRUE when initializing all the highlight groups.
722 */
723 static void
724highlight_group_link(
725 char_u *from_hg,
726 int from_len,
727 char_u *to_hg,
728 int to_len,
729 int dodefault,
730 int forceit,
731 int init)
732{
733 int from_id;
734 int to_id;
735 hl_group_T *hlgroup = NULL;
736
737 from_id = syn_check_group(from_hg, from_len);
738 if (STRNCMP(to_hg, "NONE", 4) == 0)
739 to_id = 0;
740 else
741 to_id = syn_check_group(to_hg, to_len);
742
743 if (from_id > 0)
744 {
745 hlgroup = &HL_TABLE()[from_id - 1];
746 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
747 {
748 hlgroup->sg_deflink = to_id;
749#ifdef FEAT_EVAL
750 hlgroup->sg_deflink_sctx = current_sctx;
751 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
752#endif
753 }
754 }
755
756 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
757 {
758 // Don't allow a link when there already is some highlighting
759 // for the group, unless '!' is used
760 if (to_id > 0 && !forceit && !init
761 && hl_has_settings(from_id - 1, dodefault))
762 {
763 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000764 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100765 }
766 else if (hlgroup->sg_link != to_id
767#ifdef FEAT_EVAL
768 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
769#endif
770 || hlgroup->sg_cleared)
771 {
772 if (!init)
773 hlgroup->sg_set |= SG_LINK;
774 hlgroup->sg_link = to_id;
775#ifdef FEAT_EVAL
776 hlgroup->sg_script_ctx = current_sctx;
777 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
778#endif
779 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100780 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100781
782 // Only call highlight_changed() once after multiple changes.
783 need_highlight_changed = TRUE;
784 }
785 }
786
787}
788
789/*
790 * Reset all highlighting to the defaults. Removes all highlighting for the
791 * groups added by the user.
792 */
793 static void
794highlight_reset_all(void)
795{
796 int idx;
797
798#ifdef FEAT_GUI
799 // First, we do not destroy the old values, but allocate the new
800 // ones and update the display. THEN we destroy the old values.
801 // If we destroy the old values first, then the old values
802 // (such as GuiFont's or GuiFontset's) will still be displayed but
803 // invalid because they were free'd.
804 if (gui.in_use)
805 {
806# ifdef FEAT_BEVAL_TIP
807 gui_init_tooltip_font();
808# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100809# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100810 gui_init_menu_font();
811# endif
812 }
813# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
814 gui_mch_def_colors();
815# endif
816# ifdef FEAT_GUI_X11
817# ifdef FEAT_MENU
818
819 // This only needs to be done when there is no Menu highlight
820 // group defined by default, which IS currently the case.
821 gui_mch_new_menu_colors();
822# endif
823 if (gui.in_use)
824 {
825 gui_new_scrollbar_colors();
826# ifdef FEAT_BEVAL_GUI
827 gui_mch_new_tooltip_colors();
828# endif
829# ifdef FEAT_MENU
830 gui_mch_new_menu_font();
831# endif
832 }
833# endif
834
835 // Ok, we're done allocating the new default graphics items.
836 // The screen should already be refreshed at this point.
837 // It is now Ok to clear out the old data.
838#endif
839#ifdef FEAT_EVAL
840 do_unlet((char_u *)"g:colors_name", TRUE);
841#endif
842 restore_cterm_colors();
843
844 // Clear all default highlight groups and load the defaults.
845 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
846 highlight_clear(idx);
847 init_highlight(TRUE, TRUE);
848#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
849 if (USE_24BIT)
850 highlight_gui_started();
851 else
852#endif
853 highlight_changed();
854 redraw_later_clear();
855}
856
857/*
858 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
859 * index 'idx'.
860 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
861 * 'arg' is the list of attribute names separated by comma.
862 * 'init' is set to TRUE when initializing all the highlight groups.
863 * Returns TRUE if the attributes are set.
864 */
865 static int
866highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
867{
868 int attr;
Mike Williams72a156b2024-04-09 22:04:54 +0200869 size_t off;
John Marriott34f00dd2024-04-08 23:28:12 +0200870 keyvalue_T target;
871 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100872
873 attr = 0;
874 off = 0;
John Marriott34f00dd2024-04-08 23:28:12 +0200875 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +0100876 target.value.length = 0; // not used, see cmp_keyvalue_value_ni()
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100877 while (arg[off] != NUL)
878 {
John Marriott8d4477e2024-11-02 15:59:01 +0100879 target.value.string = arg + off;
880 entry = (keyvalue_T *)bsearch(&target, &highlight_tab,
881 ARRAY_LENGTH(highlight_tab), sizeof(highlight_tab[0]),
882 cmp_keyvalue_value_ni);
John Marriott34f00dd2024-04-08 23:28:12 +0200883 if (entry == NULL)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100884 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000885 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100886 return FALSE;
887 }
John Marriott34f00dd2024-04-08 23:28:12 +0200888
889 attr |= entry->key;
John Marriott8d4477e2024-11-02 15:59:01 +0100890 off += entry->value.length;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100891 if (arg[off] == ',') // another one follows
892 ++off;
893 }
894 if (*key == 'T')
895 {
896 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
897 {
898 if (!init)
899 HL_TABLE()[idx].sg_set |= SG_TERM;
900 HL_TABLE()[idx].sg_term = attr;
901 }
902 }
903 else if (*key == 'C')
904 {
905 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
906 {
907 if (!init)
908 HL_TABLE()[idx].sg_set |= SG_CTERM;
909 HL_TABLE()[idx].sg_cterm = attr;
910 HL_TABLE()[idx].sg_cterm_bold = FALSE;
911 }
912 }
913#if defined(FEAT_GUI) || defined(FEAT_EVAL)
914 else
915 {
916 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
917 {
918 if (!init)
919 HL_TABLE()[idx].sg_set |= SG_GUI;
920 HL_TABLE()[idx].sg_gui = attr;
921 }
922 }
923#endif
924
925 return TRUE;
926}
927
928#ifdef FEAT_GUI
929/*
930 * Set the font for the highlight group at 'idx'.
931 * 'arg' is the font name.
932 * Returns TRUE if the font is changed.
933 */
934 static int
935highlight_set_font(
936 int idx,
937 char_u *arg,
938 int is_normal_group,
939 int is_menu_group,
940 int is_tooltip_group)
941{
942 int did_change = FALSE;
943
944 // in non-GUI fonts are simply ignored
945 if (HL_TABLE()[idx].sg_font_name != NULL
946 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
947 {
948 // Font name didn't change, ignore.
949 }
950 else if (!gui.shell_created)
951 {
952 // GUI not started yet, always accept the name.
953 vim_free(HL_TABLE()[idx].sg_font_name);
954 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
955 did_change = TRUE;
956 }
957 else
958 {
959 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
960# ifdef FEAT_XFONTSET
961 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
962# endif
963 // First, save the current font/fontset.
964 // Then try to allocate the font/fontset.
965 // If the allocation fails, HL_TABLE()[idx].sg_font OR
966 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
967
968 HL_TABLE()[idx].sg_font = NOFONT;
969# ifdef FEAT_XFONTSET
970 HL_TABLE()[idx].sg_fontset = NOFONTSET;
971# endif
972 hl_do_font(idx, arg, is_normal_group, is_menu_group,
973 is_tooltip_group, FALSE);
974
975# ifdef FEAT_XFONTSET
976 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
977 {
978 // New fontset was accepted. Free the old one, if there
979 // was one.
980 gui_mch_free_fontset(temp_sg_fontset);
981 vim_free(HL_TABLE()[idx].sg_font_name);
982 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
983 did_change = TRUE;
984 }
985 else
986 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
987# endif
988 if (HL_TABLE()[idx].sg_font != NOFONT)
989 {
990 // New font was accepted. Free the old one, if there was
991 // one.
992 gui_mch_free_font(temp_sg_font);
993 vim_free(HL_TABLE()[idx].sg_font_name);
994 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
995 did_change = TRUE;
996 }
997 else
998 HL_TABLE()[idx].sg_font = temp_sg_font;
999 }
1000
1001 return did_change;
1002}
1003#endif
1004
1005/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001006 * Set the cterm foreground color for the Normal highlight group to "color" and
1007 * the bold attribute to "bold".
1008 */
1009 static void
1010hl_set_ctermfg_normal_group(int color, int bold)
1011{
1012 cterm_normal_fg_color = color + 1;
1013 cterm_normal_fg_bold = bold;
1014#ifdef FEAT_GUI
1015 // Don't do this if the GUI is used.
1016 if (!gui.in_use && !gui.starting)
1017#endif
1018 {
1019 set_must_redraw(UPD_CLEAR);
1020 if (termcap_active && color >= 0)
1021 term_fg_color(color);
1022 }
1023}
1024
1025/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001026 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001027 */
1028 static void
1029highlight_set_ctermfg(int idx, int color, int is_normal_group)
1030{
1031 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1032 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001033 hl_set_ctermfg_normal_group(color,
1034 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001035}
1036
1037/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001038 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001039 */
1040 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001041hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001042{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001043 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001044#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001045 // Don't mess with 'background' if the GUI is used.
1046 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001047#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001048 {
1049 set_must_redraw(UPD_CLEAR);
1050 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001051 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001052 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001053
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001054 if (termcap_active)
1055 term_bg_color(color);
1056 if (t_colors < 16)
1057 dark = (color == 0 || color == 4);
1058 // Limit the heuristic to the standard 16 colors
1059 else if (color < 16)
1060 dark = (color < 7 || color == 8);
1061 // Set the 'background' option if the value is
1062 // wrong.
1063 if (dark != -1
1064 && dark != (*p_bg == 'd')
1065 && !option_was_set((char_u *)"bg"))
1066 {
1067 set_option_value_give_err((char_u *)"bg",
1068 0L, (char_u *)(dark ? "dark" : "light"), 0);
1069 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001070 }
1071 }
1072 }
1073}
1074
1075/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001076 * Set the cterm background color for the highlight group at 'idx' to 'color'.
1077 */
1078 static void
1079highlight_set_ctermbg(int idx, int color, int is_normal_group)
1080{
1081 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1082 if (is_normal_group)
1083 hl_set_ctermbg_normal_group(color);
1084}
1085
1086/*
1087 * Set the cterm underline color for the Normal highlight group to "color".
1088 */
1089 static void
1090hl_set_ctermul_normal_group(int color)
1091{
1092 cterm_normal_ul_color = color + 1;
1093#ifdef FEAT_GUI
1094 // Don't do this if the GUI is used.
1095 if (!gui.in_use && !gui.starting)
1096#endif
1097 {
1098 set_must_redraw(UPD_CLEAR);
1099 if (termcap_active && color >= 0)
1100 term_ul_color(color);
1101 }
1102}
1103
1104/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001105 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001106 */
1107 static void
1108highlight_set_ctermul(int idx, int color, int is_normal_group)
1109{
1110 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1111 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001112 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001113}
1114
1115/*
PMuncha606f3a2023-11-15 15:35:49 +01001116 * Set the cterm font for the highlight group at 'idx'.
1117 * 'arg' is the color name or the numeric value as a string.
1118 * 'init' is set to TRUE when initializing highlighting.
1119 * Called for the ":highlight" command and the "hlset()" function.
1120 *
1121 * Returns TRUE if the font is set.
1122 */
1123 static int
1124highlight_set_cterm_font(
1125 int idx,
1126 char_u *arg,
1127 int init)
1128{
1129 int font;
1130
1131 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1132 return FALSE;
1133
1134 if (!init)
1135 HL_TABLE()[idx].sg_set |= SG_CTERM;
1136
1137 if (VIM_ISDIGIT(*arg))
1138 font = atoi((char *)arg);
1139 else if (STRICMP(arg, "NONE") == 0)
1140 font = -1;
1141 else
1142 return FALSE;
1143
1144 HL_TABLE()[idx].sg_cterm_font = font + 1;
1145 return TRUE;
1146}
1147
1148/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001149 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1150 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1151 * 'keystart' is the color name/value.
1152 * 'arg' is the color name or the numeric value as a string.
1153 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1154 * 'init' is set to TRUE when initializing highlighting.
1155 * Called for the ":highlight" command and the "hlset()" function.
1156 *
1157 * Returns TRUE if the color is set.
1158 */
1159 static int
1160highlight_set_cterm_color(
1161 int idx,
1162 char_u *key,
1163 char_u *key_start,
1164 char_u *arg,
1165 int is_normal_group,
1166 int init)
1167{
1168 int color;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001169
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001170 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1171 return FALSE;
1172
1173 if (!init)
1174 HL_TABLE()[idx].sg_set |= SG_CTERM;
1175
1176 // When setting the foreground color, and previously the "bold"
1177 // flag was set for a light color, reset it now
1178 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001179 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001180 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1181 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1182 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001183
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001184 if (VIM_ISDIGIT(*arg))
1185 color = atoi((char *)arg);
1186 else if (STRICMP(arg, "fg") == 0)
1187 {
1188 if (cterm_normal_fg_color)
1189 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001190 else
1191 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001192 emsg(_(e_fg_color_unknown));
1193 return FALSE;
1194 }
1195 }
1196 else if (STRICMP(arg, "bg") == 0)
1197 {
1198 if (cterm_normal_bg_color > 0)
1199 color = cterm_normal_bg_color - 1;
1200 else
1201 {
1202 emsg(_(e_bg_color_unknown));
1203 return FALSE;
1204 }
1205 }
1206 else if (STRICMP(arg, "ul") == 0)
1207 {
1208 if (cterm_normal_ul_color > 0)
1209 color = cterm_normal_ul_color - 1;
1210 else
1211 {
1212 emsg(_(e_ul_color_unknown));
1213 return FALSE;
1214 }
1215 }
1216 else
1217 {
1218 int bold = MAYBE;
John Marriott34f00dd2024-04-08 23:28:12 +02001219 keyvalue_T target;
1220 keyvalue_T *entry;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001221
John Marriott34f00dd2024-04-08 23:28:12 +02001222 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01001223 target.value.string = arg;
1224 target.value.length = 0; // not used, see cmp_keyvalue_value_i()
1225 entry = (keyvalue_T *)bsearch(&target, &color_name_tab,
1226 ARRAY_LENGTH(color_name_tab), sizeof(color_name_tab[0]),
1227 cmp_keyvalue_value_i);
John Marriott34f00dd2024-04-08 23:28:12 +02001228 if (entry == NULL)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001229 {
1230 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1231 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001232 }
1233
John Marriott34f00dd2024-04-08 23:28:12 +02001234 color = lookup_color(entry->key, key[5] == 'F', &bold);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001235
1236 // set/reset bold attribute to get light foreground
1237 // colors (on some terminals, e.g. "linux")
1238 if (bold == TRUE)
1239 {
1240 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1241 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1242 }
1243 else if (bold == FALSE)
1244 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001245 }
1246
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001247 // Add one to the argument, to avoid zero. Zero is used for
1248 // "NONE", then "color" is -1.
1249 if (key[5] == 'F')
1250 highlight_set_ctermfg(idx, color, is_normal_group);
1251 else if (key[5] == 'B')
1252 highlight_set_ctermbg(idx, color, is_normal_group);
1253 else // ctermul
1254 highlight_set_ctermul(idx, color, is_normal_group);
1255
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001256 return TRUE;
1257}
1258
1259#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1260/*
1261 * Set the GUI foreground color for the highlight group at 'idx'.
1262 * Returns TRUE if the color is set.
1263 */
1264 static int
1265highlight_set_guifg(
1266 int idx,
1267 char_u *arg,
1268 int is_menu_group UNUSED,
1269 int is_scrollbar_group UNUSED,
1270 int is_tooltip_group UNUSED,
1271 int *do_colors UNUSED,
1272 int init)
1273{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001274# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001275 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001276# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001277 char_u **namep;
1278 int did_change = FALSE;
1279
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001280 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1281 return FALSE;
1282
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001283 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001284 if (!init)
1285 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001286
1287# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001288 // In GUI guifg colors are only used when recognized
1289 i = color_name2handle(arg);
1290 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1291 {
1292 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001293# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001294 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1295 {
1296 vim_free(*namep);
1297 if (STRCMP(arg, "NONE") != 0)
1298 *namep = vim_strsave(arg);
1299 else
1300 *namep = NULL;
1301 did_change = TRUE;
1302 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001303# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1304# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001305 if (is_menu_group && gui.menu_fg_pixel != i)
1306 {
1307 gui.menu_fg_pixel = i;
1308 *do_colors = TRUE;
1309 }
1310 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1311 {
1312 gui.scroll_fg_pixel = i;
1313 *do_colors = TRUE;
1314 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001315# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001316 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1317 {
1318 gui.tooltip_fg_pixel = i;
1319 *do_colors = TRUE;
1320 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001321# endif
1322# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001323 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001324# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001325
1326 return did_change;
1327}
1328
1329/*
1330 * Set the GUI background color for the highlight group at 'idx'.
1331 * Returns TRUE if the color is set.
1332 */
1333 static int
1334highlight_set_guibg(
1335 int idx,
1336 char_u *arg,
1337 int is_menu_group UNUSED,
1338 int is_scrollbar_group UNUSED,
1339 int is_tooltip_group UNUSED,
1340 int *do_colors UNUSED,
1341 int init)
1342{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001343# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001344 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001345# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001346 char_u **namep;
1347 int did_change = FALSE;
1348
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001349 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1350 return FALSE;
1351
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001352 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001353 if (!init)
1354 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001355
1356# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001357 // In GUI guibg colors are only used when recognized
1358 i = color_name2handle(arg);
1359 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1360 {
1361 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001362# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001363 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1364 {
1365 vim_free(*namep);
1366 if (STRCMP(arg, "NONE") != 0)
1367 *namep = vim_strsave(arg);
1368 else
1369 *namep = NULL;
1370 did_change = TRUE;
1371 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001372# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1373# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001374 if (is_menu_group && gui.menu_bg_pixel != i)
1375 {
1376 gui.menu_bg_pixel = i;
1377 *do_colors = TRUE;
1378 }
1379 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1380 {
1381 gui.scroll_bg_pixel = i;
1382 *do_colors = TRUE;
1383 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001384# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001385 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1386 {
1387 gui.tooltip_bg_pixel = i;
1388 *do_colors = TRUE;
1389 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001390# endif
1391# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001392 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001393# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001394
1395 return did_change;
1396}
1397
1398/*
1399 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1400 * Returns TRUE if the color is set.
1401 */
1402 static int
1403highlight_set_guisp(int idx, char_u *arg, int init)
1404{
1405# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1406 int i;
1407# endif
1408 int did_change = FALSE;
1409 char_u **namep;
1410
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001411 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1412 return FALSE;
1413
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001414 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001415 if (!init)
1416 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001417
1418# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001419 // In GUI guisp colors are only used when recognized
1420 i = color_name2handle(arg);
1421 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1422 {
1423 HL_TABLE()[idx].sg_gui_sp = i;
1424# endif
1425 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001426 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001427 vim_free(*namep);
1428 if (STRCMP(arg, "NONE") != 0)
1429 *namep = vim_strsave(arg);
1430 else
1431 *namep = NULL;
1432 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001433 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001434# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001435 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001436# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001437
1438 return did_change;
1439}
1440#endif
1441
1442/*
1443 * Set the start/stop terminal codes for a highlight group.
1444 * Returns TRUE if the terminal code is set.
1445 */
1446 static int
1447highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1448{
1449 int off;
1450 char_u buf[100];
1451 int len;
1452 char_u *tname;
1453 char_u *p;
1454
1455 if (!init)
1456 HL_TABLE()[idx].sg_set |= SG_TERM;
1457
1458 // The "start" and "stop" arguments can be a literal escape
1459 // sequence, or a comma separated list of terminal codes.
1460 if (STRNCMP(arg, "t_", 2) == 0)
1461 {
1462 off = 0;
1463 buf[0] = 0;
1464 while (arg[off] != NUL)
1465 {
1466 // Isolate one termcap name
1467 for (len = 0; arg[off + len] &&
1468 arg[off + len] != ','; ++len)
1469 ;
1470 tname = vim_strnsave(arg + off, len);
1471 if (tname == NULL) // out of memory
1472 return FALSE;
1473 // lookup the escape sequence for the item
1474 p = get_term_code(tname);
1475 vim_free(tname);
1476 if (p == NULL) // ignore non-existing things
1477 p = (char_u *)"";
1478
1479 // Append it to the already found stuff
1480 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1481 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001482 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001483 return FALSE;
1484 }
1485 STRCAT(buf, p);
1486
1487 // Advance to the next item
1488 off += len;
1489 if (arg[off] == ',') // another one follows
1490 ++off;
1491 }
1492 }
1493 else
1494 {
1495 // Copy characters from arg[] to buf[], translating <> codes.
1496 for (p = arg, off = 0; off < 100 - 6 && *p; )
1497 {
zeertzjqdb088872022-05-02 22:53:45 +01001498 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001499 if (len > 0) // recognized special char
1500 off += len;
1501 else // copy as normal char
1502 buf[off++] = *p++;
1503 }
1504 buf[off] = NUL;
1505 }
1506
1507 if (STRCMP(buf, "NONE") == 0) // resetting the value
1508 p = NULL;
1509 else
1510 p = vim_strsave(buf);
1511 if (key[2] == 'A')
1512 {
1513 vim_free(HL_TABLE()[idx].sg_start);
1514 HL_TABLE()[idx].sg_start = p;
1515 }
1516 else
1517 {
1518 vim_free(HL_TABLE()[idx].sg_stop);
1519 HL_TABLE()[idx].sg_stop = p;
1520 }
1521 return TRUE;
1522}
1523
1524/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001525 * Handle the ":highlight .." command.
1526 * When using ":hi clear" this is called recursively for each group with
1527 * "forceit" and "init" both TRUE.
1528 */
1529 void
1530do_highlight(
1531 char_u *line,
1532 int forceit,
1533 int init) // TRUE when called for initializing
1534{
1535 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001536 char_u *linep;
1537 char_u *key_start;
1538 char_u *arg_start;
1539 char_u *key = NULL, *arg = NULL;
1540 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001541 int id;
1542 int idx;
1543 hl_group_T item_before;
1544 int did_change = FALSE;
1545 int dodefault = FALSE;
1546 int doclear = FALSE;
1547 int dolink = FALSE;
1548 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001549 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001550#ifdef FEAT_GUI_X11
1551 int is_menu_group = FALSE; // "Menu" group
1552 int is_scrollbar_group = FALSE; // "Scrollbar" group
1553 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001554#else
1555# define is_menu_group 0
1556# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001557# define is_scrollbar_group 0
1558#endif
1559#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1560 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001561#endif
1562#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1563 int did_highlight_changed = FALSE;
1564#endif
1565
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001566 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001567 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001568 {
1569 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1570 // TODO: only call when the group has attributes set
1571 highlight_list_one((int)i);
1572 return;
1573 }
1574
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001575 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001576 name_end = skiptowhite(line);
1577 linep = skipwhite(name_end);
1578
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001579 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001580 if (STRNCMP(line, "default", name_end - line) == 0)
1581 {
1582 dodefault = TRUE;
1583 line = linep;
1584 name_end = skiptowhite(line);
1585 linep = skipwhite(name_end);
1586 }
1587
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001588 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001589 if (STRNCMP(line, "clear", name_end - line) == 0)
1590 doclear = TRUE;
1591 if (STRNCMP(line, "link", name_end - line) == 0)
1592 dolink = TRUE;
1593
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001594 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001595 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001596 {
1597 id = syn_namen2id(line, (int)(name_end - line));
1598 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001599 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001600 else
1601 highlight_list_one(id);
1602 return;
1603 }
1604
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001605 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001606 if (dolink)
1607 {
1608 char_u *from_start = linep;
1609 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001610 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001611 char_u *to_start;
1612 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001613 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001614
1615 from_end = skiptowhite(from_start);
1616 to_start = skipwhite(from_end);
1617 to_end = skiptowhite(to_start);
1618
Bram Moolenaar1966c242020-04-20 22:42:32 +02001619 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001620 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001621 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001622 return;
1623 }
1624
Bram Moolenaar1966c242020-04-20 22:42:32 +02001625 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001626 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001627 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001628 return;
1629 }
1630
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001631 from_len = (int)(from_end - from_start);
1632 to_len = (int)(to_end - to_start);
1633 highlight_group_link(from_start, from_len, to_start, to_len,
1634 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001635 return;
1636 }
1637
1638 if (doclear)
1639 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001640 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001641 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001642 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001643 // ":highlight clear" without group name
1644 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001645 return;
1646 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001647 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001648 name_end = skiptowhite(line);
1649 linep = skipwhite(name_end);
1650 }
1651
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001652 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001653 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001654 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001655 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001656 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001657
1658 // Return if "default" was used and the group already has settings.
1659 if (dodefault && hl_has_settings(idx, TRUE))
1660 return;
1661
1662 // Make a copy so we can check if any attribute actually changed.
1663 item_before = HL_TABLE()[idx];
1664
1665 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1666 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001667#ifdef FEAT_GUI_X11
1668 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1669 is_menu_group = TRUE;
1670 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1671 is_scrollbar_group = TRUE;
1672 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1673 is_tooltip_group = TRUE;
1674#endif
1675
1676 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1677 if (doclear || (forceit && init))
1678 {
1679 highlight_clear(idx);
1680 if (!doclear)
1681 HL_TABLE()[idx].sg_set = 0;
1682 }
1683
1684 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001685 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001686 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001687 key_start = linep;
1688 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001689 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001690 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001691 error = TRUE;
1692 break;
1693 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001694
Yee Cheng China7b81202025-02-23 09:32:47 +01001695 // Note: Keep this in sync with get_highlight_group_key.
1696
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001697 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1698 // or "guibg").
1699 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1700 ++linep;
1701 vim_free(key);
1702 key = vim_strnsave_up(key_start, linep - key_start);
1703 if (key == NULL)
1704 {
1705 error = TRUE;
1706 break;
1707 }
1708 linep = skipwhite(linep);
1709
1710 if (STRCMP(key, "NONE") == 0)
1711 {
1712 if (!init || HL_TABLE()[idx].sg_set == 0)
1713 {
1714 if (!init)
1715 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1716 highlight_clear(idx);
1717 }
1718 continue;
1719 }
1720
1721 // Check for the equal sign.
1722 if (*linep != '=')
1723 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001724 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001725 error = TRUE;
1726 break;
1727 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001728 ++linep;
1729
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001730 // Isolate the argument.
1731 linep = skipwhite(linep);
1732 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001733 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001734 arg_start = ++linep;
1735 linep = vim_strchr(linep, '\'');
1736 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001737 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001738 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001739 error = TRUE;
1740 break;
1741 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001742 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001743 else
1744 {
1745 arg_start = linep;
1746 linep = skiptowhite(linep);
1747 }
1748 if (linep == arg_start)
1749 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001750 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001751 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001752 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001753 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001754 vim_free(arg);
1755 arg = vim_strnsave(arg_start, linep - arg_start);
1756 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001757 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001758 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001759 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001760 }
1761 if (*linep == '\'')
1762 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001763
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001764 // Store the argument.
1765 if (STRCMP(key, "TERM") == 0
1766 || STRCMP(key, "CTERM") == 0
1767 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001768 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001769 if (!highlight_set_termgui_attr(idx, key, arg, init))
1770 {
1771 error = TRUE;
1772 break;
1773 }
1774 }
1775 else if (STRCMP(key, "FONT") == 0)
1776 {
1777 // in non-GUI fonts are simply ignored
1778#ifdef FEAT_GUI
1779 if (highlight_set_font(idx, arg, is_normal_group,
1780 is_menu_group, is_tooltip_group))
1781 did_change = TRUE;
1782#endif
1783 }
1784 else if (STRCMP(key, "CTERMFG") == 0
1785 || STRCMP(key, "CTERMBG") == 0
1786 || STRCMP(key, "CTERMUL") == 0)
1787 {
1788 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1789 is_normal_group, init))
1790 {
1791 error = TRUE;
1792 break;
1793 }
1794 }
PMuncha606f3a2023-11-15 15:35:49 +01001795 else if (STRCMP(key, "CTERMFONT") == 0)
1796 {
1797 if (!highlight_set_cterm_font(idx, arg, init))
1798 {
1799 error = TRUE;
1800 break;
1801 }
1802 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001803 else if (STRCMP(key, "GUIFG") == 0)
1804 {
1805#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1806 if (highlight_set_guifg(idx, arg, is_menu_group,
1807 is_scrollbar_group, is_tooltip_group,
1808 &do_colors, init))
1809 did_change = TRUE;
1810#endif
1811 }
1812 else if (STRCMP(key, "GUIBG") == 0)
1813 {
1814#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1815 if (highlight_set_guibg(idx, arg, is_menu_group,
1816 is_scrollbar_group, is_tooltip_group,
1817 &do_colors, init))
1818 did_change = TRUE;
1819#endif
1820 }
1821 else if (STRCMP(key, "GUISP") == 0)
1822 {
1823#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1824 if (highlight_set_guisp(idx, arg, init))
1825 did_change = TRUE;
1826#endif
1827 }
1828 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1829 {
1830 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1831 {
1832 error = TRUE;
1833 break;
1834 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001835 }
1836 else
1837 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001838 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001839 error = TRUE;
1840 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001841 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001842 HL_TABLE()[idx].sg_cleared = FALSE;
1843
1844 // When highlighting has been given for a group, don't link it.
1845 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1846 HL_TABLE()[idx].sg_link = 0;
1847
1848 // Continue with next argument.
1849 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001850 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001851
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001852 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001853 if (error && idx == highlight_ga.ga_len)
1854 syn_unadd_group();
1855 else
1856 {
1857 if (is_normal_group)
1858 {
1859 HL_TABLE()[idx].sg_term_attr = 0;
1860 HL_TABLE()[idx].sg_cterm_attr = 0;
1861#ifdef FEAT_GUI
1862 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001863 // Need to update all groups, because they might be using "bg"
1864 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001865#endif
1866#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1867 if (USE_24BIT)
1868 {
1869 highlight_gui_started();
1870 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001871 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001872 }
1873#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001874#ifdef FEAT_VTP
1875 control_console_color_rgb();
1876#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001877 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001878#ifdef FEAT_GUI_X11
1879# ifdef FEAT_MENU
1880 else if (is_menu_group)
1881 {
1882 if (gui.in_use && do_colors)
1883 gui_mch_new_menu_colors();
1884 }
1885# endif
1886 else if (is_scrollbar_group)
1887 {
1888 if (gui.in_use && do_colors)
1889 gui_new_scrollbar_colors();
1890 else
1891 set_hl_attr(idx);
1892 }
1893# ifdef FEAT_BEVAL_GUI
1894 else if (is_tooltip_group)
1895 {
1896 if (gui.in_use && do_colors)
1897 gui_mch_new_tooltip_colors();
1898 }
1899# endif
1900#endif
1901 else
1902 set_hl_attr(idx);
1903#ifdef FEAT_EVAL
1904 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001905 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001906#endif
1907 }
1908
1909 vim_free(key);
1910 vim_free(arg);
1911
1912 // Only call highlight_changed() once, after a sequence of highlight
1913 // commands, and only if an attribute actually changed.
1914 if ((did_change
1915 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1916#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1917 && !did_highlight_changed
1918#endif
1919 )
1920 {
1921 // Do not trigger a redraw when highlighting is changed while
1922 // redrawing. This may happen when evaluating 'statusline' changes the
1923 // StatusLine group.
1924 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001925 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001926 need_highlight_changed = TRUE;
1927 }
1928}
1929
1930#if defined(EXITFREE) || defined(PROTO)
1931 void
1932free_highlight(void)
1933{
1934 int i;
1935
1936 for (i = 0; i < highlight_ga.ga_len; ++i)
1937 {
1938 highlight_clear(i);
1939 vim_free(HL_TABLE()[i].sg_name);
1940 vim_free(HL_TABLE()[i].sg_name_u);
1941 }
1942 ga_clear(&highlight_ga);
1943}
1944#endif
1945
1946/*
1947 * Reset the cterm colors to what they were before Vim was started, if
1948 * possible. Otherwise reset them to zero.
1949 */
1950 void
1951restore_cterm_colors(void)
1952{
1953#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1954 // Since t_me has been set, this probably means that the user
1955 // wants to use this as default colors. Need to reset default
1956 // background/foreground colors.
1957 mch_set_normal_colors();
1958#else
1959# ifdef VIMDLL
1960 if (!gui.in_use)
1961 {
1962 mch_set_normal_colors();
1963 return;
1964 }
1965# endif
1966 cterm_normal_fg_color = 0;
1967 cterm_normal_fg_bold = 0;
1968 cterm_normal_bg_color = 0;
1969# ifdef FEAT_TERMGUICOLORS
1970 cterm_normal_fg_gui_color = INVALCOLOR;
1971 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001972 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001973# endif
1974#endif
1975}
1976
1977/*
1978 * Return TRUE if highlight group "idx" has any settings.
1979 * When "check_link" is TRUE also check for an existing link.
1980 */
1981 static int
1982hl_has_settings(int idx, int check_link)
1983{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001984 return HL_TABLE()[idx].sg_cleared == 0
1985 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001986 || HL_TABLE()[idx].sg_cterm_attr != 0
1987 || HL_TABLE()[idx].sg_cterm_fg != 0
1988 || HL_TABLE()[idx].sg_cterm_bg != 0
PMuncha606f3a2023-11-15 15:35:49 +01001989 || HL_TABLE()[idx].sg_cterm_font != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001990#ifdef FEAT_GUI
1991 || HL_TABLE()[idx].sg_gui_attr != 0
1992 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1993 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1994 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1995 || HL_TABLE()[idx].sg_font_name != NULL
1996#endif
1997 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1998}
1999
2000/*
2001 * Clear highlighting for one group.
2002 */
2003 static void
2004highlight_clear(int idx)
2005{
2006 HL_TABLE()[idx].sg_cleared = TRUE;
2007
2008 HL_TABLE()[idx].sg_term = 0;
2009 VIM_CLEAR(HL_TABLE()[idx].sg_start);
2010 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
2011 HL_TABLE()[idx].sg_term_attr = 0;
2012 HL_TABLE()[idx].sg_cterm = 0;
2013 HL_TABLE()[idx].sg_cterm_bold = FALSE;
2014 HL_TABLE()[idx].sg_cterm_fg = 0;
2015 HL_TABLE()[idx].sg_cterm_bg = 0;
2016 HL_TABLE()[idx].sg_cterm_attr = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002017 HL_TABLE()[idx].sg_cterm_font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002018#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2019 HL_TABLE()[idx].sg_gui = 0;
2020 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
2021 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
2022 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
2023#endif
2024#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2025 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
2026 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002027 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002028#endif
2029#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002030 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2031 HL_TABLE()[idx].sg_font = NOFONT;
2032# ifdef FEAT_XFONTSET
2033 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2034 HL_TABLE()[idx].sg_fontset = NOFONTSET;
2035# endif
2036 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
2037 HL_TABLE()[idx].sg_gui_attr = 0;
2038#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02002039 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02002040 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02002041#ifdef FEAT_EVAL
2042 // Since we set the default link, set the location to where the default
2043 // link was set.
2044 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002045#endif
2046}
2047
2048#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2049/*
2050 * Set the normal foreground and background colors according to the "Normal"
2051 * highlighting group. For X11 also set "Menu", "Scrollbar", and
2052 * "Tooltip" colors.
2053 */
2054 void
2055set_normal_colors(void)
2056{
2057# ifdef FEAT_GUI
2058# ifdef FEAT_TERMGUICOLORS
2059 if (gui.in_use)
2060# endif
2061 {
2062 if (set_group_colors((char_u *)"Normal",
2063 &gui.norm_pixel, &gui.back_pixel,
2064 FALSE, TRUE, FALSE))
2065 {
2066 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002067 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002068 }
2069# ifdef FEAT_GUI_X11
2070 if (set_group_colors((char_u *)"Menu",
2071 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
2072 TRUE, FALSE, FALSE))
2073 {
2074# ifdef FEAT_MENU
2075 gui_mch_new_menu_colors();
2076# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002077 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002078 }
2079# ifdef FEAT_BEVAL_GUI
2080 if (set_group_colors((char_u *)"Tooltip",
2081 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
2082 FALSE, FALSE, TRUE))
2083 {
2084# ifdef FEAT_TOOLBAR
2085 gui_mch_new_tooltip_colors();
2086# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002087 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002088 }
2089# endif
2090 if (set_group_colors((char_u *)"Scrollbar",
2091 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
2092 FALSE, FALSE, FALSE))
2093 {
2094 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002095 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002096 }
2097# endif
2098 }
2099# endif
2100# ifdef FEAT_TERMGUICOLORS
2101# ifdef FEAT_GUI
2102 else
2103# endif
2104 {
2105 int idx;
2106
2107 idx = syn_name2id((char_u *)"Normal") - 1;
2108 if (idx >= 0)
2109 {
2110 gui_do_one_color(idx, FALSE, FALSE);
2111
2112 // If the normal fg or bg color changed a complete redraw is
2113 // required.
2114 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
2115 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
2116 {
2117 // if the GUI color is INVALCOLOR then we use the default cterm
2118 // color
2119 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
2120 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002121 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002122 }
2123 }
2124 }
2125# endif
2126}
2127#endif
2128
2129#if defined(FEAT_GUI) || defined(PROTO)
2130/*
2131 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2132 */
2133 static int
2134set_group_colors(
2135 char_u *name,
2136 guicolor_T *fgp,
2137 guicolor_T *bgp,
2138 int do_menu,
2139 int use_norm,
2140 int do_tooltip)
2141{
2142 int idx;
2143
2144 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002145 if (idx < 0)
2146 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002147
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002148 gui_do_one_color(idx, do_menu, do_tooltip);
2149
2150 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2151 *fgp = HL_TABLE()[idx].sg_gui_fg;
2152 else if (use_norm)
2153 *fgp = gui.def_norm_pixel;
2154 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2155 *bgp = HL_TABLE()[idx].sg_gui_bg;
2156 else if (use_norm)
2157 *bgp = gui.def_back_pixel;
2158 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002159}
2160
2161/*
2162 * Get the font of the "Normal" group.
2163 * Returns "" when it's not found or not set.
2164 */
2165 char_u *
2166hl_get_font_name(void)
2167{
2168 int id;
2169 char_u *s;
2170
2171 id = syn_name2id((char_u *)"Normal");
2172 if (id > 0)
2173 {
2174 s = HL_TABLE()[id - 1].sg_font_name;
2175 if (s != NULL)
2176 return s;
2177 }
2178 return (char_u *)"";
2179}
2180
2181/*
2182 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2183 * actually chosen to be used.
2184 */
2185 void
2186hl_set_font_name(char_u *font_name)
2187{
2188 int id;
2189
2190 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002191 if (id <= 0)
2192 return;
2193
2194 vim_free(HL_TABLE()[id - 1].sg_font_name);
2195 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002196}
2197
2198/*
2199 * Set background color for "Normal" group. Called by gui_set_bg_color()
2200 * when the color is known.
2201 */
2202 void
2203hl_set_bg_color_name(
2204 char_u *name) // must have been allocated
2205{
2206 int id;
2207
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002208 if (name == NULL)
2209 return;
2210
2211 id = syn_name2id((char_u *)"Normal");
2212 if (id <= 0)
2213 return;
2214
2215 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2216 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002217}
2218
2219/*
2220 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2221 * when the color is known.
2222 */
2223 void
2224hl_set_fg_color_name(
2225 char_u *name) // must have been allocated
2226{
2227 int id;
2228
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002229 if (name == NULL)
2230 return;
2231
2232 id = syn_name2id((char_u *)"Normal");
2233 if (id <= 0)
2234 return;
2235
2236 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2237 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002238}
2239
2240/*
2241 * Return the handle for a font name.
2242 * Returns NOFONT when failed.
2243 */
2244 static GuiFont
2245font_name2handle(char_u *name)
2246{
2247 if (STRCMP(name, "NONE") == 0)
2248 return NOFONT;
2249
2250 return gui_mch_get_font(name, TRUE);
2251}
2252
2253# ifdef FEAT_XFONTSET
2254/*
2255 * Return the handle for a fontset name.
2256 * Returns NOFONTSET when failed.
2257 */
2258 static GuiFontset
2259fontset_name2handle(char_u *name, int fixed_width)
2260{
2261 if (STRCMP(name, "NONE") == 0)
2262 return NOFONTSET;
2263
2264 return gui_mch_get_fontset(name, TRUE, fixed_width);
2265}
2266# endif
2267
2268/*
2269 * Get the font or fontset for one highlight group.
2270 */
2271 static void
2272hl_do_font(
2273 int idx,
2274 char_u *arg,
2275 int do_normal, // set normal font
2276 int do_menu UNUSED, // set menu font
2277 int do_tooltip UNUSED, // set tooltip font
2278 int free_font) // free current font/fontset
2279{
2280# ifdef FEAT_XFONTSET
2281 // If 'guifontset' is not empty, first try using the name as a
2282 // fontset. If that doesn't work, use it as a font name.
2283 if (*p_guifontset != NUL
2284# ifdef FONTSET_ALWAYS
2285 || do_menu
2286# endif
2287# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002288 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002289 || do_tooltip
2290# endif
2291 )
2292 {
2293 if (free_font)
2294 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2295 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2296# ifdef FONTSET_ALWAYS
2297 || do_menu
2298# endif
2299# ifdef FEAT_BEVAL_TIP
2300 || do_tooltip
2301# endif
2302 );
2303 }
2304 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2305 {
2306 // If it worked and it's the Normal group, use it as the normal
2307 // fontset. Same for the Menu group.
2308 if (do_normal)
2309 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002310# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002311 if (do_menu)
2312 {
2313# ifdef FONTSET_ALWAYS
2314 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2315# else
2316 // YIKES! This is a bug waiting to crash the program
2317 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2318# endif
2319 gui_mch_new_menu_font();
2320 }
2321# ifdef FEAT_BEVAL_GUI
2322 if (do_tooltip)
2323 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002324 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002325 // displaying a single font and a fontset.
2326 // If the XtNinternational resource is set to True at widget
2327 // creation, then a fontset is always used, otherwise an
2328 // XFontStruct is used.
2329 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2330 gui_mch_new_tooltip_font();
2331 }
2332# endif
2333# endif
2334 }
2335 else
2336# endif
2337 {
2338 if (free_font)
2339 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2340 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2341 // If it worked and it's the Normal group, use it as the
2342 // normal font. Same for the Menu group.
2343 if (HL_TABLE()[idx].sg_font != NOFONT)
2344 {
2345 if (do_normal)
2346 gui_init_font(arg, FALSE);
2347#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002348# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002349 if (do_menu)
2350 {
2351 gui.menu_font = HL_TABLE()[idx].sg_font;
2352 gui_mch_new_menu_font();
2353 }
2354# endif
2355#endif
2356 }
2357 }
2358}
2359
2360#endif // FEAT_GUI
2361
2362#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2363/*
2364 * Return the handle for a color name.
2365 * Returns INVALCOLOR when failed.
2366 */
2367 guicolor_T
2368color_name2handle(char_u *name)
2369{
2370 if (STRCMP(name, "NONE") == 0)
2371 return INVALCOLOR;
2372
2373 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2374 {
2375#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2376 if (gui.in_use)
2377#endif
2378#ifdef FEAT_GUI
2379 return gui.norm_pixel;
2380#endif
2381#ifdef FEAT_TERMGUICOLORS
2382 if (cterm_normal_fg_gui_color != INVALCOLOR)
2383 return cterm_normal_fg_gui_color;
2384 // Guess that the foreground is black or white.
2385 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2386#endif
2387 }
2388 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2389 {
2390#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2391 if (gui.in_use)
2392#endif
2393#ifdef FEAT_GUI
2394 return gui.back_pixel;
2395#endif
2396#ifdef FEAT_TERMGUICOLORS
2397 if (cterm_normal_bg_gui_color != INVALCOLOR)
2398 return cterm_normal_bg_gui_color;
2399 // Guess that the background is white or black.
2400 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2401#endif
2402 }
2403
2404 return GUI_GET_COLOR(name);
2405}
Drew Vogele30d1022021-10-24 20:35:07 +01002406
2407// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2408// values as used by the MS-Windows GDI api. It should be used only for
2409// MS-Windows GDI builds.
2410# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2411# undef RGB
2412# endif
2413# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002414# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002415# endif
2416
2417# ifdef VIMDLL
2418 static guicolor_T
2419gui_adjust_rgb(guicolor_T c)
2420{
2421 if (gui.in_use)
2422 return c;
2423 else
2424 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2425}
2426# else
2427# define gui_adjust_rgb(c) (c)
2428# endif
2429
2430 static int
2431hex_digit(int c)
2432{
Keith Thompson184f71c2024-01-04 21:19:04 +01002433 if (SAFE_isdigit(c))
Drew Vogele30d1022021-10-24 20:35:07 +01002434 return c - '0';
2435 c = TOLOWER_ASC(c);
2436 if (c >= 'a' && c <= 'f')
2437 return c - 'a' + 10;
2438 return 0x1ffffff;
2439}
2440
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002441 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002442decode_hex_color(char_u *hex)
2443{
2444 guicolor_T color;
2445
2446 if (hex[0] != '#' || STRLEN(hex) != 7)
2447 return INVALCOLOR;
2448
2449 // Name is in "#rrggbb" format
2450 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2451 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2452 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2453 if (color > 0xffffff)
2454 return INVALCOLOR;
2455 return gui_adjust_rgb(color);
2456}
2457
Bram Moolenaar2a521962021-10-25 10:30:14 +01002458#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002459// Returns the color currently mapped to the given name or INVALCOLOR if no
2460// such name exists in the color table. The convention is to use lowercase for
2461// all keys in the v:colornames dictionary. The value can be either a string in
2462// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002463 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002464colorname2rgb(char_u *name)
2465{
2466 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2467 char_u *lc_name;
2468 dictitem_T *colentry;
2469 char_u *colstr;
2470 varnumber_T colnum;
2471
2472 lc_name = strlow_save(name);
2473 if (lc_name == NULL)
2474 return INVALCOLOR;
2475
2476 colentry = dict_find(colornames_table, lc_name, -1);
2477 vim_free(lc_name);
2478 if (colentry == NULL)
2479 return INVALCOLOR;
2480
2481 if (colentry->di_tv.v_type == VAR_STRING)
2482 {
2483 colstr = tv_get_string_strict(&colentry->di_tv);
2484 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2485 {
2486 return decode_hex_color(colstr);
2487 }
2488 else
2489 {
2490 semsg(_(e_bad_color_string_str), colstr);
2491 return INVALCOLOR;
2492 }
2493 }
2494
2495 if (colentry->di_tv.v_type == VAR_NUMBER)
2496 {
2497 colnum = tv_get_number(&colentry->di_tv);
2498 return (guicolor_T)colnum;
2499 }
2500
2501 return INVALCOLOR;
2502}
2503
Drew Vogele30d1022021-10-24 20:35:07 +01002504#endif
2505
2506 guicolor_T
2507gui_get_color_cmn(char_u *name)
2508{
Drew Vogele30d1022021-10-24 20:35:07 +01002509 guicolor_T color;
Drew Vogele30d1022021-10-24 20:35:07 +01002510 // Only non X11 colors (not present in rgb.txt) and colors in
John Marriott34f00dd2024-04-08 23:28:12 +02002511 // color_name_tab[], useful when $VIMRUNTIME is not found,.
2512 // must be sorted by the 'value' field because it is used by bsearch()!
2513 static keyvalue_T rgb_tab[] = {
2514 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x00), "black"),
2515 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0xFF), "blue"),
2516 KEYVALUE_ENTRY(RGB(0xA5, 0x2A, 0x2A), "brown"),
2517 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0xFF), "cyan"),
2518 KEYVALUE_ENTRY(RGB(0x00, 0x00, 0x8B), "darkblue"),
2519 KEYVALUE_ENTRY(RGB(0x00, 0x8B, 0x8B), "darkcyan"),
2520 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgray"),
2521 KEYVALUE_ENTRY(RGB(0x00, 0x64, 0x00), "darkgreen"),
2522 KEYVALUE_ENTRY(RGB(0xA9, 0xA9, 0xA9), "darkgrey"),
2523 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x8B), "darkmagenta"),
2524 KEYVALUE_ENTRY(RGB(0x8B, 0x00, 0x00), "darkred"),
2525 KEYVALUE_ENTRY(RGB(0x8B, 0x8B, 0x00), "darkyellow"), // No X11
2526 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "gray"),
2527 KEYVALUE_ENTRY(RGB(0x00, 0xFF, 0x00), "green"),
2528 KEYVALUE_ENTRY(RGB(0xBE, 0xBE, 0xBE), "grey"),
2529 KEYVALUE_ENTRY(RGB(0x66, 0x66, 0x66), "grey40"),
2530 KEYVALUE_ENTRY(RGB(0x7F, 0x7F, 0x7F), "grey50"),
2531 KEYVALUE_ENTRY(RGB(0xE5, 0xE5, 0xE5), "grey90"),
2532 KEYVALUE_ENTRY(RGB(0xAD, 0xD8, 0xE6), "lightblue"),
2533 KEYVALUE_ENTRY(RGB(0xE0, 0xFF, 0xFF), "lightcyan"),
2534 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgray"),
2535 KEYVALUE_ENTRY(RGB(0x90, 0xEE, 0x90), "lightgreen"),
2536 KEYVALUE_ENTRY(RGB(0xD3, 0xD3, 0xD3), "lightgrey"),
2537 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0xFF), "lightmagenta"), // No XX
2538 KEYVALUE_ENTRY(RGB(0xFF, 0x8B, 0x8B), "lightred"), // No XX
2539 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xE0), "lightyellow"),
2540 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0xFF), "magenta"),
2541 KEYVALUE_ENTRY(RGB(0xFF, 0x00, 0x00), "red"),
2542 KEYVALUE_ENTRY(RGB(0x2E, 0x8B, 0x57), "seagreen"),
2543 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0xFF), "white"),
2544 KEYVALUE_ENTRY(RGB(0xFF, 0xFF, 0x00), "yellow")
Drew Vogele30d1022021-10-24 20:35:07 +01002545 };
John Marriott34f00dd2024-04-08 23:28:12 +02002546 keyvalue_T target;
2547 keyvalue_T *entry;
Drew Vogele30d1022021-10-24 20:35:07 +01002548
2549 color = decode_hex_color(name);
2550 if (color != INVALCOLOR)
2551 return color;
2552
John Marriott34f00dd2024-04-08 23:28:12 +02002553 target.key = 0;
John Marriott8d4477e2024-11-02 15:59:01 +01002554 target.value.string = name;
2555 target.value.length = 0; // not used, see cmp_keyvalue_value_i()
2556 entry = (keyvalue_T *)bsearch(&target, &rgb_tab, ARRAY_LENGTH(rgb_tab),
2557 sizeof(rgb_tab[0]), cmp_keyvalue_value_i);
John Marriott34f00dd2024-04-08 23:28:12 +02002558 if (entry != NULL)
John Marriott34f00dd2024-04-08 23:28:12 +02002559 return gui_adjust_rgb((guicolor_T)entry->key);
Drew Vogele30d1022021-10-24 20:35:07 +01002560
2561#if defined(FEAT_EVAL)
2562 /*
2563 * Not a traditional color. Load additional color aliases and then consult the alias table.
2564 */
2565
2566 color = colorname2rgb(name);
2567 if (color == INVALCOLOR)
2568 {
2569 load_default_colors_lists();
2570 color = colorname2rgb(name);
2571 }
2572
2573 return color;
2574#else
2575 return INVALCOLOR;
2576#endif
2577}
2578
2579 guicolor_T
2580gui_get_rgb_color_cmn(int r, int g, int b)
2581{
2582 guicolor_T color = RGB(r, g, b);
2583
2584 if (color > 0xffffff)
2585 return INVALCOLOR;
2586 return gui_adjust_rgb(color);
2587}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002588#endif
2589
2590/*
2591 * Table with the specifications for an attribute number.
2592 * Note that this table is used by ALL buffers. This is required because the
2593 * GUI can redraw at any time for any buffer.
2594 */
2595static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2596
2597#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2598
2599static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2600
2601#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2602
2603#ifdef FEAT_GUI
2604static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2605
2606#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2607#endif
2608
2609/*
2610 * Return the attr number for a set of colors and font.
2611 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2612 * if the combination is new.
2613 * Return 0 for error (no more room).
2614 */
2615 static int
2616get_attr_entry(garray_T *table, attrentry_T *aep)
2617{
2618 int i;
2619 attrentry_T *taep;
2620 static int recursive = FALSE;
2621
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002622 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002623 table->ga_itemsize = sizeof(attrentry_T);
2624 table->ga_growsize = 7;
2625
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002626 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002627 for (i = 0; i < table->ga_len; ++i)
2628 {
2629 taep = &(((attrentry_T *)table->ga_data)[i]);
2630 if ( aep->ae_attr == taep->ae_attr
2631 && (
2632#ifdef FEAT_GUI
2633 (table == &gui_attr_table
2634 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2635 && aep->ae_u.gui.bg_color
2636 == taep->ae_u.gui.bg_color
2637 && aep->ae_u.gui.sp_color
2638 == taep->ae_u.gui.sp_color
2639 && aep->ae_u.gui.font == taep->ae_u.gui.font
2640# ifdef FEAT_XFONTSET
2641 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2642# endif
2643 ))
2644 ||
2645#endif
2646 (table == &term_attr_table
2647 && (aep->ae_u.term.start == NULL)
2648 == (taep->ae_u.term.start == NULL)
2649 && (aep->ae_u.term.start == NULL
2650 || STRCMP(aep->ae_u.term.start,
2651 taep->ae_u.term.start) == 0)
2652 && (aep->ae_u.term.stop == NULL)
2653 == (taep->ae_u.term.stop == NULL)
2654 && (aep->ae_u.term.stop == NULL
2655 || STRCMP(aep->ae_u.term.stop,
2656 taep->ae_u.term.stop) == 0))
2657 || (table == &cterm_attr_table
2658 && aep->ae_u.cterm.fg_color
2659 == taep->ae_u.cterm.fg_color
2660 && aep->ae_u.cterm.bg_color
2661 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002662 && aep->ae_u.cterm.ul_color
2663 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002664 && aep->ae_u.cterm.font
2665 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002666#ifdef FEAT_TERMGUICOLORS
2667 && aep->ae_u.cterm.fg_rgb
2668 == taep->ae_u.cterm.fg_rgb
2669 && aep->ae_u.cterm.bg_rgb
2670 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002671 && aep->ae_u.cterm.ul_rgb
2672 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002673#endif
2674 )))
2675
2676 return i + ATTR_OFF;
2677 }
2678
2679 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2680 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002681 // Running out of attribute entries! remove all attributes, and
2682 // compute new ones for all groups.
2683 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002684 if (recursive)
2685 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002686 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002687 return 0;
2688 }
2689 recursive = TRUE;
2690
2691 clear_hl_tables();
2692
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002693 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002694
2695 for (i = 0; i < highlight_ga.ga_len; ++i)
2696 set_hl_attr(i);
2697
2698 recursive = FALSE;
2699 }
2700
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002701 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002702 if (ga_grow(table, 1) == FAIL)
2703 return 0;
2704
2705 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002706 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002707 taep->ae_attr = aep->ae_attr;
2708#ifdef FEAT_GUI
2709 if (table == &gui_attr_table)
2710 {
2711 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2712 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2713 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2714 taep->ae_u.gui.font = aep->ae_u.gui.font;
2715# ifdef FEAT_XFONTSET
2716 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2717# endif
2718 }
2719#endif
2720 if (table == &term_attr_table)
2721 {
2722 if (aep->ae_u.term.start == NULL)
2723 taep->ae_u.term.start = NULL;
2724 else
2725 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2726 if (aep->ae_u.term.stop == NULL)
2727 taep->ae_u.term.stop = NULL;
2728 else
2729 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2730 }
2731 else if (table == &cterm_attr_table)
2732 {
2733 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2734 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002735 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002736 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002737#ifdef FEAT_TERMGUICOLORS
2738 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2739 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002740 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002741#endif
2742 }
2743 ++table->ga_len;
2744 return (table->ga_len - 1 + ATTR_OFF);
2745}
2746
2747#if defined(FEAT_TERMINAL) || defined(PROTO)
2748/*
2749 * Get an attribute index for a cterm entry.
2750 * Uses an existing entry when possible or adds one when needed.
2751 */
2752 int
2753get_cterm_attr_idx(int attr, int fg, int bg)
2754{
2755 attrentry_T at_en;
2756
Bram Moolenaara80faa82020-04-12 19:37:17 +02002757 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002758#ifdef FEAT_TERMGUICOLORS
2759 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2760 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002761 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002762#endif
2763 at_en.ae_attr = attr;
2764 at_en.ae_u.cterm.fg_color = fg;
2765 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002766 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002767 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002768 return get_attr_entry(&cterm_attr_table, &at_en);
2769}
2770#endif
2771
2772#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2773/*
2774 * Get an attribute index for a 'termguicolors' entry.
2775 * Uses an existing entry when possible or adds one when needed.
2776 */
2777 int
2778get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2779{
2780 attrentry_T at_en;
2781
Bram Moolenaara80faa82020-04-12 19:37:17 +02002782 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002783 at_en.ae_attr = attr;
2784 if (fg == INVALCOLOR && bg == INVALCOLOR)
2785 {
2786 // If both GUI colors are not set fall back to the cterm colors. Helps
2787 // if the GUI only has an attribute, such as undercurl.
2788 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2789 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2790 }
2791 else
2792 {
2793 at_en.ae_u.cterm.fg_rgb = fg;
2794 at_en.ae_u.cterm.bg_rgb = bg;
2795 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002796 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002797 return get_attr_entry(&cterm_attr_table, &at_en);
2798}
2799#endif
2800
2801#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2802/*
2803 * Get an attribute index for a cterm entry.
2804 * Uses an existing entry when possible or adds one when needed.
2805 */
2806 int
2807get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2808{
2809 attrentry_T at_en;
2810
Bram Moolenaara80faa82020-04-12 19:37:17 +02002811 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002812 at_en.ae_attr = attr;
2813 at_en.ae_u.gui.fg_color = fg;
2814 at_en.ae_u.gui.bg_color = bg;
2815 return get_attr_entry(&gui_attr_table, &at_en);
2816}
2817#endif
2818
2819/*
2820 * Clear all highlight tables.
2821 */
2822 void
2823clear_hl_tables(void)
2824{
2825 int i;
2826 attrentry_T *taep;
2827
2828#ifdef FEAT_GUI
2829 ga_clear(&gui_attr_table);
2830#endif
2831 for (i = 0; i < term_attr_table.ga_len; ++i)
2832 {
2833 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2834 vim_free(taep->ae_u.term.start);
2835 vim_free(taep->ae_u.term.stop);
2836 }
2837 ga_clear(&term_attr_table);
2838 ga_clear(&cterm_attr_table);
2839}
2840
2841/*
2842 * Combine special attributes (e.g., for spelling) with other attributes
2843 * (e.g., for syntax highlighting).
2844 * "prim_attr" overrules "char_attr".
2845 * This creates a new group when required.
2846 * Since we expect there to be few spelling mistakes we don't cache the
2847 * result.
2848 * Return the resulting attributes.
2849 */
2850 int
2851hl_combine_attr(int char_attr, int prim_attr)
2852{
2853 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002854 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002855 attrentry_T new_en;
2856
2857 if (char_attr == 0)
2858 return prim_attr;
2859 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2860 return ATTR_COMBINE(char_attr, prim_attr);
2861#ifdef FEAT_GUI
2862 if (gui.in_use)
2863 {
2864 if (char_attr > HL_ALL)
2865 char_aep = syn_gui_attr2entry(char_attr);
2866 if (char_aep != NULL)
2867 new_en = *char_aep;
2868 else
2869 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002870 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002871 new_en.ae_u.gui.fg_color = INVALCOLOR;
2872 new_en.ae_u.gui.bg_color = INVALCOLOR;
2873 new_en.ae_u.gui.sp_color = INVALCOLOR;
2874 if (char_attr <= HL_ALL)
2875 new_en.ae_attr = char_attr;
2876 }
2877
2878 if (prim_attr <= HL_ALL)
2879 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2880 else
2881 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002882 prim_aep = syn_gui_attr2entry(prim_attr);
2883 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002884 {
2885 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002886 prim_aep->ae_attr);
2887 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2888 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2889 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2890 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2891 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2892 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2893 if (prim_aep->ae_u.gui.font != NOFONT)
2894 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002895# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002896 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2897 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002898# endif
2899 }
2900 }
2901 return get_attr_entry(&gui_attr_table, &new_en);
2902 }
2903#endif
2904
2905 if (IS_CTERM)
2906 {
2907 if (char_attr > HL_ALL)
2908 char_aep = syn_cterm_attr2entry(char_attr);
2909 if (char_aep != NULL)
2910 new_en = *char_aep;
2911 else
2912 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002913 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002914#ifdef FEAT_TERMGUICOLORS
2915 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2916 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002917 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002918#endif
2919 if (char_attr <= HL_ALL)
2920 new_en.ae_attr = char_attr;
2921 }
2922
2923 if (prim_attr <= HL_ALL)
2924 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2925 else
2926 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002927 prim_aep = syn_cterm_attr2entry(prim_attr);
2928 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002929 {
2930 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002931 prim_aep->ae_attr);
2932 if (prim_aep->ae_u.cterm.fg_color > 0)
2933 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2934 if (prim_aep->ae_u.cterm.bg_color > 0)
2935 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2936 if (prim_aep->ae_u.cterm.ul_color > 0)
2937 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002938 if (prim_aep->ae_u.cterm.font > 0)
2939 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002940#ifdef FEAT_TERMGUICOLORS
2941 // If both fg and bg are not set fall back to cterm colors.
2942 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002943 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2944 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002945 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002946 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002947 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002948 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002949 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2950 }
2951 else
2952 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002953 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2954 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2955 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2956 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002957 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002958 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2959 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002960#endif
2961 }
2962 }
2963 return get_attr_entry(&cterm_attr_table, &new_en);
2964 }
2965
2966 if (char_attr > HL_ALL)
2967 char_aep = syn_term_attr2entry(char_attr);
2968 if (char_aep != NULL)
2969 new_en = *char_aep;
2970 else
2971 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002972 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002973 if (char_attr <= HL_ALL)
2974 new_en.ae_attr = char_attr;
2975 }
2976
2977 if (prim_attr <= HL_ALL)
2978 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2979 else
2980 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002981 prim_aep = syn_term_attr2entry(prim_attr);
2982 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002983 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002984 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2985 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002986 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002987 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2988 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002989 }
2990 }
2991 }
2992 return get_attr_entry(&term_attr_table, &new_en);
2993}
2994
2995#ifdef FEAT_GUI
2996 attrentry_T *
2997syn_gui_attr2entry(int attr)
2998{
2999 attr -= ATTR_OFF;
3000 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
3001 return NULL;
3002 return &(GUI_ATTR_ENTRY(attr));
3003}
3004#endif
3005
3006/*
3007 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
3008 * Only to be used when "attr" > HL_ALL.
3009 */
3010 int
3011syn_attr2attr(int attr)
3012{
3013 attrentry_T *aep;
3014
3015#ifdef FEAT_GUI
3016 if (gui.in_use)
3017 aep = syn_gui_attr2entry(attr);
3018 else
3019#endif
3020 if (IS_CTERM)
3021 aep = syn_cterm_attr2entry(attr);
3022 else
3023 aep = syn_term_attr2entry(attr);
3024
3025 if (aep == NULL) // highlighting not set
3026 return 0;
3027 return aep->ae_attr;
3028}
3029
3030
3031 attrentry_T *
3032syn_term_attr2entry(int attr)
3033{
3034 attr -= ATTR_OFF;
3035 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
3036 return NULL;
3037 return &(TERM_ATTR_ENTRY(attr));
3038}
3039
3040 attrentry_T *
3041syn_cterm_attr2entry(int attr)
3042{
3043 attr -= ATTR_OFF;
3044 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
3045 return NULL;
3046 return &(CTERM_ATTR_ENTRY(attr));
3047}
3048
3049#define LIST_ATTR 1
3050#define LIST_STRING 2
3051#define LIST_INT 3
3052
3053 static void
3054highlight_list_one(int id)
3055{
3056 hl_group_T *sgp;
3057 int didh = FALSE;
3058
3059 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
3060
3061 if (message_filtered(sgp->sg_name))
3062 return;
3063
Yee Cheng China7b81202025-02-23 09:32:47 +01003064 // Note: Keep this in sync with expand_highlight_group().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003065 didh = highlight_list_arg(id, didh, LIST_ATTR,
3066 sgp->sg_term, NULL, "term");
3067 didh = highlight_list_arg(id, didh, LIST_STRING,
3068 0, sgp->sg_start, "start");
3069 didh = highlight_list_arg(id, didh, LIST_STRING,
3070 0, sgp->sg_stop, "stop");
3071
3072 didh = highlight_list_arg(id, didh, LIST_ATTR,
3073 sgp->sg_cterm, NULL, "cterm");
3074 didh = highlight_list_arg(id, didh, LIST_INT,
3075 sgp->sg_cterm_fg, NULL, "ctermfg");
3076 didh = highlight_list_arg(id, didh, LIST_INT,
3077 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02003078 didh = highlight_list_arg(id, didh, LIST_INT,
3079 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01003080 didh = highlight_list_arg(id, didh, LIST_INT,
3081 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003082
3083#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3084 didh = highlight_list_arg(id, didh, LIST_ATTR,
3085 sgp->sg_gui, NULL, "gui");
3086 didh = highlight_list_arg(id, didh, LIST_STRING,
3087 0, sgp->sg_gui_fg_name, "guifg");
3088 didh = highlight_list_arg(id, didh, LIST_STRING,
3089 0, sgp->sg_gui_bg_name, "guibg");
3090 didh = highlight_list_arg(id, didh, LIST_STRING,
3091 0, sgp->sg_gui_sp_name, "guisp");
3092#endif
3093#ifdef FEAT_GUI
3094 didh = highlight_list_arg(id, didh, LIST_STRING,
3095 0, sgp->sg_font_name, "font");
3096#endif
3097
3098 if (sgp->sg_link && !got_int)
3099 {
3100 (void)syn_list_header(didh, 9999, id);
3101 didh = TRUE;
3102 msg_puts_attr("links to", HL_ATTR(HLF_D));
3103 msg_putchar(' ');
3104 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3105 }
3106
3107 if (!didh)
3108 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3109#ifdef FEAT_EVAL
3110 if (p_verbose > 0)
3111 last_set_msg(sgp->sg_script_ctx);
3112#endif
3113}
3114
Yee Cheng China7b81202025-02-23 09:32:47 +01003115 static char_u*
3116highlight_arg_to_string(
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003117 int type,
3118 int iarg,
3119 char_u *sarg,
Yee Cheng China7b81202025-02-23 09:32:47 +01003120 char_u *buf)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003121{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003122 if (type == LIST_INT)
3123 sprintf((char *)buf, "%d", iarg - 1);
3124 else if (type == LIST_STRING)
Yee Cheng China7b81202025-02-23 09:32:47 +01003125 return sarg;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003126 else // type == LIST_ATTR
3127 {
John Marriott34f00dd2024-04-08 23:28:12 +02003128 size_t buflen;
3129
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003130 buf[0] = NUL;
John Marriott34f00dd2024-04-08 23:28:12 +02003131 buflen = 0;
Yee Cheng China7b81202025-02-23 09:32:47 +01003132 for (int i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003133 {
John Marriott34f00dd2024-04-08 23:28:12 +02003134 if (iarg & highlight_index_tab[i]->key)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003135 {
John Marriott34f00dd2024-04-08 23:28:12 +02003136 if (buflen > 0)
3137 {
3138 STRCPY(buf + buflen, (char_u *)",");
3139 ++buflen;
3140 }
John Marriott8d4477e2024-11-02 15:59:01 +01003141 STRCPY(buf + buflen, highlight_index_tab[i]->value.string);
3142 buflen += highlight_index_tab[i]->value.length;
John Marriott34f00dd2024-04-08 23:28:12 +02003143 iarg &= ~highlight_index_tab[i]->key; // don't want "inverse"/"reverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003144 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003145 }
3146 }
Yee Cheng China7b81202025-02-23 09:32:47 +01003147 return buf;
3148}
3149
3150 static int
3151highlight_list_arg(
3152 int id,
3153 int didh,
3154 int type,
3155 int iarg,
3156 char_u *sarg,
3157 char *name)
3158{
3159 char_u buf[MAX_ATTR_LEN];
3160 char_u *ts;
3161
3162 if (got_int)
3163 return FALSE;
3164
3165 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3166 return didh;
3167
3168 ts = highlight_arg_to_string(type, iarg, sarg, buf);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003169
3170 (void)syn_list_header(didh,
3171 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3172 didh = TRUE;
3173 if (!got_int)
3174 {
3175 if (*name != NUL)
3176 {
3177 msg_puts_attr(name, HL_ATTR(HLF_D));
3178 msg_puts_attr("=", HL_ATTR(HLF_D));
3179 }
3180 msg_outtrans(ts);
3181 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003182 return didh;
3183}
3184
3185#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3186/*
3187 * Return "1" if highlight group "id" has attribute "flag".
3188 * Return NULL otherwise.
3189 */
3190 char_u *
3191highlight_has_attr(
3192 int id,
3193 int flag,
3194 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3195{
3196 int attr;
3197
3198 if (id <= 0 || id > highlight_ga.ga_len)
3199 return NULL;
3200
3201#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3202 if (modec == 'g')
3203 attr = HL_TABLE()[id - 1].sg_gui;
3204 else
3205#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003206 {
3207 if (modec == 'c')
3208 attr = HL_TABLE()[id - 1].sg_cterm;
3209 else
3210 attr = HL_TABLE()[id - 1].sg_term;
3211 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003212
3213 if (attr & flag)
3214 return (char_u *)"1";
3215 return NULL;
3216}
3217#endif
3218
3219#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3220/*
3221 * Return color name of highlight group "id".
3222 */
3223 char_u *
3224highlight_color(
3225 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003226 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003227 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3228{
3229 static char_u name[20];
3230 int n;
3231 int fg = FALSE;
3232 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003233 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003234 int font = FALSE;
3235
3236 if (id <= 0 || id > highlight_ga.ga_len)
3237 return NULL;
3238
3239 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3240 fg = TRUE;
3241 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3242 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3243 font = TRUE;
3244 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3245 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003246 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3247 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003248 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3249 return NULL;
3250 if (modec == 'g')
3251 {
3252# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3253# ifdef FEAT_GUI
3254 // return font name
3255 if (font)
3256 return HL_TABLE()[id - 1].sg_font_name;
3257# endif
3258
3259 // return #RRGGBB form (only possible when GUI is running)
3260 if ((USE_24BIT) && what[2] == '#')
3261 {
3262 guicolor_T color;
3263 long_u rgb;
3264 static char_u buf[10];
3265
3266 if (fg)
3267 color = HL_TABLE()[id - 1].sg_gui_fg;
3268 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003269 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003270 else
3271 color = HL_TABLE()[id - 1].sg_gui_bg;
3272 if (color == INVALCOLOR)
3273 return NULL;
3274 rgb = (long_u)GUI_MCH_GET_RGB(color);
3275 sprintf((char *)buf, "#%02x%02x%02x",
3276 (unsigned)(rgb >> 16),
3277 (unsigned)(rgb >> 8) & 255,
3278 (unsigned)rgb & 255);
3279 return buf;
3280 }
3281# endif
3282 if (fg)
3283 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3284 if (sp)
3285 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3286 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3287 }
PMuncha606f3a2023-11-15 15:35:49 +01003288 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003289 return NULL;
3290 if (modec == 'c')
3291 {
3292 if (fg)
3293 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003294 else if (ul)
3295 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003296 else if (font)
3297 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003298 else
3299 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3300 if (n < 0)
3301 return NULL;
3302 sprintf((char *)name, "%d", n);
3303 return name;
3304 }
3305 // term doesn't have color
3306 return NULL;
3307}
3308#endif
3309
3310#if (defined(FEAT_SYN_HL) \
3311 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3312 && defined(FEAT_PRINTER)) || defined(PROTO)
3313/*
3314 * Return color name of highlight group "id" as RGB value.
3315 */
3316 long_u
3317highlight_gui_color_rgb(
3318 int id,
3319 int fg) // TRUE = fg, FALSE = bg
3320{
3321 guicolor_T color;
3322
3323 if (id <= 0 || id > highlight_ga.ga_len)
3324 return 0L;
3325
3326 if (fg)
3327 color = HL_TABLE()[id - 1].sg_gui_fg;
3328 else
3329 color = HL_TABLE()[id - 1].sg_gui_bg;
3330
3331 if (color == INVALCOLOR)
3332 return 0L;
3333
3334 return GUI_MCH_GET_RGB(color);
3335}
3336#endif
3337
3338/*
3339 * Output the syntax list header.
3340 * Return TRUE when started a new line.
3341 */
3342 int
3343syn_list_header(
3344 int did_header, // did header already
3345 int outlen, // length of string that comes
3346 int id) // highlight group id
3347{
3348 int endcol = 19;
3349 int newline = TRUE;
3350 int name_col = 0;
3351
3352 if (!did_header)
3353 {
3354 msg_putchar('\n');
3355 if (got_int)
3356 return TRUE;
3357 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3358 name_col = msg_col;
3359 endcol = 15;
3360 }
3361 else if (msg_col + outlen + 1 >= Columns)
3362 {
3363 msg_putchar('\n');
3364 if (got_int)
3365 return TRUE;
3366 }
3367 else
3368 {
3369 if (msg_col >= endcol) // wrap around is like starting a new line
3370 newline = FALSE;
3371 }
3372
3373 if (msg_col >= endcol) // output at least one space
3374 endcol = msg_col + 1;
Christian Brabandt220474d2024-07-20 13:26:44 +02003375 if (Columns <= (long)endcol) // avoid hang for tiny window
3376 endcol = (int)(Columns - 1);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003377
3378 msg_advance(endcol);
3379
3380 // Show "xxx" with the attributes.
3381 if (!did_header)
3382 {
3383 if (endcol == Columns - 1 && endcol <= name_col)
3384 msg_putchar(' ');
3385 msg_puts_attr("xxx", syn_id2attr(id));
3386 msg_putchar(' ');
3387 }
3388
3389 return newline;
3390}
3391
3392/*
3393 * Set the attribute numbers for a highlight group.
3394 * Called after one of the attributes has changed.
3395 */
3396 static void
3397set_hl_attr(
3398 int idx) // index in array
3399{
3400 attrentry_T at_en;
3401 hl_group_T *sgp = HL_TABLE() + idx;
3402
3403 // The "Normal" group doesn't need an attribute number
3404 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3405 return;
3406
3407#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003408 // For the GUI mode: If there are other than "normal" highlighting
3409 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003410 if (sgp->sg_gui_fg == INVALCOLOR
3411 && sgp->sg_gui_bg == INVALCOLOR
3412 && sgp->sg_gui_sp == INVALCOLOR
3413 && sgp->sg_font == NOFONT
3414# ifdef FEAT_XFONTSET
3415 && sgp->sg_fontset == NOFONTSET
3416# endif
3417 )
3418 {
3419 sgp->sg_gui_attr = sgp->sg_gui;
3420 }
3421 else
3422 {
3423 at_en.ae_attr = sgp->sg_gui;
3424 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3425 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3426 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3427 at_en.ae_u.gui.font = sgp->sg_font;
3428# ifdef FEAT_XFONTSET
3429 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3430# endif
3431 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3432 }
3433#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003434 // For the term mode: If there are other than "normal" highlighting
3435 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003436 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3437 sgp->sg_term_attr = sgp->sg_term;
3438 else
3439 {
3440 at_en.ae_attr = sgp->sg_term;
3441 at_en.ae_u.term.start = sgp->sg_start;
3442 at_en.ae_u.term.stop = sgp->sg_stop;
3443 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3444 }
3445
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003446 // For the color term mode: If there are other than "normal"
3447 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003448 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3449 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003450# ifdef FEAT_TERMGUICOLORS
3451 && sgp->sg_gui_fg == INVALCOLOR
3452 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003453 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003454# endif
3455 )
3456 sgp->sg_cterm_attr = sgp->sg_cterm;
3457 else
3458 {
3459 at_en.ae_attr = sgp->sg_cterm;
3460 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3461 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003462 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003463 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003464# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003465 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3466 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003467 // Only use the underline/undercurl color when used, it may clear the
3468 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003469 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3470 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003471 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3472 else
3473 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003474 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3475 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3476 {
3477 // If both fg and bg are invalid fall back to the cterm colors.
3478 // Helps when the GUI only uses an attribute, e.g. undercurl.
3479 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3480 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3481 }
3482# endif
3483 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3484 }
3485}
3486
3487/*
3488 * Lookup a highlight group name and return its ID.
3489 * If it is not found, 0 is returned.
3490 */
3491 int
3492syn_name2id(char_u *name)
3493{
3494 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003495 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003496
3497 // Avoid using stricmp() too much, it's slow on some systems
3498 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3499 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003500 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003501 vim_strup(name_u);
3502 for (i = highlight_ga.ga_len; --i >= 0; )
3503 if (HL_TABLE()[i].sg_name_u != NULL
3504 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3505 break;
3506 return i + 1;
3507}
3508
3509/*
3510 * Lookup a highlight group name and return its attributes.
3511 * Return zero if not found.
3512 */
3513 int
3514syn_name2attr(char_u *name)
3515{
3516 int id = syn_name2id(name);
3517
3518 if (id != 0)
3519 return syn_id2attr(id);
3520 return 0;
3521}
3522
3523#if defined(FEAT_EVAL) || defined(PROTO)
3524/*
3525 * Return TRUE if highlight group "name" exists.
3526 */
3527 int
3528highlight_exists(char_u *name)
3529{
3530 return (syn_name2id(name) > 0);
3531}
3532
3533# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3534/*
3535 * Return the name of highlight group "id".
3536 * When not a valid ID return an empty string.
3537 */
3538 char_u *
3539syn_id2name(int id)
3540{
3541 if (id <= 0 || id > highlight_ga.ga_len)
3542 return (char_u *)"";
3543 return HL_TABLE()[id - 1].sg_name;
3544}
3545# endif
3546#endif
3547
3548/*
3549 * Like syn_name2id(), but take a pointer + length argument.
3550 */
3551 int
3552syn_namen2id(char_u *linep, int len)
3553{
3554 char_u *name;
3555 int id = 0;
3556
3557 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003558 if (name == NULL)
3559 return 0;
3560
3561 id = syn_name2id(name);
3562 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003563 return id;
3564}
3565
3566/*
3567 * Find highlight group name in the table and return its ID.
3568 * The argument is a pointer to the name and the length of the name.
3569 * If it doesn't exist yet, a new entry is created.
3570 * Return 0 for failure.
3571 */
3572 int
3573syn_check_group(char_u *pp, int len)
3574{
3575 int id;
3576 char_u *name;
3577
erw7f7f7aaf2021-12-07 21:29:20 +00003578 if (len > MAX_SYN_NAME)
3579 {
3580 emsg(_(e_highlight_group_name_too_long));
3581 return 0;
3582 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003583 name = vim_strnsave(pp, len);
3584 if (name == NULL)
3585 return 0;
3586
3587 id = syn_name2id(name);
3588 if (id == 0) // doesn't exist yet
3589 id = syn_add_group(name);
3590 else
3591 vim_free(name);
3592 return id;
3593}
3594
3595/*
3596 * Add new highlight group and return its ID.
3597 * "name" must be an allocated string, it will be consumed.
3598 * Return 0 for failure.
3599 */
3600 static int
3601syn_add_group(char_u *name)
3602{
3603 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003604 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003605
Gregory Andersd4376dc2023-08-20 19:14:03 +02003606 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003607 for (p = name; *p != NUL; ++p)
3608 {
3609 if (!vim_isprintc(*p))
3610 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003611 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003612 vim_free(name);
3613 return 0;
3614 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003615 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003616 {
3617 // This is an error, but since there previously was no check only
3618 // give a warning.
3619 msg_source(HL_ATTR(HLF_W));
3620 msg(_("W18: Invalid character in group name"));
3621 break;
3622 }
3623 }
3624
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003625 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003626 if (highlight_ga.ga_data == NULL)
3627 {
3628 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3629 highlight_ga.ga_growsize = 10;
3630 }
3631
3632 if (highlight_ga.ga_len >= MAX_HL_ID)
3633 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003634 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003635 vim_free(name);
3636 return 0;
3637 }
3638
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003639 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003640 if (ga_grow(&highlight_ga, 1) == FAIL)
3641 {
3642 vim_free(name);
3643 return 0;
3644 }
3645
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003646 name_up = vim_strsave_up(name);
3647 if (name_up == NULL)
3648 {
3649 vim_free(name);
3650 return 0;
3651 }
3652
Bram Moolenaara80faa82020-04-12 19:37:17 +02003653 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003654 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003655 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003656#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3657 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3658 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003659 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003660#endif
3661 ++highlight_ga.ga_len;
3662
3663 return highlight_ga.ga_len; // ID is index plus one
3664}
3665
3666/*
3667 * When, just after calling syn_add_group(), an error is discovered, this
3668 * function deletes the new name.
3669 */
3670 static void
3671syn_unadd_group(void)
3672{
3673 --highlight_ga.ga_len;
3674 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3675 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3676}
3677
3678/*
3679 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003680 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003681 */
3682 int
3683syn_id2attr(int hl_id)
3684{
3685 int attr;
3686 hl_group_T *sgp;
3687
3688 hl_id = syn_get_final_id(hl_id);
3689 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3690
3691#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003692 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003693 if (gui.in_use)
3694 attr = sgp->sg_gui_attr;
3695 else
3696#endif
3697 if (IS_CTERM)
3698 attr = sgp->sg_cterm_attr;
3699 else
3700 attr = sgp->sg_term_attr;
3701
3702 return attr;
3703}
3704
3705#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3706/*
3707 * Get the GUI colors and attributes for a group ID.
3708 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3709 */
3710 int
3711syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3712{
3713 hl_group_T *sgp;
3714
3715 hl_id = syn_get_final_id(hl_id);
3716 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3717
3718 *fgp = sgp->sg_gui_fg;
3719 *bgp = sgp->sg_gui_bg;
3720 return sgp->sg_gui;
3721}
3722#endif
3723
3724#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003725 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3726 && defined(FEAT_TERMGUICOLORS)) \
3727 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003728 void
3729syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3730{
3731 hl_group_T *sgp;
3732
3733 hl_id = syn_get_final_id(hl_id);
3734 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3735 *fgp = sgp->sg_cterm_fg - 1;
3736 *bgp = sgp->sg_cterm_bg - 1;
3737}
3738#endif
3739
3740/*
3741 * Translate a group ID to the final group ID (following links).
3742 */
3743 int
3744syn_get_final_id(int hl_id)
3745{
3746 int count;
3747 hl_group_T *sgp;
3748
3749 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3750 return 0; // Can be called from eval!!
3751
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003752 // Follow links until there is no more.
3753 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003754 for (count = 100; --count >= 0; )
3755 {
3756 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3757 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3758 break;
3759 hl_id = sgp->sg_link;
3760 }
3761
3762 return hl_id;
3763}
3764
3765#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3766/*
3767 * Call this function just after the GUI has started.
3768 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3769 * It finds the font and color handles for the highlighting groups.
3770 */
3771 void
3772highlight_gui_started(void)
3773{
3774 int idx;
3775
3776 // First get the colors from the "Normal" and "Menu" group, if set
3777 if (USE_24BIT)
3778 set_normal_colors();
3779
3780 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3781 gui_do_one_color(idx, FALSE, FALSE);
3782
3783 highlight_changed();
3784}
3785
3786 static void
3787gui_do_one_color(
3788 int idx,
3789 int do_menu UNUSED, // TRUE: might set the menu font
3790 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3791{
3792 int didit = FALSE;
3793
3794# ifdef FEAT_GUI
3795# ifdef FEAT_TERMGUICOLORS
3796 if (gui.in_use)
3797# endif
3798 if (HL_TABLE()[idx].sg_font_name != NULL)
3799 {
3800 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3801 do_tooltip, TRUE);
3802 didit = TRUE;
3803 }
3804# endif
3805 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3806 {
3807 HL_TABLE()[idx].sg_gui_fg =
3808 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3809 didit = TRUE;
3810 }
3811 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3812 {
3813 HL_TABLE()[idx].sg_gui_bg =
3814 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3815 didit = TRUE;
3816 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003817 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3818 {
3819 HL_TABLE()[idx].sg_gui_sp =
3820 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3821 didit = TRUE;
3822 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003823 if (didit) // need to get a new attr number
3824 set_hl_attr(idx);
3825}
3826#endif
3827
3828#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3829/*
3830 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3831 */
3832 static void
3833combine_stl_hlt(
3834 int id,
3835 int id_S,
3836 int id_alt,
3837 int hlcnt,
3838 int i,
3839 int hlf,
3840 int *table)
3841{
3842 hl_group_T *hlt = HL_TABLE();
3843
3844 if (id_alt == 0)
3845 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003846 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003847 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3848 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3849# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3850 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3851# endif
3852 }
3853 else
3854 mch_memmove(&hlt[hlcnt + i],
3855 &hlt[id_alt - 1],
3856 sizeof(hl_group_T));
3857 hlt[hlcnt + i].sg_link = 0;
3858
3859 hlt[hlcnt + i].sg_term ^=
3860 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3861 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3862 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3863 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3864 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3865 hlt[hlcnt + i].sg_cterm ^=
3866 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3867 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3868 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3869 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3870 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003871 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3872 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003873# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3874 hlt[hlcnt + i].sg_gui ^=
3875 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3876# endif
3877# ifdef FEAT_GUI
3878 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3879 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3880 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3881 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3882 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3883 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3884 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3885 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3886# ifdef FEAT_XFONTSET
3887 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3888 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3889# endif
3890# endif
3891 highlight_ga.ga_len = hlcnt + i + 1;
3892 set_hl_attr(hlcnt + i); // At long last we can apply
3893 table[i] = syn_id2attr(hlcnt + i + 1);
3894}
3895#endif
3896
3897/*
3898 * Translate the 'highlight' option into attributes in highlight_attr[] and
3899 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3900 * corresponding highlights to use on top of HLF_SNC is computed.
3901 * Called only when the 'highlight' option has been changed and upon first
3902 * screen redraw after any :highlight command.
3903 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3904 */
3905 int
3906highlight_changed(void)
3907{
3908 int hlf;
3909 int i;
3910 char_u *p;
3911 int attr;
3912 char_u *end;
3913 int id;
3914#ifdef USER_HIGHLIGHT
3915 char_u userhl[30]; // use 30 to avoid compiler warning
3916# ifdef FEAT_STL_OPT
3917 int id_S = -1;
3918 int id_SNC = 0;
3919# ifdef FEAT_TERMINAL
3920 int id_ST = 0;
3921 int id_STNC = 0;
3922# endif
3923 int hlcnt;
3924# endif
3925#endif
3926 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3927
3928 need_highlight_changed = FALSE;
3929
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003930#ifdef FEAT_TERMINAL
3931 term_update_colors_all();
3932 term_update_wincolor_all();
3933#endif
3934
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003935 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003936 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3937 highlight_attr[hlf] = 0;
3938
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003939 // First set all attributes to their default value.
3940 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003941 for (i = 0; i < 2; ++i)
3942 {
3943 if (i)
3944 p = p_hl;
3945 else
3946 p = get_highlight_default();
3947 if (p == NULL) // just in case
3948 continue;
3949
3950 while (*p)
3951 {
3952 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3953 if (hl_flags[hlf] == *p)
3954 break;
3955 ++p;
3956 if (hlf == (int)HLF_COUNT || *p == NUL)
3957 return FAIL;
3958
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003959 // Allow several hl_flags to be combined, like "bu" for
3960 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003961 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003962 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003963 {
3964 if (VIM_ISWHITE(*p)) // ignore white space
3965 continue;
3966
3967 if (attr > HL_ALL) // Combination with ':' is not allowed.
3968 return FAIL;
3969
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003970 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003971 switch (*p)
3972 {
3973 case 'b': attr |= HL_BOLD;
3974 break;
3975 case 'i': attr |= HL_ITALIC;
3976 break;
3977 case '-':
3978 case 'n': // no highlighting
3979 break;
3980 case 'r': attr |= HL_INVERSE;
3981 break;
3982 case 's': attr |= HL_STANDOUT;
3983 break;
3984 case 'u': attr |= HL_UNDERLINE;
3985 break;
3986 case 'c': attr |= HL_UNDERCURL;
3987 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003988 case '2': attr |= HL_UNDERDOUBLE;
3989 break;
3990 case 'd': attr |= HL_UNDERDOTTED;
3991 break;
3992 case '=': attr |= HL_UNDERDASHED;
3993 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003994 case 't': attr |= HL_STRIKETHROUGH;
3995 break;
3996 case ':': ++p; // highlight group name
3997 if (attr || *p == NUL) // no combinations
3998 return FAIL;
3999 end = vim_strchr(p, ',');
4000 if (end == NULL)
4001 end = p + STRLEN(p);
4002 id = syn_check_group(p, (int)(end - p));
4003 if (id == 0)
4004 return FAIL;
4005 attr = syn_id2attr(id);
4006 p = end - 1;
4007#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
4008 if (hlf == (int)HLF_SNC)
4009 id_SNC = syn_get_final_id(id);
4010# ifdef FEAT_TERMINAL
4011 else if (hlf == (int)HLF_ST)
4012 id_ST = syn_get_final_id(id);
4013 else if (hlf == (int)HLF_STNC)
4014 id_STNC = syn_get_final_id(id);
4015# endif
4016 else if (hlf == (int)HLF_S)
4017 id_S = syn_get_final_id(id);
4018#endif
4019 break;
4020 default: return FAIL;
4021 }
4022 }
4023 highlight_attr[hlf] = attr;
4024
4025 p = skip_to_option_part(p); // skip comma and spaces
4026 }
4027 }
4028
4029#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01004030 // Setup the user highlights
4031 //
4032 // Temporarily utilize 28 more hl entries:
4033 // 9 for User1-User9 combined with StatusLineNC
4034 // 9 for User1-User9 combined with StatusLineTerm
4035 // 9 for User1-User9 combined with StatusLineTermNC
4036 // 1 for StatusLine default
4037 // Have to be in there simultaneously in case of table overflows in
4038 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004039# ifdef FEAT_STL_OPT
4040 if (ga_grow(&highlight_ga, 28) == FAIL)
4041 return FAIL;
4042 hlcnt = highlight_ga.ga_len;
4043 if (id_S == -1)
4044 {
4045 // Make sure id_S is always valid to simplify code below. Use the last
4046 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02004047 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004048 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
4049 id_S = hlcnt + 19;
4050 }
4051# endif
4052 for (i = 0; i < 9; i++)
4053 {
4054 sprintf((char *)userhl, "User%d", i + 1);
4055 id = syn_name2id(userhl);
4056 if (id == 0)
4057 {
4058 highlight_user[i] = 0;
4059# ifdef FEAT_STL_OPT
4060 highlight_stlnc[i] = 0;
4061# ifdef FEAT_TERMINAL
4062 highlight_stlterm[i] = 0;
4063 highlight_stltermnc[i] = 0;
4064# endif
4065# endif
4066 }
4067 else
4068 {
4069 highlight_user[i] = syn_id2attr(id);
4070# ifdef FEAT_STL_OPT
4071 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
4072 HLF_SNC, highlight_stlnc);
4073# ifdef FEAT_TERMINAL
4074 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
4075 HLF_ST, highlight_stlterm);
4076 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
4077 HLF_STNC, highlight_stltermnc);
4078# endif
4079# endif
4080 }
4081 }
4082# ifdef FEAT_STL_OPT
4083 highlight_ga.ga_len = hlcnt;
4084# endif
4085
4086#endif // USER_HIGHLIGHT
4087
4088 return OK;
4089}
4090
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004091static void highlight_list(void);
4092static void highlight_list_two(int cnt, int attr);
4093
Yee Cheng China7b81202025-02-23 09:32:47 +01004094// context for :highlight <group> <arg> expansion
4095static int expand_hi_synid = 0; // ID for highlight group being completed
4096static int expand_hi_equal_col = 0; // column where the '=' is
4097static int expand_hi_include_orig = 0; // whether to fill the existing current value or not
4098static char_u *expand_hi_curvalue = NULL; // the existing current value
4099#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4100static dict_iterator_T expand_colornames_iter; // iterator for looping through v:colornames
4101#endif
4102
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004103/*
4104 * Handle command line completion for :highlight command.
4105 */
4106 void
4107set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
4108{
4109 char_u *p;
Yee Cheng China7b81202025-02-23 09:32:47 +01004110 int expand_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004111
4112 // Default: expand group names
4113 xp->xp_context = EXPAND_HIGHLIGHT;
4114 xp->xp_pattern = arg;
Yee Cheng China7b81202025-02-23 09:32:47 +01004115 include_none = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004116 include_link = 2;
4117 include_default = 1;
4118
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004119 if (*arg == NUL)
4120 return;
4121
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004122 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004123 p = skiptowhite(arg);
4124 if (*p == NUL)
4125 return;
4126
4127 // past "default" or group name
4128 include_default = 0;
4129 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004130 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004131 arg = skipwhite(p);
4132 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004133 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004134 }
4135 if (*p == NUL)
4136 return;
4137
4138 // past group name
4139 include_link = 0;
4140 if (arg[1] == 'i' && arg[0] == 'N')
Yee Cheng China7b81202025-02-23 09:32:47 +01004141 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004142 highlight_list();
Yee Cheng China7b81202025-02-23 09:32:47 +01004143 expand_group = FALSE;
4144 }
4145 if (STRNCMP("link", arg, p - arg) == 0)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004146 {
4147 xp->xp_pattern = skipwhite(p);
4148 p = skiptowhite(xp->xp_pattern);
4149 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004150 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004151 xp->xp_pattern = skipwhite(p);
4152 p = skiptowhite(xp->xp_pattern);
Yee Cheng China7b81202025-02-23 09:32:47 +01004153 include_none = 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004154 }
Yee Cheng China7b81202025-02-23 09:32:47 +01004155 expand_group = FALSE;
4156 }
4157 else if (STRNCMP("clear", arg, p - arg) == 0)
4158 {
4159 xp->xp_pattern = skipwhite(p);
4160 p = skiptowhite(xp->xp_pattern);
4161 expand_group = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004162 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004163 if (*p != NUL) // past group name(s)
Yee Cheng China7b81202025-02-23 09:32:47 +01004164 {
4165 if (expand_group)
4166 {
4167 // expansion will be done in expand_highlight_group()
4168 xp->xp_context = EXPAND_HIGHLIGHT_GROUP;
4169
4170 expand_hi_synid = syn_namen2id(arg, (int)(p - arg));
4171
4172 while (*p != NUL)
4173 {
4174 arg = skipwhite(p);
4175 p = skiptowhite(arg);
4176 }
4177
4178 p = vim_strchr(arg, '=');
4179 if (p == NULL)
4180 {
4181 // Didn't find a key=<value> pattern
4182 xp->xp_pattern = arg;
4183 expand_hi_equal_col = -1;
4184 expand_hi_include_orig = FALSE;
4185 }
4186 else
4187 {
4188 // Found key=<value> pattern, record the exact location
4189 expand_hi_equal_col = (int)(p - xp->xp_line);
4190
4191 // Only include the original value if the pattern is empty
4192 if (*(p + 1) == NUL)
4193 expand_hi_include_orig = TRUE;
4194 else
4195 expand_hi_include_orig = FALSE;
4196
4197 // Account for comma-separated values
4198 if (STRNCMP(arg, "term=", 5) == 0 ||
4199 STRNCMP(arg, "cterm=", 6) == 0 ||
4200 STRNCMP(arg, "gui=", 4) == 0)
4201 {
4202 char_u *comma = vim_strrchr(p + 1, ',');
4203 if (comma != NULL)
4204 p = comma;
4205 }
4206 xp->xp_pattern = p + 1;
4207 }
4208 }
4209 else
4210 {
4211 xp->xp_context = EXPAND_NOTHING;
4212 }
4213 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004214}
4215
4216/*
4217 * List highlighting matches in a nice way.
4218 */
4219 static void
4220highlight_list(void)
4221{
4222 int i;
4223
4224 for (i = 10; --i >= 0; )
4225 highlight_list_two(i, HL_ATTR(HLF_D));
4226 for (i = 40; --i >= 0; )
4227 highlight_list_two(99, 0);
4228}
4229
4230 static void
4231highlight_list_two(int cnt, int attr)
4232{
4233 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4234 msg_clr_eos();
4235 out_flush();
4236 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4237}
4238
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004239/*
4240 * Function given to ExpandGeneric() to obtain the list of group names.
4241 */
4242 char_u *
4243get_highlight_name(expand_T *xp UNUSED, int idx)
4244{
4245 return get_highlight_name_ext(xp, idx, TRUE);
4246}
4247
4248/*
4249 * Obtain a highlight group name.
4250 * When "skip_cleared" is TRUE don't return a cleared entry.
4251 */
4252 char_u *
4253get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4254{
4255 if (idx < 0)
4256 return NULL;
4257
4258 // Items are never removed from the table, skip the ones that were
4259 // cleared.
4260 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4261 return (char_u *)"";
4262
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004263 if (idx == highlight_ga.ga_len && include_none != 0)
Yee Cheng China7b81202025-02-23 09:32:47 +01004264 return (char_u *)"NONE";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004265 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4266 return (char_u *)"default";
4267 if (idx == highlight_ga.ga_len + include_none + include_default
4268 && include_link != 0)
4269 return (char_u *)"link";
4270 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4271 && include_link != 0)
4272 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004273 if (idx >= highlight_ga.ga_len)
4274 return NULL;
4275 return HL_TABLE()[idx].sg_name;
4276}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004277
Yee Cheng China7b81202025-02-23 09:32:47 +01004278 static char_u *
4279get_highlight_attr_name(expand_T *xp UNUSED, int idx)
4280{
4281 if (idx == 0)
4282 {
4283 // Fill with current value first
4284 if (expand_hi_curvalue != NULL)
4285 return expand_hi_curvalue;
4286 else
4287 return (char_u*)"";
4288 }
4289 if (idx < (int)ARRAY_LENGTH(highlight_index_tab) + 1)
4290 {
4291 char_u *value = highlight_index_tab[idx-1]->value.string;
4292 if (expand_hi_curvalue != NULL && STRCMP(expand_hi_curvalue, value) == 0)
4293 {
4294 // Already returned the current value above, just skip.
4295 return (char_u*)"";
4296 }
4297 return value;
4298 }
4299 return NULL;
4300}
4301
4302 static char_u *
4303get_highlight_cterm_color(expand_T *xp UNUSED, int idx)
4304{
4305 if (idx == 0)
4306 {
4307 // Fill with current value first
4308 if (expand_hi_curvalue != NULL)
4309 return expand_hi_curvalue;
4310 else
4311 return (char_u*)"";
4312 }
4313 // See highlight_set_cterm_color()
4314 else if (idx == 1)
4315 return (char_u*)"fg";
4316 else if (idx == 2)
4317 return (char_u*)"bg";
4318 if (idx < (int)ARRAY_LENGTH(color_name_tab) + 3)
4319 {
4320 char_u *value = color_name_tab[idx-3].value.string;
4321 return value;
4322 }
4323 return NULL;
4324}
4325
4326#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4327 static char_u *
4328get_highlight_gui_color(expand_T *xp UNUSED, int idx)
4329{
4330 if (idx == 0)
4331 {
4332 // Fill with current value first
4333 if (expand_hi_curvalue != NULL)
4334 return expand_hi_curvalue;
4335 else
4336 return (char_u*)"";
4337 }
4338 // See color_name2handle()
4339 else if (idx == 1)
4340 return (char_u*)"fg";
4341 else if (idx == 2)
4342 return (char_u*)"bg";
4343 else if (idx == 3)
4344 return (char_u*)"NONE";
4345
4346 // Complete from v:colornames. Don't do platform specific names for now.
4347 typval_T *tv_result;
4348 char_u *colorname = dict_iterate_next(&expand_colornames_iter, &tv_result);
4349 if (colorname != NULL)
4350 {
4351 // :hi command doesn't allow space, so don't suggest any malformed items
4352 if (vim_strchr(colorname, ' ') != NULL)
4353 return (char_u*)"";
4354
4355 if (expand_hi_curvalue != NULL && STRICMP(expand_hi_curvalue, colorname) == 0)
4356 {
4357 // Already returned the current value above, just skip.
4358 return (char_u*)"";
4359 }
4360 }
4361 return colorname;
4362}
4363#endif
4364
4365 static char_u *
4366get_highlight_group_key(expand_T *xp UNUSED, int idx)
4367{
4368 // Note: Keep this in sync with do_highlight.
4369 static char *(p_hi_group_key_values[]) =
4370 {
4371 "term=",
4372 "start=",
4373 "stop=",
4374 "cterm=",
4375 "ctermfg=",
4376 "ctermbg=",
4377 "ctermul=",
4378 "ctermfont=",
4379#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
4380 "gui=",
4381 "guifg=",
4382 "guibg=",
4383 "guisp=",
4384#endif
4385#ifdef FEAT_GUI
4386 "font=",
4387#endif
4388 "NONE",
4389 };
4390
4391 if (idx < (int)ARRAY_LENGTH(p_hi_group_key_values))
4392 return (char_u*)p_hi_group_key_values[idx];
4393 return NULL;
4394}
4395
4396/*
4397 * Command-line expansion for :hi {group-name} <args>...
4398 */
4399 int
4400expand_highlight_group(
4401 char_u *pat,
4402 expand_T *xp,
4403 regmatch_T *rmp,
4404 char_u ***matches,
4405 int *numMatches)
4406{
4407 if (expand_hi_equal_col != -1)
4408 {
4409 // List the values. First fill in the current value, then if possible colors
4410 // or attribute names.
4411 char_u *(*expandfunc)(expand_T *, int) = NULL;
4412 int type = 0;
4413 hl_group_T *sgp = NULL;
4414 int iarg = 0;
4415 char_u *sarg = NULL;
4416
4417 int unsortedItems = -1; // don't sort by default
4418
4419 if (expand_hi_synid != 0)
4420 sgp = &HL_TABLE()[expand_hi_synid - 1]; // index is ID minus one
4421
4422 // Note: Keep this in sync with highlight_list_one().
4423 char_u *name_end = xp->xp_line + expand_hi_equal_col;
4424 if (name_end - xp->xp_line >= 5
4425 && STRNCMP(name_end - 5, " term", 5) == 0)
4426 {
4427 expandfunc = get_highlight_attr_name;
4428 if (sgp)
4429 {
4430 type = LIST_ATTR;
4431 iarg = sgp->sg_term;
4432 }
4433 }
4434 else if (name_end - xp->xp_line >= 6
4435 && STRNCMP(name_end - 6, " cterm", 6) == 0)
4436 {
4437 expandfunc = get_highlight_attr_name;
4438 if (sgp)
4439 {
4440 type = LIST_ATTR;
4441 iarg = sgp->sg_cterm;
4442 }
4443 }
4444#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
4445 else if (name_end - xp->xp_line >= 4
4446 && STRNCMP(name_end - 4, " gui", 4) == 0)
4447 {
4448 expandfunc = get_highlight_attr_name;
4449 if (sgp)
4450 {
4451 type = LIST_ATTR;
4452 iarg = sgp->sg_gui;
4453 }
4454 }
4455#endif
4456 else if (name_end - xp->xp_line >= 8
4457 && STRNCMP(name_end - 8, " ctermfg", 8) == 0)
4458 {
4459 expandfunc = get_highlight_cterm_color;
4460 if (sgp)
4461 {
4462 type = LIST_INT;
4463 iarg = sgp->sg_cterm_fg;
4464 }
4465 }
4466 else if (name_end - xp->xp_line >= 8
4467 && STRNCMP(name_end - 8, " ctermbg", 8) == 0)
4468 {
4469 expandfunc = get_highlight_cterm_color;
4470 if (sgp)
4471 {
4472 type = LIST_INT;
4473 iarg = sgp->sg_cterm_bg;
4474 }
4475 }
4476 else if (name_end - xp->xp_line >= 8
4477 && STRNCMP(name_end - 8, " ctermul", 8) == 0)
4478 {
4479 expandfunc = get_highlight_cterm_color;
4480 if (sgp)
4481 {
4482 type = LIST_INT;
4483 iarg = sgp->sg_cterm_ul;
4484 }
4485 }
4486#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4487 else if (name_end - xp->xp_line >= 6
4488 && STRNCMP(name_end - 6, " guifg", 6) == 0)
4489 {
4490 expandfunc = get_highlight_gui_color;
4491 if (sgp)
4492 {
4493 type = LIST_STRING;
4494 sarg = sgp->sg_gui_fg_name;
4495 }
4496 }
4497 else if (name_end - xp->xp_line >= 6
4498 && STRNCMP(name_end - 6, " guibg", 6) == 0)
4499 {
4500 expandfunc = get_highlight_gui_color;
4501 if (sgp)
4502 {
4503 type = LIST_STRING;
4504 sarg = sgp->sg_gui_bg_name;
4505 }
4506 }
4507 else if (name_end - xp->xp_line >= 6
4508 && STRNCMP(name_end - 6, " guisp", 6) == 0)
4509 {
4510 expandfunc = get_highlight_gui_color;
4511 if (sgp)
4512 {
4513 type = LIST_STRING;
4514 sarg = sgp->sg_gui_sp_name;
4515 }
4516 }
4517#endif
4518
4519#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
4520 if (expandfunc == get_highlight_gui_color)
4521 {
4522 // Top 4 items are special, after that sort all the color names
4523 unsortedItems = 4;
4524
4525 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
4526 typval_T colornames_val;
4527 colornames_val.v_type = VAR_DICT;
4528 colornames_val.vval.v_dict = colornames_table;
4529 dict_iterate_start(&colornames_val, &expand_colornames_iter);
4530 }
4531#endif
4532
4533 char_u buf[MAX_ATTR_LEN];
4534
Yee Cheng Chin9b41e8f2025-02-25 20:41:52 +01004535 expand_hi_curvalue = NULL;
4536 if (expand_hi_include_orig)
Yee Cheng China7b81202025-02-23 09:32:47 +01004537 {
Yee Cheng Chin9b41e8f2025-02-25 20:41:52 +01004538 if (((type == LIST_ATTR || type == LIST_INT) && iarg != 0) ||
4539 (type == LIST_STRING && sarg != NULL))
4540 {
4541 // Retrieve the current value to go first in completion
4542 expand_hi_curvalue = highlight_arg_to_string(
4543 type, iarg, sarg, buf);
4544 }
Yee Cheng China7b81202025-02-23 09:32:47 +01004545 }
Yee Cheng China7b81202025-02-23 09:32:47 +01004546
4547 if (expandfunc != NULL)
4548 {
4549 return ExpandGenericExt(
4550 pat,
4551 xp,
4552 rmp,
4553 matches,
4554 numMatches,
4555 expandfunc,
4556 FALSE,
4557 unsortedItems);
4558 }
4559
4560 return FAIL;
4561 }
4562
4563 // List all the key names
4564 return ExpandGenericExt(
4565 pat,
4566 xp,
4567 rmp,
4568 matches,
4569 numMatches,
4570 get_highlight_group_key,
4571 FALSE,
4572 -1);
4573}
4574
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004575#if defined(FEAT_GUI) || defined(PROTO)
4576/*
4577 * Free all the highlight group fonts.
4578 * Used when quitting for systems which need it.
4579 */
4580 void
4581free_highlight_fonts(void)
4582{
4583 int idx;
4584
4585 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4586 {
4587 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4588 HL_TABLE()[idx].sg_font = NOFONT;
4589# ifdef FEAT_XFONTSET
4590 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4591 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4592# endif
4593 }
4594
4595 gui_mch_free_font(gui.norm_font);
4596# ifdef FEAT_XFONTSET
4597 gui_mch_free_fontset(gui.fontset);
4598# endif
4599# ifndef FEAT_GUI_GTK
4600 gui_mch_free_font(gui.bold_font);
4601 gui_mch_free_font(gui.ital_font);
4602 gui_mch_free_font(gui.boldital_font);
4603# endif
4604}
4605#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004606
4607#if defined(FEAT_EVAL) || defined(PROTO)
4608/*
4609 * Convert each of the highlight attribute bits (bold, standout, underline,
4610 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4611 * the attribute name as the key.
4612 */
4613 static dict_T *
4614highlight_get_attr_dict(int hlattr)
4615{
4616 dict_T *dict;
4617 int i;
4618
4619 dict = dict_alloc();
4620 if (dict == NULL)
4621 return NULL;
4622
John Marriott34f00dd2024-04-08 23:28:12 +02004623 for (i = 0; i < (int)ARRAY_LENGTH(highlight_index_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004624 {
John Marriott34f00dd2024-04-08 23:28:12 +02004625 if (hlattr & highlight_index_tab[i]->key)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004626 {
John Marriott8d4477e2024-11-02 15:59:01 +01004627 dict_add_bool(dict, (char *)highlight_index_tab[i]->value.string,
4628 VVAL_TRUE);
4629 // don't want "inverse"/"reverse"
4630 hlattr &= ~highlight_index_tab[i]->key;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004631 }
4632 }
4633
4634 return dict;
4635}
4636
4637/*
4638 * Return the attributes of the highlight group at index 'hl_idx' as a
4639 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4640 * links recursively.
4641 */
4642 static dict_T *
4643highlight_get_info(int hl_idx, int resolve_link)
4644{
4645 dict_T *dict;
4646 hl_group_T *sgp;
4647 dict_T *attr_dict;
4648 int hlgid;
4649
4650 dict = dict_alloc();
4651 if (dict == NULL)
4652 return dict;
4653
4654 sgp = &HL_TABLE()[hl_idx];
4655 // highlight group id is 1-based
4656 hlgid = hl_idx + 1;
4657
4658 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4659 goto error;
4660 if (dict_add_number(dict, "id", hlgid) == FAIL)
4661 goto error;
4662
4663 if (sgp->sg_link && resolve_link)
4664 {
4665 // resolve the highlight group link recursively
4666 while (sgp->sg_link)
4667 {
4668 hlgid = sgp->sg_link;
4669 sgp = &HL_TABLE()[sgp->sg_link - 1];
4670 }
4671 }
4672
4673 if (sgp->sg_term != 0)
4674 {
4675 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4676 if (attr_dict != NULL)
4677 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4678 goto error;
4679 }
4680 if (sgp->sg_start != NULL)
4681 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4682 goto error;
4683 if (sgp->sg_stop != NULL)
4684 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4685 goto error;
4686 if (sgp->sg_cterm != 0)
4687 {
4688 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4689 if (attr_dict != NULL)
4690 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4691 goto error;
4692 }
4693 if (sgp->sg_cterm_fg != 0)
4694 if (dict_add_string(dict, "ctermfg",
4695 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4696 goto error;
4697 if (sgp->sg_cterm_bg != 0)
4698 if (dict_add_string(dict, "ctermbg",
4699 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4700 goto error;
4701 if (sgp->sg_cterm_ul != 0)
4702 if (dict_add_string(dict, "ctermul",
4703 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4704 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004705 if (sgp->sg_cterm_font != 0)
4706 if (dict_add_string(dict, "ctermfont",
4707 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4708 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004709 if (sgp->sg_gui != 0)
4710 {
4711 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4712 if (attr_dict != NULL)
4713 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4714 goto error;
4715 }
4716 if (sgp->sg_gui_fg_name != NULL)
4717 if (dict_add_string(dict, "guifg",
4718 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4719 goto error;
4720 if (sgp->sg_gui_bg_name != NULL)
4721 if (dict_add_string(dict, "guibg",
4722 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4723 goto error;
4724 if (sgp->sg_gui_sp_name != NULL)
4725 if (dict_add_string(dict, "guisp",
4726 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4727 goto error;
4728# ifdef FEAT_GUI
4729 if (sgp->sg_font_name != NULL)
4730 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4731 goto error;
4732# endif
4733 if (sgp->sg_link)
4734 {
4735 char_u *link;
4736
4737 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4738 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4739 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004740
4741 if (sgp->sg_deflink)
4742 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004743 }
4744 if (dict_len(dict) == 2)
4745 // If only 'name' is present, then the highlight group is cleared.
4746 dict_add_bool(dict, "cleared", VVAL_TRUE);
4747
4748 return dict;
4749
4750error:
4751 vim_free(dict);
4752 return NULL;
4753}
4754
4755/*
4756 * "hlget([name])" function
4757 * Return the attributes of a specific highlight group (if specified) or all
4758 * the highlight groups.
4759 */
4760 void
4761f_hlget(typval_T *argvars, typval_T *rettv)
4762{
4763 list_T *list;
4764 dict_T *dict;
4765 int i;
4766 char_u *hlarg = NULL;
4767 int resolve_link = FALSE;
4768
4769 if (rettv_list_alloc(rettv) == FAIL)
4770 return;
4771
4772 if (check_for_opt_string_arg(argvars, 0) == FAIL
4773 || (argvars[0].v_type != VAR_UNKNOWN
4774 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4775 return;
4776
4777 if (argvars[0].v_type != VAR_UNKNOWN)
4778 {
4779 // highlight group name supplied
4780 hlarg = tv_get_string_chk(&argvars[0]);
4781 if (hlarg == NULL)
4782 return;
4783
4784 if (argvars[1].v_type != VAR_UNKNOWN)
4785 {
4786 int error = FALSE;
4787
4788 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4789 if (error)
4790 return;
4791 }
4792 }
4793
4794 list = rettv->vval.v_list;
4795 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4796 {
4797 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4798 {
4799 dict = highlight_get_info(i, resolve_link);
4800 if (dict != NULL)
4801 list_append_dict(list, dict);
4802 }
4803 }
4804}
4805
4806/*
4807 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4808 * 'dict' or the value is not a string type. If the value is not a string type
4809 * or is NULL, then 'error' is set to TRUE.
4810 */
4811 static char_u *
4812hldict_get_string(dict_T *dict, char_u *key, int *error)
4813{
4814 dictitem_T *di;
4815
4816 *error = FALSE;
4817 di = dict_find(dict, key, -1);
4818 if (di == NULL)
4819 return NULL;
4820
4821 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4822 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004823 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004824 *error = TRUE;
4825 return NULL;
4826 }
4827
4828 return di->di_tv.vval.v_string;
4829}
4830
4831/*
4832 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4833 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4834 * Dictionary or is NULL.
4835 */
4836 static int
4837hldict_attr_to_str(
4838 dict_T *dict,
4839 char_u *key,
4840 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004841 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004842{
4843 dictitem_T *di;
4844 dict_T *attrdict;
4845 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004846 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004847
4848 attr_str[0] = NUL;
4849 di = dict_find(dict, key, -1);
4850 if (di == NULL)
4851 return TRUE;
4852
4853 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4854 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004855 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004856 return FALSE;
4857 }
4858
4859 attrdict = di->di_tv.vval.v_dict;
4860
4861 // If the attribute dict is empty, then return NONE to clear the attributes
4862 if (dict_len(attrdict) == 0)
4863 {
4864 vim_strcat(attr_str, (char_u *)"NONE", len);
4865 return TRUE;
4866 }
4867
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004868 p = attr_str;
John Marriott34f00dd2024-04-08 23:28:12 +02004869 for (i = 0; i < (int)ARRAY_LENGTH(highlight_tab); ++i)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004870 {
John Marriott8d4477e2024-11-02 15:59:01 +01004871 if (dict_get_bool(attrdict, (char *)highlight_tab[i].value.string,
4872 VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004873 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004874 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4875 STRCPY(p, (char_u *)",");
John Marriott8d4477e2024-11-02 15:59:01 +01004876 if (p - attr_str + highlight_tab[i].value.length + 1 < len)
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004877 {
John Marriott8d4477e2024-11-02 15:59:01 +01004878 STRCPY(p, highlight_tab[i].value.string);
4879 p += highlight_tab[i].value.length;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004880 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004881 }
4882 }
4883
4884 return TRUE;
4885}
4886
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004887// Temporary buffer used to store the command string produced by hlset().
4888// IObuff cannot be used for this as the error messages produced by hlset()
4889// internally use IObuff.
4890#define HLSETBUFSZ 512
4891static char_u hlsetBuf[HLSETBUFSZ + 1];
4892
4893/*
4894 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4895 * "dptr", which points into "hlsetBuf".
4896 * Returns the updated pointer.
4897 */
4898 static char_u *
4899add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4900{
4901 size_t vallen;
4902
4903 // Do nothing if the value is not specified or is empty
4904 if (value == NULL || *value == NUL)
4905 return dptr;
4906
4907 vallen = STRLEN(value);
4908 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4909 {
4910 STRCPY(dptr, attr);
4911 dptr += attrlen;
4912 STRCPY(dptr, value);
4913 dptr += vallen;
4914 }
4915
4916 return dptr;
4917}
4918
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004919/*
4920 * Add or update a highlight group using 'dict' items. Returns TRUE if
4921 * successfully updated the highlight group.
4922 */
4923 static int
4924hlg_add_or_update(dict_T *dict)
4925{
4926 char_u *name;
4927 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004928 char_u term_attr[MAX_ATTR_LEN];
4929 char_u cterm_attr[MAX_ATTR_LEN];
4930 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004931 char_u *start;
4932 char_u *stop;
4933 char_u *ctermfg;
4934 char_u *ctermbg;
4935 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004936 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004937 char_u *guifg;
4938 char_u *guibg;
4939 char_u *guisp;
4940# ifdef FEAT_GUI
4941 char_u *font;
4942# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004943 int forceit = FALSE;
4944 int dodefault = FALSE;
4945 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004946 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004947
4948 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004949 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004950 return FALSE;
4951
Bram Moolenaard61efa52022-07-23 09:52:04 +01004952 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004953 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004954
Bram Moolenaard61efa52022-07-23 09:52:04 +01004955 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004956 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004957
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004958 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004959 {
4960 varnumber_T cleared;
4961
4962 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004963 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004964 if (cleared == TRUE)
4965 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004966 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4967 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004968 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004969 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004970 }
4971
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004972 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004973 {
4974 char_u *linksto;
4975
4976 // link highlight groups
4977 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004978 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004979 return FALSE;
4980
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004981 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004982 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004983 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004984
4985 done = TRUE;
4986 }
4987
4988 // If 'cleared' or 'linksto' are specified, then don't process the other
4989 // attributes.
4990 if (done)
4991 return TRUE;
4992
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004993 start = hldict_get_string(dict, (char_u *)"start", &error);
4994 if (error)
4995 return FALSE;
4996
4997 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4998 if (error)
4999 return FALSE;
5000
5001 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005002 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005003 return FALSE;
5004
5005 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005006 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005007 return FALSE;
5008
5009 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
5010 if (error)
5011 return FALSE;
5012
5013 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
5014 if (error)
5015 return FALSE;
5016
5017 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
5018 if (error)
5019 return FALSE;
5020
PMuncha606f3a2023-11-15 15:35:49 +01005021 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
5022 if (error)
5023 return FALSE;
5024
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005025 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005026 return FALSE;
5027
5028 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
5029 if (error)
5030 return FALSE;
5031
5032 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
5033 if (error)
5034 return FALSE;
5035
5036 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
5037 if (error)
5038 return FALSE;
5039
5040# ifdef FEAT_GUI
5041 font = hldict_get_string(dict, (char_u *)"font", &error);
5042 if (error)
5043 return FALSE;
5044# endif
5045
5046 // If none of the attributes are specified, then do nothing.
5047 if (term_attr[0] == NUL && start == NULL && stop == NULL
5048 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01005049 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005050# ifdef FEAT_GUI
5051 && font == NULL
5052# endif
5053 && guifg == NULL && guibg == NULL && guisp == NULL
5054 )
5055 return TRUE;
5056
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005057 hlsetBuf[0] = NUL;
5058 p = hlsetBuf;
5059 if (dodefault)
5060 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
5061 p = add_attr_and_value(p, (char_u *)"", 0, name);
5062 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
5063 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
5064 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
5065 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
5066 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
5067 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
5068 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01005069 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005070 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005071# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005072 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005073# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005074 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
5075 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01005076 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005077
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00005078 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005079
5080 return TRUE;
5081}
5082
5083/*
5084 * "hlset([{highlight_attr}])" function
5085 * Add or modify highlight groups
5086 */
5087 void
5088f_hlset(typval_T *argvars, typval_T *rettv)
5089{
5090 listitem_T *li;
5091 dict_T *dict;
5092
5093 rettv->vval.v_number = -1;
5094
5095 if (check_for_list_arg(argvars, 0) == FAIL)
5096 return;
5097
5098 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
5099 {
5100 if (li->li_tv.v_type != VAR_DICT)
5101 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00005102 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00005103 return;
5104 }
5105
5106 dict = li->li_tv.vval.v_dict;
5107 if (!hlg_add_or_update(dict))
5108 return;
5109 }
5110
5111 rettv->vval.v_number = 0;
5112}
5113#endif