blob: 3381208fd1eb9f1d7c6f111cf332881d23d485a5 [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 */
27static char *(hl_name_table[]) =
Bram Moolenaar84f54632022-06-29 18:39:11 +010028 {"bold", "standout", "underline",
29 "undercurl", "underdouble", "underdotted", "underdashed",
30 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020031static int hl_attr_table[] =
Bram Moolenaar84f54632022-06-29 18:39:11 +010032 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE,
33 HL_UNDERCURL, HL_UNDERDOUBLE, HL_UNDERDOTTED, HL_UNDERDASHED,
34 HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
35// length of all attribute names, plus commas, together (and a bit more)
36#define MAX_ATTR_LEN 120
37
kylo252ae6f1d82022-02-16 19:24:07 +000038#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? (attr_b) : (attr_a)) | (attr_b))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020039
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020040/*
41 * Structure that stores information about a highlight group.
42 * The ID of a highlight group is also called group ID. It is the index in
43 * the highlight_ga array PLUS ONE.
44 */
45typedef struct
46{
47 char_u *sg_name; // highlight group name
48 char_u *sg_name_u; // uppercase of sg_name
49 int sg_cleared; // "hi clear" was used
50// for normal terminals
51 int sg_term; // "term=" highlighting attributes
52 char_u *sg_start; // terminal string for start highl
53 char_u *sg_stop; // terminal string for stop highl
54 int sg_term_attr; // Screen attr for term mode
55// for color terminals
56 int sg_cterm; // "cterm=" highlighting attr
57 int sg_cterm_bold; // bold attr was set for light color
58 int sg_cterm_fg; // terminal fg color number + 1
59 int sg_cterm_bg; // terminal bg color number + 1
Bram Moolenaare023e882020-05-31 16:42:30 +020060 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020061 int sg_cterm_attr; // Screen attr for color term mode
PMuncha606f3a2023-11-15 15:35:49 +010062 int sg_cterm_font; // terminal alternative font (0 for normal)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020063// for when using the GUI
64#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
65 guicolor_T sg_gui_fg; // GUI foreground color handle
66 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020067 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020068#endif
69#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020070 GuiFont sg_font; // GUI font handle
71#ifdef FEAT_XFONTSET
72 GuiFontset sg_fontset; // GUI fontset handle
73#endif
74 char_u *sg_font_name; // GUI font or fontset name
75 int sg_gui_attr; // Screen attr for GUI mode
76#endif
77#if defined(FEAT_GUI) || defined(FEAT_EVAL)
78// Store the sp color name for the GUI or synIDattr()
79 int sg_gui; // "gui=" highlighting attributes
80 char_u *sg_gui_fg_name;// GUI foreground color name
81 char_u *sg_gui_bg_name;// GUI background color name
82 char_u *sg_gui_sp_name;// GUI special color name
83#endif
84 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +020085 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020086 int sg_set; // combination of SG_* flags
87#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +020088 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020089 sctx_T sg_script_ctx; // script in which the group was last set
90#endif
91} hl_group_T;
92
93// highlight groups for 'highlight' option
94static garray_T highlight_ga;
95#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
96
97/*
98 * An attribute number is the index in attr_table plus ATTR_OFF.
99 */
100#define ATTR_OFF (HL_ALL + 1)
101
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200102static void syn_unadd_group(void);
103static void set_hl_attr(int idx);
104static void highlight_list_one(int id);
105static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
106static int syn_add_group(char_u *name);
107static int hl_has_settings(int idx, int check_link);
108static void highlight_clear(int idx);
109
110#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
111static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
112#endif
113#ifdef FEAT_GUI
114static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
115static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
116#endif
117
118/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200119 * The default highlight groups. These are compiled-in for fast startup and
120 * they still work when the runtime files can't be found.
121 * When making changes here, also change runtime/colors/default.vim!
122 * The #ifdefs are needed to reduce the amount of static data. Helps to make
123 * the 16 bit DOS (museum) version compile.
124 */
125#if defined(FEAT_GUI) || defined(FEAT_EVAL)
126# define CENT(a, b) b
127#else
128# define CENT(a, b) a
129#endif
130static char *(highlight_init_both[]) = {
131 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
132 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
133 CENT("IncSearch term=reverse cterm=reverse",
134 "IncSearch term=reverse cterm=reverse gui=reverse"),
135 CENT("ModeMsg term=bold cterm=bold",
136 "ModeMsg term=bold cterm=bold gui=bold"),
137 CENT("NonText term=bold ctermfg=Blue",
138 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
139 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
140 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
141 CENT("StatusLineNC term=reverse cterm=reverse",
142 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
143 "default link EndOfBuffer NonText",
144 CENT("VertSplit term=reverse cterm=reverse",
145 "VertSplit term=reverse cterm=reverse gui=reverse"),
146#ifdef FEAT_CLIPBOARD
147 CENT("VisualNOS term=underline,bold cterm=underline,bold",
148 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
149#endif
150#ifdef FEAT_DIFF
151 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
152 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
153#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200154 CENT("PmenuSbar ctermbg=Grey",
155 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200156 CENT("TabLineSel term=bold cterm=bold",
157 "TabLineSel term=bold cterm=bold gui=bold"),
158 CENT("TabLineFill term=reverse cterm=reverse",
159 "TabLineFill term=reverse cterm=reverse gui=reverse"),
160#ifdef FEAT_GUI
161 "Cursor guibg=fg guifg=bg",
162 "lCursor guibg=fg guifg=bg", // should be different, but what?
163#endif
164 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000165 "default link CursorLineSign SignColumn",
166 "default link CursorLineFold FoldColumn",
LemonBoya4399382022-04-09 21:04:08 +0100167 "default link CurSearch Search",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000168 "default link PmenuKind Pmenu",
169 "default link PmenuKindSel PmenuSel",
170 "default link PmenuExtra Pmenu",
171 "default link PmenuExtraSel PmenuSel",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200172 CENT("Normal cterm=NONE", "Normal gui=NONE"),
173 NULL
174};
175
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200176// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200177static char *(highlight_init_light[]) = {
178 CENT("Directory term=bold ctermfg=DarkBlue",
179 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
180 CENT("LineNr term=underline ctermfg=Brown",
181 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200182 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
183 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200184 CENT("MoreMsg term=bold ctermfg=DarkGreen",
185 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
186 CENT("Question term=standout ctermfg=DarkGreen",
187 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
188 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
189 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
190#ifdef FEAT_SPELL
191 CENT("SpellBad term=reverse ctermbg=LightRed",
192 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
193 CENT("SpellCap term=reverse ctermbg=LightBlue",
194 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
195 CENT("SpellRare term=reverse ctermbg=LightMagenta",
196 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
197 CENT("SpellLocal term=underline ctermbg=Cyan",
198 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
199#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200200 CENT("PmenuThumb ctermbg=Black",
201 "PmenuThumb ctermbg=Black guibg=Black"),
202 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
203 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
204 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
205 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200206 CENT("SpecialKey term=bold ctermfg=DarkBlue",
207 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
208 CENT("Title term=bold ctermfg=DarkMagenta",
209 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
210 CENT("WarningMsg term=standout ctermfg=DarkRed",
211 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200212 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
213 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200214#ifdef FEAT_FOLDING
215 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
216 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
217 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
218 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
219#endif
220#ifdef FEAT_SIGNS
221 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
222 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
223#endif
224 CENT("Visual term=reverse",
225 "Visual term=reverse guibg=LightGrey"),
226#ifdef FEAT_DIFF
227 CENT("DiffAdd term=bold ctermbg=LightBlue",
228 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
229 CENT("DiffChange term=bold ctermbg=LightMagenta",
230 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
231 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
232 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
233#endif
234 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
235 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
236#ifdef FEAT_SYN_HL
237 CENT("CursorColumn term=reverse ctermbg=LightGrey",
238 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
239 CENT("CursorLine term=underline cterm=underline",
240 "CursorLine term=underline cterm=underline guibg=Grey90"),
241 CENT("ColorColumn term=reverse ctermbg=LightRed",
242 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
243#endif
244#ifdef FEAT_CONCEAL
245 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
246 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
247#endif
248 CENT("MatchParen term=reverse ctermbg=Cyan",
249 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
250#ifdef FEAT_TERMINAL
251 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
252 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
253 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
254 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
255#endif
256#ifdef FEAT_MENU
257 CENT("ToolbarLine term=underline ctermbg=LightGrey",
258 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
259 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
260 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
261#endif
262 NULL
263};
264
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200265// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200266static char *(highlight_init_dark[]) = {
267 CENT("Directory term=bold ctermfg=LightCyan",
268 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
269 CENT("LineNr term=underline ctermfg=Yellow",
270 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200271 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
272 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200273 CENT("MoreMsg term=bold ctermfg=LightGreen",
274 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
275 CENT("Question term=standout ctermfg=LightGreen",
276 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
277 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
278 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
279 CENT("SpecialKey term=bold ctermfg=LightBlue",
280 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
281#ifdef FEAT_SPELL
282 CENT("SpellBad term=reverse ctermbg=Red",
283 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
284 CENT("SpellCap term=reverse ctermbg=Blue",
285 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
286 CENT("SpellRare term=reverse ctermbg=Magenta",
287 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
288 CENT("SpellLocal term=underline ctermbg=Cyan",
289 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
290#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200291 CENT("PmenuThumb ctermbg=White",
292 "PmenuThumb ctermbg=White guibg=White"),
293 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
294 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
295 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
296 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200297 CENT("Title term=bold ctermfg=LightMagenta",
298 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
299 CENT("WarningMsg term=standout ctermfg=LightRed",
300 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200301 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
302 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200303#ifdef FEAT_FOLDING
304 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
305 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
306 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
307 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
308#endif
309#ifdef FEAT_SIGNS
310 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
311 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
312#endif
313 CENT("Visual term=reverse",
314 "Visual term=reverse guibg=DarkGrey"),
315#ifdef FEAT_DIFF
316 CENT("DiffAdd term=bold ctermbg=DarkBlue",
317 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
318 CENT("DiffChange term=bold ctermbg=DarkMagenta",
319 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
320 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
321 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
322#endif
323 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
324 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
325#ifdef FEAT_SYN_HL
326 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
327 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
328 CENT("CursorLine term=underline cterm=underline",
329 "CursorLine term=underline cterm=underline guibg=Grey40"),
330 CENT("ColorColumn term=reverse ctermbg=DarkRed",
331 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
332#endif
333 CENT("MatchParen term=reverse ctermbg=DarkCyan",
334 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
335#ifdef FEAT_CONCEAL
336 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
337 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
338#endif
339#ifdef FEAT_TERMINAL
340 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
341 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
342 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
343 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
344#endif
345#ifdef FEAT_MENU
346 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
347 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
348 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
349 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
350#endif
351 NULL
352};
353
Dominique Pelle748b3082022-01-08 12:41:16 +0000354#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200355/*
356 * Returns the number of highlight groups.
357 */
358 int
359highlight_num_groups(void)
360{
361 return highlight_ga.ga_len;
362}
363
364/*
365 * Returns the name of a highlight group.
366 */
367 char_u *
368highlight_group_name(int id)
369{
370 return HL_TABLE()[id].sg_name;
371}
372
373/*
374 * Returns the ID of the link to a highlight group.
375 */
376 int
377highlight_link_id(int id)
378{
379 return HL_TABLE()[id].sg_link;
380}
Dominique Pelle748b3082022-01-08 12:41:16 +0000381#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200382
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200383 void
384init_highlight(
385 int both, // include groups where 'bg' doesn't matter
386 int reset) // clear group first
387{
388 int i;
389 char **pp;
390 static int had_both = FALSE;
391#ifdef FEAT_EVAL
392 char_u *p;
393
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100394 // Try finding the color scheme file. Used when a color file was loaded
395 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200396 p = get_var_value((char_u *)"g:colors_name");
397 if (p != NULL)
398 {
399 // The value of g:colors_name could be freed when sourcing the script,
400 // making "p" invalid, so copy it.
401 char_u *copy_p = vim_strsave(p);
402 int r;
403
404 if (copy_p != NULL)
405 {
406 r = load_colors(copy_p);
407 vim_free(copy_p);
408 if (r == OK)
409 return;
410 }
411 }
412
413#endif
414
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100415 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200416 if (both)
417 {
418 had_both = TRUE;
419 pp = highlight_init_both;
420 for (i = 0; pp[i] != NULL; ++i)
421 do_highlight((char_u *)pp[i], reset, TRUE);
422 }
423 else if (!had_both)
424 // Don't do anything before the call with both == TRUE from main().
425 // Not everything has been setup then, and that call will overrule
426 // everything anyway.
427 return;
428
429 if (*p_bg == 'l')
430 pp = highlight_init_light;
431 else
432 pp = highlight_init_dark;
433 for (i = 0; pp[i] != NULL; ++i)
434 do_highlight((char_u *)pp[i], reset, TRUE);
435
436 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
437 // depend on the number of colors available.
438 // With 8 colors brown is equal to yellow, need to use black for Search fg
439 // to avoid Statement highlighted text disappears.
440 // Clear the attributes, needed when changing the t_Co value.
441 if (t_colors > 8)
442 do_highlight((char_u *)(*p_bg == 'l'
443 ? "Visual cterm=NONE ctermbg=LightGrey"
444 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
445 else
446 {
447 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
448 FALSE, TRUE);
449 if (*p_bg == 'l')
450 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
451 }
452
453#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100454 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200455 if (get_var_value((char_u *)"g:syntax_on") != NULL)
456 {
457 static int recursive = 0;
458
459 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000460 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200461 else
462 {
463 ++recursive;
464 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
465 --recursive;
466 }
467 }
468#endif
469}
470
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000471#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
472/*
473 * Load a default color list. Intended to support legacy color names but allows
474 * the user to override the color values. Only loaded once.
475 */
476 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000477load_default_colors_lists(void)
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000478{
479 // Lacking a default color list isn't the end of the world but it is likely
480 // an inconvenience so users should know when it is missing.
481 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
482 msg("failed to load colors/lists/default.vim");
483}
484#endif
485
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200486/*
487 * Load color file "name".
488 * Return OK for success, FAIL for failure.
489 */
490 int
491load_colors(char_u *name)
492{
493 char_u *buf;
494 int retval = FAIL;
495 static int recursive = FALSE;
496
497 // When being called recursively, this is probably because setting
498 // 'background' caused the highlighting to be reloaded. This means it is
499 // working, thus we should return OK.
500 if (recursive)
501 return OK;
502
503 recursive = TRUE;
504 buf = alloc(STRLEN(name) + 12);
505 if (buf != NULL)
506 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100507#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100508 load_default_colors_lists();
509#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200510 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
511 curbuf->b_fname, FALSE, curbuf);
512 sprintf((char *)buf, "colors/%s.vim", name);
513 retval = source_runtime(buf, DIP_START + DIP_OPT);
514 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100515 if (retval == OK)
516 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
517 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200518 }
519 recursive = FALSE;
520
521 return retval;
522}
523
524static char *(color_names[28]) = {
525 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
526 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
527 "Gray", "Grey", "LightGray", "LightGrey",
528 "DarkGray", "DarkGrey",
529 "Blue", "LightBlue", "Green", "LightGreen",
530 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
531 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
532 // indices:
533 // 0, 1, 2, 3,
534 // 4, 5, 6, 7,
535 // 8, 9, 10, 11,
536 // 12, 13,
537 // 14, 15, 16, 17,
538 // 18, 19, 20, 21, 22,
539 // 23, 24, 25, 26, 27
540static int color_numbers_16[28] = {0, 1, 2, 3,
541 4, 5, 6, 6,
542 7, 7, 7, 7,
543 8, 8,
544 9, 9, 10, 10,
545 11, 11, 12, 12, 13,
546 13, 14, 14, 15, -1};
547// for xterm with 88 colors...
548static int color_numbers_88[28] = {0, 4, 2, 6,
549 1, 5, 32, 72,
550 84, 84, 7, 7,
551 82, 82,
552 12, 43, 10, 61,
553 14, 63, 9, 74, 13,
554 75, 11, 78, 15, -1};
555// for xterm with 256 colors...
556static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200557 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200558 248, 248, 7, 7,
559 242, 242,
560 12, 81, 10, 121,
561 14, 159, 9, 224, 13,
562 225, 11, 229, 15, -1};
563// for terminals with less than 16 colors...
564static int color_numbers_8[28] = {0, 4, 2, 6,
565 1, 5, 3, 3,
566 7, 7, 7, 7,
567 0+8, 0+8,
568 4+8, 4+8, 2+8, 2+8,
569 6+8, 6+8, 1+8, 1+8, 5+8,
570 5+8, 3+8, 3+8, 7+8, -1};
571
572/*
573 * Lookup the "cterm" value to be used for color with index "idx" in
574 * color_names[].
575 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
576 * colors, otherwise it will be unchanged.
577 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100578 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200579lookup_color(int idx, int foreground, int *boldp)
580{
581 int color = color_numbers_16[idx];
582 char_u *p;
583
584 // Use the _16 table to check if it's a valid color name.
585 if (color < 0)
586 return -1;
587
588 if (t_colors == 8)
589 {
590 // t_Co is 8: use the 8 colors table
591#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100592 // On qnx, the 8 & 16 color arrays are the same
593 if (STRNCMP(T_NAME, "qansi", 5) == 0)
594 color = color_numbers_16[idx];
595 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200596#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100597 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200598 if (foreground)
599 {
600 // set/reset bold attribute to get light foreground
601 // colors (on some terminals, e.g. "linux")
602 if (color & 8)
603 *boldp = TRUE;
604 else
605 *boldp = FALSE;
606 }
607 color &= 7; // truncate to 8 colors
608 }
609 else if (t_colors == 16 || t_colors == 88
610 || t_colors >= 256)
611 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100612 // Guess: if the termcap entry ends in 'm', it is
613 // probably an xterm-like terminal. Use the changed
614 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200615 if (*T_CAF != NUL)
616 p = T_CAF;
617 else
618 p = T_CSF;
619 if (*p != NUL && (t_colors > 256
620 || *(p + STRLEN(p) - 1) == 'm'))
621 {
622 if (t_colors == 88)
623 color = color_numbers_88[idx];
624 else if (t_colors >= 256)
625 color = color_numbers_256[idx];
626 else
627 color = color_numbers_8[idx];
628 }
629#ifdef FEAT_TERMRESPONSE
630 if (t_colors >= 256 && color == 15 && is_mac_terminal)
631 // Terminal.app has a bug: 15 is light grey. Use white
632 // from the color cube instead.
633 color = 231;
634#endif
635 }
636 return color;
637}
638
639/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100640 * Link highlight group 'from_hg' to 'to_hg'.
641 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000642 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100643 * 'init' is set to TRUE when initializing all the highlight groups.
644 */
645 static void
646highlight_group_link(
647 char_u *from_hg,
648 int from_len,
649 char_u *to_hg,
650 int to_len,
651 int dodefault,
652 int forceit,
653 int init)
654{
655 int from_id;
656 int to_id;
657 hl_group_T *hlgroup = NULL;
658
659 from_id = syn_check_group(from_hg, from_len);
660 if (STRNCMP(to_hg, "NONE", 4) == 0)
661 to_id = 0;
662 else
663 to_id = syn_check_group(to_hg, to_len);
664
665 if (from_id > 0)
666 {
667 hlgroup = &HL_TABLE()[from_id - 1];
668 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
669 {
670 hlgroup->sg_deflink = to_id;
671#ifdef FEAT_EVAL
672 hlgroup->sg_deflink_sctx = current_sctx;
673 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
674#endif
675 }
676 }
677
678 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
679 {
680 // Don't allow a link when there already is some highlighting
681 // for the group, unless '!' is used
682 if (to_id > 0 && !forceit && !init
683 && hl_has_settings(from_id - 1, dodefault))
684 {
685 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000686 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100687 }
688 else if (hlgroup->sg_link != to_id
689#ifdef FEAT_EVAL
690 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
691#endif
692 || hlgroup->sg_cleared)
693 {
694 if (!init)
695 hlgroup->sg_set |= SG_LINK;
696 hlgroup->sg_link = to_id;
697#ifdef FEAT_EVAL
698 hlgroup->sg_script_ctx = current_sctx;
699 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
700#endif
701 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100702 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100703
704 // Only call highlight_changed() once after multiple changes.
705 need_highlight_changed = TRUE;
706 }
707 }
708
709}
710
711/*
712 * Reset all highlighting to the defaults. Removes all highlighting for the
713 * groups added by the user.
714 */
715 static void
716highlight_reset_all(void)
717{
718 int idx;
719
720#ifdef FEAT_GUI
721 // First, we do not destroy the old values, but allocate the new
722 // ones and update the display. THEN we destroy the old values.
723 // If we destroy the old values first, then the old values
724 // (such as GuiFont's or GuiFontset's) will still be displayed but
725 // invalid because they were free'd.
726 if (gui.in_use)
727 {
728# ifdef FEAT_BEVAL_TIP
729 gui_init_tooltip_font();
730# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100731# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100732 gui_init_menu_font();
733# endif
734 }
735# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
736 gui_mch_def_colors();
737# endif
738# ifdef FEAT_GUI_X11
739# ifdef FEAT_MENU
740
741 // This only needs to be done when there is no Menu highlight
742 // group defined by default, which IS currently the case.
743 gui_mch_new_menu_colors();
744# endif
745 if (gui.in_use)
746 {
747 gui_new_scrollbar_colors();
748# ifdef FEAT_BEVAL_GUI
749 gui_mch_new_tooltip_colors();
750# endif
751# ifdef FEAT_MENU
752 gui_mch_new_menu_font();
753# endif
754 }
755# endif
756
757 // Ok, we're done allocating the new default graphics items.
758 // The screen should already be refreshed at this point.
759 // It is now Ok to clear out the old data.
760#endif
761#ifdef FEAT_EVAL
762 do_unlet((char_u *)"g:colors_name", TRUE);
763#endif
764 restore_cterm_colors();
765
766 // Clear all default highlight groups and load the defaults.
767 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
768 highlight_clear(idx);
769 init_highlight(TRUE, TRUE);
770#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
771 if (USE_24BIT)
772 highlight_gui_started();
773 else
774#endif
775 highlight_changed();
776 redraw_later_clear();
777}
778
779/*
780 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
781 * index 'idx'.
782 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
783 * 'arg' is the list of attribute names separated by comma.
784 * 'init' is set to TRUE when initializing all the highlight groups.
785 * Returns TRUE if the attributes are set.
786 */
787 static int
788highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
789{
790 int attr;
791 int off;
792 long i;
793 int len;
794
795 attr = 0;
796 off = 0;
797 while (arg[off] != NUL)
798 {
799 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
800 {
801 len = (int)STRLEN(hl_name_table[i]);
802 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
803 {
804 attr |= hl_attr_table[i];
805 off += len;
806 break;
807 }
808 }
809 if (i < 0)
810 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000811 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100812 return FALSE;
813 }
814 if (arg[off] == ',') // another one follows
815 ++off;
816 }
817 if (*key == 'T')
818 {
819 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
820 {
821 if (!init)
822 HL_TABLE()[idx].sg_set |= SG_TERM;
823 HL_TABLE()[idx].sg_term = attr;
824 }
825 }
826 else if (*key == 'C')
827 {
828 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
829 {
830 if (!init)
831 HL_TABLE()[idx].sg_set |= SG_CTERM;
832 HL_TABLE()[idx].sg_cterm = attr;
833 HL_TABLE()[idx].sg_cterm_bold = FALSE;
834 }
835 }
836#if defined(FEAT_GUI) || defined(FEAT_EVAL)
837 else
838 {
839 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
840 {
841 if (!init)
842 HL_TABLE()[idx].sg_set |= SG_GUI;
843 HL_TABLE()[idx].sg_gui = attr;
844 }
845 }
846#endif
847
848 return TRUE;
849}
850
851#ifdef FEAT_GUI
852/*
853 * Set the font for the highlight group at 'idx'.
854 * 'arg' is the font name.
855 * Returns TRUE if the font is changed.
856 */
857 static int
858highlight_set_font(
859 int idx,
860 char_u *arg,
861 int is_normal_group,
862 int is_menu_group,
863 int is_tooltip_group)
864{
865 int did_change = FALSE;
866
867 // in non-GUI fonts are simply ignored
868 if (HL_TABLE()[idx].sg_font_name != NULL
869 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
870 {
871 // Font name didn't change, ignore.
872 }
873 else if (!gui.shell_created)
874 {
875 // GUI not started yet, always accept the name.
876 vim_free(HL_TABLE()[idx].sg_font_name);
877 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
878 did_change = TRUE;
879 }
880 else
881 {
882 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
883# ifdef FEAT_XFONTSET
884 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
885# endif
886 // First, save the current font/fontset.
887 // Then try to allocate the font/fontset.
888 // If the allocation fails, HL_TABLE()[idx].sg_font OR
889 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
890
891 HL_TABLE()[idx].sg_font = NOFONT;
892# ifdef FEAT_XFONTSET
893 HL_TABLE()[idx].sg_fontset = NOFONTSET;
894# endif
895 hl_do_font(idx, arg, is_normal_group, is_menu_group,
896 is_tooltip_group, FALSE);
897
898# ifdef FEAT_XFONTSET
899 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
900 {
901 // New fontset was accepted. Free the old one, if there
902 // was one.
903 gui_mch_free_fontset(temp_sg_fontset);
904 vim_free(HL_TABLE()[idx].sg_font_name);
905 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
906 did_change = TRUE;
907 }
908 else
909 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
910# endif
911 if (HL_TABLE()[idx].sg_font != NOFONT)
912 {
913 // New font was accepted. Free the old one, if there was
914 // one.
915 gui_mch_free_font(temp_sg_font);
916 vim_free(HL_TABLE()[idx].sg_font_name);
917 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
918 did_change = TRUE;
919 }
920 else
921 HL_TABLE()[idx].sg_font = temp_sg_font;
922 }
923
924 return did_change;
925}
926#endif
927
928/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000929 * Set the cterm foreground color for the Normal highlight group to "color" and
930 * the bold attribute to "bold".
931 */
932 static void
933hl_set_ctermfg_normal_group(int color, int bold)
934{
935 cterm_normal_fg_color = color + 1;
936 cterm_normal_fg_bold = bold;
937#ifdef FEAT_GUI
938 // Don't do this if the GUI is used.
939 if (!gui.in_use && !gui.starting)
940#endif
941 {
942 set_must_redraw(UPD_CLEAR);
943 if (termcap_active && color >= 0)
944 term_fg_color(color);
945 }
946}
947
948/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100949 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100950 */
951 static void
952highlight_set_ctermfg(int idx, int color, int is_normal_group)
953{
954 HL_TABLE()[idx].sg_cterm_fg = color + 1;
955 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000956 hl_set_ctermfg_normal_group(color,
957 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100958}
959
960/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000961 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100962 */
963 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000964hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100965{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000966 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100967#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000968 // Don't mess with 'background' if the GUI is used.
969 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100970#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000971 {
972 set_must_redraw(UPD_CLEAR);
973 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100974 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000975 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100976
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000977 if (termcap_active)
978 term_bg_color(color);
979 if (t_colors < 16)
980 dark = (color == 0 || color == 4);
981 // Limit the heuristic to the standard 16 colors
982 else if (color < 16)
983 dark = (color < 7 || color == 8);
984 // Set the 'background' option if the value is
985 // wrong.
986 if (dark != -1
987 && dark != (*p_bg == 'd')
988 && !option_was_set((char_u *)"bg"))
989 {
990 set_option_value_give_err((char_u *)"bg",
991 0L, (char_u *)(dark ? "dark" : "light"), 0);
992 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100993 }
994 }
995 }
996}
997
998/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000999 * Set the cterm background color for the highlight group at 'idx' to 'color'.
1000 */
1001 static void
1002highlight_set_ctermbg(int idx, int color, int is_normal_group)
1003{
1004 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1005 if (is_normal_group)
1006 hl_set_ctermbg_normal_group(color);
1007}
1008
1009/*
1010 * Set the cterm underline color for the Normal highlight group to "color".
1011 */
1012 static void
1013hl_set_ctermul_normal_group(int color)
1014{
1015 cterm_normal_ul_color = color + 1;
1016#ifdef FEAT_GUI
1017 // Don't do this if the GUI is used.
1018 if (!gui.in_use && !gui.starting)
1019#endif
1020 {
1021 set_must_redraw(UPD_CLEAR);
1022 if (termcap_active && color >= 0)
1023 term_ul_color(color);
1024 }
1025}
1026
1027/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001028 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001029 */
1030 static void
1031highlight_set_ctermul(int idx, int color, int is_normal_group)
1032{
1033 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1034 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001035 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001036}
1037
1038/*
PMuncha606f3a2023-11-15 15:35:49 +01001039 * Set the cterm font for the highlight group at 'idx'.
1040 * 'arg' is the color name or the numeric value as a string.
1041 * 'init' is set to TRUE when initializing highlighting.
1042 * Called for the ":highlight" command and the "hlset()" function.
1043 *
1044 * Returns TRUE if the font is set.
1045 */
1046 static int
1047highlight_set_cterm_font(
1048 int idx,
1049 char_u *arg,
1050 int init)
1051{
1052 int font;
1053
1054 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1055 return FALSE;
1056
1057 if (!init)
1058 HL_TABLE()[idx].sg_set |= SG_CTERM;
1059
1060 if (VIM_ISDIGIT(*arg))
1061 font = atoi((char *)arg);
1062 else if (STRICMP(arg, "NONE") == 0)
1063 font = -1;
1064 else
1065 return FALSE;
1066
1067 HL_TABLE()[idx].sg_cterm_font = font + 1;
1068 return TRUE;
1069}
1070
1071/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001072 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1073 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1074 * 'keystart' is the color name/value.
1075 * 'arg' is the color name or the numeric value as a string.
1076 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1077 * 'init' is set to TRUE when initializing highlighting.
1078 * Called for the ":highlight" command and the "hlset()" function.
1079 *
1080 * Returns TRUE if the color is set.
1081 */
1082 static int
1083highlight_set_cterm_color(
1084 int idx,
1085 char_u *key,
1086 char_u *key_start,
1087 char_u *arg,
1088 int is_normal_group,
1089 int init)
1090{
1091 int color;
1092 long i;
1093 int off;
1094
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001095 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1096 return FALSE;
1097
1098 if (!init)
1099 HL_TABLE()[idx].sg_set |= SG_CTERM;
1100
1101 // When setting the foreground color, and previously the "bold"
1102 // flag was set for a light color, reset it now
1103 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001104 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001105 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1106 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1107 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001108
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001109 if (VIM_ISDIGIT(*arg))
1110 color = atoi((char *)arg);
1111 else if (STRICMP(arg, "fg") == 0)
1112 {
1113 if (cterm_normal_fg_color)
1114 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001115 else
1116 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001117 emsg(_(e_fg_color_unknown));
1118 return FALSE;
1119 }
1120 }
1121 else if (STRICMP(arg, "bg") == 0)
1122 {
1123 if (cterm_normal_bg_color > 0)
1124 color = cterm_normal_bg_color - 1;
1125 else
1126 {
1127 emsg(_(e_bg_color_unknown));
1128 return FALSE;
1129 }
1130 }
1131 else if (STRICMP(arg, "ul") == 0)
1132 {
1133 if (cterm_normal_ul_color > 0)
1134 color = cterm_normal_ul_color - 1;
1135 else
1136 {
1137 emsg(_(e_ul_color_unknown));
1138 return FALSE;
1139 }
1140 }
1141 else
1142 {
1143 int bold = MAYBE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001144
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001145 // reduce calls to STRICMP a bit, it can be slow
1146 off = TOUPPER_ASC(*arg);
1147 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1148 if (off == color_names[i][0]
1149 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1150 break;
1151 if (i < 0)
1152 {
1153 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1154 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001155 }
1156
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001157 color = lookup_color(i, key[5] == 'F', &bold);
1158
1159 // set/reset bold attribute to get light foreground
1160 // colors (on some terminals, e.g. "linux")
1161 if (bold == TRUE)
1162 {
1163 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1164 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1165 }
1166 else if (bold == FALSE)
1167 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001168 }
1169
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001170 // Add one to the argument, to avoid zero. Zero is used for
1171 // "NONE", then "color" is -1.
1172 if (key[5] == 'F')
1173 highlight_set_ctermfg(idx, color, is_normal_group);
1174 else if (key[5] == 'B')
1175 highlight_set_ctermbg(idx, color, is_normal_group);
1176 else // ctermul
1177 highlight_set_ctermul(idx, color, is_normal_group);
1178
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001179 return TRUE;
1180}
1181
1182#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1183/*
1184 * Set the GUI foreground color for the highlight group at 'idx'.
1185 * Returns TRUE if the color is set.
1186 */
1187 static int
1188highlight_set_guifg(
1189 int idx,
1190 char_u *arg,
1191 int is_menu_group UNUSED,
1192 int is_scrollbar_group UNUSED,
1193 int is_tooltip_group UNUSED,
1194 int *do_colors UNUSED,
1195 int init)
1196{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001197# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001198 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001199# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001200 char_u **namep;
1201 int did_change = FALSE;
1202
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001203 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1204 return FALSE;
1205
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001206 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001207 if (!init)
1208 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001209
1210# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001211 // In GUI guifg colors are only used when recognized
1212 i = color_name2handle(arg);
1213 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1214 {
1215 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001216# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001217 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1218 {
1219 vim_free(*namep);
1220 if (STRCMP(arg, "NONE") != 0)
1221 *namep = vim_strsave(arg);
1222 else
1223 *namep = NULL;
1224 did_change = TRUE;
1225 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001226# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1227# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001228 if (is_menu_group && gui.menu_fg_pixel != i)
1229 {
1230 gui.menu_fg_pixel = i;
1231 *do_colors = TRUE;
1232 }
1233 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1234 {
1235 gui.scroll_fg_pixel = i;
1236 *do_colors = TRUE;
1237 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001238# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001239 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1240 {
1241 gui.tooltip_fg_pixel = i;
1242 *do_colors = TRUE;
1243 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001244# endif
1245# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001246 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001247# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001248
1249 return did_change;
1250}
1251
1252/*
1253 * Set the GUI background color for the highlight group at 'idx'.
1254 * Returns TRUE if the color is set.
1255 */
1256 static int
1257highlight_set_guibg(
1258 int idx,
1259 char_u *arg,
1260 int is_menu_group UNUSED,
1261 int is_scrollbar_group UNUSED,
1262 int is_tooltip_group UNUSED,
1263 int *do_colors UNUSED,
1264 int init)
1265{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001266# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001267 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001268# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001269 char_u **namep;
1270 int did_change = FALSE;
1271
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001272 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1273 return FALSE;
1274
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001275 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001276 if (!init)
1277 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001278
1279# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001280 // In GUI guibg colors are only used when recognized
1281 i = color_name2handle(arg);
1282 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1283 {
1284 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001285# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001286 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1287 {
1288 vim_free(*namep);
1289 if (STRCMP(arg, "NONE") != 0)
1290 *namep = vim_strsave(arg);
1291 else
1292 *namep = NULL;
1293 did_change = TRUE;
1294 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001295# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1296# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001297 if (is_menu_group && gui.menu_bg_pixel != i)
1298 {
1299 gui.menu_bg_pixel = i;
1300 *do_colors = TRUE;
1301 }
1302 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1303 {
1304 gui.scroll_bg_pixel = i;
1305 *do_colors = TRUE;
1306 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001307# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001308 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1309 {
1310 gui.tooltip_bg_pixel = i;
1311 *do_colors = TRUE;
1312 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001313# endif
1314# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001315 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001316# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001317
1318 return did_change;
1319}
1320
1321/*
1322 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1323 * Returns TRUE if the color is set.
1324 */
1325 static int
1326highlight_set_guisp(int idx, char_u *arg, int init)
1327{
1328# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1329 int i;
1330# endif
1331 int did_change = FALSE;
1332 char_u **namep;
1333
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001334 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1335 return FALSE;
1336
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001337 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001338 if (!init)
1339 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001340
1341# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001342 // In GUI guisp colors are only used when recognized
1343 i = color_name2handle(arg);
1344 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1345 {
1346 HL_TABLE()[idx].sg_gui_sp = i;
1347# endif
1348 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001349 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001350 vim_free(*namep);
1351 if (STRCMP(arg, "NONE") != 0)
1352 *namep = vim_strsave(arg);
1353 else
1354 *namep = NULL;
1355 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001356 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001357# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001358 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001359# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001360
1361 return did_change;
1362}
1363#endif
1364
1365/*
1366 * Set the start/stop terminal codes for a highlight group.
1367 * Returns TRUE if the terminal code is set.
1368 */
1369 static int
1370highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1371{
1372 int off;
1373 char_u buf[100];
1374 int len;
1375 char_u *tname;
1376 char_u *p;
1377
1378 if (!init)
1379 HL_TABLE()[idx].sg_set |= SG_TERM;
1380
1381 // The "start" and "stop" arguments can be a literal escape
1382 // sequence, or a comma separated list of terminal codes.
1383 if (STRNCMP(arg, "t_", 2) == 0)
1384 {
1385 off = 0;
1386 buf[0] = 0;
1387 while (arg[off] != NUL)
1388 {
1389 // Isolate one termcap name
1390 for (len = 0; arg[off + len] &&
1391 arg[off + len] != ','; ++len)
1392 ;
1393 tname = vim_strnsave(arg + off, len);
1394 if (tname == NULL) // out of memory
1395 return FALSE;
1396 // lookup the escape sequence for the item
1397 p = get_term_code(tname);
1398 vim_free(tname);
1399 if (p == NULL) // ignore non-existing things
1400 p = (char_u *)"";
1401
1402 // Append it to the already found stuff
1403 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1404 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001405 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001406 return FALSE;
1407 }
1408 STRCAT(buf, p);
1409
1410 // Advance to the next item
1411 off += len;
1412 if (arg[off] == ',') // another one follows
1413 ++off;
1414 }
1415 }
1416 else
1417 {
1418 // Copy characters from arg[] to buf[], translating <> codes.
1419 for (p = arg, off = 0; off < 100 - 6 && *p; )
1420 {
zeertzjqdb088872022-05-02 22:53:45 +01001421 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001422 if (len > 0) // recognized special char
1423 off += len;
1424 else // copy as normal char
1425 buf[off++] = *p++;
1426 }
1427 buf[off] = NUL;
1428 }
1429
1430 if (STRCMP(buf, "NONE") == 0) // resetting the value
1431 p = NULL;
1432 else
1433 p = vim_strsave(buf);
1434 if (key[2] == 'A')
1435 {
1436 vim_free(HL_TABLE()[idx].sg_start);
1437 HL_TABLE()[idx].sg_start = p;
1438 }
1439 else
1440 {
1441 vim_free(HL_TABLE()[idx].sg_stop);
1442 HL_TABLE()[idx].sg_stop = p;
1443 }
1444 return TRUE;
1445}
1446
1447/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001448 * Handle the ":highlight .." command.
1449 * When using ":hi clear" this is called recursively for each group with
1450 * "forceit" and "init" both TRUE.
1451 */
1452 void
1453do_highlight(
1454 char_u *line,
1455 int forceit,
1456 int init) // TRUE when called for initializing
1457{
1458 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001459 char_u *linep;
1460 char_u *key_start;
1461 char_u *arg_start;
1462 char_u *key = NULL, *arg = NULL;
1463 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001464 int id;
1465 int idx;
1466 hl_group_T item_before;
1467 int did_change = FALSE;
1468 int dodefault = FALSE;
1469 int doclear = FALSE;
1470 int dolink = FALSE;
1471 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001472 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001473#ifdef FEAT_GUI_X11
1474 int is_menu_group = FALSE; // "Menu" group
1475 int is_scrollbar_group = FALSE; // "Scrollbar" group
1476 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001477#else
1478# define is_menu_group 0
1479# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001480# define is_scrollbar_group 0
1481#endif
1482#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1483 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001484#endif
1485#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1486 int did_highlight_changed = FALSE;
1487#endif
1488
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001489 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001490 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001491 {
1492 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1493 // TODO: only call when the group has attributes set
1494 highlight_list_one((int)i);
1495 return;
1496 }
1497
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001498 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001499 name_end = skiptowhite(line);
1500 linep = skipwhite(name_end);
1501
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001502 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001503 if (STRNCMP(line, "default", name_end - line) == 0)
1504 {
1505 dodefault = TRUE;
1506 line = linep;
1507 name_end = skiptowhite(line);
1508 linep = skipwhite(name_end);
1509 }
1510
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001511 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001512 if (STRNCMP(line, "clear", name_end - line) == 0)
1513 doclear = TRUE;
1514 if (STRNCMP(line, "link", name_end - line) == 0)
1515 dolink = TRUE;
1516
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001517 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001518 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001519 {
1520 id = syn_namen2id(line, (int)(name_end - line));
1521 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001522 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001523 else
1524 highlight_list_one(id);
1525 return;
1526 }
1527
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001528 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001529 if (dolink)
1530 {
1531 char_u *from_start = linep;
1532 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001533 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001534 char_u *to_start;
1535 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001536 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001537
1538 from_end = skiptowhite(from_start);
1539 to_start = skipwhite(from_end);
1540 to_end = skiptowhite(to_start);
1541
Bram Moolenaar1966c242020-04-20 22:42:32 +02001542 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001543 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001544 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001545 return;
1546 }
1547
Bram Moolenaar1966c242020-04-20 22:42:32 +02001548 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001549 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001550 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001551 return;
1552 }
1553
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001554 from_len = (int)(from_end - from_start);
1555 to_len = (int)(to_end - to_start);
1556 highlight_group_link(from_start, from_len, to_start, to_len,
1557 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001558 return;
1559 }
1560
1561 if (doclear)
1562 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001563 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001564 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001565 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001566 // ":highlight clear" without group name
1567 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001568 return;
1569 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001570 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001571 name_end = skiptowhite(line);
1572 linep = skipwhite(name_end);
1573 }
1574
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001575 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001576 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001577 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001578 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001579 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001580
1581 // Return if "default" was used and the group already has settings.
1582 if (dodefault && hl_has_settings(idx, TRUE))
1583 return;
1584
1585 // Make a copy so we can check if any attribute actually changed.
1586 item_before = HL_TABLE()[idx];
1587
1588 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1589 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001590#ifdef FEAT_GUI_X11
1591 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1592 is_menu_group = TRUE;
1593 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1594 is_scrollbar_group = TRUE;
1595 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1596 is_tooltip_group = TRUE;
1597#endif
1598
1599 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1600 if (doclear || (forceit && init))
1601 {
1602 highlight_clear(idx);
1603 if (!doclear)
1604 HL_TABLE()[idx].sg_set = 0;
1605 }
1606
1607 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001608 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001609 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001610 key_start = linep;
1611 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001612 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001613 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001614 error = TRUE;
1615 break;
1616 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001617
1618 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1619 // or "guibg").
1620 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1621 ++linep;
1622 vim_free(key);
1623 key = vim_strnsave_up(key_start, linep - key_start);
1624 if (key == NULL)
1625 {
1626 error = TRUE;
1627 break;
1628 }
1629 linep = skipwhite(linep);
1630
1631 if (STRCMP(key, "NONE") == 0)
1632 {
1633 if (!init || HL_TABLE()[idx].sg_set == 0)
1634 {
1635 if (!init)
1636 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1637 highlight_clear(idx);
1638 }
1639 continue;
1640 }
1641
1642 // Check for the equal sign.
1643 if (*linep != '=')
1644 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001645 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001646 error = TRUE;
1647 break;
1648 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001649 ++linep;
1650
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001651 // Isolate the argument.
1652 linep = skipwhite(linep);
1653 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001654 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001655 arg_start = ++linep;
1656 linep = vim_strchr(linep, '\'');
1657 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001658 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001659 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001660 error = TRUE;
1661 break;
1662 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001663 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001664 else
1665 {
1666 arg_start = linep;
1667 linep = skiptowhite(linep);
1668 }
1669 if (linep == arg_start)
1670 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001671 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001672 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001673 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001674 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001675 vim_free(arg);
1676 arg = vim_strnsave(arg_start, linep - arg_start);
1677 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001678 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001679 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001680 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001681 }
1682 if (*linep == '\'')
1683 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001684
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001685 // Store the argument.
1686 if (STRCMP(key, "TERM") == 0
1687 || STRCMP(key, "CTERM") == 0
1688 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001689 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001690 if (!highlight_set_termgui_attr(idx, key, arg, init))
1691 {
1692 error = TRUE;
1693 break;
1694 }
1695 }
1696 else if (STRCMP(key, "FONT") == 0)
1697 {
1698 // in non-GUI fonts are simply ignored
1699#ifdef FEAT_GUI
1700 if (highlight_set_font(idx, arg, is_normal_group,
1701 is_menu_group, is_tooltip_group))
1702 did_change = TRUE;
1703#endif
1704 }
1705 else if (STRCMP(key, "CTERMFG") == 0
1706 || STRCMP(key, "CTERMBG") == 0
1707 || STRCMP(key, "CTERMUL") == 0)
1708 {
1709 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1710 is_normal_group, init))
1711 {
1712 error = TRUE;
1713 break;
1714 }
1715 }
PMuncha606f3a2023-11-15 15:35:49 +01001716 else if (STRCMP(key, "CTERMFONT") == 0)
1717 {
1718 if (!highlight_set_cterm_font(idx, arg, init))
1719 {
1720 error = TRUE;
1721 break;
1722 }
1723 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001724 else if (STRCMP(key, "GUIFG") == 0)
1725 {
1726#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1727 if (highlight_set_guifg(idx, arg, is_menu_group,
1728 is_scrollbar_group, is_tooltip_group,
1729 &do_colors, init))
1730 did_change = TRUE;
1731#endif
1732 }
1733 else if (STRCMP(key, "GUIBG") == 0)
1734 {
1735#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1736 if (highlight_set_guibg(idx, arg, is_menu_group,
1737 is_scrollbar_group, is_tooltip_group,
1738 &do_colors, init))
1739 did_change = TRUE;
1740#endif
1741 }
1742 else if (STRCMP(key, "GUISP") == 0)
1743 {
1744#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1745 if (highlight_set_guisp(idx, arg, init))
1746 did_change = TRUE;
1747#endif
1748 }
1749 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1750 {
1751 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1752 {
1753 error = TRUE;
1754 break;
1755 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001756 }
1757 else
1758 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001759 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001760 error = TRUE;
1761 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001762 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001763 HL_TABLE()[idx].sg_cleared = FALSE;
1764
1765 // When highlighting has been given for a group, don't link it.
1766 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1767 HL_TABLE()[idx].sg_link = 0;
1768
1769 // Continue with next argument.
1770 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001771 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001772
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001773 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001774 if (error && idx == highlight_ga.ga_len)
1775 syn_unadd_group();
1776 else
1777 {
1778 if (is_normal_group)
1779 {
1780 HL_TABLE()[idx].sg_term_attr = 0;
1781 HL_TABLE()[idx].sg_cterm_attr = 0;
1782#ifdef FEAT_GUI
1783 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001784 // Need to update all groups, because they might be using "bg"
1785 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001786#endif
1787#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1788 if (USE_24BIT)
1789 {
1790 highlight_gui_started();
1791 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001792 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001793 }
1794#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001795#ifdef FEAT_VTP
1796 control_console_color_rgb();
1797#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001798 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001799#ifdef FEAT_GUI_X11
1800# ifdef FEAT_MENU
1801 else if (is_menu_group)
1802 {
1803 if (gui.in_use && do_colors)
1804 gui_mch_new_menu_colors();
1805 }
1806# endif
1807 else if (is_scrollbar_group)
1808 {
1809 if (gui.in_use && do_colors)
1810 gui_new_scrollbar_colors();
1811 else
1812 set_hl_attr(idx);
1813 }
1814# ifdef FEAT_BEVAL_GUI
1815 else if (is_tooltip_group)
1816 {
1817 if (gui.in_use && do_colors)
1818 gui_mch_new_tooltip_colors();
1819 }
1820# endif
1821#endif
1822 else
1823 set_hl_attr(idx);
1824#ifdef FEAT_EVAL
1825 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001826 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001827#endif
1828 }
1829
1830 vim_free(key);
1831 vim_free(arg);
1832
1833 // Only call highlight_changed() once, after a sequence of highlight
1834 // commands, and only if an attribute actually changed.
1835 if ((did_change
1836 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1837#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1838 && !did_highlight_changed
1839#endif
1840 )
1841 {
1842 // Do not trigger a redraw when highlighting is changed while
1843 // redrawing. This may happen when evaluating 'statusline' changes the
1844 // StatusLine group.
1845 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001846 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001847 need_highlight_changed = TRUE;
1848 }
1849}
1850
1851#if defined(EXITFREE) || defined(PROTO)
1852 void
1853free_highlight(void)
1854{
1855 int i;
1856
1857 for (i = 0; i < highlight_ga.ga_len; ++i)
1858 {
1859 highlight_clear(i);
1860 vim_free(HL_TABLE()[i].sg_name);
1861 vim_free(HL_TABLE()[i].sg_name_u);
1862 }
1863 ga_clear(&highlight_ga);
1864}
1865#endif
1866
1867/*
1868 * Reset the cterm colors to what they were before Vim was started, if
1869 * possible. Otherwise reset them to zero.
1870 */
1871 void
1872restore_cterm_colors(void)
1873{
1874#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1875 // Since t_me has been set, this probably means that the user
1876 // wants to use this as default colors. Need to reset default
1877 // background/foreground colors.
1878 mch_set_normal_colors();
1879#else
1880# ifdef VIMDLL
1881 if (!gui.in_use)
1882 {
1883 mch_set_normal_colors();
1884 return;
1885 }
1886# endif
1887 cterm_normal_fg_color = 0;
1888 cterm_normal_fg_bold = 0;
1889 cterm_normal_bg_color = 0;
1890# ifdef FEAT_TERMGUICOLORS
1891 cterm_normal_fg_gui_color = INVALCOLOR;
1892 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001893 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001894# endif
1895#endif
1896}
1897
1898/*
1899 * Return TRUE if highlight group "idx" has any settings.
1900 * When "check_link" is TRUE also check for an existing link.
1901 */
1902 static int
1903hl_has_settings(int idx, int check_link)
1904{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001905 return HL_TABLE()[idx].sg_cleared == 0
1906 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001907 || HL_TABLE()[idx].sg_cterm_attr != 0
1908 || HL_TABLE()[idx].sg_cterm_fg != 0
1909 || HL_TABLE()[idx].sg_cterm_bg != 0
PMuncha606f3a2023-11-15 15:35:49 +01001910 || HL_TABLE()[idx].sg_cterm_font != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001911#ifdef FEAT_GUI
1912 || HL_TABLE()[idx].sg_gui_attr != 0
1913 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1914 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1915 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1916 || HL_TABLE()[idx].sg_font_name != NULL
1917#endif
1918 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1919}
1920
1921/*
1922 * Clear highlighting for one group.
1923 */
1924 static void
1925highlight_clear(int idx)
1926{
1927 HL_TABLE()[idx].sg_cleared = TRUE;
1928
1929 HL_TABLE()[idx].sg_term = 0;
1930 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1931 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1932 HL_TABLE()[idx].sg_term_attr = 0;
1933 HL_TABLE()[idx].sg_cterm = 0;
1934 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1935 HL_TABLE()[idx].sg_cterm_fg = 0;
1936 HL_TABLE()[idx].sg_cterm_bg = 0;
1937 HL_TABLE()[idx].sg_cterm_attr = 0;
PMuncha606f3a2023-11-15 15:35:49 +01001938 HL_TABLE()[idx].sg_cterm_font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001939#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1940 HL_TABLE()[idx].sg_gui = 0;
1941 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1942 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1943 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1944#endif
1945#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1946 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1947 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001948 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001949#endif
1950#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001951 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1952 HL_TABLE()[idx].sg_font = NOFONT;
1953# ifdef FEAT_XFONTSET
1954 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1955 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1956# endif
1957 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1958 HL_TABLE()[idx].sg_gui_attr = 0;
1959#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001960 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001961 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001962#ifdef FEAT_EVAL
1963 // Since we set the default link, set the location to where the default
1964 // link was set.
1965 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001966#endif
1967}
1968
1969#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1970/*
1971 * Set the normal foreground and background colors according to the "Normal"
1972 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1973 * "Tooltip" colors.
1974 */
1975 void
1976set_normal_colors(void)
1977{
1978# ifdef FEAT_GUI
1979# ifdef FEAT_TERMGUICOLORS
1980 if (gui.in_use)
1981# endif
1982 {
1983 if (set_group_colors((char_u *)"Normal",
1984 &gui.norm_pixel, &gui.back_pixel,
1985 FALSE, TRUE, FALSE))
1986 {
1987 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001988 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001989 }
1990# ifdef FEAT_GUI_X11
1991 if (set_group_colors((char_u *)"Menu",
1992 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1993 TRUE, FALSE, FALSE))
1994 {
1995# ifdef FEAT_MENU
1996 gui_mch_new_menu_colors();
1997# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001998 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001999 }
2000# ifdef FEAT_BEVAL_GUI
2001 if (set_group_colors((char_u *)"Tooltip",
2002 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
2003 FALSE, FALSE, TRUE))
2004 {
2005# ifdef FEAT_TOOLBAR
2006 gui_mch_new_tooltip_colors();
2007# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002008 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002009 }
2010# endif
2011 if (set_group_colors((char_u *)"Scrollbar",
2012 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
2013 FALSE, FALSE, FALSE))
2014 {
2015 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002016 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002017 }
2018# endif
2019 }
2020# endif
2021# ifdef FEAT_TERMGUICOLORS
2022# ifdef FEAT_GUI
2023 else
2024# endif
2025 {
2026 int idx;
2027
2028 idx = syn_name2id((char_u *)"Normal") - 1;
2029 if (idx >= 0)
2030 {
2031 gui_do_one_color(idx, FALSE, FALSE);
2032
2033 // If the normal fg or bg color changed a complete redraw is
2034 // required.
2035 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
2036 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
2037 {
2038 // if the GUI color is INVALCOLOR then we use the default cterm
2039 // color
2040 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
2041 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002042 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002043 }
2044 }
2045 }
2046# endif
2047}
2048#endif
2049
2050#if defined(FEAT_GUI) || defined(PROTO)
2051/*
2052 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2053 */
2054 static int
2055set_group_colors(
2056 char_u *name,
2057 guicolor_T *fgp,
2058 guicolor_T *bgp,
2059 int do_menu,
2060 int use_norm,
2061 int do_tooltip)
2062{
2063 int idx;
2064
2065 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002066 if (idx < 0)
2067 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002068
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002069 gui_do_one_color(idx, do_menu, do_tooltip);
2070
2071 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2072 *fgp = HL_TABLE()[idx].sg_gui_fg;
2073 else if (use_norm)
2074 *fgp = gui.def_norm_pixel;
2075 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2076 *bgp = HL_TABLE()[idx].sg_gui_bg;
2077 else if (use_norm)
2078 *bgp = gui.def_back_pixel;
2079 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002080}
2081
2082/*
2083 * Get the font of the "Normal" group.
2084 * Returns "" when it's not found or not set.
2085 */
2086 char_u *
2087hl_get_font_name(void)
2088{
2089 int id;
2090 char_u *s;
2091
2092 id = syn_name2id((char_u *)"Normal");
2093 if (id > 0)
2094 {
2095 s = HL_TABLE()[id - 1].sg_font_name;
2096 if (s != NULL)
2097 return s;
2098 }
2099 return (char_u *)"";
2100}
2101
2102/*
2103 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2104 * actually chosen to be used.
2105 */
2106 void
2107hl_set_font_name(char_u *font_name)
2108{
2109 int id;
2110
2111 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002112 if (id <= 0)
2113 return;
2114
2115 vim_free(HL_TABLE()[id - 1].sg_font_name);
2116 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002117}
2118
2119/*
2120 * Set background color for "Normal" group. Called by gui_set_bg_color()
2121 * when the color is known.
2122 */
2123 void
2124hl_set_bg_color_name(
2125 char_u *name) // must have been allocated
2126{
2127 int id;
2128
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002129 if (name == NULL)
2130 return;
2131
2132 id = syn_name2id((char_u *)"Normal");
2133 if (id <= 0)
2134 return;
2135
2136 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2137 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002138}
2139
2140/*
2141 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2142 * when the color is known.
2143 */
2144 void
2145hl_set_fg_color_name(
2146 char_u *name) // must have been allocated
2147{
2148 int id;
2149
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002150 if (name == NULL)
2151 return;
2152
2153 id = syn_name2id((char_u *)"Normal");
2154 if (id <= 0)
2155 return;
2156
2157 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2158 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002159}
2160
2161/*
2162 * Return the handle for a font name.
2163 * Returns NOFONT when failed.
2164 */
2165 static GuiFont
2166font_name2handle(char_u *name)
2167{
2168 if (STRCMP(name, "NONE") == 0)
2169 return NOFONT;
2170
2171 return gui_mch_get_font(name, TRUE);
2172}
2173
2174# ifdef FEAT_XFONTSET
2175/*
2176 * Return the handle for a fontset name.
2177 * Returns NOFONTSET when failed.
2178 */
2179 static GuiFontset
2180fontset_name2handle(char_u *name, int fixed_width)
2181{
2182 if (STRCMP(name, "NONE") == 0)
2183 return NOFONTSET;
2184
2185 return gui_mch_get_fontset(name, TRUE, fixed_width);
2186}
2187# endif
2188
2189/*
2190 * Get the font or fontset for one highlight group.
2191 */
2192 static void
2193hl_do_font(
2194 int idx,
2195 char_u *arg,
2196 int do_normal, // set normal font
2197 int do_menu UNUSED, // set menu font
2198 int do_tooltip UNUSED, // set tooltip font
2199 int free_font) // free current font/fontset
2200{
2201# ifdef FEAT_XFONTSET
2202 // If 'guifontset' is not empty, first try using the name as a
2203 // fontset. If that doesn't work, use it as a font name.
2204 if (*p_guifontset != NUL
2205# ifdef FONTSET_ALWAYS
2206 || do_menu
2207# endif
2208# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002209 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002210 || do_tooltip
2211# endif
2212 )
2213 {
2214 if (free_font)
2215 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2216 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2217# ifdef FONTSET_ALWAYS
2218 || do_menu
2219# endif
2220# ifdef FEAT_BEVAL_TIP
2221 || do_tooltip
2222# endif
2223 );
2224 }
2225 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2226 {
2227 // If it worked and it's the Normal group, use it as the normal
2228 // fontset. Same for the Menu group.
2229 if (do_normal)
2230 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002231# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002232 if (do_menu)
2233 {
2234# ifdef FONTSET_ALWAYS
2235 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2236# else
2237 // YIKES! This is a bug waiting to crash the program
2238 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2239# endif
2240 gui_mch_new_menu_font();
2241 }
2242# ifdef FEAT_BEVAL_GUI
2243 if (do_tooltip)
2244 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002245 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002246 // displaying a single font and a fontset.
2247 // If the XtNinternational resource is set to True at widget
2248 // creation, then a fontset is always used, otherwise an
2249 // XFontStruct is used.
2250 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2251 gui_mch_new_tooltip_font();
2252 }
2253# endif
2254# endif
2255 }
2256 else
2257# endif
2258 {
2259 if (free_font)
2260 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2261 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2262 // If it worked and it's the Normal group, use it as the
2263 // normal font. Same for the Menu group.
2264 if (HL_TABLE()[idx].sg_font != NOFONT)
2265 {
2266 if (do_normal)
2267 gui_init_font(arg, FALSE);
2268#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002269# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002270 if (do_menu)
2271 {
2272 gui.menu_font = HL_TABLE()[idx].sg_font;
2273 gui_mch_new_menu_font();
2274 }
2275# endif
2276#endif
2277 }
2278 }
2279}
2280
2281#endif // FEAT_GUI
2282
2283#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2284/*
2285 * Return the handle for a color name.
2286 * Returns INVALCOLOR when failed.
2287 */
2288 guicolor_T
2289color_name2handle(char_u *name)
2290{
2291 if (STRCMP(name, "NONE") == 0)
2292 return INVALCOLOR;
2293
2294 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2295 {
2296#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2297 if (gui.in_use)
2298#endif
2299#ifdef FEAT_GUI
2300 return gui.norm_pixel;
2301#endif
2302#ifdef FEAT_TERMGUICOLORS
2303 if (cterm_normal_fg_gui_color != INVALCOLOR)
2304 return cterm_normal_fg_gui_color;
2305 // Guess that the foreground is black or white.
2306 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2307#endif
2308 }
2309 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2310 {
2311#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2312 if (gui.in_use)
2313#endif
2314#ifdef FEAT_GUI
2315 return gui.back_pixel;
2316#endif
2317#ifdef FEAT_TERMGUICOLORS
2318 if (cterm_normal_bg_gui_color != INVALCOLOR)
2319 return cterm_normal_bg_gui_color;
2320 // Guess that the background is white or black.
2321 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2322#endif
2323 }
2324
2325 return GUI_GET_COLOR(name);
2326}
Drew Vogele30d1022021-10-24 20:35:07 +01002327
2328// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2329// values as used by the MS-Windows GDI api. It should be used only for
2330// MS-Windows GDI builds.
2331# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2332# undef RGB
2333# endif
2334# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002335# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002336# endif
2337
2338# ifdef VIMDLL
2339 static guicolor_T
2340gui_adjust_rgb(guicolor_T c)
2341{
2342 if (gui.in_use)
2343 return c;
2344 else
2345 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2346}
2347# else
2348# define gui_adjust_rgb(c) (c)
2349# endif
2350
2351 static int
2352hex_digit(int c)
2353{
Keith Thompson184f71c2024-01-04 21:19:04 +01002354 if (SAFE_isdigit(c))
Drew Vogele30d1022021-10-24 20:35:07 +01002355 return c - '0';
2356 c = TOLOWER_ASC(c);
2357 if (c >= 'a' && c <= 'f')
2358 return c - 'a' + 10;
2359 return 0x1ffffff;
2360}
2361
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002362 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002363decode_hex_color(char_u *hex)
2364{
2365 guicolor_T color;
2366
2367 if (hex[0] != '#' || STRLEN(hex) != 7)
2368 return INVALCOLOR;
2369
2370 // Name is in "#rrggbb" format
2371 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2372 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2373 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2374 if (color > 0xffffff)
2375 return INVALCOLOR;
2376 return gui_adjust_rgb(color);
2377}
2378
Bram Moolenaar2a521962021-10-25 10:30:14 +01002379#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002380// Returns the color currently mapped to the given name or INVALCOLOR if no
2381// such name exists in the color table. The convention is to use lowercase for
2382// all keys in the v:colornames dictionary. The value can be either a string in
2383// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002384 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002385colorname2rgb(char_u *name)
2386{
2387 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2388 char_u *lc_name;
2389 dictitem_T *colentry;
2390 char_u *colstr;
2391 varnumber_T colnum;
2392
2393 lc_name = strlow_save(name);
2394 if (lc_name == NULL)
2395 return INVALCOLOR;
2396
2397 colentry = dict_find(colornames_table, lc_name, -1);
2398 vim_free(lc_name);
2399 if (colentry == NULL)
2400 return INVALCOLOR;
2401
2402 if (colentry->di_tv.v_type == VAR_STRING)
2403 {
2404 colstr = tv_get_string_strict(&colentry->di_tv);
2405 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2406 {
2407 return decode_hex_color(colstr);
2408 }
2409 else
2410 {
2411 semsg(_(e_bad_color_string_str), colstr);
2412 return INVALCOLOR;
2413 }
2414 }
2415
2416 if (colentry->di_tv.v_type == VAR_NUMBER)
2417 {
2418 colnum = tv_get_number(&colentry->di_tv);
2419 return (guicolor_T)colnum;
2420 }
2421
2422 return INVALCOLOR;
2423}
2424
Drew Vogele30d1022021-10-24 20:35:07 +01002425#endif
2426
2427 guicolor_T
2428gui_get_color_cmn(char_u *name)
2429{
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002430 int i;
Drew Vogele30d1022021-10-24 20:35:07 +01002431 guicolor_T color;
2432
2433 struct rgbcolor_table_S {
2434 char_u *color_name;
2435 guicolor_T color;
2436 };
2437
2438 // Only non X11 colors (not present in rgb.txt) and colors in
2439 // color_names[], useful when $VIMRUNTIME is not found,.
2440 static struct rgbcolor_table_S rgb_table[] = {
2441 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2442 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2443 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2444 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2445 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2446 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2447 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2448 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2449 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2450 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2451 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2452 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2453 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2454 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2455 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2456 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2457 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2458 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2459 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2460 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2461 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2462 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2463 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2464 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2465 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2466 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2467 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2468 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2469 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2470 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2471 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2472 };
2473
2474 color = decode_hex_color(name);
2475 if (color != INVALCOLOR)
2476 return color;
2477
2478 // Check if the name is one of the colors we know
2479 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2480 if (STRICMP(name, rgb_table[i].color_name) == 0)
2481 return gui_adjust_rgb(rgb_table[i].color);
2482
2483#if defined(FEAT_EVAL)
2484 /*
2485 * Not a traditional color. Load additional color aliases and then consult the alias table.
2486 */
2487
2488 color = colorname2rgb(name);
2489 if (color == INVALCOLOR)
2490 {
2491 load_default_colors_lists();
2492 color = colorname2rgb(name);
2493 }
2494
2495 return color;
2496#else
2497 return INVALCOLOR;
2498#endif
2499}
2500
2501 guicolor_T
2502gui_get_rgb_color_cmn(int r, int g, int b)
2503{
2504 guicolor_T color = RGB(r, g, b);
2505
2506 if (color > 0xffffff)
2507 return INVALCOLOR;
2508 return gui_adjust_rgb(color);
2509}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002510#endif
2511
2512/*
2513 * Table with the specifications for an attribute number.
2514 * Note that this table is used by ALL buffers. This is required because the
2515 * GUI can redraw at any time for any buffer.
2516 */
2517static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2518
2519#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2520
2521static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2522
2523#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2524
2525#ifdef FEAT_GUI
2526static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2527
2528#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2529#endif
2530
2531/*
2532 * Return the attr number for a set of colors and font.
2533 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2534 * if the combination is new.
2535 * Return 0 for error (no more room).
2536 */
2537 static int
2538get_attr_entry(garray_T *table, attrentry_T *aep)
2539{
2540 int i;
2541 attrentry_T *taep;
2542 static int recursive = FALSE;
2543
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002544 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002545 table->ga_itemsize = sizeof(attrentry_T);
2546 table->ga_growsize = 7;
2547
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002548 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002549 for (i = 0; i < table->ga_len; ++i)
2550 {
2551 taep = &(((attrentry_T *)table->ga_data)[i]);
2552 if ( aep->ae_attr == taep->ae_attr
2553 && (
2554#ifdef FEAT_GUI
2555 (table == &gui_attr_table
2556 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2557 && aep->ae_u.gui.bg_color
2558 == taep->ae_u.gui.bg_color
2559 && aep->ae_u.gui.sp_color
2560 == taep->ae_u.gui.sp_color
2561 && aep->ae_u.gui.font == taep->ae_u.gui.font
2562# ifdef FEAT_XFONTSET
2563 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2564# endif
2565 ))
2566 ||
2567#endif
2568 (table == &term_attr_table
2569 && (aep->ae_u.term.start == NULL)
2570 == (taep->ae_u.term.start == NULL)
2571 && (aep->ae_u.term.start == NULL
2572 || STRCMP(aep->ae_u.term.start,
2573 taep->ae_u.term.start) == 0)
2574 && (aep->ae_u.term.stop == NULL)
2575 == (taep->ae_u.term.stop == NULL)
2576 && (aep->ae_u.term.stop == NULL
2577 || STRCMP(aep->ae_u.term.stop,
2578 taep->ae_u.term.stop) == 0))
2579 || (table == &cterm_attr_table
2580 && aep->ae_u.cterm.fg_color
2581 == taep->ae_u.cterm.fg_color
2582 && aep->ae_u.cterm.bg_color
2583 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002584 && aep->ae_u.cterm.ul_color
2585 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002586 && aep->ae_u.cterm.font
2587 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002588#ifdef FEAT_TERMGUICOLORS
2589 && aep->ae_u.cterm.fg_rgb
2590 == taep->ae_u.cterm.fg_rgb
2591 && aep->ae_u.cterm.bg_rgb
2592 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002593 && aep->ae_u.cterm.ul_rgb
2594 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002595#endif
2596 )))
2597
2598 return i + ATTR_OFF;
2599 }
2600
2601 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2602 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002603 // Running out of attribute entries! remove all attributes, and
2604 // compute new ones for all groups.
2605 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002606 if (recursive)
2607 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002608 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002609 return 0;
2610 }
2611 recursive = TRUE;
2612
2613 clear_hl_tables();
2614
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002615 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002616
2617 for (i = 0; i < highlight_ga.ga_len; ++i)
2618 set_hl_attr(i);
2619
2620 recursive = FALSE;
2621 }
2622
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002623 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002624 if (ga_grow(table, 1) == FAIL)
2625 return 0;
2626
2627 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002628 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002629 taep->ae_attr = aep->ae_attr;
2630#ifdef FEAT_GUI
2631 if (table == &gui_attr_table)
2632 {
2633 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2634 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2635 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2636 taep->ae_u.gui.font = aep->ae_u.gui.font;
2637# ifdef FEAT_XFONTSET
2638 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2639# endif
2640 }
2641#endif
2642 if (table == &term_attr_table)
2643 {
2644 if (aep->ae_u.term.start == NULL)
2645 taep->ae_u.term.start = NULL;
2646 else
2647 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2648 if (aep->ae_u.term.stop == NULL)
2649 taep->ae_u.term.stop = NULL;
2650 else
2651 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2652 }
2653 else if (table == &cterm_attr_table)
2654 {
2655 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2656 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002657 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002658 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002659#ifdef FEAT_TERMGUICOLORS
2660 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2661 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002662 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002663#endif
2664 }
2665 ++table->ga_len;
2666 return (table->ga_len - 1 + ATTR_OFF);
2667}
2668
2669#if defined(FEAT_TERMINAL) || defined(PROTO)
2670/*
2671 * Get an attribute index for a cterm entry.
2672 * Uses an existing entry when possible or adds one when needed.
2673 */
2674 int
2675get_cterm_attr_idx(int attr, int fg, int bg)
2676{
2677 attrentry_T at_en;
2678
Bram Moolenaara80faa82020-04-12 19:37:17 +02002679 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002680#ifdef FEAT_TERMGUICOLORS
2681 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2682 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002683 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002684#endif
2685 at_en.ae_attr = attr;
2686 at_en.ae_u.cterm.fg_color = fg;
2687 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002688 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002689 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002690 return get_attr_entry(&cterm_attr_table, &at_en);
2691}
2692#endif
2693
2694#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2695/*
2696 * Get an attribute index for a 'termguicolors' entry.
2697 * Uses an existing entry when possible or adds one when needed.
2698 */
2699 int
2700get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2701{
2702 attrentry_T at_en;
2703
Bram Moolenaara80faa82020-04-12 19:37:17 +02002704 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002705 at_en.ae_attr = attr;
2706 if (fg == INVALCOLOR && bg == INVALCOLOR)
2707 {
2708 // If both GUI colors are not set fall back to the cterm colors. Helps
2709 // if the GUI only has an attribute, such as undercurl.
2710 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2711 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2712 }
2713 else
2714 {
2715 at_en.ae_u.cterm.fg_rgb = fg;
2716 at_en.ae_u.cterm.bg_rgb = bg;
2717 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002718 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002719 return get_attr_entry(&cterm_attr_table, &at_en);
2720}
2721#endif
2722
2723#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2724/*
2725 * Get an attribute index for a cterm entry.
2726 * Uses an existing entry when possible or adds one when needed.
2727 */
2728 int
2729get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2730{
2731 attrentry_T at_en;
2732
Bram Moolenaara80faa82020-04-12 19:37:17 +02002733 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002734 at_en.ae_attr = attr;
2735 at_en.ae_u.gui.fg_color = fg;
2736 at_en.ae_u.gui.bg_color = bg;
2737 return get_attr_entry(&gui_attr_table, &at_en);
2738}
2739#endif
2740
2741/*
2742 * Clear all highlight tables.
2743 */
2744 void
2745clear_hl_tables(void)
2746{
2747 int i;
2748 attrentry_T *taep;
2749
2750#ifdef FEAT_GUI
2751 ga_clear(&gui_attr_table);
2752#endif
2753 for (i = 0; i < term_attr_table.ga_len; ++i)
2754 {
2755 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2756 vim_free(taep->ae_u.term.start);
2757 vim_free(taep->ae_u.term.stop);
2758 }
2759 ga_clear(&term_attr_table);
2760 ga_clear(&cterm_attr_table);
2761}
2762
2763/*
2764 * Combine special attributes (e.g., for spelling) with other attributes
2765 * (e.g., for syntax highlighting).
2766 * "prim_attr" overrules "char_attr".
2767 * This creates a new group when required.
2768 * Since we expect there to be few spelling mistakes we don't cache the
2769 * result.
2770 * Return the resulting attributes.
2771 */
2772 int
2773hl_combine_attr(int char_attr, int prim_attr)
2774{
2775 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002776 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002777 attrentry_T new_en;
2778
2779 if (char_attr == 0)
2780 return prim_attr;
2781 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2782 return ATTR_COMBINE(char_attr, prim_attr);
2783#ifdef FEAT_GUI
2784 if (gui.in_use)
2785 {
2786 if (char_attr > HL_ALL)
2787 char_aep = syn_gui_attr2entry(char_attr);
2788 if (char_aep != NULL)
2789 new_en = *char_aep;
2790 else
2791 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002792 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002793 new_en.ae_u.gui.fg_color = INVALCOLOR;
2794 new_en.ae_u.gui.bg_color = INVALCOLOR;
2795 new_en.ae_u.gui.sp_color = INVALCOLOR;
2796 if (char_attr <= HL_ALL)
2797 new_en.ae_attr = char_attr;
2798 }
2799
2800 if (prim_attr <= HL_ALL)
2801 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2802 else
2803 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002804 prim_aep = syn_gui_attr2entry(prim_attr);
2805 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002806 {
2807 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002808 prim_aep->ae_attr);
2809 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2810 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2811 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2812 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2813 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2814 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2815 if (prim_aep->ae_u.gui.font != NOFONT)
2816 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002817# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002818 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2819 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002820# endif
2821 }
2822 }
2823 return get_attr_entry(&gui_attr_table, &new_en);
2824 }
2825#endif
2826
2827 if (IS_CTERM)
2828 {
2829 if (char_attr > HL_ALL)
2830 char_aep = syn_cterm_attr2entry(char_attr);
2831 if (char_aep != NULL)
2832 new_en = *char_aep;
2833 else
2834 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002835 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002836#ifdef FEAT_TERMGUICOLORS
2837 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2838 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002839 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002840#endif
2841 if (char_attr <= HL_ALL)
2842 new_en.ae_attr = char_attr;
2843 }
2844
2845 if (prim_attr <= HL_ALL)
2846 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2847 else
2848 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002849 prim_aep = syn_cterm_attr2entry(prim_attr);
2850 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002851 {
2852 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002853 prim_aep->ae_attr);
2854 if (prim_aep->ae_u.cterm.fg_color > 0)
2855 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2856 if (prim_aep->ae_u.cterm.bg_color > 0)
2857 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2858 if (prim_aep->ae_u.cterm.ul_color > 0)
2859 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002860 if (prim_aep->ae_u.cterm.font > 0)
2861 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002862#ifdef FEAT_TERMGUICOLORS
2863 // If both fg and bg are not set fall back to cterm colors.
2864 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002865 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2866 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002867 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002868 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002869 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002870 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002871 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2872 }
2873 else
2874 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002875 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2876 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2877 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2878 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002879 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002880 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2881 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002882#endif
2883 }
2884 }
2885 return get_attr_entry(&cterm_attr_table, &new_en);
2886 }
2887
2888 if (char_attr > HL_ALL)
2889 char_aep = syn_term_attr2entry(char_attr);
2890 if (char_aep != NULL)
2891 new_en = *char_aep;
2892 else
2893 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002894 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002895 if (char_attr <= HL_ALL)
2896 new_en.ae_attr = char_attr;
2897 }
2898
2899 if (prim_attr <= HL_ALL)
2900 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2901 else
2902 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002903 prim_aep = syn_term_attr2entry(prim_attr);
2904 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002905 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002906 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2907 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002908 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002909 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2910 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002911 }
2912 }
2913 }
2914 return get_attr_entry(&term_attr_table, &new_en);
2915}
2916
2917#ifdef FEAT_GUI
2918 attrentry_T *
2919syn_gui_attr2entry(int attr)
2920{
2921 attr -= ATTR_OFF;
2922 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2923 return NULL;
2924 return &(GUI_ATTR_ENTRY(attr));
2925}
2926#endif
2927
2928/*
2929 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2930 * Only to be used when "attr" > HL_ALL.
2931 */
2932 int
2933syn_attr2attr(int attr)
2934{
2935 attrentry_T *aep;
2936
2937#ifdef FEAT_GUI
2938 if (gui.in_use)
2939 aep = syn_gui_attr2entry(attr);
2940 else
2941#endif
2942 if (IS_CTERM)
2943 aep = syn_cterm_attr2entry(attr);
2944 else
2945 aep = syn_term_attr2entry(attr);
2946
2947 if (aep == NULL) // highlighting not set
2948 return 0;
2949 return aep->ae_attr;
2950}
2951
2952
2953 attrentry_T *
2954syn_term_attr2entry(int attr)
2955{
2956 attr -= ATTR_OFF;
2957 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2958 return NULL;
2959 return &(TERM_ATTR_ENTRY(attr));
2960}
2961
2962 attrentry_T *
2963syn_cterm_attr2entry(int attr)
2964{
2965 attr -= ATTR_OFF;
2966 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2967 return NULL;
2968 return &(CTERM_ATTR_ENTRY(attr));
2969}
2970
2971#define LIST_ATTR 1
2972#define LIST_STRING 2
2973#define LIST_INT 3
2974
2975 static void
2976highlight_list_one(int id)
2977{
2978 hl_group_T *sgp;
2979 int didh = FALSE;
2980
2981 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2982
2983 if (message_filtered(sgp->sg_name))
2984 return;
2985
2986 didh = highlight_list_arg(id, didh, LIST_ATTR,
2987 sgp->sg_term, NULL, "term");
2988 didh = highlight_list_arg(id, didh, LIST_STRING,
2989 0, sgp->sg_start, "start");
2990 didh = highlight_list_arg(id, didh, LIST_STRING,
2991 0, sgp->sg_stop, "stop");
2992
2993 didh = highlight_list_arg(id, didh, LIST_ATTR,
2994 sgp->sg_cterm, NULL, "cterm");
2995 didh = highlight_list_arg(id, didh, LIST_INT,
2996 sgp->sg_cterm_fg, NULL, "ctermfg");
2997 didh = highlight_list_arg(id, didh, LIST_INT,
2998 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002999 didh = highlight_list_arg(id, didh, LIST_INT,
3000 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01003001 didh = highlight_list_arg(id, didh, LIST_INT,
3002 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003003
3004#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3005 didh = highlight_list_arg(id, didh, LIST_ATTR,
3006 sgp->sg_gui, NULL, "gui");
3007 didh = highlight_list_arg(id, didh, LIST_STRING,
3008 0, sgp->sg_gui_fg_name, "guifg");
3009 didh = highlight_list_arg(id, didh, LIST_STRING,
3010 0, sgp->sg_gui_bg_name, "guibg");
3011 didh = highlight_list_arg(id, didh, LIST_STRING,
3012 0, sgp->sg_gui_sp_name, "guisp");
3013#endif
3014#ifdef FEAT_GUI
3015 didh = highlight_list_arg(id, didh, LIST_STRING,
3016 0, sgp->sg_font_name, "font");
3017#endif
3018
3019 if (sgp->sg_link && !got_int)
3020 {
3021 (void)syn_list_header(didh, 9999, id);
3022 didh = TRUE;
3023 msg_puts_attr("links to", HL_ATTR(HLF_D));
3024 msg_putchar(' ');
3025 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3026 }
3027
3028 if (!didh)
3029 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3030#ifdef FEAT_EVAL
3031 if (p_verbose > 0)
3032 last_set_msg(sgp->sg_script_ctx);
3033#endif
3034}
3035
3036 static int
3037highlight_list_arg(
3038 int id,
3039 int didh,
3040 int type,
3041 int iarg,
3042 char_u *sarg,
3043 char *name)
3044{
Bram Moolenaar84f54632022-06-29 18:39:11 +01003045 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003046 char_u *ts;
3047 int i;
3048
3049 if (got_int)
3050 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003051
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003052 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3053 return didh;
3054
3055 ts = buf;
3056 if (type == LIST_INT)
3057 sprintf((char *)buf, "%d", iarg - 1);
3058 else if (type == LIST_STRING)
3059 ts = sarg;
3060 else // type == LIST_ATTR
3061 {
3062 buf[0] = NUL;
3063 for (i = 0; hl_attr_table[i] != 0; ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003064 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003065 if (iarg & hl_attr_table[i])
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003066 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003067 if (buf[0] != NUL)
3068 vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
3069 vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
3070 iarg &= ~hl_attr_table[i]; // don't want "inverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003071 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003072 }
3073 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003074
3075 (void)syn_list_header(didh,
3076 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3077 didh = TRUE;
3078 if (!got_int)
3079 {
3080 if (*name != NUL)
3081 {
3082 msg_puts_attr(name, HL_ATTR(HLF_D));
3083 msg_puts_attr("=", HL_ATTR(HLF_D));
3084 }
3085 msg_outtrans(ts);
3086 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003087 return didh;
3088}
3089
3090#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3091/*
3092 * Return "1" if highlight group "id" has attribute "flag".
3093 * Return NULL otherwise.
3094 */
3095 char_u *
3096highlight_has_attr(
3097 int id,
3098 int flag,
3099 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3100{
3101 int attr;
3102
3103 if (id <= 0 || id > highlight_ga.ga_len)
3104 return NULL;
3105
3106#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3107 if (modec == 'g')
3108 attr = HL_TABLE()[id - 1].sg_gui;
3109 else
3110#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003111 {
3112 if (modec == 'c')
3113 attr = HL_TABLE()[id - 1].sg_cterm;
3114 else
3115 attr = HL_TABLE()[id - 1].sg_term;
3116 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003117
3118 if (attr & flag)
3119 return (char_u *)"1";
3120 return NULL;
3121}
3122#endif
3123
3124#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3125/*
3126 * Return color name of highlight group "id".
3127 */
3128 char_u *
3129highlight_color(
3130 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003131 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003132 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3133{
3134 static char_u name[20];
3135 int n;
3136 int fg = FALSE;
3137 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003138 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003139 int font = FALSE;
3140
3141 if (id <= 0 || id > highlight_ga.ga_len)
3142 return NULL;
3143
3144 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3145 fg = TRUE;
3146 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3147 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3148 font = TRUE;
3149 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3150 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003151 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3152 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003153 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3154 return NULL;
3155 if (modec == 'g')
3156 {
3157# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3158# ifdef FEAT_GUI
3159 // return font name
3160 if (font)
3161 return HL_TABLE()[id - 1].sg_font_name;
3162# endif
3163
3164 // return #RRGGBB form (only possible when GUI is running)
3165 if ((USE_24BIT) && what[2] == '#')
3166 {
3167 guicolor_T color;
3168 long_u rgb;
3169 static char_u buf[10];
3170
3171 if (fg)
3172 color = HL_TABLE()[id - 1].sg_gui_fg;
3173 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003174 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003175 else
3176 color = HL_TABLE()[id - 1].sg_gui_bg;
3177 if (color == INVALCOLOR)
3178 return NULL;
3179 rgb = (long_u)GUI_MCH_GET_RGB(color);
3180 sprintf((char *)buf, "#%02x%02x%02x",
3181 (unsigned)(rgb >> 16),
3182 (unsigned)(rgb >> 8) & 255,
3183 (unsigned)rgb & 255);
3184 return buf;
3185 }
3186# endif
3187 if (fg)
3188 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3189 if (sp)
3190 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3191 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3192 }
PMuncha606f3a2023-11-15 15:35:49 +01003193 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003194 return NULL;
3195 if (modec == 'c')
3196 {
3197 if (fg)
3198 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003199 else if (ul)
3200 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003201 else if (font)
3202 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003203 else
3204 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3205 if (n < 0)
3206 return NULL;
3207 sprintf((char *)name, "%d", n);
3208 return name;
3209 }
3210 // term doesn't have color
3211 return NULL;
3212}
3213#endif
3214
3215#if (defined(FEAT_SYN_HL) \
3216 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3217 && defined(FEAT_PRINTER)) || defined(PROTO)
3218/*
3219 * Return color name of highlight group "id" as RGB value.
3220 */
3221 long_u
3222highlight_gui_color_rgb(
3223 int id,
3224 int fg) // TRUE = fg, FALSE = bg
3225{
3226 guicolor_T color;
3227
3228 if (id <= 0 || id > highlight_ga.ga_len)
3229 return 0L;
3230
3231 if (fg)
3232 color = HL_TABLE()[id - 1].sg_gui_fg;
3233 else
3234 color = HL_TABLE()[id - 1].sg_gui_bg;
3235
3236 if (color == INVALCOLOR)
3237 return 0L;
3238
3239 return GUI_MCH_GET_RGB(color);
3240}
3241#endif
3242
3243/*
3244 * Output the syntax list header.
3245 * Return TRUE when started a new line.
3246 */
3247 int
3248syn_list_header(
3249 int did_header, // did header already
3250 int outlen, // length of string that comes
3251 int id) // highlight group id
3252{
3253 int endcol = 19;
3254 int newline = TRUE;
3255 int name_col = 0;
3256
3257 if (!did_header)
3258 {
3259 msg_putchar('\n');
3260 if (got_int)
3261 return TRUE;
3262 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3263 name_col = msg_col;
3264 endcol = 15;
3265 }
3266 else if (msg_col + outlen + 1 >= Columns)
3267 {
3268 msg_putchar('\n');
3269 if (got_int)
3270 return TRUE;
3271 }
3272 else
3273 {
3274 if (msg_col >= endcol) // wrap around is like starting a new line
3275 newline = FALSE;
3276 }
3277
3278 if (msg_col >= endcol) // output at least one space
3279 endcol = msg_col + 1;
3280 if (Columns <= endcol) // avoid hang for tiny window
3281 endcol = Columns - 1;
3282
3283 msg_advance(endcol);
3284
3285 // Show "xxx" with the attributes.
3286 if (!did_header)
3287 {
3288 if (endcol == Columns - 1 && endcol <= name_col)
3289 msg_putchar(' ');
3290 msg_puts_attr("xxx", syn_id2attr(id));
3291 msg_putchar(' ');
3292 }
3293
3294 return newline;
3295}
3296
3297/*
3298 * Set the attribute numbers for a highlight group.
3299 * Called after one of the attributes has changed.
3300 */
3301 static void
3302set_hl_attr(
3303 int idx) // index in array
3304{
3305 attrentry_T at_en;
3306 hl_group_T *sgp = HL_TABLE() + idx;
3307
3308 // The "Normal" group doesn't need an attribute number
3309 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3310 return;
3311
3312#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003313 // For the GUI mode: If there are other than "normal" highlighting
3314 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003315 if (sgp->sg_gui_fg == INVALCOLOR
3316 && sgp->sg_gui_bg == INVALCOLOR
3317 && sgp->sg_gui_sp == INVALCOLOR
3318 && sgp->sg_font == NOFONT
3319# ifdef FEAT_XFONTSET
3320 && sgp->sg_fontset == NOFONTSET
3321# endif
3322 )
3323 {
3324 sgp->sg_gui_attr = sgp->sg_gui;
3325 }
3326 else
3327 {
3328 at_en.ae_attr = sgp->sg_gui;
3329 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3330 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3331 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3332 at_en.ae_u.gui.font = sgp->sg_font;
3333# ifdef FEAT_XFONTSET
3334 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3335# endif
3336 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3337 }
3338#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003339 // For the term mode: If there are other than "normal" highlighting
3340 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003341 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3342 sgp->sg_term_attr = sgp->sg_term;
3343 else
3344 {
3345 at_en.ae_attr = sgp->sg_term;
3346 at_en.ae_u.term.start = sgp->sg_start;
3347 at_en.ae_u.term.stop = sgp->sg_stop;
3348 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3349 }
3350
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003351 // For the color term mode: If there are other than "normal"
3352 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003353 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3354 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003355# ifdef FEAT_TERMGUICOLORS
3356 && sgp->sg_gui_fg == INVALCOLOR
3357 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003358 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003359# endif
3360 )
3361 sgp->sg_cterm_attr = sgp->sg_cterm;
3362 else
3363 {
3364 at_en.ae_attr = sgp->sg_cterm;
3365 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3366 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003367 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003368 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003369# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003370 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3371 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003372 // Only use the underline/undercurl color when used, it may clear the
3373 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003374 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3375 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003376 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3377 else
3378 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003379 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3380 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3381 {
3382 // If both fg and bg are invalid fall back to the cterm colors.
3383 // Helps when the GUI only uses an attribute, e.g. undercurl.
3384 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3385 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3386 }
3387# endif
3388 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3389 }
3390}
3391
3392/*
3393 * Lookup a highlight group name and return its ID.
3394 * If it is not found, 0 is returned.
3395 */
3396 int
3397syn_name2id(char_u *name)
3398{
3399 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003400 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003401
3402 // Avoid using stricmp() too much, it's slow on some systems
3403 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3404 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003405 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003406 vim_strup(name_u);
3407 for (i = highlight_ga.ga_len; --i >= 0; )
3408 if (HL_TABLE()[i].sg_name_u != NULL
3409 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3410 break;
3411 return i + 1;
3412}
3413
3414/*
3415 * Lookup a highlight group name and return its attributes.
3416 * Return zero if not found.
3417 */
3418 int
3419syn_name2attr(char_u *name)
3420{
3421 int id = syn_name2id(name);
3422
3423 if (id != 0)
3424 return syn_id2attr(id);
3425 return 0;
3426}
3427
3428#if defined(FEAT_EVAL) || defined(PROTO)
3429/*
3430 * Return TRUE if highlight group "name" exists.
3431 */
3432 int
3433highlight_exists(char_u *name)
3434{
3435 return (syn_name2id(name) > 0);
3436}
3437
3438# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3439/*
3440 * Return the name of highlight group "id".
3441 * When not a valid ID return an empty string.
3442 */
3443 char_u *
3444syn_id2name(int id)
3445{
3446 if (id <= 0 || id > highlight_ga.ga_len)
3447 return (char_u *)"";
3448 return HL_TABLE()[id - 1].sg_name;
3449}
3450# endif
3451#endif
3452
3453/*
3454 * Like syn_name2id(), but take a pointer + length argument.
3455 */
3456 int
3457syn_namen2id(char_u *linep, int len)
3458{
3459 char_u *name;
3460 int id = 0;
3461
3462 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003463 if (name == NULL)
3464 return 0;
3465
3466 id = syn_name2id(name);
3467 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003468 return id;
3469}
3470
3471/*
3472 * Find highlight group name in the table and return its ID.
3473 * The argument is a pointer to the name and the length of the name.
3474 * If it doesn't exist yet, a new entry is created.
3475 * Return 0 for failure.
3476 */
3477 int
3478syn_check_group(char_u *pp, int len)
3479{
3480 int id;
3481 char_u *name;
3482
erw7f7f7aaf2021-12-07 21:29:20 +00003483 if (len > MAX_SYN_NAME)
3484 {
3485 emsg(_(e_highlight_group_name_too_long));
3486 return 0;
3487 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003488 name = vim_strnsave(pp, len);
3489 if (name == NULL)
3490 return 0;
3491
3492 id = syn_name2id(name);
3493 if (id == 0) // doesn't exist yet
3494 id = syn_add_group(name);
3495 else
3496 vim_free(name);
3497 return id;
3498}
3499
3500/*
3501 * Add new highlight group and return its ID.
3502 * "name" must be an allocated string, it will be consumed.
3503 * Return 0 for failure.
3504 */
3505 static int
3506syn_add_group(char_u *name)
3507{
3508 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003509 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003510
Gregory Andersd4376dc2023-08-20 19:14:03 +02003511 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003512 for (p = name; *p != NUL; ++p)
3513 {
3514 if (!vim_isprintc(*p))
3515 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003516 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003517 vim_free(name);
3518 return 0;
3519 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003520 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003521 {
3522 // This is an error, but since there previously was no check only
3523 // give a warning.
3524 msg_source(HL_ATTR(HLF_W));
3525 msg(_("W18: Invalid character in group name"));
3526 break;
3527 }
3528 }
3529
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003530 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003531 if (highlight_ga.ga_data == NULL)
3532 {
3533 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3534 highlight_ga.ga_growsize = 10;
3535 }
3536
3537 if (highlight_ga.ga_len >= MAX_HL_ID)
3538 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003539 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003540 vim_free(name);
3541 return 0;
3542 }
3543
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003544 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003545 if (ga_grow(&highlight_ga, 1) == FAIL)
3546 {
3547 vim_free(name);
3548 return 0;
3549 }
3550
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003551 name_up = vim_strsave_up(name);
3552 if (name_up == NULL)
3553 {
3554 vim_free(name);
3555 return 0;
3556 }
3557
Bram Moolenaara80faa82020-04-12 19:37:17 +02003558 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003559 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003560 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003561#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3562 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3563 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003564 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003565#endif
3566 ++highlight_ga.ga_len;
3567
3568 return highlight_ga.ga_len; // ID is index plus one
3569}
3570
3571/*
3572 * When, just after calling syn_add_group(), an error is discovered, this
3573 * function deletes the new name.
3574 */
3575 static void
3576syn_unadd_group(void)
3577{
3578 --highlight_ga.ga_len;
3579 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3580 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3581}
3582
3583/*
3584 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003585 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003586 */
3587 int
3588syn_id2attr(int hl_id)
3589{
3590 int attr;
3591 hl_group_T *sgp;
3592
3593 hl_id = syn_get_final_id(hl_id);
3594 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3595
3596#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003597 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003598 if (gui.in_use)
3599 attr = sgp->sg_gui_attr;
3600 else
3601#endif
3602 if (IS_CTERM)
3603 attr = sgp->sg_cterm_attr;
3604 else
3605 attr = sgp->sg_term_attr;
3606
3607 return attr;
3608}
3609
3610#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3611/*
3612 * Get the GUI colors and attributes for a group ID.
3613 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3614 */
3615 int
3616syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3617{
3618 hl_group_T *sgp;
3619
3620 hl_id = syn_get_final_id(hl_id);
3621 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3622
3623 *fgp = sgp->sg_gui_fg;
3624 *bgp = sgp->sg_gui_bg;
3625 return sgp->sg_gui;
3626}
3627#endif
3628
3629#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003630 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3631 && defined(FEAT_TERMGUICOLORS)) \
3632 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003633 void
3634syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3635{
3636 hl_group_T *sgp;
3637
3638 hl_id = syn_get_final_id(hl_id);
3639 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3640 *fgp = sgp->sg_cterm_fg - 1;
3641 *bgp = sgp->sg_cterm_bg - 1;
3642}
3643#endif
3644
3645/*
3646 * Translate a group ID to the final group ID (following links).
3647 */
3648 int
3649syn_get_final_id(int hl_id)
3650{
3651 int count;
3652 hl_group_T *sgp;
3653
3654 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3655 return 0; // Can be called from eval!!
3656
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003657 // Follow links until there is no more.
3658 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003659 for (count = 100; --count >= 0; )
3660 {
3661 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3662 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3663 break;
3664 hl_id = sgp->sg_link;
3665 }
3666
3667 return hl_id;
3668}
3669
3670#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3671/*
3672 * Call this function just after the GUI has started.
3673 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3674 * It finds the font and color handles for the highlighting groups.
3675 */
3676 void
3677highlight_gui_started(void)
3678{
3679 int idx;
3680
3681 // First get the colors from the "Normal" and "Menu" group, if set
3682 if (USE_24BIT)
3683 set_normal_colors();
3684
3685 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3686 gui_do_one_color(idx, FALSE, FALSE);
3687
3688 highlight_changed();
3689}
3690
3691 static void
3692gui_do_one_color(
3693 int idx,
3694 int do_menu UNUSED, // TRUE: might set the menu font
3695 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3696{
3697 int didit = FALSE;
3698
3699# ifdef FEAT_GUI
3700# ifdef FEAT_TERMGUICOLORS
3701 if (gui.in_use)
3702# endif
3703 if (HL_TABLE()[idx].sg_font_name != NULL)
3704 {
3705 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3706 do_tooltip, TRUE);
3707 didit = TRUE;
3708 }
3709# endif
3710 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3711 {
3712 HL_TABLE()[idx].sg_gui_fg =
3713 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3714 didit = TRUE;
3715 }
3716 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3717 {
3718 HL_TABLE()[idx].sg_gui_bg =
3719 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3720 didit = TRUE;
3721 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003722 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3723 {
3724 HL_TABLE()[idx].sg_gui_sp =
3725 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3726 didit = TRUE;
3727 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003728 if (didit) // need to get a new attr number
3729 set_hl_attr(idx);
3730}
3731#endif
3732
3733#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3734/*
3735 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3736 */
3737 static void
3738combine_stl_hlt(
3739 int id,
3740 int id_S,
3741 int id_alt,
3742 int hlcnt,
3743 int i,
3744 int hlf,
3745 int *table)
3746{
3747 hl_group_T *hlt = HL_TABLE();
3748
3749 if (id_alt == 0)
3750 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003751 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003752 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3753 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3754# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3755 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3756# endif
3757 }
3758 else
3759 mch_memmove(&hlt[hlcnt + i],
3760 &hlt[id_alt - 1],
3761 sizeof(hl_group_T));
3762 hlt[hlcnt + i].sg_link = 0;
3763
3764 hlt[hlcnt + i].sg_term ^=
3765 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3766 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3767 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3768 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3769 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3770 hlt[hlcnt + i].sg_cterm ^=
3771 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3772 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3773 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3774 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3775 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003776 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3777 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003778# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3779 hlt[hlcnt + i].sg_gui ^=
3780 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3781# endif
3782# ifdef FEAT_GUI
3783 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3784 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3785 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3786 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3787 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3788 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3789 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3790 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3791# ifdef FEAT_XFONTSET
3792 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3793 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3794# endif
3795# endif
3796 highlight_ga.ga_len = hlcnt + i + 1;
3797 set_hl_attr(hlcnt + i); // At long last we can apply
3798 table[i] = syn_id2attr(hlcnt + i + 1);
3799}
3800#endif
3801
3802/*
3803 * Translate the 'highlight' option into attributes in highlight_attr[] and
3804 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3805 * corresponding highlights to use on top of HLF_SNC is computed.
3806 * Called only when the 'highlight' option has been changed and upon first
3807 * screen redraw after any :highlight command.
3808 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3809 */
3810 int
3811highlight_changed(void)
3812{
3813 int hlf;
3814 int i;
3815 char_u *p;
3816 int attr;
3817 char_u *end;
3818 int id;
3819#ifdef USER_HIGHLIGHT
3820 char_u userhl[30]; // use 30 to avoid compiler warning
3821# ifdef FEAT_STL_OPT
3822 int id_S = -1;
3823 int id_SNC = 0;
3824# ifdef FEAT_TERMINAL
3825 int id_ST = 0;
3826 int id_STNC = 0;
3827# endif
3828 int hlcnt;
3829# endif
3830#endif
3831 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3832
3833 need_highlight_changed = FALSE;
3834
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003835#ifdef FEAT_TERMINAL
3836 term_update_colors_all();
3837 term_update_wincolor_all();
3838#endif
3839
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003840 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003841 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3842 highlight_attr[hlf] = 0;
3843
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003844 // First set all attributes to their default value.
3845 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003846 for (i = 0; i < 2; ++i)
3847 {
3848 if (i)
3849 p = p_hl;
3850 else
3851 p = get_highlight_default();
3852 if (p == NULL) // just in case
3853 continue;
3854
3855 while (*p)
3856 {
3857 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3858 if (hl_flags[hlf] == *p)
3859 break;
3860 ++p;
3861 if (hlf == (int)HLF_COUNT || *p == NUL)
3862 return FAIL;
3863
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003864 // Allow several hl_flags to be combined, like "bu" for
3865 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003866 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003867 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003868 {
3869 if (VIM_ISWHITE(*p)) // ignore white space
3870 continue;
3871
3872 if (attr > HL_ALL) // Combination with ':' is not allowed.
3873 return FAIL;
3874
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003875 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003876 switch (*p)
3877 {
3878 case 'b': attr |= HL_BOLD;
3879 break;
3880 case 'i': attr |= HL_ITALIC;
3881 break;
3882 case '-':
3883 case 'n': // no highlighting
3884 break;
3885 case 'r': attr |= HL_INVERSE;
3886 break;
3887 case 's': attr |= HL_STANDOUT;
3888 break;
3889 case 'u': attr |= HL_UNDERLINE;
3890 break;
3891 case 'c': attr |= HL_UNDERCURL;
3892 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003893 case '2': attr |= HL_UNDERDOUBLE;
3894 break;
3895 case 'd': attr |= HL_UNDERDOTTED;
3896 break;
3897 case '=': attr |= HL_UNDERDASHED;
3898 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003899 case 't': attr |= HL_STRIKETHROUGH;
3900 break;
3901 case ':': ++p; // highlight group name
3902 if (attr || *p == NUL) // no combinations
3903 return FAIL;
3904 end = vim_strchr(p, ',');
3905 if (end == NULL)
3906 end = p + STRLEN(p);
3907 id = syn_check_group(p, (int)(end - p));
3908 if (id == 0)
3909 return FAIL;
3910 attr = syn_id2attr(id);
3911 p = end - 1;
3912#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3913 if (hlf == (int)HLF_SNC)
3914 id_SNC = syn_get_final_id(id);
3915# ifdef FEAT_TERMINAL
3916 else if (hlf == (int)HLF_ST)
3917 id_ST = syn_get_final_id(id);
3918 else if (hlf == (int)HLF_STNC)
3919 id_STNC = syn_get_final_id(id);
3920# endif
3921 else if (hlf == (int)HLF_S)
3922 id_S = syn_get_final_id(id);
3923#endif
3924 break;
3925 default: return FAIL;
3926 }
3927 }
3928 highlight_attr[hlf] = attr;
3929
3930 p = skip_to_option_part(p); // skip comma and spaces
3931 }
3932 }
3933
3934#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003935 // Setup the user highlights
3936 //
3937 // Temporarily utilize 28 more hl entries:
3938 // 9 for User1-User9 combined with StatusLineNC
3939 // 9 for User1-User9 combined with StatusLineTerm
3940 // 9 for User1-User9 combined with StatusLineTermNC
3941 // 1 for StatusLine default
3942 // Have to be in there simultaneously in case of table overflows in
3943 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003944# ifdef FEAT_STL_OPT
3945 if (ga_grow(&highlight_ga, 28) == FAIL)
3946 return FAIL;
3947 hlcnt = highlight_ga.ga_len;
3948 if (id_S == -1)
3949 {
3950 // Make sure id_S is always valid to simplify code below. Use the last
3951 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003952 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003953 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3954 id_S = hlcnt + 19;
3955 }
3956# endif
3957 for (i = 0; i < 9; i++)
3958 {
3959 sprintf((char *)userhl, "User%d", i + 1);
3960 id = syn_name2id(userhl);
3961 if (id == 0)
3962 {
3963 highlight_user[i] = 0;
3964# ifdef FEAT_STL_OPT
3965 highlight_stlnc[i] = 0;
3966# ifdef FEAT_TERMINAL
3967 highlight_stlterm[i] = 0;
3968 highlight_stltermnc[i] = 0;
3969# endif
3970# endif
3971 }
3972 else
3973 {
3974 highlight_user[i] = syn_id2attr(id);
3975# ifdef FEAT_STL_OPT
3976 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3977 HLF_SNC, highlight_stlnc);
3978# ifdef FEAT_TERMINAL
3979 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3980 HLF_ST, highlight_stlterm);
3981 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3982 HLF_STNC, highlight_stltermnc);
3983# endif
3984# endif
3985 }
3986 }
3987# ifdef FEAT_STL_OPT
3988 highlight_ga.ga_len = hlcnt;
3989# endif
3990
3991#endif // USER_HIGHLIGHT
3992
3993 return OK;
3994}
3995
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003996static void highlight_list(void);
3997static void highlight_list_two(int cnt, int attr);
3998
3999/*
4000 * Handle command line completion for :highlight command.
4001 */
4002 void
4003set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
4004{
4005 char_u *p;
4006
4007 // Default: expand group names
4008 xp->xp_context = EXPAND_HIGHLIGHT;
4009 xp->xp_pattern = arg;
4010 include_link = 2;
4011 include_default = 1;
4012
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004013 if (*arg == NUL)
4014 return;
4015
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004016 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004017 p = skiptowhite(arg);
4018 if (*p == NUL)
4019 return;
4020
4021 // past "default" or group name
4022 include_default = 0;
4023 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004024 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004025 arg = skipwhite(p);
4026 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004027 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004028 }
4029 if (*p == NUL)
4030 return;
4031
4032 // past group name
4033 include_link = 0;
4034 if (arg[1] == 'i' && arg[0] == 'N')
4035 highlight_list();
4036 if (STRNCMP("link", arg, p - arg) == 0
4037 || STRNCMP("clear", arg, p - arg) == 0)
4038 {
4039 xp->xp_pattern = skipwhite(p);
4040 p = skiptowhite(xp->xp_pattern);
4041 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004042 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004043 xp->xp_pattern = skipwhite(p);
4044 p = skiptowhite(xp->xp_pattern);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004045 }
4046 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004047 if (*p != NUL) // past group name(s)
4048 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004049}
4050
4051/*
4052 * List highlighting matches in a nice way.
4053 */
4054 static void
4055highlight_list(void)
4056{
4057 int i;
4058
4059 for (i = 10; --i >= 0; )
4060 highlight_list_two(i, HL_ATTR(HLF_D));
4061 for (i = 40; --i >= 0; )
4062 highlight_list_two(99, 0);
4063}
4064
4065 static void
4066highlight_list_two(int cnt, int attr)
4067{
4068 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4069 msg_clr_eos();
4070 out_flush();
4071 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4072}
4073
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004074/*
4075 * Function given to ExpandGeneric() to obtain the list of group names.
4076 */
4077 char_u *
4078get_highlight_name(expand_T *xp UNUSED, int idx)
4079{
4080 return get_highlight_name_ext(xp, idx, TRUE);
4081}
4082
4083/*
4084 * Obtain a highlight group name.
4085 * When "skip_cleared" is TRUE don't return a cleared entry.
4086 */
4087 char_u *
4088get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4089{
4090 if (idx < 0)
4091 return NULL;
4092
4093 // Items are never removed from the table, skip the ones that were
4094 // cleared.
4095 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4096 return (char_u *)"";
4097
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004098 if (idx == highlight_ga.ga_len && include_none != 0)
4099 return (char_u *)"none";
4100 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4101 return (char_u *)"default";
4102 if (idx == highlight_ga.ga_len + include_none + include_default
4103 && include_link != 0)
4104 return (char_u *)"link";
4105 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4106 && include_link != 0)
4107 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004108 if (idx >= highlight_ga.ga_len)
4109 return NULL;
4110 return HL_TABLE()[idx].sg_name;
4111}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004112
4113#if defined(FEAT_GUI) || defined(PROTO)
4114/*
4115 * Free all the highlight group fonts.
4116 * Used when quitting for systems which need it.
4117 */
4118 void
4119free_highlight_fonts(void)
4120{
4121 int idx;
4122
4123 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4124 {
4125 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4126 HL_TABLE()[idx].sg_font = NOFONT;
4127# ifdef FEAT_XFONTSET
4128 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4129 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4130# endif
4131 }
4132
4133 gui_mch_free_font(gui.norm_font);
4134# ifdef FEAT_XFONTSET
4135 gui_mch_free_fontset(gui.fontset);
4136# endif
4137# ifndef FEAT_GUI_GTK
4138 gui_mch_free_font(gui.bold_font);
4139 gui_mch_free_font(gui.ital_font);
4140 gui_mch_free_font(gui.boldital_font);
4141# endif
4142}
4143#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004144
4145#if defined(FEAT_EVAL) || defined(PROTO)
4146/*
4147 * Convert each of the highlight attribute bits (bold, standout, underline,
4148 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4149 * the attribute name as the key.
4150 */
4151 static dict_T *
4152highlight_get_attr_dict(int hlattr)
4153{
4154 dict_T *dict;
4155 int i;
4156
4157 dict = dict_alloc();
4158 if (dict == NULL)
4159 return NULL;
4160
4161 for (i = 0; hl_attr_table[i] != 0; ++i)
4162 {
4163 if (hlattr & hl_attr_table[i])
4164 {
4165 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4166 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4167 }
4168 }
4169
4170 return dict;
4171}
4172
4173/*
4174 * Return the attributes of the highlight group at index 'hl_idx' as a
4175 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4176 * links recursively.
4177 */
4178 static dict_T *
4179highlight_get_info(int hl_idx, int resolve_link)
4180{
4181 dict_T *dict;
4182 hl_group_T *sgp;
4183 dict_T *attr_dict;
4184 int hlgid;
4185
4186 dict = dict_alloc();
4187 if (dict == NULL)
4188 return dict;
4189
4190 sgp = &HL_TABLE()[hl_idx];
4191 // highlight group id is 1-based
4192 hlgid = hl_idx + 1;
4193
4194 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4195 goto error;
4196 if (dict_add_number(dict, "id", hlgid) == FAIL)
4197 goto error;
4198
4199 if (sgp->sg_link && resolve_link)
4200 {
4201 // resolve the highlight group link recursively
4202 while (sgp->sg_link)
4203 {
4204 hlgid = sgp->sg_link;
4205 sgp = &HL_TABLE()[sgp->sg_link - 1];
4206 }
4207 }
4208
4209 if (sgp->sg_term != 0)
4210 {
4211 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4212 if (attr_dict != NULL)
4213 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4214 goto error;
4215 }
4216 if (sgp->sg_start != NULL)
4217 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4218 goto error;
4219 if (sgp->sg_stop != NULL)
4220 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4221 goto error;
4222 if (sgp->sg_cterm != 0)
4223 {
4224 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4225 if (attr_dict != NULL)
4226 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4227 goto error;
4228 }
4229 if (sgp->sg_cterm_fg != 0)
4230 if (dict_add_string(dict, "ctermfg",
4231 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4232 goto error;
4233 if (sgp->sg_cterm_bg != 0)
4234 if (dict_add_string(dict, "ctermbg",
4235 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4236 goto error;
4237 if (sgp->sg_cterm_ul != 0)
4238 if (dict_add_string(dict, "ctermul",
4239 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4240 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004241 if (sgp->sg_cterm_font != 0)
4242 if (dict_add_string(dict, "ctermfont",
4243 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4244 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004245 if (sgp->sg_gui != 0)
4246 {
4247 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4248 if (attr_dict != NULL)
4249 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4250 goto error;
4251 }
4252 if (sgp->sg_gui_fg_name != NULL)
4253 if (dict_add_string(dict, "guifg",
4254 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4255 goto error;
4256 if (sgp->sg_gui_bg_name != NULL)
4257 if (dict_add_string(dict, "guibg",
4258 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4259 goto error;
4260 if (sgp->sg_gui_sp_name != NULL)
4261 if (dict_add_string(dict, "guisp",
4262 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4263 goto error;
4264# ifdef FEAT_GUI
4265 if (sgp->sg_font_name != NULL)
4266 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4267 goto error;
4268# endif
4269 if (sgp->sg_link)
4270 {
4271 char_u *link;
4272
4273 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4274 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4275 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004276
4277 if (sgp->sg_deflink)
4278 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004279 }
4280 if (dict_len(dict) == 2)
4281 // If only 'name' is present, then the highlight group is cleared.
4282 dict_add_bool(dict, "cleared", VVAL_TRUE);
4283
4284 return dict;
4285
4286error:
4287 vim_free(dict);
4288 return NULL;
4289}
4290
4291/*
4292 * "hlget([name])" function
4293 * Return the attributes of a specific highlight group (if specified) or all
4294 * the highlight groups.
4295 */
4296 void
4297f_hlget(typval_T *argvars, typval_T *rettv)
4298{
4299 list_T *list;
4300 dict_T *dict;
4301 int i;
4302 char_u *hlarg = NULL;
4303 int resolve_link = FALSE;
4304
4305 if (rettv_list_alloc(rettv) == FAIL)
4306 return;
4307
4308 if (check_for_opt_string_arg(argvars, 0) == FAIL
4309 || (argvars[0].v_type != VAR_UNKNOWN
4310 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4311 return;
4312
4313 if (argvars[0].v_type != VAR_UNKNOWN)
4314 {
4315 // highlight group name supplied
4316 hlarg = tv_get_string_chk(&argvars[0]);
4317 if (hlarg == NULL)
4318 return;
4319
4320 if (argvars[1].v_type != VAR_UNKNOWN)
4321 {
4322 int error = FALSE;
4323
4324 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4325 if (error)
4326 return;
4327 }
4328 }
4329
4330 list = rettv->vval.v_list;
4331 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4332 {
4333 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4334 {
4335 dict = highlight_get_info(i, resolve_link);
4336 if (dict != NULL)
4337 list_append_dict(list, dict);
4338 }
4339 }
4340}
4341
4342/*
4343 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4344 * 'dict' or the value is not a string type. If the value is not a string type
4345 * or is NULL, then 'error' is set to TRUE.
4346 */
4347 static char_u *
4348hldict_get_string(dict_T *dict, char_u *key, int *error)
4349{
4350 dictitem_T *di;
4351
4352 *error = FALSE;
4353 di = dict_find(dict, key, -1);
4354 if (di == NULL)
4355 return NULL;
4356
4357 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4358 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004359 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004360 *error = TRUE;
4361 return NULL;
4362 }
4363
4364 return di->di_tv.vval.v_string;
4365}
4366
4367/*
4368 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4369 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4370 * Dictionary or is NULL.
4371 */
4372 static int
4373hldict_attr_to_str(
4374 dict_T *dict,
4375 char_u *key,
4376 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004377 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004378{
4379 dictitem_T *di;
4380 dict_T *attrdict;
4381 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004382 char_u *p;
4383 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004384
4385 attr_str[0] = NUL;
4386 di = dict_find(dict, key, -1);
4387 if (di == NULL)
4388 return TRUE;
4389
4390 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4391 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004392 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004393 return FALSE;
4394 }
4395
4396 attrdict = di->di_tv.vval.v_dict;
4397
4398 // If the attribute dict is empty, then return NONE to clear the attributes
4399 if (dict_len(attrdict) == 0)
4400 {
4401 vim_strcat(attr_str, (char_u *)"NONE", len);
4402 return TRUE;
4403 }
4404
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004405 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004406 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4407 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01004408 if (dict_get_bool(attrdict, hl_name_table[i], VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004409 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004410 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4411 STRCPY(p, (char_u *)",");
4412 sz = STRLEN(hl_name_table[i]);
4413 if (p - attr_str + sz + 1 < len)
4414 {
4415 STRCPY(p, (char_u *)hl_name_table[i]);
4416 p += sz;
4417 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004418 }
4419 }
4420
4421 return TRUE;
4422}
4423
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004424// Temporary buffer used to store the command string produced by hlset().
4425// IObuff cannot be used for this as the error messages produced by hlset()
4426// internally use IObuff.
4427#define HLSETBUFSZ 512
4428static char_u hlsetBuf[HLSETBUFSZ + 1];
4429
4430/*
4431 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4432 * "dptr", which points into "hlsetBuf".
4433 * Returns the updated pointer.
4434 */
4435 static char_u *
4436add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4437{
4438 size_t vallen;
4439
4440 // Do nothing if the value is not specified or is empty
4441 if (value == NULL || *value == NUL)
4442 return dptr;
4443
4444 vallen = STRLEN(value);
4445 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4446 {
4447 STRCPY(dptr, attr);
4448 dptr += attrlen;
4449 STRCPY(dptr, value);
4450 dptr += vallen;
4451 }
4452
4453 return dptr;
4454}
4455
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004456/*
4457 * Add or update a highlight group using 'dict' items. Returns TRUE if
4458 * successfully updated the highlight group.
4459 */
4460 static int
4461hlg_add_or_update(dict_T *dict)
4462{
4463 char_u *name;
4464 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004465 char_u term_attr[MAX_ATTR_LEN];
4466 char_u cterm_attr[MAX_ATTR_LEN];
4467 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004468 char_u *start;
4469 char_u *stop;
4470 char_u *ctermfg;
4471 char_u *ctermbg;
4472 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004473 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004474 char_u *guifg;
4475 char_u *guibg;
4476 char_u *guisp;
4477# ifdef FEAT_GUI
4478 char_u *font;
4479# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004480 int forceit = FALSE;
4481 int dodefault = FALSE;
4482 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004483 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004484
4485 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004486 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004487 return FALSE;
4488
Bram Moolenaard61efa52022-07-23 09:52:04 +01004489 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004490 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004491
Bram Moolenaard61efa52022-07-23 09:52:04 +01004492 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004493 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004494
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004495 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004496 {
4497 varnumber_T cleared;
4498
4499 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004500 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004501 if (cleared == TRUE)
4502 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004503 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4504 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004505 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004506 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004507 }
4508
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004509 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004510 {
4511 char_u *linksto;
4512
4513 // link highlight groups
4514 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004515 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004516 return FALSE;
4517
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004518 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004519 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004520 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004521
4522 done = TRUE;
4523 }
4524
4525 // If 'cleared' or 'linksto' are specified, then don't process the other
4526 // attributes.
4527 if (done)
4528 return TRUE;
4529
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004530 start = hldict_get_string(dict, (char_u *)"start", &error);
4531 if (error)
4532 return FALSE;
4533
4534 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4535 if (error)
4536 return FALSE;
4537
4538 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004539 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004540 return FALSE;
4541
4542 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004543 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004544 return FALSE;
4545
4546 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4547 if (error)
4548 return FALSE;
4549
4550 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4551 if (error)
4552 return FALSE;
4553
4554 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4555 if (error)
4556 return FALSE;
4557
PMuncha606f3a2023-11-15 15:35:49 +01004558 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
4559 if (error)
4560 return FALSE;
4561
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004562 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004563 return FALSE;
4564
4565 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4566 if (error)
4567 return FALSE;
4568
4569 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4570 if (error)
4571 return FALSE;
4572
4573 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4574 if (error)
4575 return FALSE;
4576
4577# ifdef FEAT_GUI
4578 font = hldict_get_string(dict, (char_u *)"font", &error);
4579 if (error)
4580 return FALSE;
4581# endif
4582
4583 // If none of the attributes are specified, then do nothing.
4584 if (term_attr[0] == NUL && start == NULL && stop == NULL
4585 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01004586 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004587# ifdef FEAT_GUI
4588 && font == NULL
4589# endif
4590 && guifg == NULL && guibg == NULL && guisp == NULL
4591 )
4592 return TRUE;
4593
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004594 hlsetBuf[0] = NUL;
4595 p = hlsetBuf;
4596 if (dodefault)
4597 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4598 p = add_attr_and_value(p, (char_u *)"", 0, name);
4599 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4600 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4601 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4602 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4603 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4604 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4605 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01004606 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004607 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004608# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004609 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004610# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004611 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4612 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004613 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004614
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004615 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004616
4617 return TRUE;
4618}
4619
4620/*
4621 * "hlset([{highlight_attr}])" function
4622 * Add or modify highlight groups
4623 */
4624 void
4625f_hlset(typval_T *argvars, typval_T *rettv)
4626{
4627 listitem_T *li;
4628 dict_T *dict;
4629
4630 rettv->vval.v_number = -1;
4631
4632 if (check_for_list_arg(argvars, 0) == FAIL)
4633 return;
4634
4635 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4636 {
4637 if (li->li_tv.v_type != VAR_DICT)
4638 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004639 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004640 return;
4641 }
4642
4643 dict = li->li_tv.vval.v_dict;
4644 if (!hlg_add_or_update(dict))
4645 return;
4646 }
4647
4648 rettv->vval.v_number = 0;
4649}
4650#endif