blob: 85726f828dcbcf087b1b63d52c99b0e79580846f [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
Christian Brabandte6d8b462024-01-28 23:33:29 +0100224 CENT("Visual ctermbg=DarkGrey ctermfg=White",
225 "Visual ctermbg=DarkGrey ctermfg=White guibg=LightGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200226#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
Christian Brabandte6d8b462024-01-28 23:33:29 +0100313 CENT("Visual ctermbg=Grey ctermfg=Black",
314 "Visual ctermbg=Grey ctermfg=Black guibg=#575757"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200315#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.
Christian Brabandte6d8b462024-01-28 23:33:29 +0100441 if (t_colors <= 8)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200442 {
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200443 if (*p_bg == 'l')
444 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
445 }
446
447#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100448 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200449 if (get_var_value((char_u *)"g:syntax_on") != NULL)
450 {
451 static int recursive = 0;
452
453 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000454 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200455 else
456 {
457 ++recursive;
458 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
459 --recursive;
460 }
461 }
462#endif
463}
464
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000465#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
466/*
467 * Load a default color list. Intended to support legacy color names but allows
468 * the user to override the color values. Only loaded once.
469 */
470 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000471load_default_colors_lists(void)
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000472{
473 // Lacking a default color list isn't the end of the world but it is likely
474 // an inconvenience so users should know when it is missing.
475 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
476 msg("failed to load colors/lists/default.vim");
477}
478#endif
479
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200480/*
481 * Load color file "name".
482 * Return OK for success, FAIL for failure.
483 */
484 int
485load_colors(char_u *name)
486{
487 char_u *buf;
488 int retval = FAIL;
489 static int recursive = FALSE;
490
491 // When being called recursively, this is probably because setting
492 // 'background' caused the highlighting to be reloaded. This means it is
493 // working, thus we should return OK.
494 if (recursive)
495 return OK;
496
497 recursive = TRUE;
498 buf = alloc(STRLEN(name) + 12);
499 if (buf != NULL)
500 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100501#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100502 load_default_colors_lists();
503#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200504 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
505 curbuf->b_fname, FALSE, curbuf);
506 sprintf((char *)buf, "colors/%s.vim", name);
507 retval = source_runtime(buf, DIP_START + DIP_OPT);
508 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100509 if (retval == OK)
510 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
511 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200512 }
513 recursive = FALSE;
514
515 return retval;
516}
517
518static char *(color_names[28]) = {
519 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
520 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
521 "Gray", "Grey", "LightGray", "LightGrey",
522 "DarkGray", "DarkGrey",
523 "Blue", "LightBlue", "Green", "LightGreen",
524 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
525 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
526 // indices:
527 // 0, 1, 2, 3,
528 // 4, 5, 6, 7,
529 // 8, 9, 10, 11,
530 // 12, 13,
531 // 14, 15, 16, 17,
532 // 18, 19, 20, 21, 22,
533 // 23, 24, 25, 26, 27
534static int color_numbers_16[28] = {0, 1, 2, 3,
535 4, 5, 6, 6,
536 7, 7, 7, 7,
537 8, 8,
538 9, 9, 10, 10,
539 11, 11, 12, 12, 13,
540 13, 14, 14, 15, -1};
541// for xterm with 88 colors...
542static int color_numbers_88[28] = {0, 4, 2, 6,
543 1, 5, 32, 72,
544 84, 84, 7, 7,
545 82, 82,
546 12, 43, 10, 61,
547 14, 63, 9, 74, 13,
548 75, 11, 78, 15, -1};
549// for xterm with 256 colors...
550static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200551 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200552 248, 248, 7, 7,
553 242, 242,
554 12, 81, 10, 121,
555 14, 159, 9, 224, 13,
556 225, 11, 229, 15, -1};
557// for terminals with less than 16 colors...
558static int color_numbers_8[28] = {0, 4, 2, 6,
559 1, 5, 3, 3,
560 7, 7, 7, 7,
561 0+8, 0+8,
562 4+8, 4+8, 2+8, 2+8,
563 6+8, 6+8, 1+8, 1+8, 5+8,
564 5+8, 3+8, 3+8, 7+8, -1};
565
566/*
567 * Lookup the "cterm" value to be used for color with index "idx" in
568 * color_names[].
569 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
570 * colors, otherwise it will be unchanged.
571 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100572 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200573lookup_color(int idx, int foreground, int *boldp)
574{
575 int color = color_numbers_16[idx];
576 char_u *p;
577
578 // Use the _16 table to check if it's a valid color name.
579 if (color < 0)
580 return -1;
581
582 if (t_colors == 8)
583 {
584 // t_Co is 8: use the 8 colors table
585#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100586 // On qnx, the 8 & 16 color arrays are the same
587 if (STRNCMP(T_NAME, "qansi", 5) == 0)
588 color = color_numbers_16[idx];
589 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200590#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100591 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200592 if (foreground)
593 {
594 // set/reset bold attribute to get light foreground
595 // colors (on some terminals, e.g. "linux")
596 if (color & 8)
597 *boldp = TRUE;
598 else
599 *boldp = FALSE;
600 }
601 color &= 7; // truncate to 8 colors
602 }
603 else if (t_colors == 16 || t_colors == 88
604 || t_colors >= 256)
605 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100606 // Guess: if the termcap entry ends in 'm', it is
607 // probably an xterm-like terminal. Use the changed
608 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200609 if (*T_CAF != NUL)
610 p = T_CAF;
611 else
612 p = T_CSF;
613 if (*p != NUL && (t_colors > 256
614 || *(p + STRLEN(p) - 1) == 'm'))
615 {
616 if (t_colors == 88)
617 color = color_numbers_88[idx];
618 else if (t_colors >= 256)
619 color = color_numbers_256[idx];
620 else
621 color = color_numbers_8[idx];
622 }
623#ifdef FEAT_TERMRESPONSE
624 if (t_colors >= 256 && color == 15 && is_mac_terminal)
625 // Terminal.app has a bug: 15 is light grey. Use white
626 // from the color cube instead.
627 color = 231;
628#endif
629 }
630 return color;
631}
632
633/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100634 * Link highlight group 'from_hg' to 'to_hg'.
635 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000636 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100637 * 'init' is set to TRUE when initializing all the highlight groups.
638 */
639 static void
640highlight_group_link(
641 char_u *from_hg,
642 int from_len,
643 char_u *to_hg,
644 int to_len,
645 int dodefault,
646 int forceit,
647 int init)
648{
649 int from_id;
650 int to_id;
651 hl_group_T *hlgroup = NULL;
652
653 from_id = syn_check_group(from_hg, from_len);
654 if (STRNCMP(to_hg, "NONE", 4) == 0)
655 to_id = 0;
656 else
657 to_id = syn_check_group(to_hg, to_len);
658
659 if (from_id > 0)
660 {
661 hlgroup = &HL_TABLE()[from_id - 1];
662 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
663 {
664 hlgroup->sg_deflink = to_id;
665#ifdef FEAT_EVAL
666 hlgroup->sg_deflink_sctx = current_sctx;
667 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
668#endif
669 }
670 }
671
672 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
673 {
674 // Don't allow a link when there already is some highlighting
675 // for the group, unless '!' is used
676 if (to_id > 0 && !forceit && !init
677 && hl_has_settings(from_id - 1, dodefault))
678 {
679 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000680 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100681 }
682 else if (hlgroup->sg_link != to_id
683#ifdef FEAT_EVAL
684 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
685#endif
686 || hlgroup->sg_cleared)
687 {
688 if (!init)
689 hlgroup->sg_set |= SG_LINK;
690 hlgroup->sg_link = to_id;
691#ifdef FEAT_EVAL
692 hlgroup->sg_script_ctx = current_sctx;
693 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
694#endif
695 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100696 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100697
698 // Only call highlight_changed() once after multiple changes.
699 need_highlight_changed = TRUE;
700 }
701 }
702
703}
704
705/*
706 * Reset all highlighting to the defaults. Removes all highlighting for the
707 * groups added by the user.
708 */
709 static void
710highlight_reset_all(void)
711{
712 int idx;
713
714#ifdef FEAT_GUI
715 // First, we do not destroy the old values, but allocate the new
716 // ones and update the display. THEN we destroy the old values.
717 // If we destroy the old values first, then the old values
718 // (such as GuiFont's or GuiFontset's) will still be displayed but
719 // invalid because they were free'd.
720 if (gui.in_use)
721 {
722# ifdef FEAT_BEVAL_TIP
723 gui_init_tooltip_font();
724# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100725# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100726 gui_init_menu_font();
727# endif
728 }
729# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
730 gui_mch_def_colors();
731# endif
732# ifdef FEAT_GUI_X11
733# ifdef FEAT_MENU
734
735 // This only needs to be done when there is no Menu highlight
736 // group defined by default, which IS currently the case.
737 gui_mch_new_menu_colors();
738# endif
739 if (gui.in_use)
740 {
741 gui_new_scrollbar_colors();
742# ifdef FEAT_BEVAL_GUI
743 gui_mch_new_tooltip_colors();
744# endif
745# ifdef FEAT_MENU
746 gui_mch_new_menu_font();
747# endif
748 }
749# endif
750
751 // Ok, we're done allocating the new default graphics items.
752 // The screen should already be refreshed at this point.
753 // It is now Ok to clear out the old data.
754#endif
755#ifdef FEAT_EVAL
756 do_unlet((char_u *)"g:colors_name", TRUE);
757#endif
758 restore_cterm_colors();
759
760 // Clear all default highlight groups and load the defaults.
761 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
762 highlight_clear(idx);
763 init_highlight(TRUE, TRUE);
764#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
765 if (USE_24BIT)
766 highlight_gui_started();
767 else
768#endif
769 highlight_changed();
770 redraw_later_clear();
771}
772
773/*
774 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
775 * index 'idx'.
776 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
777 * 'arg' is the list of attribute names separated by comma.
778 * 'init' is set to TRUE when initializing all the highlight groups.
779 * Returns TRUE if the attributes are set.
780 */
781 static int
782highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
783{
784 int attr;
785 int off;
786 long i;
787 int len;
788
789 attr = 0;
790 off = 0;
791 while (arg[off] != NUL)
792 {
793 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
794 {
795 len = (int)STRLEN(hl_name_table[i]);
796 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
797 {
798 attr |= hl_attr_table[i];
799 off += len;
800 break;
801 }
802 }
803 if (i < 0)
804 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000805 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100806 return FALSE;
807 }
808 if (arg[off] == ',') // another one follows
809 ++off;
810 }
811 if (*key == 'T')
812 {
813 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
814 {
815 if (!init)
816 HL_TABLE()[idx].sg_set |= SG_TERM;
817 HL_TABLE()[idx].sg_term = attr;
818 }
819 }
820 else if (*key == 'C')
821 {
822 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
823 {
824 if (!init)
825 HL_TABLE()[idx].sg_set |= SG_CTERM;
826 HL_TABLE()[idx].sg_cterm = attr;
827 HL_TABLE()[idx].sg_cterm_bold = FALSE;
828 }
829 }
830#if defined(FEAT_GUI) || defined(FEAT_EVAL)
831 else
832 {
833 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
834 {
835 if (!init)
836 HL_TABLE()[idx].sg_set |= SG_GUI;
837 HL_TABLE()[idx].sg_gui = attr;
838 }
839 }
840#endif
841
842 return TRUE;
843}
844
845#ifdef FEAT_GUI
846/*
847 * Set the font for the highlight group at 'idx'.
848 * 'arg' is the font name.
849 * Returns TRUE if the font is changed.
850 */
851 static int
852highlight_set_font(
853 int idx,
854 char_u *arg,
855 int is_normal_group,
856 int is_menu_group,
857 int is_tooltip_group)
858{
859 int did_change = FALSE;
860
861 // in non-GUI fonts are simply ignored
862 if (HL_TABLE()[idx].sg_font_name != NULL
863 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
864 {
865 // Font name didn't change, ignore.
866 }
867 else if (!gui.shell_created)
868 {
869 // GUI not started yet, always accept the name.
870 vim_free(HL_TABLE()[idx].sg_font_name);
871 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
872 did_change = TRUE;
873 }
874 else
875 {
876 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
877# ifdef FEAT_XFONTSET
878 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
879# endif
880 // First, save the current font/fontset.
881 // Then try to allocate the font/fontset.
882 // If the allocation fails, HL_TABLE()[idx].sg_font OR
883 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
884
885 HL_TABLE()[idx].sg_font = NOFONT;
886# ifdef FEAT_XFONTSET
887 HL_TABLE()[idx].sg_fontset = NOFONTSET;
888# endif
889 hl_do_font(idx, arg, is_normal_group, is_menu_group,
890 is_tooltip_group, FALSE);
891
892# ifdef FEAT_XFONTSET
893 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
894 {
895 // New fontset was accepted. Free the old one, if there
896 // was one.
897 gui_mch_free_fontset(temp_sg_fontset);
898 vim_free(HL_TABLE()[idx].sg_font_name);
899 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
900 did_change = TRUE;
901 }
902 else
903 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
904# endif
905 if (HL_TABLE()[idx].sg_font != NOFONT)
906 {
907 // New font was accepted. Free the old one, if there was
908 // one.
909 gui_mch_free_font(temp_sg_font);
910 vim_free(HL_TABLE()[idx].sg_font_name);
911 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
912 did_change = TRUE;
913 }
914 else
915 HL_TABLE()[idx].sg_font = temp_sg_font;
916 }
917
918 return did_change;
919}
920#endif
921
922/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000923 * Set the cterm foreground color for the Normal highlight group to "color" and
924 * the bold attribute to "bold".
925 */
926 static void
927hl_set_ctermfg_normal_group(int color, int bold)
928{
929 cterm_normal_fg_color = color + 1;
930 cterm_normal_fg_bold = bold;
931#ifdef FEAT_GUI
932 // Don't do this if the GUI is used.
933 if (!gui.in_use && !gui.starting)
934#endif
935 {
936 set_must_redraw(UPD_CLEAR);
937 if (termcap_active && color >= 0)
938 term_fg_color(color);
939 }
940}
941
942/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100943 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100944 */
945 static void
946highlight_set_ctermfg(int idx, int color, int is_normal_group)
947{
948 HL_TABLE()[idx].sg_cterm_fg = color + 1;
949 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000950 hl_set_ctermfg_normal_group(color,
951 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100952}
953
954/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000955 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100956 */
957 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000958hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100959{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000960 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100961#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000962 // Don't mess with 'background' if the GUI is used.
963 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100964#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000965 {
966 set_must_redraw(UPD_CLEAR);
967 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100968 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000969 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100970
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000971 if (termcap_active)
972 term_bg_color(color);
973 if (t_colors < 16)
974 dark = (color == 0 || color == 4);
975 // Limit the heuristic to the standard 16 colors
976 else if (color < 16)
977 dark = (color < 7 || color == 8);
978 // Set the 'background' option if the value is
979 // wrong.
980 if (dark != -1
981 && dark != (*p_bg == 'd')
982 && !option_was_set((char_u *)"bg"))
983 {
984 set_option_value_give_err((char_u *)"bg",
985 0L, (char_u *)(dark ? "dark" : "light"), 0);
986 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100987 }
988 }
989 }
990}
991
992/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000993 * Set the cterm background color for the highlight group at 'idx' to 'color'.
994 */
995 static void
996highlight_set_ctermbg(int idx, int color, int is_normal_group)
997{
998 HL_TABLE()[idx].sg_cterm_bg = color + 1;
999 if (is_normal_group)
1000 hl_set_ctermbg_normal_group(color);
1001}
1002
1003/*
1004 * Set the cterm underline color for the Normal highlight group to "color".
1005 */
1006 static void
1007hl_set_ctermul_normal_group(int color)
1008{
1009 cterm_normal_ul_color = color + 1;
1010#ifdef FEAT_GUI
1011 // Don't do this if the GUI is used.
1012 if (!gui.in_use && !gui.starting)
1013#endif
1014 {
1015 set_must_redraw(UPD_CLEAR);
1016 if (termcap_active && color >= 0)
1017 term_ul_color(color);
1018 }
1019}
1020
1021/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001022 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001023 */
1024 static void
1025highlight_set_ctermul(int idx, int color, int is_normal_group)
1026{
1027 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1028 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001029 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001030}
1031
1032/*
PMuncha606f3a2023-11-15 15:35:49 +01001033 * Set the cterm font for the highlight group at 'idx'.
1034 * 'arg' is the color name or the numeric value as a string.
1035 * 'init' is set to TRUE when initializing highlighting.
1036 * Called for the ":highlight" command and the "hlset()" function.
1037 *
1038 * Returns TRUE if the font is set.
1039 */
1040 static int
1041highlight_set_cterm_font(
1042 int idx,
1043 char_u *arg,
1044 int init)
1045{
1046 int font;
1047
1048 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1049 return FALSE;
1050
1051 if (!init)
1052 HL_TABLE()[idx].sg_set |= SG_CTERM;
1053
1054 if (VIM_ISDIGIT(*arg))
1055 font = atoi((char *)arg);
1056 else if (STRICMP(arg, "NONE") == 0)
1057 font = -1;
1058 else
1059 return FALSE;
1060
1061 HL_TABLE()[idx].sg_cterm_font = font + 1;
1062 return TRUE;
1063}
1064
1065/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001066 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1067 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1068 * 'keystart' is the color name/value.
1069 * 'arg' is the color name or the numeric value as a string.
1070 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1071 * 'init' is set to TRUE when initializing highlighting.
1072 * Called for the ":highlight" command and the "hlset()" function.
1073 *
1074 * Returns TRUE if the color is set.
1075 */
1076 static int
1077highlight_set_cterm_color(
1078 int idx,
1079 char_u *key,
1080 char_u *key_start,
1081 char_u *arg,
1082 int is_normal_group,
1083 int init)
1084{
1085 int color;
1086 long i;
1087 int off;
1088
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001089 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1090 return FALSE;
1091
1092 if (!init)
1093 HL_TABLE()[idx].sg_set |= SG_CTERM;
1094
1095 // When setting the foreground color, and previously the "bold"
1096 // flag was set for a light color, reset it now
1097 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001098 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001099 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1100 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1101 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001102
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001103 if (VIM_ISDIGIT(*arg))
1104 color = atoi((char *)arg);
1105 else if (STRICMP(arg, "fg") == 0)
1106 {
1107 if (cterm_normal_fg_color)
1108 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001109 else
1110 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001111 emsg(_(e_fg_color_unknown));
1112 return FALSE;
1113 }
1114 }
1115 else if (STRICMP(arg, "bg") == 0)
1116 {
1117 if (cterm_normal_bg_color > 0)
1118 color = cterm_normal_bg_color - 1;
1119 else
1120 {
1121 emsg(_(e_bg_color_unknown));
1122 return FALSE;
1123 }
1124 }
1125 else if (STRICMP(arg, "ul") == 0)
1126 {
1127 if (cterm_normal_ul_color > 0)
1128 color = cterm_normal_ul_color - 1;
1129 else
1130 {
1131 emsg(_(e_ul_color_unknown));
1132 return FALSE;
1133 }
1134 }
1135 else
1136 {
1137 int bold = MAYBE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001138
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001139 // reduce calls to STRICMP a bit, it can be slow
1140 off = TOUPPER_ASC(*arg);
1141 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1142 if (off == color_names[i][0]
1143 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1144 break;
1145 if (i < 0)
1146 {
1147 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1148 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001149 }
1150
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001151 color = lookup_color(i, key[5] == 'F', &bold);
1152
1153 // set/reset bold attribute to get light foreground
1154 // colors (on some terminals, e.g. "linux")
1155 if (bold == TRUE)
1156 {
1157 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1158 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1159 }
1160 else if (bold == FALSE)
1161 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001162 }
1163
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001164 // Add one to the argument, to avoid zero. Zero is used for
1165 // "NONE", then "color" is -1.
1166 if (key[5] == 'F')
1167 highlight_set_ctermfg(idx, color, is_normal_group);
1168 else if (key[5] == 'B')
1169 highlight_set_ctermbg(idx, color, is_normal_group);
1170 else // ctermul
1171 highlight_set_ctermul(idx, color, is_normal_group);
1172
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001173 return TRUE;
1174}
1175
1176#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1177/*
1178 * Set the GUI foreground color for the highlight group at 'idx'.
1179 * Returns TRUE if the color is set.
1180 */
1181 static int
1182highlight_set_guifg(
1183 int idx,
1184 char_u *arg,
1185 int is_menu_group UNUSED,
1186 int is_scrollbar_group UNUSED,
1187 int is_tooltip_group UNUSED,
1188 int *do_colors UNUSED,
1189 int init)
1190{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001191# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001192 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001193# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001194 char_u **namep;
1195 int did_change = FALSE;
1196
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001197 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1198 return FALSE;
1199
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001200 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001201 if (!init)
1202 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001203
1204# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001205 // In GUI guifg colors are only used when recognized
1206 i = color_name2handle(arg);
1207 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1208 {
1209 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001210# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001211 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1212 {
1213 vim_free(*namep);
1214 if (STRCMP(arg, "NONE") != 0)
1215 *namep = vim_strsave(arg);
1216 else
1217 *namep = NULL;
1218 did_change = TRUE;
1219 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001220# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1221# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001222 if (is_menu_group && gui.menu_fg_pixel != i)
1223 {
1224 gui.menu_fg_pixel = i;
1225 *do_colors = TRUE;
1226 }
1227 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1228 {
1229 gui.scroll_fg_pixel = i;
1230 *do_colors = TRUE;
1231 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001232# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001233 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1234 {
1235 gui.tooltip_fg_pixel = i;
1236 *do_colors = TRUE;
1237 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001238# endif
1239# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001240 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001241# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001242
1243 return did_change;
1244}
1245
1246/*
1247 * Set the GUI background color for the highlight group at 'idx'.
1248 * Returns TRUE if the color is set.
1249 */
1250 static int
1251highlight_set_guibg(
1252 int idx,
1253 char_u *arg,
1254 int is_menu_group UNUSED,
1255 int is_scrollbar_group UNUSED,
1256 int is_tooltip_group UNUSED,
1257 int *do_colors UNUSED,
1258 int init)
1259{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001260# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001261 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001262# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001263 char_u **namep;
1264 int did_change = FALSE;
1265
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001266 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1267 return FALSE;
1268
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001269 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001270 if (!init)
1271 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001272
1273# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001274 // In GUI guibg colors are only used when recognized
1275 i = color_name2handle(arg);
1276 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1277 {
1278 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001279# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001280 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1281 {
1282 vim_free(*namep);
1283 if (STRCMP(arg, "NONE") != 0)
1284 *namep = vim_strsave(arg);
1285 else
1286 *namep = NULL;
1287 did_change = TRUE;
1288 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001289# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1290# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001291 if (is_menu_group && gui.menu_bg_pixel != i)
1292 {
1293 gui.menu_bg_pixel = i;
1294 *do_colors = TRUE;
1295 }
1296 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1297 {
1298 gui.scroll_bg_pixel = i;
1299 *do_colors = TRUE;
1300 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001301# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001302 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1303 {
1304 gui.tooltip_bg_pixel = i;
1305 *do_colors = TRUE;
1306 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001307# endif
1308# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001309 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001310# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001311
1312 return did_change;
1313}
1314
1315/*
1316 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1317 * Returns TRUE if the color is set.
1318 */
1319 static int
1320highlight_set_guisp(int idx, char_u *arg, int init)
1321{
1322# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1323 int i;
1324# endif
1325 int did_change = FALSE;
1326 char_u **namep;
1327
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001328 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1329 return FALSE;
1330
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001331 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001332 if (!init)
1333 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001334
1335# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001336 // In GUI guisp colors are only used when recognized
1337 i = color_name2handle(arg);
1338 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1339 {
1340 HL_TABLE()[idx].sg_gui_sp = i;
1341# endif
1342 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001343 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001344 vim_free(*namep);
1345 if (STRCMP(arg, "NONE") != 0)
1346 *namep = vim_strsave(arg);
1347 else
1348 *namep = NULL;
1349 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001350 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001351# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001352 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001353# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001354
1355 return did_change;
1356}
1357#endif
1358
1359/*
1360 * Set the start/stop terminal codes for a highlight group.
1361 * Returns TRUE if the terminal code is set.
1362 */
1363 static int
1364highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1365{
1366 int off;
1367 char_u buf[100];
1368 int len;
1369 char_u *tname;
1370 char_u *p;
1371
1372 if (!init)
1373 HL_TABLE()[idx].sg_set |= SG_TERM;
1374
1375 // The "start" and "stop" arguments can be a literal escape
1376 // sequence, or a comma separated list of terminal codes.
1377 if (STRNCMP(arg, "t_", 2) == 0)
1378 {
1379 off = 0;
1380 buf[0] = 0;
1381 while (arg[off] != NUL)
1382 {
1383 // Isolate one termcap name
1384 for (len = 0; arg[off + len] &&
1385 arg[off + len] != ','; ++len)
1386 ;
1387 tname = vim_strnsave(arg + off, len);
1388 if (tname == NULL) // out of memory
1389 return FALSE;
1390 // lookup the escape sequence for the item
1391 p = get_term_code(tname);
1392 vim_free(tname);
1393 if (p == NULL) // ignore non-existing things
1394 p = (char_u *)"";
1395
1396 // Append it to the already found stuff
1397 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1398 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001399 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001400 return FALSE;
1401 }
1402 STRCAT(buf, p);
1403
1404 // Advance to the next item
1405 off += len;
1406 if (arg[off] == ',') // another one follows
1407 ++off;
1408 }
1409 }
1410 else
1411 {
1412 // Copy characters from arg[] to buf[], translating <> codes.
1413 for (p = arg, off = 0; off < 100 - 6 && *p; )
1414 {
zeertzjqdb088872022-05-02 22:53:45 +01001415 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001416 if (len > 0) // recognized special char
1417 off += len;
1418 else // copy as normal char
1419 buf[off++] = *p++;
1420 }
1421 buf[off] = NUL;
1422 }
1423
1424 if (STRCMP(buf, "NONE") == 0) // resetting the value
1425 p = NULL;
1426 else
1427 p = vim_strsave(buf);
1428 if (key[2] == 'A')
1429 {
1430 vim_free(HL_TABLE()[idx].sg_start);
1431 HL_TABLE()[idx].sg_start = p;
1432 }
1433 else
1434 {
1435 vim_free(HL_TABLE()[idx].sg_stop);
1436 HL_TABLE()[idx].sg_stop = p;
1437 }
1438 return TRUE;
1439}
1440
1441/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001442 * Handle the ":highlight .." command.
1443 * When using ":hi clear" this is called recursively for each group with
1444 * "forceit" and "init" both TRUE.
1445 */
1446 void
1447do_highlight(
1448 char_u *line,
1449 int forceit,
1450 int init) // TRUE when called for initializing
1451{
1452 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001453 char_u *linep;
1454 char_u *key_start;
1455 char_u *arg_start;
1456 char_u *key = NULL, *arg = NULL;
1457 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001458 int id;
1459 int idx;
1460 hl_group_T item_before;
1461 int did_change = FALSE;
1462 int dodefault = FALSE;
1463 int doclear = FALSE;
1464 int dolink = FALSE;
1465 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001466 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001467#ifdef FEAT_GUI_X11
1468 int is_menu_group = FALSE; // "Menu" group
1469 int is_scrollbar_group = FALSE; // "Scrollbar" group
1470 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001471#else
1472# define is_menu_group 0
1473# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001474# define is_scrollbar_group 0
1475#endif
1476#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1477 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001478#endif
1479#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1480 int did_highlight_changed = FALSE;
1481#endif
1482
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001483 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001484 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001485 {
1486 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1487 // TODO: only call when the group has attributes set
1488 highlight_list_one((int)i);
1489 return;
1490 }
1491
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001492 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001493 name_end = skiptowhite(line);
1494 linep = skipwhite(name_end);
1495
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001496 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001497 if (STRNCMP(line, "default", name_end - line) == 0)
1498 {
1499 dodefault = TRUE;
1500 line = linep;
1501 name_end = skiptowhite(line);
1502 linep = skipwhite(name_end);
1503 }
1504
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001505 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001506 if (STRNCMP(line, "clear", name_end - line) == 0)
1507 doclear = TRUE;
1508 if (STRNCMP(line, "link", name_end - line) == 0)
1509 dolink = TRUE;
1510
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001511 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001512 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001513 {
1514 id = syn_namen2id(line, (int)(name_end - line));
1515 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001516 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001517 else
1518 highlight_list_one(id);
1519 return;
1520 }
1521
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001522 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001523 if (dolink)
1524 {
1525 char_u *from_start = linep;
1526 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001527 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001528 char_u *to_start;
1529 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001530 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001531
1532 from_end = skiptowhite(from_start);
1533 to_start = skipwhite(from_end);
1534 to_end = skiptowhite(to_start);
1535
Bram Moolenaar1966c242020-04-20 22:42:32 +02001536 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001537 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001538 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001539 return;
1540 }
1541
Bram Moolenaar1966c242020-04-20 22:42:32 +02001542 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001543 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001544 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001545 return;
1546 }
1547
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001548 from_len = (int)(from_end - from_start);
1549 to_len = (int)(to_end - to_start);
1550 highlight_group_link(from_start, from_len, to_start, to_len,
1551 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001552 return;
1553 }
1554
1555 if (doclear)
1556 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001557 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001558 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001559 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001560 // ":highlight clear" without group name
1561 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001562 return;
1563 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001564 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001565 name_end = skiptowhite(line);
1566 linep = skipwhite(name_end);
1567 }
1568
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001569 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001570 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001571 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001572 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001573 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001574
1575 // Return if "default" was used and the group already has settings.
1576 if (dodefault && hl_has_settings(idx, TRUE))
1577 return;
1578
1579 // Make a copy so we can check if any attribute actually changed.
1580 item_before = HL_TABLE()[idx];
1581
1582 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1583 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001584#ifdef FEAT_GUI_X11
1585 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1586 is_menu_group = TRUE;
1587 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1588 is_scrollbar_group = TRUE;
1589 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1590 is_tooltip_group = TRUE;
1591#endif
1592
1593 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1594 if (doclear || (forceit && init))
1595 {
1596 highlight_clear(idx);
1597 if (!doclear)
1598 HL_TABLE()[idx].sg_set = 0;
1599 }
1600
1601 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001602 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001603 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001604 key_start = linep;
1605 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001606 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001607 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001608 error = TRUE;
1609 break;
1610 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001611
1612 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1613 // or "guibg").
1614 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1615 ++linep;
1616 vim_free(key);
1617 key = vim_strnsave_up(key_start, linep - key_start);
1618 if (key == NULL)
1619 {
1620 error = TRUE;
1621 break;
1622 }
1623 linep = skipwhite(linep);
1624
1625 if (STRCMP(key, "NONE") == 0)
1626 {
1627 if (!init || HL_TABLE()[idx].sg_set == 0)
1628 {
1629 if (!init)
1630 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1631 highlight_clear(idx);
1632 }
1633 continue;
1634 }
1635
1636 // Check for the equal sign.
1637 if (*linep != '=')
1638 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001639 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001640 error = TRUE;
1641 break;
1642 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001643 ++linep;
1644
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001645 // Isolate the argument.
1646 linep = skipwhite(linep);
1647 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001648 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001649 arg_start = ++linep;
1650 linep = vim_strchr(linep, '\'');
1651 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001652 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001653 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001654 error = TRUE;
1655 break;
1656 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001657 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001658 else
1659 {
1660 arg_start = linep;
1661 linep = skiptowhite(linep);
1662 }
1663 if (linep == arg_start)
1664 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001665 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001666 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001667 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001668 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001669 vim_free(arg);
1670 arg = vim_strnsave(arg_start, linep - arg_start);
1671 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001672 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001673 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001674 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001675 }
1676 if (*linep == '\'')
1677 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001678
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001679 // Store the argument.
1680 if (STRCMP(key, "TERM") == 0
1681 || STRCMP(key, "CTERM") == 0
1682 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001683 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001684 if (!highlight_set_termgui_attr(idx, key, arg, init))
1685 {
1686 error = TRUE;
1687 break;
1688 }
1689 }
1690 else if (STRCMP(key, "FONT") == 0)
1691 {
1692 // in non-GUI fonts are simply ignored
1693#ifdef FEAT_GUI
1694 if (highlight_set_font(idx, arg, is_normal_group,
1695 is_menu_group, is_tooltip_group))
1696 did_change = TRUE;
1697#endif
1698 }
1699 else if (STRCMP(key, "CTERMFG") == 0
1700 || STRCMP(key, "CTERMBG") == 0
1701 || STRCMP(key, "CTERMUL") == 0)
1702 {
1703 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1704 is_normal_group, init))
1705 {
1706 error = TRUE;
1707 break;
1708 }
1709 }
PMuncha606f3a2023-11-15 15:35:49 +01001710 else if (STRCMP(key, "CTERMFONT") == 0)
1711 {
1712 if (!highlight_set_cterm_font(idx, arg, init))
1713 {
1714 error = TRUE;
1715 break;
1716 }
1717 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001718 else if (STRCMP(key, "GUIFG") == 0)
1719 {
1720#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1721 if (highlight_set_guifg(idx, arg, is_menu_group,
1722 is_scrollbar_group, is_tooltip_group,
1723 &do_colors, init))
1724 did_change = TRUE;
1725#endif
1726 }
1727 else if (STRCMP(key, "GUIBG") == 0)
1728 {
1729#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1730 if (highlight_set_guibg(idx, arg, is_menu_group,
1731 is_scrollbar_group, is_tooltip_group,
1732 &do_colors, init))
1733 did_change = TRUE;
1734#endif
1735 }
1736 else if (STRCMP(key, "GUISP") == 0)
1737 {
1738#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1739 if (highlight_set_guisp(idx, arg, init))
1740 did_change = TRUE;
1741#endif
1742 }
1743 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1744 {
1745 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1746 {
1747 error = TRUE;
1748 break;
1749 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001750 }
1751 else
1752 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001753 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001754 error = TRUE;
1755 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001756 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001757 HL_TABLE()[idx].sg_cleared = FALSE;
1758
1759 // When highlighting has been given for a group, don't link it.
1760 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1761 HL_TABLE()[idx].sg_link = 0;
1762
1763 // Continue with next argument.
1764 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001765 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001766
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001767 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001768 if (error && idx == highlight_ga.ga_len)
1769 syn_unadd_group();
1770 else
1771 {
1772 if (is_normal_group)
1773 {
1774 HL_TABLE()[idx].sg_term_attr = 0;
1775 HL_TABLE()[idx].sg_cterm_attr = 0;
1776#ifdef FEAT_GUI
1777 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001778 // Need to update all groups, because they might be using "bg"
1779 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001780#endif
1781#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1782 if (USE_24BIT)
1783 {
1784 highlight_gui_started();
1785 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001786 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001787 }
1788#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001789#ifdef FEAT_VTP
1790 control_console_color_rgb();
1791#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001792 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001793#ifdef FEAT_GUI_X11
1794# ifdef FEAT_MENU
1795 else if (is_menu_group)
1796 {
1797 if (gui.in_use && do_colors)
1798 gui_mch_new_menu_colors();
1799 }
1800# endif
1801 else if (is_scrollbar_group)
1802 {
1803 if (gui.in_use && do_colors)
1804 gui_new_scrollbar_colors();
1805 else
1806 set_hl_attr(idx);
1807 }
1808# ifdef FEAT_BEVAL_GUI
1809 else if (is_tooltip_group)
1810 {
1811 if (gui.in_use && do_colors)
1812 gui_mch_new_tooltip_colors();
1813 }
1814# endif
1815#endif
1816 else
1817 set_hl_attr(idx);
1818#ifdef FEAT_EVAL
1819 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001820 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001821#endif
1822 }
1823
1824 vim_free(key);
1825 vim_free(arg);
1826
1827 // Only call highlight_changed() once, after a sequence of highlight
1828 // commands, and only if an attribute actually changed.
1829 if ((did_change
1830 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1831#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1832 && !did_highlight_changed
1833#endif
1834 )
1835 {
1836 // Do not trigger a redraw when highlighting is changed while
1837 // redrawing. This may happen when evaluating 'statusline' changes the
1838 // StatusLine group.
1839 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001840 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001841 need_highlight_changed = TRUE;
1842 }
1843}
1844
1845#if defined(EXITFREE) || defined(PROTO)
1846 void
1847free_highlight(void)
1848{
1849 int i;
1850
1851 for (i = 0; i < highlight_ga.ga_len; ++i)
1852 {
1853 highlight_clear(i);
1854 vim_free(HL_TABLE()[i].sg_name);
1855 vim_free(HL_TABLE()[i].sg_name_u);
1856 }
1857 ga_clear(&highlight_ga);
1858}
1859#endif
1860
1861/*
1862 * Reset the cterm colors to what they were before Vim was started, if
1863 * possible. Otherwise reset them to zero.
1864 */
1865 void
1866restore_cterm_colors(void)
1867{
1868#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1869 // Since t_me has been set, this probably means that the user
1870 // wants to use this as default colors. Need to reset default
1871 // background/foreground colors.
1872 mch_set_normal_colors();
1873#else
1874# ifdef VIMDLL
1875 if (!gui.in_use)
1876 {
1877 mch_set_normal_colors();
1878 return;
1879 }
1880# endif
1881 cterm_normal_fg_color = 0;
1882 cterm_normal_fg_bold = 0;
1883 cterm_normal_bg_color = 0;
1884# ifdef FEAT_TERMGUICOLORS
1885 cterm_normal_fg_gui_color = INVALCOLOR;
1886 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001887 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001888# endif
1889#endif
1890}
1891
1892/*
1893 * Return TRUE if highlight group "idx" has any settings.
1894 * When "check_link" is TRUE also check for an existing link.
1895 */
1896 static int
1897hl_has_settings(int idx, int check_link)
1898{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001899 return HL_TABLE()[idx].sg_cleared == 0
1900 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001901 || HL_TABLE()[idx].sg_cterm_attr != 0
1902 || HL_TABLE()[idx].sg_cterm_fg != 0
1903 || HL_TABLE()[idx].sg_cterm_bg != 0
PMuncha606f3a2023-11-15 15:35:49 +01001904 || HL_TABLE()[idx].sg_cterm_font != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001905#ifdef FEAT_GUI
1906 || HL_TABLE()[idx].sg_gui_attr != 0
1907 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1908 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1909 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1910 || HL_TABLE()[idx].sg_font_name != NULL
1911#endif
1912 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1913}
1914
1915/*
1916 * Clear highlighting for one group.
1917 */
1918 static void
1919highlight_clear(int idx)
1920{
1921 HL_TABLE()[idx].sg_cleared = TRUE;
1922
1923 HL_TABLE()[idx].sg_term = 0;
1924 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1925 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1926 HL_TABLE()[idx].sg_term_attr = 0;
1927 HL_TABLE()[idx].sg_cterm = 0;
1928 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1929 HL_TABLE()[idx].sg_cterm_fg = 0;
1930 HL_TABLE()[idx].sg_cterm_bg = 0;
1931 HL_TABLE()[idx].sg_cterm_attr = 0;
PMuncha606f3a2023-11-15 15:35:49 +01001932 HL_TABLE()[idx].sg_cterm_font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001933#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1934 HL_TABLE()[idx].sg_gui = 0;
1935 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1936 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1937 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1938#endif
1939#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1940 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1941 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001942 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001943#endif
1944#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001945 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1946 HL_TABLE()[idx].sg_font = NOFONT;
1947# ifdef FEAT_XFONTSET
1948 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1949 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1950# endif
1951 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1952 HL_TABLE()[idx].sg_gui_attr = 0;
1953#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001954 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001955 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001956#ifdef FEAT_EVAL
1957 // Since we set the default link, set the location to where the default
1958 // link was set.
1959 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001960#endif
1961}
1962
1963#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1964/*
1965 * Set the normal foreground and background colors according to the "Normal"
1966 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1967 * "Tooltip" colors.
1968 */
1969 void
1970set_normal_colors(void)
1971{
1972# ifdef FEAT_GUI
1973# ifdef FEAT_TERMGUICOLORS
1974 if (gui.in_use)
1975# endif
1976 {
1977 if (set_group_colors((char_u *)"Normal",
1978 &gui.norm_pixel, &gui.back_pixel,
1979 FALSE, TRUE, FALSE))
1980 {
1981 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001982 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001983 }
1984# ifdef FEAT_GUI_X11
1985 if (set_group_colors((char_u *)"Menu",
1986 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1987 TRUE, FALSE, FALSE))
1988 {
1989# ifdef FEAT_MENU
1990 gui_mch_new_menu_colors();
1991# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001992 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001993 }
1994# ifdef FEAT_BEVAL_GUI
1995 if (set_group_colors((char_u *)"Tooltip",
1996 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1997 FALSE, FALSE, TRUE))
1998 {
1999# ifdef FEAT_TOOLBAR
2000 gui_mch_new_tooltip_colors();
2001# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002002 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002003 }
2004# endif
2005 if (set_group_colors((char_u *)"Scrollbar",
2006 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
2007 FALSE, FALSE, FALSE))
2008 {
2009 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002010 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002011 }
2012# endif
2013 }
2014# endif
2015# ifdef FEAT_TERMGUICOLORS
2016# ifdef FEAT_GUI
2017 else
2018# endif
2019 {
2020 int idx;
2021
2022 idx = syn_name2id((char_u *)"Normal") - 1;
2023 if (idx >= 0)
2024 {
2025 gui_do_one_color(idx, FALSE, FALSE);
2026
2027 // If the normal fg or bg color changed a complete redraw is
2028 // required.
2029 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
2030 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
2031 {
2032 // if the GUI color is INVALCOLOR then we use the default cterm
2033 // color
2034 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
2035 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002036 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002037 }
2038 }
2039 }
2040# endif
2041}
2042#endif
2043
2044#if defined(FEAT_GUI) || defined(PROTO)
2045/*
2046 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2047 */
2048 static int
2049set_group_colors(
2050 char_u *name,
2051 guicolor_T *fgp,
2052 guicolor_T *bgp,
2053 int do_menu,
2054 int use_norm,
2055 int do_tooltip)
2056{
2057 int idx;
2058
2059 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002060 if (idx < 0)
2061 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002062
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002063 gui_do_one_color(idx, do_menu, do_tooltip);
2064
2065 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2066 *fgp = HL_TABLE()[idx].sg_gui_fg;
2067 else if (use_norm)
2068 *fgp = gui.def_norm_pixel;
2069 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2070 *bgp = HL_TABLE()[idx].sg_gui_bg;
2071 else if (use_norm)
2072 *bgp = gui.def_back_pixel;
2073 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002074}
2075
2076/*
2077 * Get the font of the "Normal" group.
2078 * Returns "" when it's not found or not set.
2079 */
2080 char_u *
2081hl_get_font_name(void)
2082{
2083 int id;
2084 char_u *s;
2085
2086 id = syn_name2id((char_u *)"Normal");
2087 if (id > 0)
2088 {
2089 s = HL_TABLE()[id - 1].sg_font_name;
2090 if (s != NULL)
2091 return s;
2092 }
2093 return (char_u *)"";
2094}
2095
2096/*
2097 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2098 * actually chosen to be used.
2099 */
2100 void
2101hl_set_font_name(char_u *font_name)
2102{
2103 int id;
2104
2105 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002106 if (id <= 0)
2107 return;
2108
2109 vim_free(HL_TABLE()[id - 1].sg_font_name);
2110 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002111}
2112
2113/*
2114 * Set background color for "Normal" group. Called by gui_set_bg_color()
2115 * when the color is known.
2116 */
2117 void
2118hl_set_bg_color_name(
2119 char_u *name) // must have been allocated
2120{
2121 int id;
2122
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002123 if (name == NULL)
2124 return;
2125
2126 id = syn_name2id((char_u *)"Normal");
2127 if (id <= 0)
2128 return;
2129
2130 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2131 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002132}
2133
2134/*
2135 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2136 * when the color is known.
2137 */
2138 void
2139hl_set_fg_color_name(
2140 char_u *name) // must have been allocated
2141{
2142 int id;
2143
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002144 if (name == NULL)
2145 return;
2146
2147 id = syn_name2id((char_u *)"Normal");
2148 if (id <= 0)
2149 return;
2150
2151 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2152 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002153}
2154
2155/*
2156 * Return the handle for a font name.
2157 * Returns NOFONT when failed.
2158 */
2159 static GuiFont
2160font_name2handle(char_u *name)
2161{
2162 if (STRCMP(name, "NONE") == 0)
2163 return NOFONT;
2164
2165 return gui_mch_get_font(name, TRUE);
2166}
2167
2168# ifdef FEAT_XFONTSET
2169/*
2170 * Return the handle for a fontset name.
2171 * Returns NOFONTSET when failed.
2172 */
2173 static GuiFontset
2174fontset_name2handle(char_u *name, int fixed_width)
2175{
2176 if (STRCMP(name, "NONE") == 0)
2177 return NOFONTSET;
2178
2179 return gui_mch_get_fontset(name, TRUE, fixed_width);
2180}
2181# endif
2182
2183/*
2184 * Get the font or fontset for one highlight group.
2185 */
2186 static void
2187hl_do_font(
2188 int idx,
2189 char_u *arg,
2190 int do_normal, // set normal font
2191 int do_menu UNUSED, // set menu font
2192 int do_tooltip UNUSED, // set tooltip font
2193 int free_font) // free current font/fontset
2194{
2195# ifdef FEAT_XFONTSET
2196 // If 'guifontset' is not empty, first try using the name as a
2197 // fontset. If that doesn't work, use it as a font name.
2198 if (*p_guifontset != NUL
2199# ifdef FONTSET_ALWAYS
2200 || do_menu
2201# endif
2202# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002203 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002204 || do_tooltip
2205# endif
2206 )
2207 {
2208 if (free_font)
2209 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2210 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2211# ifdef FONTSET_ALWAYS
2212 || do_menu
2213# endif
2214# ifdef FEAT_BEVAL_TIP
2215 || do_tooltip
2216# endif
2217 );
2218 }
2219 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2220 {
2221 // If it worked and it's the Normal group, use it as the normal
2222 // fontset. Same for the Menu group.
2223 if (do_normal)
2224 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002225# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002226 if (do_menu)
2227 {
2228# ifdef FONTSET_ALWAYS
2229 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2230# else
2231 // YIKES! This is a bug waiting to crash the program
2232 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2233# endif
2234 gui_mch_new_menu_font();
2235 }
2236# ifdef FEAT_BEVAL_GUI
2237 if (do_tooltip)
2238 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002239 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002240 // displaying a single font and a fontset.
2241 // If the XtNinternational resource is set to True at widget
2242 // creation, then a fontset is always used, otherwise an
2243 // XFontStruct is used.
2244 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2245 gui_mch_new_tooltip_font();
2246 }
2247# endif
2248# endif
2249 }
2250 else
2251# endif
2252 {
2253 if (free_font)
2254 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2255 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2256 // If it worked and it's the Normal group, use it as the
2257 // normal font. Same for the Menu group.
2258 if (HL_TABLE()[idx].sg_font != NOFONT)
2259 {
2260 if (do_normal)
2261 gui_init_font(arg, FALSE);
2262#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002263# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002264 if (do_menu)
2265 {
2266 gui.menu_font = HL_TABLE()[idx].sg_font;
2267 gui_mch_new_menu_font();
2268 }
2269# endif
2270#endif
2271 }
2272 }
2273}
2274
2275#endif // FEAT_GUI
2276
2277#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2278/*
2279 * Return the handle for a color name.
2280 * Returns INVALCOLOR when failed.
2281 */
2282 guicolor_T
2283color_name2handle(char_u *name)
2284{
2285 if (STRCMP(name, "NONE") == 0)
2286 return INVALCOLOR;
2287
2288 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2289 {
2290#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2291 if (gui.in_use)
2292#endif
2293#ifdef FEAT_GUI
2294 return gui.norm_pixel;
2295#endif
2296#ifdef FEAT_TERMGUICOLORS
2297 if (cterm_normal_fg_gui_color != INVALCOLOR)
2298 return cterm_normal_fg_gui_color;
2299 // Guess that the foreground is black or white.
2300 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2301#endif
2302 }
2303 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2304 {
2305#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2306 if (gui.in_use)
2307#endif
2308#ifdef FEAT_GUI
2309 return gui.back_pixel;
2310#endif
2311#ifdef FEAT_TERMGUICOLORS
2312 if (cterm_normal_bg_gui_color != INVALCOLOR)
2313 return cterm_normal_bg_gui_color;
2314 // Guess that the background is white or black.
2315 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2316#endif
2317 }
2318
2319 return GUI_GET_COLOR(name);
2320}
Drew Vogele30d1022021-10-24 20:35:07 +01002321
2322// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2323// values as used by the MS-Windows GDI api. It should be used only for
2324// MS-Windows GDI builds.
2325# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2326# undef RGB
2327# endif
2328# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002329# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002330# endif
2331
2332# ifdef VIMDLL
2333 static guicolor_T
2334gui_adjust_rgb(guicolor_T c)
2335{
2336 if (gui.in_use)
2337 return c;
2338 else
2339 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2340}
2341# else
2342# define gui_adjust_rgb(c) (c)
2343# endif
2344
2345 static int
2346hex_digit(int c)
2347{
Keith Thompson184f71c2024-01-04 21:19:04 +01002348 if (SAFE_isdigit(c))
Drew Vogele30d1022021-10-24 20:35:07 +01002349 return c - '0';
2350 c = TOLOWER_ASC(c);
2351 if (c >= 'a' && c <= 'f')
2352 return c - 'a' + 10;
2353 return 0x1ffffff;
2354}
2355
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002356 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002357decode_hex_color(char_u *hex)
2358{
2359 guicolor_T color;
2360
2361 if (hex[0] != '#' || STRLEN(hex) != 7)
2362 return INVALCOLOR;
2363
2364 // Name is in "#rrggbb" format
2365 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2366 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2367 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2368 if (color > 0xffffff)
2369 return INVALCOLOR;
2370 return gui_adjust_rgb(color);
2371}
2372
Bram Moolenaar2a521962021-10-25 10:30:14 +01002373#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002374// Returns the color currently mapped to the given name or INVALCOLOR if no
2375// such name exists in the color table. The convention is to use lowercase for
2376// all keys in the v:colornames dictionary. The value can be either a string in
2377// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002378 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002379colorname2rgb(char_u *name)
2380{
2381 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2382 char_u *lc_name;
2383 dictitem_T *colentry;
2384 char_u *colstr;
2385 varnumber_T colnum;
2386
2387 lc_name = strlow_save(name);
2388 if (lc_name == NULL)
2389 return INVALCOLOR;
2390
2391 colentry = dict_find(colornames_table, lc_name, -1);
2392 vim_free(lc_name);
2393 if (colentry == NULL)
2394 return INVALCOLOR;
2395
2396 if (colentry->di_tv.v_type == VAR_STRING)
2397 {
2398 colstr = tv_get_string_strict(&colentry->di_tv);
2399 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2400 {
2401 return decode_hex_color(colstr);
2402 }
2403 else
2404 {
2405 semsg(_(e_bad_color_string_str), colstr);
2406 return INVALCOLOR;
2407 }
2408 }
2409
2410 if (colentry->di_tv.v_type == VAR_NUMBER)
2411 {
2412 colnum = tv_get_number(&colentry->di_tv);
2413 return (guicolor_T)colnum;
2414 }
2415
2416 return INVALCOLOR;
2417}
2418
Drew Vogele30d1022021-10-24 20:35:07 +01002419#endif
2420
2421 guicolor_T
2422gui_get_color_cmn(char_u *name)
2423{
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002424 int i;
Drew Vogele30d1022021-10-24 20:35:07 +01002425 guicolor_T color;
2426
2427 struct rgbcolor_table_S {
2428 char_u *color_name;
2429 guicolor_T color;
2430 };
2431
2432 // Only non X11 colors (not present in rgb.txt) and colors in
2433 // color_names[], useful when $VIMRUNTIME is not found,.
2434 static struct rgbcolor_table_S rgb_table[] = {
2435 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2436 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2437 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2438 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2439 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2440 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2441 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2442 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2443 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2444 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2445 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2446 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2447 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2448 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2449 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2450 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2451 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2452 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2453 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2454 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2455 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2456 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2457 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2458 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2459 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2460 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2461 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2462 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2463 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2464 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2465 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2466 };
2467
2468 color = decode_hex_color(name);
2469 if (color != INVALCOLOR)
2470 return color;
2471
2472 // Check if the name is one of the colors we know
2473 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2474 if (STRICMP(name, rgb_table[i].color_name) == 0)
2475 return gui_adjust_rgb(rgb_table[i].color);
2476
2477#if defined(FEAT_EVAL)
2478 /*
2479 * Not a traditional color. Load additional color aliases and then consult the alias table.
2480 */
2481
2482 color = colorname2rgb(name);
2483 if (color == INVALCOLOR)
2484 {
2485 load_default_colors_lists();
2486 color = colorname2rgb(name);
2487 }
2488
2489 return color;
2490#else
2491 return INVALCOLOR;
2492#endif
2493}
2494
2495 guicolor_T
2496gui_get_rgb_color_cmn(int r, int g, int b)
2497{
2498 guicolor_T color = RGB(r, g, b);
2499
2500 if (color > 0xffffff)
2501 return INVALCOLOR;
2502 return gui_adjust_rgb(color);
2503}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002504#endif
2505
2506/*
2507 * Table with the specifications for an attribute number.
2508 * Note that this table is used by ALL buffers. This is required because the
2509 * GUI can redraw at any time for any buffer.
2510 */
2511static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2512
2513#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2514
2515static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2516
2517#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2518
2519#ifdef FEAT_GUI
2520static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2521
2522#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2523#endif
2524
2525/*
2526 * Return the attr number for a set of colors and font.
2527 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2528 * if the combination is new.
2529 * Return 0 for error (no more room).
2530 */
2531 static int
2532get_attr_entry(garray_T *table, attrentry_T *aep)
2533{
2534 int i;
2535 attrentry_T *taep;
2536 static int recursive = FALSE;
2537
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002538 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002539 table->ga_itemsize = sizeof(attrentry_T);
2540 table->ga_growsize = 7;
2541
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002542 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002543 for (i = 0; i < table->ga_len; ++i)
2544 {
2545 taep = &(((attrentry_T *)table->ga_data)[i]);
2546 if ( aep->ae_attr == taep->ae_attr
2547 && (
2548#ifdef FEAT_GUI
2549 (table == &gui_attr_table
2550 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2551 && aep->ae_u.gui.bg_color
2552 == taep->ae_u.gui.bg_color
2553 && aep->ae_u.gui.sp_color
2554 == taep->ae_u.gui.sp_color
2555 && aep->ae_u.gui.font == taep->ae_u.gui.font
2556# ifdef FEAT_XFONTSET
2557 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2558# endif
2559 ))
2560 ||
2561#endif
2562 (table == &term_attr_table
2563 && (aep->ae_u.term.start == NULL)
2564 == (taep->ae_u.term.start == NULL)
2565 && (aep->ae_u.term.start == NULL
2566 || STRCMP(aep->ae_u.term.start,
2567 taep->ae_u.term.start) == 0)
2568 && (aep->ae_u.term.stop == NULL)
2569 == (taep->ae_u.term.stop == NULL)
2570 && (aep->ae_u.term.stop == NULL
2571 || STRCMP(aep->ae_u.term.stop,
2572 taep->ae_u.term.stop) == 0))
2573 || (table == &cterm_attr_table
2574 && aep->ae_u.cterm.fg_color
2575 == taep->ae_u.cterm.fg_color
2576 && aep->ae_u.cterm.bg_color
2577 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002578 && aep->ae_u.cterm.ul_color
2579 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002580 && aep->ae_u.cterm.font
2581 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002582#ifdef FEAT_TERMGUICOLORS
2583 && aep->ae_u.cterm.fg_rgb
2584 == taep->ae_u.cterm.fg_rgb
2585 && aep->ae_u.cterm.bg_rgb
2586 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002587 && aep->ae_u.cterm.ul_rgb
2588 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002589#endif
2590 )))
2591
2592 return i + ATTR_OFF;
2593 }
2594
2595 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2596 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002597 // Running out of attribute entries! remove all attributes, and
2598 // compute new ones for all groups.
2599 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002600 if (recursive)
2601 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002602 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002603 return 0;
2604 }
2605 recursive = TRUE;
2606
2607 clear_hl_tables();
2608
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002609 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002610
2611 for (i = 0; i < highlight_ga.ga_len; ++i)
2612 set_hl_attr(i);
2613
2614 recursive = FALSE;
2615 }
2616
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002617 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002618 if (ga_grow(table, 1) == FAIL)
2619 return 0;
2620
2621 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002622 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002623 taep->ae_attr = aep->ae_attr;
2624#ifdef FEAT_GUI
2625 if (table == &gui_attr_table)
2626 {
2627 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2628 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2629 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2630 taep->ae_u.gui.font = aep->ae_u.gui.font;
2631# ifdef FEAT_XFONTSET
2632 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2633# endif
2634 }
2635#endif
2636 if (table == &term_attr_table)
2637 {
2638 if (aep->ae_u.term.start == NULL)
2639 taep->ae_u.term.start = NULL;
2640 else
2641 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2642 if (aep->ae_u.term.stop == NULL)
2643 taep->ae_u.term.stop = NULL;
2644 else
2645 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2646 }
2647 else if (table == &cterm_attr_table)
2648 {
2649 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2650 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002651 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002652 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002653#ifdef FEAT_TERMGUICOLORS
2654 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2655 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002656 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002657#endif
2658 }
2659 ++table->ga_len;
2660 return (table->ga_len - 1 + ATTR_OFF);
2661}
2662
2663#if defined(FEAT_TERMINAL) || defined(PROTO)
2664/*
2665 * Get an attribute index for a cterm entry.
2666 * Uses an existing entry when possible or adds one when needed.
2667 */
2668 int
2669get_cterm_attr_idx(int attr, int fg, int bg)
2670{
2671 attrentry_T at_en;
2672
Bram Moolenaara80faa82020-04-12 19:37:17 +02002673 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002674#ifdef FEAT_TERMGUICOLORS
2675 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2676 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002677 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002678#endif
2679 at_en.ae_attr = attr;
2680 at_en.ae_u.cterm.fg_color = fg;
2681 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002682 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002683 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002684 return get_attr_entry(&cterm_attr_table, &at_en);
2685}
2686#endif
2687
2688#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2689/*
2690 * Get an attribute index for a 'termguicolors' entry.
2691 * Uses an existing entry when possible or adds one when needed.
2692 */
2693 int
2694get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2695{
2696 attrentry_T at_en;
2697
Bram Moolenaara80faa82020-04-12 19:37:17 +02002698 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002699 at_en.ae_attr = attr;
2700 if (fg == INVALCOLOR && bg == INVALCOLOR)
2701 {
2702 // If both GUI colors are not set fall back to the cterm colors. Helps
2703 // if the GUI only has an attribute, such as undercurl.
2704 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2705 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2706 }
2707 else
2708 {
2709 at_en.ae_u.cterm.fg_rgb = fg;
2710 at_en.ae_u.cterm.bg_rgb = bg;
2711 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002712 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002713 return get_attr_entry(&cterm_attr_table, &at_en);
2714}
2715#endif
2716
2717#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2718/*
2719 * Get an attribute index for a cterm entry.
2720 * Uses an existing entry when possible or adds one when needed.
2721 */
2722 int
2723get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2724{
2725 attrentry_T at_en;
2726
Bram Moolenaara80faa82020-04-12 19:37:17 +02002727 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002728 at_en.ae_attr = attr;
2729 at_en.ae_u.gui.fg_color = fg;
2730 at_en.ae_u.gui.bg_color = bg;
2731 return get_attr_entry(&gui_attr_table, &at_en);
2732}
2733#endif
2734
2735/*
2736 * Clear all highlight tables.
2737 */
2738 void
2739clear_hl_tables(void)
2740{
2741 int i;
2742 attrentry_T *taep;
2743
2744#ifdef FEAT_GUI
2745 ga_clear(&gui_attr_table);
2746#endif
2747 for (i = 0; i < term_attr_table.ga_len; ++i)
2748 {
2749 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2750 vim_free(taep->ae_u.term.start);
2751 vim_free(taep->ae_u.term.stop);
2752 }
2753 ga_clear(&term_attr_table);
2754 ga_clear(&cterm_attr_table);
2755}
2756
2757/*
2758 * Combine special attributes (e.g., for spelling) with other attributes
2759 * (e.g., for syntax highlighting).
2760 * "prim_attr" overrules "char_attr".
2761 * This creates a new group when required.
2762 * Since we expect there to be few spelling mistakes we don't cache the
2763 * result.
2764 * Return the resulting attributes.
2765 */
2766 int
2767hl_combine_attr(int char_attr, int prim_attr)
2768{
2769 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002770 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002771 attrentry_T new_en;
2772
2773 if (char_attr == 0)
2774 return prim_attr;
2775 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2776 return ATTR_COMBINE(char_attr, prim_attr);
2777#ifdef FEAT_GUI
2778 if (gui.in_use)
2779 {
2780 if (char_attr > HL_ALL)
2781 char_aep = syn_gui_attr2entry(char_attr);
2782 if (char_aep != NULL)
2783 new_en = *char_aep;
2784 else
2785 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002786 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002787 new_en.ae_u.gui.fg_color = INVALCOLOR;
2788 new_en.ae_u.gui.bg_color = INVALCOLOR;
2789 new_en.ae_u.gui.sp_color = INVALCOLOR;
2790 if (char_attr <= HL_ALL)
2791 new_en.ae_attr = char_attr;
2792 }
2793
2794 if (prim_attr <= HL_ALL)
2795 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2796 else
2797 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002798 prim_aep = syn_gui_attr2entry(prim_attr);
2799 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002800 {
2801 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002802 prim_aep->ae_attr);
2803 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2804 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2805 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2806 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2807 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2808 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2809 if (prim_aep->ae_u.gui.font != NOFONT)
2810 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002811# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002812 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2813 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002814# endif
2815 }
2816 }
2817 return get_attr_entry(&gui_attr_table, &new_en);
2818 }
2819#endif
2820
2821 if (IS_CTERM)
2822 {
2823 if (char_attr > HL_ALL)
2824 char_aep = syn_cterm_attr2entry(char_attr);
2825 if (char_aep != NULL)
2826 new_en = *char_aep;
2827 else
2828 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002829 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002830#ifdef FEAT_TERMGUICOLORS
2831 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2832 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002833 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002834#endif
2835 if (char_attr <= HL_ALL)
2836 new_en.ae_attr = char_attr;
2837 }
2838
2839 if (prim_attr <= HL_ALL)
2840 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2841 else
2842 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002843 prim_aep = syn_cterm_attr2entry(prim_attr);
2844 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002845 {
2846 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002847 prim_aep->ae_attr);
2848 if (prim_aep->ae_u.cterm.fg_color > 0)
2849 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2850 if (prim_aep->ae_u.cterm.bg_color > 0)
2851 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2852 if (prim_aep->ae_u.cterm.ul_color > 0)
2853 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002854 if (prim_aep->ae_u.cterm.font > 0)
2855 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002856#ifdef FEAT_TERMGUICOLORS
2857 // If both fg and bg are not set fall back to cterm colors.
2858 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002859 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2860 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002861 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002862 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002863 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002864 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002865 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2866 }
2867 else
2868 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002869 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2870 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2871 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2872 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002873 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002874 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2875 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002876#endif
2877 }
2878 }
2879 return get_attr_entry(&cterm_attr_table, &new_en);
2880 }
2881
2882 if (char_attr > HL_ALL)
2883 char_aep = syn_term_attr2entry(char_attr);
2884 if (char_aep != NULL)
2885 new_en = *char_aep;
2886 else
2887 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002888 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002889 if (char_attr <= HL_ALL)
2890 new_en.ae_attr = char_attr;
2891 }
2892
2893 if (prim_attr <= HL_ALL)
2894 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2895 else
2896 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002897 prim_aep = syn_term_attr2entry(prim_attr);
2898 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002899 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002900 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2901 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002902 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002903 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2904 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002905 }
2906 }
2907 }
2908 return get_attr_entry(&term_attr_table, &new_en);
2909}
2910
2911#ifdef FEAT_GUI
2912 attrentry_T *
2913syn_gui_attr2entry(int attr)
2914{
2915 attr -= ATTR_OFF;
2916 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2917 return NULL;
2918 return &(GUI_ATTR_ENTRY(attr));
2919}
2920#endif
2921
2922/*
2923 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2924 * Only to be used when "attr" > HL_ALL.
2925 */
2926 int
2927syn_attr2attr(int attr)
2928{
2929 attrentry_T *aep;
2930
2931#ifdef FEAT_GUI
2932 if (gui.in_use)
2933 aep = syn_gui_attr2entry(attr);
2934 else
2935#endif
2936 if (IS_CTERM)
2937 aep = syn_cterm_attr2entry(attr);
2938 else
2939 aep = syn_term_attr2entry(attr);
2940
2941 if (aep == NULL) // highlighting not set
2942 return 0;
2943 return aep->ae_attr;
2944}
2945
2946
2947 attrentry_T *
2948syn_term_attr2entry(int attr)
2949{
2950 attr -= ATTR_OFF;
2951 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2952 return NULL;
2953 return &(TERM_ATTR_ENTRY(attr));
2954}
2955
2956 attrentry_T *
2957syn_cterm_attr2entry(int attr)
2958{
2959 attr -= ATTR_OFF;
2960 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2961 return NULL;
2962 return &(CTERM_ATTR_ENTRY(attr));
2963}
2964
2965#define LIST_ATTR 1
2966#define LIST_STRING 2
2967#define LIST_INT 3
2968
2969 static void
2970highlight_list_one(int id)
2971{
2972 hl_group_T *sgp;
2973 int didh = FALSE;
2974
2975 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2976
2977 if (message_filtered(sgp->sg_name))
2978 return;
2979
2980 didh = highlight_list_arg(id, didh, LIST_ATTR,
2981 sgp->sg_term, NULL, "term");
2982 didh = highlight_list_arg(id, didh, LIST_STRING,
2983 0, sgp->sg_start, "start");
2984 didh = highlight_list_arg(id, didh, LIST_STRING,
2985 0, sgp->sg_stop, "stop");
2986
2987 didh = highlight_list_arg(id, didh, LIST_ATTR,
2988 sgp->sg_cterm, NULL, "cterm");
2989 didh = highlight_list_arg(id, didh, LIST_INT,
2990 sgp->sg_cterm_fg, NULL, "ctermfg");
2991 didh = highlight_list_arg(id, didh, LIST_INT,
2992 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002993 didh = highlight_list_arg(id, didh, LIST_INT,
2994 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01002995 didh = highlight_list_arg(id, didh, LIST_INT,
2996 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002997
2998#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2999 didh = highlight_list_arg(id, didh, LIST_ATTR,
3000 sgp->sg_gui, NULL, "gui");
3001 didh = highlight_list_arg(id, didh, LIST_STRING,
3002 0, sgp->sg_gui_fg_name, "guifg");
3003 didh = highlight_list_arg(id, didh, LIST_STRING,
3004 0, sgp->sg_gui_bg_name, "guibg");
3005 didh = highlight_list_arg(id, didh, LIST_STRING,
3006 0, sgp->sg_gui_sp_name, "guisp");
3007#endif
3008#ifdef FEAT_GUI
3009 didh = highlight_list_arg(id, didh, LIST_STRING,
3010 0, sgp->sg_font_name, "font");
3011#endif
3012
3013 if (sgp->sg_link && !got_int)
3014 {
3015 (void)syn_list_header(didh, 9999, id);
3016 didh = TRUE;
3017 msg_puts_attr("links to", HL_ATTR(HLF_D));
3018 msg_putchar(' ');
3019 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3020 }
3021
3022 if (!didh)
3023 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3024#ifdef FEAT_EVAL
3025 if (p_verbose > 0)
3026 last_set_msg(sgp->sg_script_ctx);
3027#endif
3028}
3029
3030 static int
3031highlight_list_arg(
3032 int id,
3033 int didh,
3034 int type,
3035 int iarg,
3036 char_u *sarg,
3037 char *name)
3038{
Bram Moolenaar84f54632022-06-29 18:39:11 +01003039 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003040 char_u *ts;
3041 int i;
3042
3043 if (got_int)
3044 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003045
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003046 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3047 return didh;
3048
3049 ts = buf;
3050 if (type == LIST_INT)
3051 sprintf((char *)buf, "%d", iarg - 1);
3052 else if (type == LIST_STRING)
3053 ts = sarg;
3054 else // type == LIST_ATTR
3055 {
3056 buf[0] = NUL;
3057 for (i = 0; hl_attr_table[i] != 0; ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003058 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003059 if (iarg & hl_attr_table[i])
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003060 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003061 if (buf[0] != NUL)
3062 vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
3063 vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
3064 iarg &= ~hl_attr_table[i]; // don't want "inverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003065 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003066 }
3067 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003068
3069 (void)syn_list_header(didh,
3070 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3071 didh = TRUE;
3072 if (!got_int)
3073 {
3074 if (*name != NUL)
3075 {
3076 msg_puts_attr(name, HL_ATTR(HLF_D));
3077 msg_puts_attr("=", HL_ATTR(HLF_D));
3078 }
3079 msg_outtrans(ts);
3080 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003081 return didh;
3082}
3083
3084#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3085/*
3086 * Return "1" if highlight group "id" has attribute "flag".
3087 * Return NULL otherwise.
3088 */
3089 char_u *
3090highlight_has_attr(
3091 int id,
3092 int flag,
3093 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3094{
3095 int attr;
3096
3097 if (id <= 0 || id > highlight_ga.ga_len)
3098 return NULL;
3099
3100#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3101 if (modec == 'g')
3102 attr = HL_TABLE()[id - 1].sg_gui;
3103 else
3104#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003105 {
3106 if (modec == 'c')
3107 attr = HL_TABLE()[id - 1].sg_cterm;
3108 else
3109 attr = HL_TABLE()[id - 1].sg_term;
3110 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003111
3112 if (attr & flag)
3113 return (char_u *)"1";
3114 return NULL;
3115}
3116#endif
3117
3118#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3119/*
3120 * Return color name of highlight group "id".
3121 */
3122 char_u *
3123highlight_color(
3124 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003125 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003126 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3127{
3128 static char_u name[20];
3129 int n;
3130 int fg = FALSE;
3131 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003132 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003133 int font = FALSE;
3134
3135 if (id <= 0 || id > highlight_ga.ga_len)
3136 return NULL;
3137
3138 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3139 fg = TRUE;
3140 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3141 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3142 font = TRUE;
3143 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3144 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003145 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3146 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003147 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3148 return NULL;
3149 if (modec == 'g')
3150 {
3151# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3152# ifdef FEAT_GUI
3153 // return font name
3154 if (font)
3155 return HL_TABLE()[id - 1].sg_font_name;
3156# endif
3157
3158 // return #RRGGBB form (only possible when GUI is running)
3159 if ((USE_24BIT) && what[2] == '#')
3160 {
3161 guicolor_T color;
3162 long_u rgb;
3163 static char_u buf[10];
3164
3165 if (fg)
3166 color = HL_TABLE()[id - 1].sg_gui_fg;
3167 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003168 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003169 else
3170 color = HL_TABLE()[id - 1].sg_gui_bg;
3171 if (color == INVALCOLOR)
3172 return NULL;
3173 rgb = (long_u)GUI_MCH_GET_RGB(color);
3174 sprintf((char *)buf, "#%02x%02x%02x",
3175 (unsigned)(rgb >> 16),
3176 (unsigned)(rgb >> 8) & 255,
3177 (unsigned)rgb & 255);
3178 return buf;
3179 }
3180# endif
3181 if (fg)
3182 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3183 if (sp)
3184 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3185 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3186 }
PMuncha606f3a2023-11-15 15:35:49 +01003187 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003188 return NULL;
3189 if (modec == 'c')
3190 {
3191 if (fg)
3192 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003193 else if (ul)
3194 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003195 else if (font)
3196 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003197 else
3198 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3199 if (n < 0)
3200 return NULL;
3201 sprintf((char *)name, "%d", n);
3202 return name;
3203 }
3204 // term doesn't have color
3205 return NULL;
3206}
3207#endif
3208
3209#if (defined(FEAT_SYN_HL) \
3210 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3211 && defined(FEAT_PRINTER)) || defined(PROTO)
3212/*
3213 * Return color name of highlight group "id" as RGB value.
3214 */
3215 long_u
3216highlight_gui_color_rgb(
3217 int id,
3218 int fg) // TRUE = fg, FALSE = bg
3219{
3220 guicolor_T color;
3221
3222 if (id <= 0 || id > highlight_ga.ga_len)
3223 return 0L;
3224
3225 if (fg)
3226 color = HL_TABLE()[id - 1].sg_gui_fg;
3227 else
3228 color = HL_TABLE()[id - 1].sg_gui_bg;
3229
3230 if (color == INVALCOLOR)
3231 return 0L;
3232
3233 return GUI_MCH_GET_RGB(color);
3234}
3235#endif
3236
3237/*
3238 * Output the syntax list header.
3239 * Return TRUE when started a new line.
3240 */
3241 int
3242syn_list_header(
3243 int did_header, // did header already
3244 int outlen, // length of string that comes
3245 int id) // highlight group id
3246{
3247 int endcol = 19;
3248 int newline = TRUE;
3249 int name_col = 0;
3250
3251 if (!did_header)
3252 {
3253 msg_putchar('\n');
3254 if (got_int)
3255 return TRUE;
3256 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3257 name_col = msg_col;
3258 endcol = 15;
3259 }
3260 else if (msg_col + outlen + 1 >= Columns)
3261 {
3262 msg_putchar('\n');
3263 if (got_int)
3264 return TRUE;
3265 }
3266 else
3267 {
3268 if (msg_col >= endcol) // wrap around is like starting a new line
3269 newline = FALSE;
3270 }
3271
3272 if (msg_col >= endcol) // output at least one space
3273 endcol = msg_col + 1;
3274 if (Columns <= endcol) // avoid hang for tiny window
3275 endcol = Columns - 1;
3276
3277 msg_advance(endcol);
3278
3279 // Show "xxx" with the attributes.
3280 if (!did_header)
3281 {
3282 if (endcol == Columns - 1 && endcol <= name_col)
3283 msg_putchar(' ');
3284 msg_puts_attr("xxx", syn_id2attr(id));
3285 msg_putchar(' ');
3286 }
3287
3288 return newline;
3289}
3290
3291/*
3292 * Set the attribute numbers for a highlight group.
3293 * Called after one of the attributes has changed.
3294 */
3295 static void
3296set_hl_attr(
3297 int idx) // index in array
3298{
3299 attrentry_T at_en;
3300 hl_group_T *sgp = HL_TABLE() + idx;
3301
3302 // The "Normal" group doesn't need an attribute number
3303 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3304 return;
3305
3306#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003307 // For the GUI mode: If there are other than "normal" highlighting
3308 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003309 if (sgp->sg_gui_fg == INVALCOLOR
3310 && sgp->sg_gui_bg == INVALCOLOR
3311 && sgp->sg_gui_sp == INVALCOLOR
3312 && sgp->sg_font == NOFONT
3313# ifdef FEAT_XFONTSET
3314 && sgp->sg_fontset == NOFONTSET
3315# endif
3316 )
3317 {
3318 sgp->sg_gui_attr = sgp->sg_gui;
3319 }
3320 else
3321 {
3322 at_en.ae_attr = sgp->sg_gui;
3323 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3324 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3325 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3326 at_en.ae_u.gui.font = sgp->sg_font;
3327# ifdef FEAT_XFONTSET
3328 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3329# endif
3330 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3331 }
3332#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003333 // For the term mode: If there are other than "normal" highlighting
3334 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003335 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3336 sgp->sg_term_attr = sgp->sg_term;
3337 else
3338 {
3339 at_en.ae_attr = sgp->sg_term;
3340 at_en.ae_u.term.start = sgp->sg_start;
3341 at_en.ae_u.term.stop = sgp->sg_stop;
3342 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3343 }
3344
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003345 // For the color term mode: If there are other than "normal"
3346 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003347 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3348 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003349# ifdef FEAT_TERMGUICOLORS
3350 && sgp->sg_gui_fg == INVALCOLOR
3351 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003352 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003353# endif
3354 )
3355 sgp->sg_cterm_attr = sgp->sg_cterm;
3356 else
3357 {
3358 at_en.ae_attr = sgp->sg_cterm;
3359 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3360 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003361 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003362 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003363# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003364 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3365 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003366 // Only use the underline/undercurl color when used, it may clear the
3367 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003368 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3369 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003370 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3371 else
3372 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003373 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3374 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3375 {
3376 // If both fg and bg are invalid fall back to the cterm colors.
3377 // Helps when the GUI only uses an attribute, e.g. undercurl.
3378 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3379 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3380 }
3381# endif
3382 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3383 }
3384}
3385
3386/*
3387 * Lookup a highlight group name and return its ID.
3388 * If it is not found, 0 is returned.
3389 */
3390 int
3391syn_name2id(char_u *name)
3392{
3393 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003394 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003395
3396 // Avoid using stricmp() too much, it's slow on some systems
3397 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3398 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003399 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003400 vim_strup(name_u);
3401 for (i = highlight_ga.ga_len; --i >= 0; )
3402 if (HL_TABLE()[i].sg_name_u != NULL
3403 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3404 break;
3405 return i + 1;
3406}
3407
3408/*
3409 * Lookup a highlight group name and return its attributes.
3410 * Return zero if not found.
3411 */
3412 int
3413syn_name2attr(char_u *name)
3414{
3415 int id = syn_name2id(name);
3416
3417 if (id != 0)
3418 return syn_id2attr(id);
3419 return 0;
3420}
3421
3422#if defined(FEAT_EVAL) || defined(PROTO)
3423/*
3424 * Return TRUE if highlight group "name" exists.
3425 */
3426 int
3427highlight_exists(char_u *name)
3428{
3429 return (syn_name2id(name) > 0);
3430}
3431
3432# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3433/*
3434 * Return the name of highlight group "id".
3435 * When not a valid ID return an empty string.
3436 */
3437 char_u *
3438syn_id2name(int id)
3439{
3440 if (id <= 0 || id > highlight_ga.ga_len)
3441 return (char_u *)"";
3442 return HL_TABLE()[id - 1].sg_name;
3443}
3444# endif
3445#endif
3446
3447/*
3448 * Like syn_name2id(), but take a pointer + length argument.
3449 */
3450 int
3451syn_namen2id(char_u *linep, int len)
3452{
3453 char_u *name;
3454 int id = 0;
3455
3456 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003457 if (name == NULL)
3458 return 0;
3459
3460 id = syn_name2id(name);
3461 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003462 return id;
3463}
3464
3465/*
3466 * Find highlight group name in the table and return its ID.
3467 * The argument is a pointer to the name and the length of the name.
3468 * If it doesn't exist yet, a new entry is created.
3469 * Return 0 for failure.
3470 */
3471 int
3472syn_check_group(char_u *pp, int len)
3473{
3474 int id;
3475 char_u *name;
3476
erw7f7f7aaf2021-12-07 21:29:20 +00003477 if (len > MAX_SYN_NAME)
3478 {
3479 emsg(_(e_highlight_group_name_too_long));
3480 return 0;
3481 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003482 name = vim_strnsave(pp, len);
3483 if (name == NULL)
3484 return 0;
3485
3486 id = syn_name2id(name);
3487 if (id == 0) // doesn't exist yet
3488 id = syn_add_group(name);
3489 else
3490 vim_free(name);
3491 return id;
3492}
3493
3494/*
3495 * Add new highlight group and return its ID.
3496 * "name" must be an allocated string, it will be consumed.
3497 * Return 0 for failure.
3498 */
3499 static int
3500syn_add_group(char_u *name)
3501{
3502 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003503 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003504
Gregory Andersd4376dc2023-08-20 19:14:03 +02003505 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003506 for (p = name; *p != NUL; ++p)
3507 {
3508 if (!vim_isprintc(*p))
3509 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003510 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003511 vim_free(name);
3512 return 0;
3513 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003514 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003515 {
3516 // This is an error, but since there previously was no check only
3517 // give a warning.
3518 msg_source(HL_ATTR(HLF_W));
3519 msg(_("W18: Invalid character in group name"));
3520 break;
3521 }
3522 }
3523
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003524 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003525 if (highlight_ga.ga_data == NULL)
3526 {
3527 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3528 highlight_ga.ga_growsize = 10;
3529 }
3530
3531 if (highlight_ga.ga_len >= MAX_HL_ID)
3532 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003533 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003534 vim_free(name);
3535 return 0;
3536 }
3537
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003538 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003539 if (ga_grow(&highlight_ga, 1) == FAIL)
3540 {
3541 vim_free(name);
3542 return 0;
3543 }
3544
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003545 name_up = vim_strsave_up(name);
3546 if (name_up == NULL)
3547 {
3548 vim_free(name);
3549 return 0;
3550 }
3551
Bram Moolenaara80faa82020-04-12 19:37:17 +02003552 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003553 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003554 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003555#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3556 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3557 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003558 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003559#endif
3560 ++highlight_ga.ga_len;
3561
3562 return highlight_ga.ga_len; // ID is index plus one
3563}
3564
3565/*
3566 * When, just after calling syn_add_group(), an error is discovered, this
3567 * function deletes the new name.
3568 */
3569 static void
3570syn_unadd_group(void)
3571{
3572 --highlight_ga.ga_len;
3573 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3574 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3575}
3576
3577/*
3578 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003579 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003580 */
3581 int
3582syn_id2attr(int hl_id)
3583{
3584 int attr;
3585 hl_group_T *sgp;
3586
3587 hl_id = syn_get_final_id(hl_id);
3588 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3589
3590#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003591 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003592 if (gui.in_use)
3593 attr = sgp->sg_gui_attr;
3594 else
3595#endif
3596 if (IS_CTERM)
3597 attr = sgp->sg_cterm_attr;
3598 else
3599 attr = sgp->sg_term_attr;
3600
3601 return attr;
3602}
3603
3604#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3605/*
3606 * Get the GUI colors and attributes for a group ID.
3607 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3608 */
3609 int
3610syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3611{
3612 hl_group_T *sgp;
3613
3614 hl_id = syn_get_final_id(hl_id);
3615 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3616
3617 *fgp = sgp->sg_gui_fg;
3618 *bgp = sgp->sg_gui_bg;
3619 return sgp->sg_gui;
3620}
3621#endif
3622
3623#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003624 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3625 && defined(FEAT_TERMGUICOLORS)) \
3626 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003627 void
3628syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3629{
3630 hl_group_T *sgp;
3631
3632 hl_id = syn_get_final_id(hl_id);
3633 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3634 *fgp = sgp->sg_cterm_fg - 1;
3635 *bgp = sgp->sg_cterm_bg - 1;
3636}
3637#endif
3638
3639/*
3640 * Translate a group ID to the final group ID (following links).
3641 */
3642 int
3643syn_get_final_id(int hl_id)
3644{
3645 int count;
3646 hl_group_T *sgp;
3647
3648 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3649 return 0; // Can be called from eval!!
3650
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003651 // Follow links until there is no more.
3652 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003653 for (count = 100; --count >= 0; )
3654 {
3655 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3656 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3657 break;
3658 hl_id = sgp->sg_link;
3659 }
3660
3661 return hl_id;
3662}
3663
3664#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3665/*
3666 * Call this function just after the GUI has started.
3667 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3668 * It finds the font and color handles for the highlighting groups.
3669 */
3670 void
3671highlight_gui_started(void)
3672{
3673 int idx;
3674
3675 // First get the colors from the "Normal" and "Menu" group, if set
3676 if (USE_24BIT)
3677 set_normal_colors();
3678
3679 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3680 gui_do_one_color(idx, FALSE, FALSE);
3681
3682 highlight_changed();
3683}
3684
3685 static void
3686gui_do_one_color(
3687 int idx,
3688 int do_menu UNUSED, // TRUE: might set the menu font
3689 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3690{
3691 int didit = FALSE;
3692
3693# ifdef FEAT_GUI
3694# ifdef FEAT_TERMGUICOLORS
3695 if (gui.in_use)
3696# endif
3697 if (HL_TABLE()[idx].sg_font_name != NULL)
3698 {
3699 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3700 do_tooltip, TRUE);
3701 didit = TRUE;
3702 }
3703# endif
3704 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3705 {
3706 HL_TABLE()[idx].sg_gui_fg =
3707 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3708 didit = TRUE;
3709 }
3710 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3711 {
3712 HL_TABLE()[idx].sg_gui_bg =
3713 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3714 didit = TRUE;
3715 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003716 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3717 {
3718 HL_TABLE()[idx].sg_gui_sp =
3719 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3720 didit = TRUE;
3721 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003722 if (didit) // need to get a new attr number
3723 set_hl_attr(idx);
3724}
3725#endif
3726
3727#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3728/*
3729 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3730 */
3731 static void
3732combine_stl_hlt(
3733 int id,
3734 int id_S,
3735 int id_alt,
3736 int hlcnt,
3737 int i,
3738 int hlf,
3739 int *table)
3740{
3741 hl_group_T *hlt = HL_TABLE();
3742
3743 if (id_alt == 0)
3744 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003745 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003746 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3747 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3748# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3749 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3750# endif
3751 }
3752 else
3753 mch_memmove(&hlt[hlcnt + i],
3754 &hlt[id_alt - 1],
3755 sizeof(hl_group_T));
3756 hlt[hlcnt + i].sg_link = 0;
3757
3758 hlt[hlcnt + i].sg_term ^=
3759 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3760 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3761 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3762 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3763 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3764 hlt[hlcnt + i].sg_cterm ^=
3765 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3766 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3767 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3768 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3769 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003770 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3771 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003772# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3773 hlt[hlcnt + i].sg_gui ^=
3774 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3775# endif
3776# ifdef FEAT_GUI
3777 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3778 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3779 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3780 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3781 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3782 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3783 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3784 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3785# ifdef FEAT_XFONTSET
3786 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3787 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3788# endif
3789# endif
3790 highlight_ga.ga_len = hlcnt + i + 1;
3791 set_hl_attr(hlcnt + i); // At long last we can apply
3792 table[i] = syn_id2attr(hlcnt + i + 1);
3793}
3794#endif
3795
3796/*
3797 * Translate the 'highlight' option into attributes in highlight_attr[] and
3798 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3799 * corresponding highlights to use on top of HLF_SNC is computed.
3800 * Called only when the 'highlight' option has been changed and upon first
3801 * screen redraw after any :highlight command.
3802 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3803 */
3804 int
3805highlight_changed(void)
3806{
3807 int hlf;
3808 int i;
3809 char_u *p;
3810 int attr;
3811 char_u *end;
3812 int id;
3813#ifdef USER_HIGHLIGHT
3814 char_u userhl[30]; // use 30 to avoid compiler warning
3815# ifdef FEAT_STL_OPT
3816 int id_S = -1;
3817 int id_SNC = 0;
3818# ifdef FEAT_TERMINAL
3819 int id_ST = 0;
3820 int id_STNC = 0;
3821# endif
3822 int hlcnt;
3823# endif
3824#endif
3825 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3826
3827 need_highlight_changed = FALSE;
3828
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003829#ifdef FEAT_TERMINAL
3830 term_update_colors_all();
3831 term_update_wincolor_all();
3832#endif
3833
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003834 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003835 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3836 highlight_attr[hlf] = 0;
3837
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003838 // First set all attributes to their default value.
3839 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003840 for (i = 0; i < 2; ++i)
3841 {
3842 if (i)
3843 p = p_hl;
3844 else
3845 p = get_highlight_default();
3846 if (p == NULL) // just in case
3847 continue;
3848
3849 while (*p)
3850 {
3851 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3852 if (hl_flags[hlf] == *p)
3853 break;
3854 ++p;
3855 if (hlf == (int)HLF_COUNT || *p == NUL)
3856 return FAIL;
3857
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003858 // Allow several hl_flags to be combined, like "bu" for
3859 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003860 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003861 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003862 {
3863 if (VIM_ISWHITE(*p)) // ignore white space
3864 continue;
3865
3866 if (attr > HL_ALL) // Combination with ':' is not allowed.
3867 return FAIL;
3868
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003869 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003870 switch (*p)
3871 {
3872 case 'b': attr |= HL_BOLD;
3873 break;
3874 case 'i': attr |= HL_ITALIC;
3875 break;
3876 case '-':
3877 case 'n': // no highlighting
3878 break;
3879 case 'r': attr |= HL_INVERSE;
3880 break;
3881 case 's': attr |= HL_STANDOUT;
3882 break;
3883 case 'u': attr |= HL_UNDERLINE;
3884 break;
3885 case 'c': attr |= HL_UNDERCURL;
3886 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003887 case '2': attr |= HL_UNDERDOUBLE;
3888 break;
3889 case 'd': attr |= HL_UNDERDOTTED;
3890 break;
3891 case '=': attr |= HL_UNDERDASHED;
3892 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003893 case 't': attr |= HL_STRIKETHROUGH;
3894 break;
3895 case ':': ++p; // highlight group name
3896 if (attr || *p == NUL) // no combinations
3897 return FAIL;
3898 end = vim_strchr(p, ',');
3899 if (end == NULL)
3900 end = p + STRLEN(p);
3901 id = syn_check_group(p, (int)(end - p));
3902 if (id == 0)
3903 return FAIL;
3904 attr = syn_id2attr(id);
3905 p = end - 1;
3906#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3907 if (hlf == (int)HLF_SNC)
3908 id_SNC = syn_get_final_id(id);
3909# ifdef FEAT_TERMINAL
3910 else if (hlf == (int)HLF_ST)
3911 id_ST = syn_get_final_id(id);
3912 else if (hlf == (int)HLF_STNC)
3913 id_STNC = syn_get_final_id(id);
3914# endif
3915 else if (hlf == (int)HLF_S)
3916 id_S = syn_get_final_id(id);
3917#endif
3918 break;
3919 default: return FAIL;
3920 }
3921 }
3922 highlight_attr[hlf] = attr;
3923
3924 p = skip_to_option_part(p); // skip comma and spaces
3925 }
3926 }
3927
3928#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003929 // Setup the user highlights
3930 //
3931 // Temporarily utilize 28 more hl entries:
3932 // 9 for User1-User9 combined with StatusLineNC
3933 // 9 for User1-User9 combined with StatusLineTerm
3934 // 9 for User1-User9 combined with StatusLineTermNC
3935 // 1 for StatusLine default
3936 // Have to be in there simultaneously in case of table overflows in
3937 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003938# ifdef FEAT_STL_OPT
3939 if (ga_grow(&highlight_ga, 28) == FAIL)
3940 return FAIL;
3941 hlcnt = highlight_ga.ga_len;
3942 if (id_S == -1)
3943 {
3944 // Make sure id_S is always valid to simplify code below. Use the last
3945 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003946 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003947 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3948 id_S = hlcnt + 19;
3949 }
3950# endif
3951 for (i = 0; i < 9; i++)
3952 {
3953 sprintf((char *)userhl, "User%d", i + 1);
3954 id = syn_name2id(userhl);
3955 if (id == 0)
3956 {
3957 highlight_user[i] = 0;
3958# ifdef FEAT_STL_OPT
3959 highlight_stlnc[i] = 0;
3960# ifdef FEAT_TERMINAL
3961 highlight_stlterm[i] = 0;
3962 highlight_stltermnc[i] = 0;
3963# endif
3964# endif
3965 }
3966 else
3967 {
3968 highlight_user[i] = syn_id2attr(id);
3969# ifdef FEAT_STL_OPT
3970 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3971 HLF_SNC, highlight_stlnc);
3972# ifdef FEAT_TERMINAL
3973 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3974 HLF_ST, highlight_stlterm);
3975 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3976 HLF_STNC, highlight_stltermnc);
3977# endif
3978# endif
3979 }
3980 }
3981# ifdef FEAT_STL_OPT
3982 highlight_ga.ga_len = hlcnt;
3983# endif
3984
3985#endif // USER_HIGHLIGHT
3986
3987 return OK;
3988}
3989
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003990static void highlight_list(void);
3991static void highlight_list_two(int cnt, int attr);
3992
3993/*
3994 * Handle command line completion for :highlight command.
3995 */
3996 void
3997set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3998{
3999 char_u *p;
4000
4001 // Default: expand group names
4002 xp->xp_context = EXPAND_HIGHLIGHT;
4003 xp->xp_pattern = arg;
4004 include_link = 2;
4005 include_default = 1;
4006
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004007 if (*arg == NUL)
4008 return;
4009
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004010 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004011 p = skiptowhite(arg);
4012 if (*p == NUL)
4013 return;
4014
4015 // past "default" or group name
4016 include_default = 0;
4017 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004018 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004019 arg = skipwhite(p);
4020 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004021 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004022 }
4023 if (*p == NUL)
4024 return;
4025
4026 // past group name
4027 include_link = 0;
4028 if (arg[1] == 'i' && arg[0] == 'N')
4029 highlight_list();
4030 if (STRNCMP("link", arg, p - arg) == 0
4031 || STRNCMP("clear", arg, p - arg) == 0)
4032 {
4033 xp->xp_pattern = skipwhite(p);
4034 p = skiptowhite(xp->xp_pattern);
4035 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004036 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004037 xp->xp_pattern = skipwhite(p);
4038 p = skiptowhite(xp->xp_pattern);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004039 }
4040 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004041 if (*p != NUL) // past group name(s)
4042 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004043}
4044
4045/*
4046 * List highlighting matches in a nice way.
4047 */
4048 static void
4049highlight_list(void)
4050{
4051 int i;
4052
4053 for (i = 10; --i >= 0; )
4054 highlight_list_two(i, HL_ATTR(HLF_D));
4055 for (i = 40; --i >= 0; )
4056 highlight_list_two(99, 0);
4057}
4058
4059 static void
4060highlight_list_two(int cnt, int attr)
4061{
4062 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4063 msg_clr_eos();
4064 out_flush();
4065 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4066}
4067
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004068/*
4069 * Function given to ExpandGeneric() to obtain the list of group names.
4070 */
4071 char_u *
4072get_highlight_name(expand_T *xp UNUSED, int idx)
4073{
4074 return get_highlight_name_ext(xp, idx, TRUE);
4075}
4076
4077/*
4078 * Obtain a highlight group name.
4079 * When "skip_cleared" is TRUE don't return a cleared entry.
4080 */
4081 char_u *
4082get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4083{
4084 if (idx < 0)
4085 return NULL;
4086
4087 // Items are never removed from the table, skip the ones that were
4088 // cleared.
4089 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4090 return (char_u *)"";
4091
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004092 if (idx == highlight_ga.ga_len && include_none != 0)
4093 return (char_u *)"none";
4094 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4095 return (char_u *)"default";
4096 if (idx == highlight_ga.ga_len + include_none + include_default
4097 && include_link != 0)
4098 return (char_u *)"link";
4099 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4100 && include_link != 0)
4101 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004102 if (idx >= highlight_ga.ga_len)
4103 return NULL;
4104 return HL_TABLE()[idx].sg_name;
4105}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004106
4107#if defined(FEAT_GUI) || defined(PROTO)
4108/*
4109 * Free all the highlight group fonts.
4110 * Used when quitting for systems which need it.
4111 */
4112 void
4113free_highlight_fonts(void)
4114{
4115 int idx;
4116
4117 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4118 {
4119 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4120 HL_TABLE()[idx].sg_font = NOFONT;
4121# ifdef FEAT_XFONTSET
4122 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4123 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4124# endif
4125 }
4126
4127 gui_mch_free_font(gui.norm_font);
4128# ifdef FEAT_XFONTSET
4129 gui_mch_free_fontset(gui.fontset);
4130# endif
4131# ifndef FEAT_GUI_GTK
4132 gui_mch_free_font(gui.bold_font);
4133 gui_mch_free_font(gui.ital_font);
4134 gui_mch_free_font(gui.boldital_font);
4135# endif
4136}
4137#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004138
4139#if defined(FEAT_EVAL) || defined(PROTO)
4140/*
4141 * Convert each of the highlight attribute bits (bold, standout, underline,
4142 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4143 * the attribute name as the key.
4144 */
4145 static dict_T *
4146highlight_get_attr_dict(int hlattr)
4147{
4148 dict_T *dict;
4149 int i;
4150
4151 dict = dict_alloc();
4152 if (dict == NULL)
4153 return NULL;
4154
4155 for (i = 0; hl_attr_table[i] != 0; ++i)
4156 {
4157 if (hlattr & hl_attr_table[i])
4158 {
4159 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4160 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4161 }
4162 }
4163
4164 return dict;
4165}
4166
4167/*
4168 * Return the attributes of the highlight group at index 'hl_idx' as a
4169 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4170 * links recursively.
4171 */
4172 static dict_T *
4173highlight_get_info(int hl_idx, int resolve_link)
4174{
4175 dict_T *dict;
4176 hl_group_T *sgp;
4177 dict_T *attr_dict;
4178 int hlgid;
4179
4180 dict = dict_alloc();
4181 if (dict == NULL)
4182 return dict;
4183
4184 sgp = &HL_TABLE()[hl_idx];
4185 // highlight group id is 1-based
4186 hlgid = hl_idx + 1;
4187
4188 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4189 goto error;
4190 if (dict_add_number(dict, "id", hlgid) == FAIL)
4191 goto error;
4192
4193 if (sgp->sg_link && resolve_link)
4194 {
4195 // resolve the highlight group link recursively
4196 while (sgp->sg_link)
4197 {
4198 hlgid = sgp->sg_link;
4199 sgp = &HL_TABLE()[sgp->sg_link - 1];
4200 }
4201 }
4202
4203 if (sgp->sg_term != 0)
4204 {
4205 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4206 if (attr_dict != NULL)
4207 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4208 goto error;
4209 }
4210 if (sgp->sg_start != NULL)
4211 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4212 goto error;
4213 if (sgp->sg_stop != NULL)
4214 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4215 goto error;
4216 if (sgp->sg_cterm != 0)
4217 {
4218 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4219 if (attr_dict != NULL)
4220 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4221 goto error;
4222 }
4223 if (sgp->sg_cterm_fg != 0)
4224 if (dict_add_string(dict, "ctermfg",
4225 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4226 goto error;
4227 if (sgp->sg_cterm_bg != 0)
4228 if (dict_add_string(dict, "ctermbg",
4229 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4230 goto error;
4231 if (sgp->sg_cterm_ul != 0)
4232 if (dict_add_string(dict, "ctermul",
4233 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4234 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004235 if (sgp->sg_cterm_font != 0)
4236 if (dict_add_string(dict, "ctermfont",
4237 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4238 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004239 if (sgp->sg_gui != 0)
4240 {
4241 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4242 if (attr_dict != NULL)
4243 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4244 goto error;
4245 }
4246 if (sgp->sg_gui_fg_name != NULL)
4247 if (dict_add_string(dict, "guifg",
4248 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4249 goto error;
4250 if (sgp->sg_gui_bg_name != NULL)
4251 if (dict_add_string(dict, "guibg",
4252 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4253 goto error;
4254 if (sgp->sg_gui_sp_name != NULL)
4255 if (dict_add_string(dict, "guisp",
4256 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4257 goto error;
4258# ifdef FEAT_GUI
4259 if (sgp->sg_font_name != NULL)
4260 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4261 goto error;
4262# endif
4263 if (sgp->sg_link)
4264 {
4265 char_u *link;
4266
4267 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4268 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4269 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004270
4271 if (sgp->sg_deflink)
4272 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004273 }
4274 if (dict_len(dict) == 2)
4275 // If only 'name' is present, then the highlight group is cleared.
4276 dict_add_bool(dict, "cleared", VVAL_TRUE);
4277
4278 return dict;
4279
4280error:
4281 vim_free(dict);
4282 return NULL;
4283}
4284
4285/*
4286 * "hlget([name])" function
4287 * Return the attributes of a specific highlight group (if specified) or all
4288 * the highlight groups.
4289 */
4290 void
4291f_hlget(typval_T *argvars, typval_T *rettv)
4292{
4293 list_T *list;
4294 dict_T *dict;
4295 int i;
4296 char_u *hlarg = NULL;
4297 int resolve_link = FALSE;
4298
4299 if (rettv_list_alloc(rettv) == FAIL)
4300 return;
4301
4302 if (check_for_opt_string_arg(argvars, 0) == FAIL
4303 || (argvars[0].v_type != VAR_UNKNOWN
4304 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4305 return;
4306
4307 if (argvars[0].v_type != VAR_UNKNOWN)
4308 {
4309 // highlight group name supplied
4310 hlarg = tv_get_string_chk(&argvars[0]);
4311 if (hlarg == NULL)
4312 return;
4313
4314 if (argvars[1].v_type != VAR_UNKNOWN)
4315 {
4316 int error = FALSE;
4317
4318 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4319 if (error)
4320 return;
4321 }
4322 }
4323
4324 list = rettv->vval.v_list;
4325 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4326 {
4327 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4328 {
4329 dict = highlight_get_info(i, resolve_link);
4330 if (dict != NULL)
4331 list_append_dict(list, dict);
4332 }
4333 }
4334}
4335
4336/*
4337 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4338 * 'dict' or the value is not a string type. If the value is not a string type
4339 * or is NULL, then 'error' is set to TRUE.
4340 */
4341 static char_u *
4342hldict_get_string(dict_T *dict, char_u *key, int *error)
4343{
4344 dictitem_T *di;
4345
4346 *error = FALSE;
4347 di = dict_find(dict, key, -1);
4348 if (di == NULL)
4349 return NULL;
4350
4351 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4352 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004353 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004354 *error = TRUE;
4355 return NULL;
4356 }
4357
4358 return di->di_tv.vval.v_string;
4359}
4360
4361/*
4362 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4363 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4364 * Dictionary or is NULL.
4365 */
4366 static int
4367hldict_attr_to_str(
4368 dict_T *dict,
4369 char_u *key,
4370 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004371 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004372{
4373 dictitem_T *di;
4374 dict_T *attrdict;
4375 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004376 char_u *p;
4377 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004378
4379 attr_str[0] = NUL;
4380 di = dict_find(dict, key, -1);
4381 if (di == NULL)
4382 return TRUE;
4383
4384 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4385 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004386 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004387 return FALSE;
4388 }
4389
4390 attrdict = di->di_tv.vval.v_dict;
4391
4392 // If the attribute dict is empty, then return NONE to clear the attributes
4393 if (dict_len(attrdict) == 0)
4394 {
4395 vim_strcat(attr_str, (char_u *)"NONE", len);
4396 return TRUE;
4397 }
4398
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004399 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004400 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4401 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01004402 if (dict_get_bool(attrdict, hl_name_table[i], VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004403 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004404 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4405 STRCPY(p, (char_u *)",");
4406 sz = STRLEN(hl_name_table[i]);
4407 if (p - attr_str + sz + 1 < len)
4408 {
4409 STRCPY(p, (char_u *)hl_name_table[i]);
4410 p += sz;
4411 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004412 }
4413 }
4414
4415 return TRUE;
4416}
4417
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004418// Temporary buffer used to store the command string produced by hlset().
4419// IObuff cannot be used for this as the error messages produced by hlset()
4420// internally use IObuff.
4421#define HLSETBUFSZ 512
4422static char_u hlsetBuf[HLSETBUFSZ + 1];
4423
4424/*
4425 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4426 * "dptr", which points into "hlsetBuf".
4427 * Returns the updated pointer.
4428 */
4429 static char_u *
4430add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4431{
4432 size_t vallen;
4433
4434 // Do nothing if the value is not specified or is empty
4435 if (value == NULL || *value == NUL)
4436 return dptr;
4437
4438 vallen = STRLEN(value);
4439 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4440 {
4441 STRCPY(dptr, attr);
4442 dptr += attrlen;
4443 STRCPY(dptr, value);
4444 dptr += vallen;
4445 }
4446
4447 return dptr;
4448}
4449
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004450/*
4451 * Add or update a highlight group using 'dict' items. Returns TRUE if
4452 * successfully updated the highlight group.
4453 */
4454 static int
4455hlg_add_or_update(dict_T *dict)
4456{
4457 char_u *name;
4458 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004459 char_u term_attr[MAX_ATTR_LEN];
4460 char_u cterm_attr[MAX_ATTR_LEN];
4461 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004462 char_u *start;
4463 char_u *stop;
4464 char_u *ctermfg;
4465 char_u *ctermbg;
4466 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004467 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004468 char_u *guifg;
4469 char_u *guibg;
4470 char_u *guisp;
4471# ifdef FEAT_GUI
4472 char_u *font;
4473# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004474 int forceit = FALSE;
4475 int dodefault = FALSE;
4476 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004477 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004478
4479 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004480 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004481 return FALSE;
4482
Bram Moolenaard61efa52022-07-23 09:52:04 +01004483 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004484 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004485
Bram Moolenaard61efa52022-07-23 09:52:04 +01004486 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004487 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004488
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004489 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004490 {
4491 varnumber_T cleared;
4492
4493 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004494 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004495 if (cleared == TRUE)
4496 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004497 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4498 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004499 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004500 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004501 }
4502
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004503 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004504 {
4505 char_u *linksto;
4506
4507 // link highlight groups
4508 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004509 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004510 return FALSE;
4511
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004512 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004513 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004514 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004515
4516 done = TRUE;
4517 }
4518
4519 // If 'cleared' or 'linksto' are specified, then don't process the other
4520 // attributes.
4521 if (done)
4522 return TRUE;
4523
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004524 start = hldict_get_string(dict, (char_u *)"start", &error);
4525 if (error)
4526 return FALSE;
4527
4528 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4529 if (error)
4530 return FALSE;
4531
4532 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004533 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004534 return FALSE;
4535
4536 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004537 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004538 return FALSE;
4539
4540 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4541 if (error)
4542 return FALSE;
4543
4544 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4545 if (error)
4546 return FALSE;
4547
4548 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4549 if (error)
4550 return FALSE;
4551
PMuncha606f3a2023-11-15 15:35:49 +01004552 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
4553 if (error)
4554 return FALSE;
4555
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004556 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004557 return FALSE;
4558
4559 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4560 if (error)
4561 return FALSE;
4562
4563 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4564 if (error)
4565 return FALSE;
4566
4567 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4568 if (error)
4569 return FALSE;
4570
4571# ifdef FEAT_GUI
4572 font = hldict_get_string(dict, (char_u *)"font", &error);
4573 if (error)
4574 return FALSE;
4575# endif
4576
4577 // If none of the attributes are specified, then do nothing.
4578 if (term_attr[0] == NUL && start == NULL && stop == NULL
4579 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01004580 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004581# ifdef FEAT_GUI
4582 && font == NULL
4583# endif
4584 && guifg == NULL && guibg == NULL && guisp == NULL
4585 )
4586 return TRUE;
4587
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004588 hlsetBuf[0] = NUL;
4589 p = hlsetBuf;
4590 if (dodefault)
4591 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4592 p = add_attr_and_value(p, (char_u *)"", 0, name);
4593 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4594 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4595 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4596 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4597 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4598 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4599 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01004600 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004601 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004602# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004603 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004604# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004605 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4606 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004607 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004608
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004609 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004610
4611 return TRUE;
4612}
4613
4614/*
4615 * "hlset([{highlight_attr}])" function
4616 * Add or modify highlight groups
4617 */
4618 void
4619f_hlset(typval_T *argvars, typval_T *rettv)
4620{
4621 listitem_T *li;
4622 dict_T *dict;
4623
4624 rettv->vval.v_number = -1;
4625
4626 if (check_for_list_arg(argvars, 0) == FAIL)
4627 return;
4628
4629 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4630 {
4631 if (li->li_tv.v_type != VAR_DICT)
4632 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004633 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004634 return;
4635 }
4636
4637 dict = li->li_tv.vval.v_dict;
4638 if (!hlg_add_or_update(dict))
4639 return;
4640 }
4641
4642 rettv->vval.v_number = 0;
4643}
4644#endif