blob: 79501d2b2ce292362d25099d6a07f0d156340e20 [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
62// for when using the GUI
63#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
64 guicolor_T sg_gui_fg; // GUI foreground color handle
65 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020066 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020067#endif
68#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020069 GuiFont sg_font; // GUI font handle
70#ifdef FEAT_XFONTSET
71 GuiFontset sg_fontset; // GUI fontset handle
72#endif
73 char_u *sg_font_name; // GUI font or fontset name
74 int sg_gui_attr; // Screen attr for GUI mode
75#endif
76#if defined(FEAT_GUI) || defined(FEAT_EVAL)
77// Store the sp color name for the GUI or synIDattr()
78 int sg_gui; // "gui=" highlighting attributes
79 char_u *sg_gui_fg_name;// GUI foreground color name
80 char_u *sg_gui_bg_name;// GUI background color name
81 char_u *sg_gui_sp_name;// GUI special color name
82#endif
83 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +020084 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020085 int sg_set; // combination of SG_* flags
86#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +020087 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020088 sctx_T sg_script_ctx; // script in which the group was last set
89#endif
90} hl_group_T;
91
92// highlight groups for 'highlight' option
93static garray_T highlight_ga;
94#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
95
96/*
97 * An attribute number is the index in attr_table plus ATTR_OFF.
98 */
99#define ATTR_OFF (HL_ALL + 1)
100
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200101static void syn_unadd_group(void);
102static void set_hl_attr(int idx);
103static void highlight_list_one(int id);
104static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
105static int syn_add_group(char_u *name);
106static int hl_has_settings(int idx, int check_link);
107static void highlight_clear(int idx);
108
109#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
110static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
111#endif
112#ifdef FEAT_GUI
113static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
114static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
115#endif
116
117/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200118 * The default highlight groups. These are compiled-in for fast startup and
119 * they still work when the runtime files can't be found.
120 * When making changes here, also change runtime/colors/default.vim!
121 * The #ifdefs are needed to reduce the amount of static data. Helps to make
122 * the 16 bit DOS (museum) version compile.
123 */
124#if defined(FEAT_GUI) || defined(FEAT_EVAL)
125# define CENT(a, b) b
126#else
127# define CENT(a, b) a
128#endif
129static char *(highlight_init_both[]) = {
130 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
131 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
132 CENT("IncSearch term=reverse cterm=reverse",
133 "IncSearch term=reverse cterm=reverse gui=reverse"),
134 CENT("ModeMsg term=bold cterm=bold",
135 "ModeMsg term=bold cterm=bold gui=bold"),
136 CENT("NonText term=bold ctermfg=Blue",
137 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
138 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
139 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
140 CENT("StatusLineNC term=reverse cterm=reverse",
141 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
142 "default link EndOfBuffer NonText",
143 CENT("VertSplit term=reverse cterm=reverse",
144 "VertSplit term=reverse cterm=reverse gui=reverse"),
145#ifdef FEAT_CLIPBOARD
146 CENT("VisualNOS term=underline,bold cterm=underline,bold",
147 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
148#endif
149#ifdef FEAT_DIFF
150 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
151 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
152#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200153 CENT("PmenuSbar ctermbg=Grey",
154 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200155 CENT("TabLineSel term=bold cterm=bold",
156 "TabLineSel term=bold cterm=bold gui=bold"),
157 CENT("TabLineFill term=reverse cterm=reverse",
158 "TabLineFill term=reverse cterm=reverse gui=reverse"),
159#ifdef FEAT_GUI
160 "Cursor guibg=fg guifg=bg",
161 "lCursor guibg=fg guifg=bg", // should be different, but what?
162#endif
163 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000164 "default link CursorLineSign SignColumn",
165 "default link CursorLineFold FoldColumn",
LemonBoya4399382022-04-09 21:04:08 +0100166 "default link CurSearch Search",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200167 CENT("Normal cterm=NONE", "Normal gui=NONE"),
168 NULL
169};
170
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200171// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200172static char *(highlight_init_light[]) = {
173 CENT("Directory term=bold ctermfg=DarkBlue",
174 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
175 CENT("LineNr term=underline ctermfg=Brown",
176 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200177 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
178 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200179 CENT("MoreMsg term=bold ctermfg=DarkGreen",
180 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
181 CENT("Question term=standout ctermfg=DarkGreen",
182 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
183 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
184 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
185#ifdef FEAT_SPELL
186 CENT("SpellBad term=reverse ctermbg=LightRed",
187 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
188 CENT("SpellCap term=reverse ctermbg=LightBlue",
189 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
190 CENT("SpellRare term=reverse ctermbg=LightMagenta",
191 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
192 CENT("SpellLocal term=underline ctermbg=Cyan",
193 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
194#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200195 CENT("PmenuThumb ctermbg=Black",
196 "PmenuThumb ctermbg=Black guibg=Black"),
197 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
198 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
199 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
200 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200201 CENT("SpecialKey term=bold ctermfg=DarkBlue",
202 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
203 CENT("Title term=bold ctermfg=DarkMagenta",
204 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
205 CENT("WarningMsg term=standout ctermfg=DarkRed",
206 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200207 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
208 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200209#ifdef FEAT_FOLDING
210 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
211 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
212 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
213 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
214#endif
215#ifdef FEAT_SIGNS
216 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
217 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
218#endif
219 CENT("Visual term=reverse",
220 "Visual term=reverse guibg=LightGrey"),
221#ifdef FEAT_DIFF
222 CENT("DiffAdd term=bold ctermbg=LightBlue",
223 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
224 CENT("DiffChange term=bold ctermbg=LightMagenta",
225 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
226 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
227 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
228#endif
229 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
230 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
231#ifdef FEAT_SYN_HL
232 CENT("CursorColumn term=reverse ctermbg=LightGrey",
233 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
234 CENT("CursorLine term=underline cterm=underline",
235 "CursorLine term=underline cterm=underline guibg=Grey90"),
236 CENT("ColorColumn term=reverse ctermbg=LightRed",
237 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
238#endif
239#ifdef FEAT_CONCEAL
240 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
241 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
242#endif
243 CENT("MatchParen term=reverse ctermbg=Cyan",
244 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
245#ifdef FEAT_TERMINAL
246 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
247 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
248 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
249 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
250#endif
251#ifdef FEAT_MENU
252 CENT("ToolbarLine term=underline ctermbg=LightGrey",
253 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
254 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
255 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
256#endif
257 NULL
258};
259
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200260// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200261static char *(highlight_init_dark[]) = {
262 CENT("Directory term=bold ctermfg=LightCyan",
263 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
264 CENT("LineNr term=underline ctermfg=Yellow",
265 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200266 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
267 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200268 CENT("MoreMsg term=bold ctermfg=LightGreen",
269 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
270 CENT("Question term=standout ctermfg=LightGreen",
271 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
272 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
273 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
274 CENT("SpecialKey term=bold ctermfg=LightBlue",
275 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
276#ifdef FEAT_SPELL
277 CENT("SpellBad term=reverse ctermbg=Red",
278 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
279 CENT("SpellCap term=reverse ctermbg=Blue",
280 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
281 CENT("SpellRare term=reverse ctermbg=Magenta",
282 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
283 CENT("SpellLocal term=underline ctermbg=Cyan",
284 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
285#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200286 CENT("PmenuThumb ctermbg=White",
287 "PmenuThumb ctermbg=White guibg=White"),
288 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
289 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
290 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
291 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200292 CENT("Title term=bold ctermfg=LightMagenta",
293 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
294 CENT("WarningMsg term=standout ctermfg=LightRed",
295 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200296 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
297 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200298#ifdef FEAT_FOLDING
299 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
300 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
301 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
302 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
303#endif
304#ifdef FEAT_SIGNS
305 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
306 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
307#endif
308 CENT("Visual term=reverse",
309 "Visual term=reverse guibg=DarkGrey"),
310#ifdef FEAT_DIFF
311 CENT("DiffAdd term=bold ctermbg=DarkBlue",
312 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
313 CENT("DiffChange term=bold ctermbg=DarkMagenta",
314 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
315 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
316 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
317#endif
318 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
319 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
320#ifdef FEAT_SYN_HL
321 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
322 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
323 CENT("CursorLine term=underline cterm=underline",
324 "CursorLine term=underline cterm=underline guibg=Grey40"),
325 CENT("ColorColumn term=reverse ctermbg=DarkRed",
326 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
327#endif
328 CENT("MatchParen term=reverse ctermbg=DarkCyan",
329 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
330#ifdef FEAT_CONCEAL
331 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
332 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
333#endif
334#ifdef FEAT_TERMINAL
335 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
336 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
337 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
338 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
339#endif
340#ifdef FEAT_MENU
341 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
342 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
343 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
344 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
345#endif
346 NULL
347};
348
Dominique Pelle748b3082022-01-08 12:41:16 +0000349#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200350/*
351 * Returns the number of highlight groups.
352 */
353 int
354highlight_num_groups(void)
355{
356 return highlight_ga.ga_len;
357}
358
359/*
360 * Returns the name of a highlight group.
361 */
362 char_u *
363highlight_group_name(int id)
364{
365 return HL_TABLE()[id].sg_name;
366}
367
368/*
369 * Returns the ID of the link to a highlight group.
370 */
371 int
372highlight_link_id(int id)
373{
374 return HL_TABLE()[id].sg_link;
375}
Dominique Pelle748b3082022-01-08 12:41:16 +0000376#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200377
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200378 void
379init_highlight(
380 int both, // include groups where 'bg' doesn't matter
381 int reset) // clear group first
382{
383 int i;
384 char **pp;
385 static int had_both = FALSE;
386#ifdef FEAT_EVAL
387 char_u *p;
388
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100389 // Try finding the color scheme file. Used when a color file was loaded
390 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200391 p = get_var_value((char_u *)"g:colors_name");
392 if (p != NULL)
393 {
394 // The value of g:colors_name could be freed when sourcing the script,
395 // making "p" invalid, so copy it.
396 char_u *copy_p = vim_strsave(p);
397 int r;
398
399 if (copy_p != NULL)
400 {
401 r = load_colors(copy_p);
402 vim_free(copy_p);
403 if (r == OK)
404 return;
405 }
406 }
407
408#endif
409
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100410 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200411 if (both)
412 {
413 had_both = TRUE;
414 pp = highlight_init_both;
415 for (i = 0; pp[i] != NULL; ++i)
416 do_highlight((char_u *)pp[i], reset, TRUE);
417 }
418 else if (!had_both)
419 // Don't do anything before the call with both == TRUE from main().
420 // Not everything has been setup then, and that call will overrule
421 // everything anyway.
422 return;
423
424 if (*p_bg == 'l')
425 pp = highlight_init_light;
426 else
427 pp = highlight_init_dark;
428 for (i = 0; pp[i] != NULL; ++i)
429 do_highlight((char_u *)pp[i], reset, TRUE);
430
431 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
432 // depend on the number of colors available.
433 // With 8 colors brown is equal to yellow, need to use black for Search fg
434 // to avoid Statement highlighted text disappears.
435 // Clear the attributes, needed when changing the t_Co value.
436 if (t_colors > 8)
437 do_highlight((char_u *)(*p_bg == 'l'
438 ? "Visual cterm=NONE ctermbg=LightGrey"
439 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
440 else
441 {
442 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
443 FALSE, TRUE);
444 if (*p_bg == 'l')
445 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
446 }
447
448#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100449 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200450 if (get_var_value((char_u *)"g:syntax_on") != NULL)
451 {
452 static int recursive = 0;
453
454 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000455 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200456 else
457 {
458 ++recursive;
459 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
460 --recursive;
461 }
462 }
463#endif
464}
465
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000466#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
467/*
468 * Load a default color list. Intended to support legacy color names but allows
469 * the user to override the color values. Only loaded once.
470 */
471 static void
472load_default_colors_lists()
473{
474 // Lacking a default color list isn't the end of the world but it is likely
475 // an inconvenience so users should know when it is missing.
476 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
477 msg("failed to load colors/lists/default.vim");
478}
479#endif
480
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200481/*
482 * Load color file "name".
483 * Return OK for success, FAIL for failure.
484 */
485 int
486load_colors(char_u *name)
487{
488 char_u *buf;
489 int retval = FAIL;
490 static int recursive = FALSE;
491
492 // When being called recursively, this is probably because setting
493 // 'background' caused the highlighting to be reloaded. This means it is
494 // working, thus we should return OK.
495 if (recursive)
496 return OK;
497
498 recursive = TRUE;
499 buf = alloc(STRLEN(name) + 12);
500 if (buf != NULL)
501 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100502#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100503 load_default_colors_lists();
504#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200505 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
506 curbuf->b_fname, FALSE, curbuf);
507 sprintf((char *)buf, "colors/%s.vim", name);
508 retval = source_runtime(buf, DIP_START + DIP_OPT);
509 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100510 if (retval == OK)
511 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
512 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200513 }
514 recursive = FALSE;
515
516 return retval;
517}
518
519static char *(color_names[28]) = {
520 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
521 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
522 "Gray", "Grey", "LightGray", "LightGrey",
523 "DarkGray", "DarkGrey",
524 "Blue", "LightBlue", "Green", "LightGreen",
525 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
526 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
527 // indices:
528 // 0, 1, 2, 3,
529 // 4, 5, 6, 7,
530 // 8, 9, 10, 11,
531 // 12, 13,
532 // 14, 15, 16, 17,
533 // 18, 19, 20, 21, 22,
534 // 23, 24, 25, 26, 27
535static int color_numbers_16[28] = {0, 1, 2, 3,
536 4, 5, 6, 6,
537 7, 7, 7, 7,
538 8, 8,
539 9, 9, 10, 10,
540 11, 11, 12, 12, 13,
541 13, 14, 14, 15, -1};
542// for xterm with 88 colors...
543static int color_numbers_88[28] = {0, 4, 2, 6,
544 1, 5, 32, 72,
545 84, 84, 7, 7,
546 82, 82,
547 12, 43, 10, 61,
548 14, 63, 9, 74, 13,
549 75, 11, 78, 15, -1};
550// for xterm with 256 colors...
551static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200552 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200553 248, 248, 7, 7,
554 242, 242,
555 12, 81, 10, 121,
556 14, 159, 9, 224, 13,
557 225, 11, 229, 15, -1};
558// for terminals with less than 16 colors...
559static int color_numbers_8[28] = {0, 4, 2, 6,
560 1, 5, 3, 3,
561 7, 7, 7, 7,
562 0+8, 0+8,
563 4+8, 4+8, 2+8, 2+8,
564 6+8, 6+8, 1+8, 1+8, 5+8,
565 5+8, 3+8, 3+8, 7+8, -1};
566
567/*
568 * Lookup the "cterm" value to be used for color with index "idx" in
569 * color_names[].
570 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
571 * colors, otherwise it will be unchanged.
572 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100573 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200574lookup_color(int idx, int foreground, int *boldp)
575{
576 int color = color_numbers_16[idx];
577 char_u *p;
578
579 // Use the _16 table to check if it's a valid color name.
580 if (color < 0)
581 return -1;
582
583 if (t_colors == 8)
584 {
585 // t_Co is 8: use the 8 colors table
586#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100587 // On qnx, the 8 & 16 color arrays are the same
588 if (STRNCMP(T_NAME, "qansi", 5) == 0)
589 color = color_numbers_16[idx];
590 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200591#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100592 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200593 if (foreground)
594 {
595 // set/reset bold attribute to get light foreground
596 // colors (on some terminals, e.g. "linux")
597 if (color & 8)
598 *boldp = TRUE;
599 else
600 *boldp = FALSE;
601 }
602 color &= 7; // truncate to 8 colors
603 }
604 else if (t_colors == 16 || t_colors == 88
605 || t_colors >= 256)
606 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100607 // Guess: if the termcap entry ends in 'm', it is
608 // probably an xterm-like terminal. Use the changed
609 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200610 if (*T_CAF != NUL)
611 p = T_CAF;
612 else
613 p = T_CSF;
614 if (*p != NUL && (t_colors > 256
615 || *(p + STRLEN(p) - 1) == 'm'))
616 {
617 if (t_colors == 88)
618 color = color_numbers_88[idx];
619 else if (t_colors >= 256)
620 color = color_numbers_256[idx];
621 else
622 color = color_numbers_8[idx];
623 }
624#ifdef FEAT_TERMRESPONSE
625 if (t_colors >= 256 && color == 15 && is_mac_terminal)
626 // Terminal.app has a bug: 15 is light grey. Use white
627 // from the color cube instead.
628 color = 231;
629#endif
630 }
631 return color;
632}
633
634/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100635 * Link highlight group 'from_hg' to 'to_hg'.
636 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000637 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100638 * 'init' is set to TRUE when initializing all the highlight groups.
639 */
640 static void
641highlight_group_link(
642 char_u *from_hg,
643 int from_len,
644 char_u *to_hg,
645 int to_len,
646 int dodefault,
647 int forceit,
648 int init)
649{
650 int from_id;
651 int to_id;
652 hl_group_T *hlgroup = NULL;
653
654 from_id = syn_check_group(from_hg, from_len);
655 if (STRNCMP(to_hg, "NONE", 4) == 0)
656 to_id = 0;
657 else
658 to_id = syn_check_group(to_hg, to_len);
659
660 if (from_id > 0)
661 {
662 hlgroup = &HL_TABLE()[from_id - 1];
663 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
664 {
665 hlgroup->sg_deflink = to_id;
666#ifdef FEAT_EVAL
667 hlgroup->sg_deflink_sctx = current_sctx;
668 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
669#endif
670 }
671 }
672
673 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
674 {
675 // Don't allow a link when there already is some highlighting
676 // for the group, unless '!' is used
677 if (to_id > 0 && !forceit && !init
678 && hl_has_settings(from_id - 1, dodefault))
679 {
680 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000681 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100682 }
683 else if (hlgroup->sg_link != to_id
684#ifdef FEAT_EVAL
685 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
686#endif
687 || hlgroup->sg_cleared)
688 {
689 if (!init)
690 hlgroup->sg_set |= SG_LINK;
691 hlgroup->sg_link = to_id;
692#ifdef FEAT_EVAL
693 hlgroup->sg_script_ctx = current_sctx;
694 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
695#endif
696 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100697 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100698
699 // Only call highlight_changed() once after multiple changes.
700 need_highlight_changed = TRUE;
701 }
702 }
703
704}
705
706/*
707 * Reset all highlighting to the defaults. Removes all highlighting for the
708 * groups added by the user.
709 */
710 static void
711highlight_reset_all(void)
712{
713 int idx;
714
715#ifdef FEAT_GUI
716 // First, we do not destroy the old values, but allocate the new
717 // ones and update the display. THEN we destroy the old values.
718 // If we destroy the old values first, then the old values
719 // (such as GuiFont's or GuiFontset's) will still be displayed but
720 // invalid because they were free'd.
721 if (gui.in_use)
722 {
723# ifdef FEAT_BEVAL_TIP
724 gui_init_tooltip_font();
725# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100726# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100727 gui_init_menu_font();
728# endif
729 }
730# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
731 gui_mch_def_colors();
732# endif
733# ifdef FEAT_GUI_X11
734# ifdef FEAT_MENU
735
736 // This only needs to be done when there is no Menu highlight
737 // group defined by default, which IS currently the case.
738 gui_mch_new_menu_colors();
739# endif
740 if (gui.in_use)
741 {
742 gui_new_scrollbar_colors();
743# ifdef FEAT_BEVAL_GUI
744 gui_mch_new_tooltip_colors();
745# endif
746# ifdef FEAT_MENU
747 gui_mch_new_menu_font();
748# endif
749 }
750# endif
751
752 // Ok, we're done allocating the new default graphics items.
753 // The screen should already be refreshed at this point.
754 // It is now Ok to clear out the old data.
755#endif
756#ifdef FEAT_EVAL
757 do_unlet((char_u *)"g:colors_name", TRUE);
758#endif
759 restore_cterm_colors();
760
761 // Clear all default highlight groups and load the defaults.
762 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
763 highlight_clear(idx);
764 init_highlight(TRUE, TRUE);
765#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
766 if (USE_24BIT)
767 highlight_gui_started();
768 else
769#endif
770 highlight_changed();
771 redraw_later_clear();
772}
773
774/*
775 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
776 * index 'idx'.
777 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
778 * 'arg' is the list of attribute names separated by comma.
779 * 'init' is set to TRUE when initializing all the highlight groups.
780 * Returns TRUE if the attributes are set.
781 */
782 static int
783highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
784{
785 int attr;
786 int off;
787 long i;
788 int len;
789
790 attr = 0;
791 off = 0;
792 while (arg[off] != NUL)
793 {
794 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
795 {
796 len = (int)STRLEN(hl_name_table[i]);
797 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
798 {
799 attr |= hl_attr_table[i];
800 off += len;
801 break;
802 }
803 }
804 if (i < 0)
805 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000806 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100807 return FALSE;
808 }
809 if (arg[off] == ',') // another one follows
810 ++off;
811 }
812 if (*key == 'T')
813 {
814 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
815 {
816 if (!init)
817 HL_TABLE()[idx].sg_set |= SG_TERM;
818 HL_TABLE()[idx].sg_term = attr;
819 }
820 }
821 else if (*key == 'C')
822 {
823 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
824 {
825 if (!init)
826 HL_TABLE()[idx].sg_set |= SG_CTERM;
827 HL_TABLE()[idx].sg_cterm = attr;
828 HL_TABLE()[idx].sg_cterm_bold = FALSE;
829 }
830 }
831#if defined(FEAT_GUI) || defined(FEAT_EVAL)
832 else
833 {
834 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
835 {
836 if (!init)
837 HL_TABLE()[idx].sg_set |= SG_GUI;
838 HL_TABLE()[idx].sg_gui = attr;
839 }
840 }
841#endif
842
843 return TRUE;
844}
845
846#ifdef FEAT_GUI
847/*
848 * Set the font for the highlight group at 'idx'.
849 * 'arg' is the font name.
850 * Returns TRUE if the font is changed.
851 */
852 static int
853highlight_set_font(
854 int idx,
855 char_u *arg,
856 int is_normal_group,
857 int is_menu_group,
858 int is_tooltip_group)
859{
860 int did_change = FALSE;
861
862 // in non-GUI fonts are simply ignored
863 if (HL_TABLE()[idx].sg_font_name != NULL
864 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
865 {
866 // Font name didn't change, ignore.
867 }
868 else if (!gui.shell_created)
869 {
870 // GUI not started yet, always accept the name.
871 vim_free(HL_TABLE()[idx].sg_font_name);
872 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
873 did_change = TRUE;
874 }
875 else
876 {
877 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
878# ifdef FEAT_XFONTSET
879 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
880# endif
881 // First, save the current font/fontset.
882 // Then try to allocate the font/fontset.
883 // If the allocation fails, HL_TABLE()[idx].sg_font OR
884 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
885
886 HL_TABLE()[idx].sg_font = NOFONT;
887# ifdef FEAT_XFONTSET
888 HL_TABLE()[idx].sg_fontset = NOFONTSET;
889# endif
890 hl_do_font(idx, arg, is_normal_group, is_menu_group,
891 is_tooltip_group, FALSE);
892
893# ifdef FEAT_XFONTSET
894 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
895 {
896 // New fontset was accepted. Free the old one, if there
897 // was one.
898 gui_mch_free_fontset(temp_sg_fontset);
899 vim_free(HL_TABLE()[idx].sg_font_name);
900 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
901 did_change = TRUE;
902 }
903 else
904 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
905# endif
906 if (HL_TABLE()[idx].sg_font != NOFONT)
907 {
908 // New font was accepted. Free the old one, if there was
909 // one.
910 gui_mch_free_font(temp_sg_font);
911 vim_free(HL_TABLE()[idx].sg_font_name);
912 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
913 did_change = TRUE;
914 }
915 else
916 HL_TABLE()[idx].sg_font = temp_sg_font;
917 }
918
919 return did_change;
920}
921#endif
922
923/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000924 * Set the cterm foreground color for the Normal highlight group to "color" and
925 * the bold attribute to "bold".
926 */
927 static void
928hl_set_ctermfg_normal_group(int color, int bold)
929{
930 cterm_normal_fg_color = color + 1;
931 cterm_normal_fg_bold = bold;
932#ifdef FEAT_GUI
933 // Don't do this if the GUI is used.
934 if (!gui.in_use && !gui.starting)
935#endif
936 {
937 set_must_redraw(UPD_CLEAR);
938 if (termcap_active && color >= 0)
939 term_fg_color(color);
940 }
941}
942
943/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100944 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100945 */
946 static void
947highlight_set_ctermfg(int idx, int color, int is_normal_group)
948{
949 HL_TABLE()[idx].sg_cterm_fg = color + 1;
950 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000951 hl_set_ctermfg_normal_group(color,
952 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100953}
954
955/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000956 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100957 */
958 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000959hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100960{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000961 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100962#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000963 // Don't mess with 'background' if the GUI is used.
964 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100965#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000966 {
967 set_must_redraw(UPD_CLEAR);
968 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100969 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000970 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100971
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000972 if (termcap_active)
973 term_bg_color(color);
974 if (t_colors < 16)
975 dark = (color == 0 || color == 4);
976 // Limit the heuristic to the standard 16 colors
977 else if (color < 16)
978 dark = (color < 7 || color == 8);
979 // Set the 'background' option if the value is
980 // wrong.
981 if (dark != -1
982 && dark != (*p_bg == 'd')
983 && !option_was_set((char_u *)"bg"))
984 {
985 set_option_value_give_err((char_u *)"bg",
986 0L, (char_u *)(dark ? "dark" : "light"), 0);
987 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100988 }
989 }
990 }
991}
992
993/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000994 * Set the cterm background color for the highlight group at 'idx' to 'color'.
995 */
996 static void
997highlight_set_ctermbg(int idx, int color, int is_normal_group)
998{
999 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1000 if (is_normal_group)
1001 hl_set_ctermbg_normal_group(color);
1002}
1003
1004/*
1005 * Set the cterm underline color for the Normal highlight group to "color".
1006 */
1007 static void
1008hl_set_ctermul_normal_group(int color)
1009{
1010 cterm_normal_ul_color = color + 1;
1011#ifdef FEAT_GUI
1012 // Don't do this if the GUI is used.
1013 if (!gui.in_use && !gui.starting)
1014#endif
1015 {
1016 set_must_redraw(UPD_CLEAR);
1017 if (termcap_active && color >= 0)
1018 term_ul_color(color);
1019 }
1020}
1021
1022/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001023 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001024 */
1025 static void
1026highlight_set_ctermul(int idx, int color, int is_normal_group)
1027{
1028 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1029 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001030 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001031}
1032
1033/*
1034 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1035 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1036 * 'keystart' is the color name/value.
1037 * 'arg' is the color name or the numeric value as a string.
1038 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1039 * 'init' is set to TRUE when initializing highlighting.
1040 * Called for the ":highlight" command and the "hlset()" function.
1041 *
1042 * Returns TRUE if the color is set.
1043 */
1044 static int
1045highlight_set_cterm_color(
1046 int idx,
1047 char_u *key,
1048 char_u *key_start,
1049 char_u *arg,
1050 int is_normal_group,
1051 int init)
1052{
1053 int color;
1054 long i;
1055 int off;
1056
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001057 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1058 return FALSE;
1059
1060 if (!init)
1061 HL_TABLE()[idx].sg_set |= SG_CTERM;
1062
1063 // When setting the foreground color, and previously the "bold"
1064 // flag was set for a light color, reset it now
1065 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001066 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001067 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1068 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1069 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001070
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001071 if (VIM_ISDIGIT(*arg))
1072 color = atoi((char *)arg);
1073 else if (STRICMP(arg, "fg") == 0)
1074 {
1075 if (cterm_normal_fg_color)
1076 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001077 else
1078 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001079 emsg(_(e_fg_color_unknown));
1080 return FALSE;
1081 }
1082 }
1083 else if (STRICMP(arg, "bg") == 0)
1084 {
1085 if (cterm_normal_bg_color > 0)
1086 color = cterm_normal_bg_color - 1;
1087 else
1088 {
1089 emsg(_(e_bg_color_unknown));
1090 return FALSE;
1091 }
1092 }
1093 else if (STRICMP(arg, "ul") == 0)
1094 {
1095 if (cterm_normal_ul_color > 0)
1096 color = cterm_normal_ul_color - 1;
1097 else
1098 {
1099 emsg(_(e_ul_color_unknown));
1100 return FALSE;
1101 }
1102 }
1103 else
1104 {
1105 int bold = MAYBE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001106
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001107 // reduce calls to STRICMP a bit, it can be slow
1108 off = TOUPPER_ASC(*arg);
1109 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1110 if (off == color_names[i][0]
1111 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1112 break;
1113 if (i < 0)
1114 {
1115 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1116 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001117 }
1118
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001119 color = lookup_color(i, key[5] == 'F', &bold);
1120
1121 // set/reset bold attribute to get light foreground
1122 // colors (on some terminals, e.g. "linux")
1123 if (bold == TRUE)
1124 {
1125 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1126 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1127 }
1128 else if (bold == FALSE)
1129 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001130 }
1131
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001132 // Add one to the argument, to avoid zero. Zero is used for
1133 // "NONE", then "color" is -1.
1134 if (key[5] == 'F')
1135 highlight_set_ctermfg(idx, color, is_normal_group);
1136 else if (key[5] == 'B')
1137 highlight_set_ctermbg(idx, color, is_normal_group);
1138 else // ctermul
1139 highlight_set_ctermul(idx, color, is_normal_group);
1140
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001141 return TRUE;
1142}
1143
1144#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1145/*
1146 * Set the GUI foreground color for the highlight group at 'idx'.
1147 * Returns TRUE if the color is set.
1148 */
1149 static int
1150highlight_set_guifg(
1151 int idx,
1152 char_u *arg,
1153 int is_menu_group UNUSED,
1154 int is_scrollbar_group UNUSED,
1155 int is_tooltip_group UNUSED,
1156 int *do_colors UNUSED,
1157 int init)
1158{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001159# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001160 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001161# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001162 char_u **namep;
1163 int did_change = FALSE;
1164
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001165 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1166 return FALSE;
1167
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001168 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001169 if (!init)
1170 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001171
1172# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001173 // In GUI guifg colors are only used when recognized
1174 i = color_name2handle(arg);
1175 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1176 {
1177 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001178# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001179 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1180 {
1181 vim_free(*namep);
1182 if (STRCMP(arg, "NONE") != 0)
1183 *namep = vim_strsave(arg);
1184 else
1185 *namep = NULL;
1186 did_change = TRUE;
1187 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001188# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1189# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001190 if (is_menu_group && gui.menu_fg_pixel != i)
1191 {
1192 gui.menu_fg_pixel = i;
1193 *do_colors = TRUE;
1194 }
1195 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1196 {
1197 gui.scroll_fg_pixel = i;
1198 *do_colors = TRUE;
1199 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001200# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001201 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1202 {
1203 gui.tooltip_fg_pixel = i;
1204 *do_colors = TRUE;
1205 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001206# endif
1207# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001208 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001209# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001210
1211 return did_change;
1212}
1213
1214/*
1215 * Set the GUI background color for the highlight group at 'idx'.
1216 * Returns TRUE if the color is set.
1217 */
1218 static int
1219highlight_set_guibg(
1220 int idx,
1221 char_u *arg,
1222 int is_menu_group UNUSED,
1223 int is_scrollbar_group UNUSED,
1224 int is_tooltip_group UNUSED,
1225 int *do_colors UNUSED,
1226 int init)
1227{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001228# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001229 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001230# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001231 char_u **namep;
1232 int did_change = FALSE;
1233
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001234 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1235 return FALSE;
1236
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001237 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001238 if (!init)
1239 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001240
1241# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001242 // In GUI guibg colors are only used when recognized
1243 i = color_name2handle(arg);
1244 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1245 {
1246 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001247# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001248 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1249 {
1250 vim_free(*namep);
1251 if (STRCMP(arg, "NONE") != 0)
1252 *namep = vim_strsave(arg);
1253 else
1254 *namep = NULL;
1255 did_change = TRUE;
1256 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001257# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1258# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001259 if (is_menu_group && gui.menu_bg_pixel != i)
1260 {
1261 gui.menu_bg_pixel = i;
1262 *do_colors = TRUE;
1263 }
1264 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1265 {
1266 gui.scroll_bg_pixel = i;
1267 *do_colors = TRUE;
1268 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001269# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001270 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1271 {
1272 gui.tooltip_bg_pixel = i;
1273 *do_colors = TRUE;
1274 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001275# endif
1276# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001277 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001278# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001279
1280 return did_change;
1281}
1282
1283/*
1284 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1285 * Returns TRUE if the color is set.
1286 */
1287 static int
1288highlight_set_guisp(int idx, char_u *arg, int init)
1289{
1290# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1291 int i;
1292# endif
1293 int did_change = FALSE;
1294 char_u **namep;
1295
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001296 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1297 return FALSE;
1298
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001299 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001300 if (!init)
1301 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001302
1303# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001304 // In GUI guisp colors are only used when recognized
1305 i = color_name2handle(arg);
1306 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1307 {
1308 HL_TABLE()[idx].sg_gui_sp = i;
1309# endif
1310 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001311 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001312 vim_free(*namep);
1313 if (STRCMP(arg, "NONE") != 0)
1314 *namep = vim_strsave(arg);
1315 else
1316 *namep = NULL;
1317 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001318 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001319# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001320 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001321# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001322
1323 return did_change;
1324}
1325#endif
1326
1327/*
1328 * Set the start/stop terminal codes for a highlight group.
1329 * Returns TRUE if the terminal code is set.
1330 */
1331 static int
1332highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1333{
1334 int off;
1335 char_u buf[100];
1336 int len;
1337 char_u *tname;
1338 char_u *p;
1339
1340 if (!init)
1341 HL_TABLE()[idx].sg_set |= SG_TERM;
1342
1343 // The "start" and "stop" arguments can be a literal escape
1344 // sequence, or a comma separated list of terminal codes.
1345 if (STRNCMP(arg, "t_", 2) == 0)
1346 {
1347 off = 0;
1348 buf[0] = 0;
1349 while (arg[off] != NUL)
1350 {
1351 // Isolate one termcap name
1352 for (len = 0; arg[off + len] &&
1353 arg[off + len] != ','; ++len)
1354 ;
1355 tname = vim_strnsave(arg + off, len);
1356 if (tname == NULL) // out of memory
1357 return FALSE;
1358 // lookup the escape sequence for the item
1359 p = get_term_code(tname);
1360 vim_free(tname);
1361 if (p == NULL) // ignore non-existing things
1362 p = (char_u *)"";
1363
1364 // Append it to the already found stuff
1365 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1366 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001367 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001368 return FALSE;
1369 }
1370 STRCAT(buf, p);
1371
1372 // Advance to the next item
1373 off += len;
1374 if (arg[off] == ',') // another one follows
1375 ++off;
1376 }
1377 }
1378 else
1379 {
1380 // Copy characters from arg[] to buf[], translating <> codes.
1381 for (p = arg, off = 0; off < 100 - 6 && *p; )
1382 {
zeertzjqdb088872022-05-02 22:53:45 +01001383 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001384 if (len > 0) // recognized special char
1385 off += len;
1386 else // copy as normal char
1387 buf[off++] = *p++;
1388 }
1389 buf[off] = NUL;
1390 }
1391
1392 if (STRCMP(buf, "NONE") == 0) // resetting the value
1393 p = NULL;
1394 else
1395 p = vim_strsave(buf);
1396 if (key[2] == 'A')
1397 {
1398 vim_free(HL_TABLE()[idx].sg_start);
1399 HL_TABLE()[idx].sg_start = p;
1400 }
1401 else
1402 {
1403 vim_free(HL_TABLE()[idx].sg_stop);
1404 HL_TABLE()[idx].sg_stop = p;
1405 }
1406 return TRUE;
1407}
1408
1409/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001410 * Handle the ":highlight .." command.
1411 * When using ":hi clear" this is called recursively for each group with
1412 * "forceit" and "init" both TRUE.
1413 */
1414 void
1415do_highlight(
1416 char_u *line,
1417 int forceit,
1418 int init) // TRUE when called for initializing
1419{
1420 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001421 char_u *linep;
1422 char_u *key_start;
1423 char_u *arg_start;
1424 char_u *key = NULL, *arg = NULL;
1425 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001426 int id;
1427 int idx;
1428 hl_group_T item_before;
1429 int did_change = FALSE;
1430 int dodefault = FALSE;
1431 int doclear = FALSE;
1432 int dolink = FALSE;
1433 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001434 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001435#ifdef FEAT_GUI_X11
1436 int is_menu_group = FALSE; // "Menu" group
1437 int is_scrollbar_group = FALSE; // "Scrollbar" group
1438 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001439#else
1440# define is_menu_group 0
1441# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001442# define is_scrollbar_group 0
1443#endif
1444#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1445 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001446#endif
1447#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1448 int did_highlight_changed = FALSE;
1449#endif
1450
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001451 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001452 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001453 {
1454 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1455 // TODO: only call when the group has attributes set
1456 highlight_list_one((int)i);
1457 return;
1458 }
1459
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001460 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001461 name_end = skiptowhite(line);
1462 linep = skipwhite(name_end);
1463
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001464 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001465 if (STRNCMP(line, "default", name_end - line) == 0)
1466 {
1467 dodefault = TRUE;
1468 line = linep;
1469 name_end = skiptowhite(line);
1470 linep = skipwhite(name_end);
1471 }
1472
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001473 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001474 if (STRNCMP(line, "clear", name_end - line) == 0)
1475 doclear = TRUE;
1476 if (STRNCMP(line, "link", name_end - line) == 0)
1477 dolink = TRUE;
1478
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001479 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001480 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001481 {
1482 id = syn_namen2id(line, (int)(name_end - line));
1483 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001484 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001485 else
1486 highlight_list_one(id);
1487 return;
1488 }
1489
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001490 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001491 if (dolink)
1492 {
1493 char_u *from_start = linep;
1494 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001495 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001496 char_u *to_start;
1497 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001498 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001499
1500 from_end = skiptowhite(from_start);
1501 to_start = skipwhite(from_end);
1502 to_end = skiptowhite(to_start);
1503
Bram Moolenaar1966c242020-04-20 22:42:32 +02001504 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001505 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001506 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001507 return;
1508 }
1509
Bram Moolenaar1966c242020-04-20 22:42:32 +02001510 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001511 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001512 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001513 return;
1514 }
1515
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001516 from_len = (int)(from_end - from_start);
1517 to_len = (int)(to_end - to_start);
1518 highlight_group_link(from_start, from_len, to_start, to_len,
1519 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001520 return;
1521 }
1522
1523 if (doclear)
1524 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001525 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001526 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001527 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001528 // ":highlight clear" without group name
1529 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001530 return;
1531 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001532 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001533 name_end = skiptowhite(line);
1534 linep = skipwhite(name_end);
1535 }
1536
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001537 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001538 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001539 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001540 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001541 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001542
1543 // Return if "default" was used and the group already has settings.
1544 if (dodefault && hl_has_settings(idx, TRUE))
1545 return;
1546
1547 // Make a copy so we can check if any attribute actually changed.
1548 item_before = HL_TABLE()[idx];
1549
1550 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1551 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001552#ifdef FEAT_GUI_X11
1553 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1554 is_menu_group = TRUE;
1555 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1556 is_scrollbar_group = TRUE;
1557 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1558 is_tooltip_group = TRUE;
1559#endif
1560
1561 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1562 if (doclear || (forceit && init))
1563 {
1564 highlight_clear(idx);
1565 if (!doclear)
1566 HL_TABLE()[idx].sg_set = 0;
1567 }
1568
1569 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001570 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001571 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001572 key_start = linep;
1573 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001574 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001575 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001576 error = TRUE;
1577 break;
1578 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001579
1580 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1581 // or "guibg").
1582 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1583 ++linep;
1584 vim_free(key);
1585 key = vim_strnsave_up(key_start, linep - key_start);
1586 if (key == NULL)
1587 {
1588 error = TRUE;
1589 break;
1590 }
1591 linep = skipwhite(linep);
1592
1593 if (STRCMP(key, "NONE") == 0)
1594 {
1595 if (!init || HL_TABLE()[idx].sg_set == 0)
1596 {
1597 if (!init)
1598 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1599 highlight_clear(idx);
1600 }
1601 continue;
1602 }
1603
1604 // Check for the equal sign.
1605 if (*linep != '=')
1606 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001607 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001608 error = TRUE;
1609 break;
1610 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001611 ++linep;
1612
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001613 // Isolate the argument.
1614 linep = skipwhite(linep);
1615 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001616 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001617 arg_start = ++linep;
1618 linep = vim_strchr(linep, '\'');
1619 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001620 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001621 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001622 error = TRUE;
1623 break;
1624 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001625 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001626 else
1627 {
1628 arg_start = linep;
1629 linep = skiptowhite(linep);
1630 }
1631 if (linep == arg_start)
1632 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001633 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001634 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001635 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001636 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001637 vim_free(arg);
1638 arg = vim_strnsave(arg_start, linep - arg_start);
1639 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001640 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001641 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001642 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001643 }
1644 if (*linep == '\'')
1645 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001646
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001647 // Store the argument.
1648 if (STRCMP(key, "TERM") == 0
1649 || STRCMP(key, "CTERM") == 0
1650 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001651 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001652 if (!highlight_set_termgui_attr(idx, key, arg, init))
1653 {
1654 error = TRUE;
1655 break;
1656 }
1657 }
1658 else if (STRCMP(key, "FONT") == 0)
1659 {
1660 // in non-GUI fonts are simply ignored
1661#ifdef FEAT_GUI
1662 if (highlight_set_font(idx, arg, is_normal_group,
1663 is_menu_group, is_tooltip_group))
1664 did_change = TRUE;
1665#endif
1666 }
1667 else if (STRCMP(key, "CTERMFG") == 0
1668 || STRCMP(key, "CTERMBG") == 0
1669 || STRCMP(key, "CTERMUL") == 0)
1670 {
1671 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1672 is_normal_group, init))
1673 {
1674 error = TRUE;
1675 break;
1676 }
1677 }
1678 else if (STRCMP(key, "GUIFG") == 0)
1679 {
1680#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1681 if (highlight_set_guifg(idx, arg, is_menu_group,
1682 is_scrollbar_group, is_tooltip_group,
1683 &do_colors, init))
1684 did_change = TRUE;
1685#endif
1686 }
1687 else if (STRCMP(key, "GUIBG") == 0)
1688 {
1689#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1690 if (highlight_set_guibg(idx, arg, is_menu_group,
1691 is_scrollbar_group, is_tooltip_group,
1692 &do_colors, init))
1693 did_change = TRUE;
1694#endif
1695 }
1696 else if (STRCMP(key, "GUISP") == 0)
1697 {
1698#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1699 if (highlight_set_guisp(idx, arg, init))
1700 did_change = TRUE;
1701#endif
1702 }
1703 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1704 {
1705 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1706 {
1707 error = TRUE;
1708 break;
1709 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001710 }
1711 else
1712 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001713 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001714 error = TRUE;
1715 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001716 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001717 HL_TABLE()[idx].sg_cleared = FALSE;
1718
1719 // When highlighting has been given for a group, don't link it.
1720 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1721 HL_TABLE()[idx].sg_link = 0;
1722
1723 // Continue with next argument.
1724 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001725 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001726
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001727 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001728 if (error && idx == highlight_ga.ga_len)
1729 syn_unadd_group();
1730 else
1731 {
1732 if (is_normal_group)
1733 {
1734 HL_TABLE()[idx].sg_term_attr = 0;
1735 HL_TABLE()[idx].sg_cterm_attr = 0;
1736#ifdef FEAT_GUI
1737 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001738 // Need to update all groups, because they might be using "bg"
1739 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001740#endif
1741#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1742 if (USE_24BIT)
1743 {
1744 highlight_gui_started();
1745 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001746 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001747 }
1748#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001749#ifdef FEAT_VTP
1750 control_console_color_rgb();
1751#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001752 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001753#ifdef FEAT_GUI_X11
1754# ifdef FEAT_MENU
1755 else if (is_menu_group)
1756 {
1757 if (gui.in_use && do_colors)
1758 gui_mch_new_menu_colors();
1759 }
1760# endif
1761 else if (is_scrollbar_group)
1762 {
1763 if (gui.in_use && do_colors)
1764 gui_new_scrollbar_colors();
1765 else
1766 set_hl_attr(idx);
1767 }
1768# ifdef FEAT_BEVAL_GUI
1769 else if (is_tooltip_group)
1770 {
1771 if (gui.in_use && do_colors)
1772 gui_mch_new_tooltip_colors();
1773 }
1774# endif
1775#endif
1776 else
1777 set_hl_attr(idx);
1778#ifdef FEAT_EVAL
1779 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001780 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001781#endif
1782 }
1783
1784 vim_free(key);
1785 vim_free(arg);
1786
1787 // Only call highlight_changed() once, after a sequence of highlight
1788 // commands, and only if an attribute actually changed.
1789 if ((did_change
1790 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1791#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1792 && !did_highlight_changed
1793#endif
1794 )
1795 {
1796 // Do not trigger a redraw when highlighting is changed while
1797 // redrawing. This may happen when evaluating 'statusline' changes the
1798 // StatusLine group.
1799 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001800 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001801 need_highlight_changed = TRUE;
1802 }
1803}
1804
1805#if defined(EXITFREE) || defined(PROTO)
1806 void
1807free_highlight(void)
1808{
1809 int i;
1810
1811 for (i = 0; i < highlight_ga.ga_len; ++i)
1812 {
1813 highlight_clear(i);
1814 vim_free(HL_TABLE()[i].sg_name);
1815 vim_free(HL_TABLE()[i].sg_name_u);
1816 }
1817 ga_clear(&highlight_ga);
1818}
1819#endif
1820
1821/*
1822 * Reset the cterm colors to what they were before Vim was started, if
1823 * possible. Otherwise reset them to zero.
1824 */
1825 void
1826restore_cterm_colors(void)
1827{
1828#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1829 // Since t_me has been set, this probably means that the user
1830 // wants to use this as default colors. Need to reset default
1831 // background/foreground colors.
1832 mch_set_normal_colors();
1833#else
1834# ifdef VIMDLL
1835 if (!gui.in_use)
1836 {
1837 mch_set_normal_colors();
1838 return;
1839 }
1840# endif
1841 cterm_normal_fg_color = 0;
1842 cterm_normal_fg_bold = 0;
1843 cterm_normal_bg_color = 0;
1844# ifdef FEAT_TERMGUICOLORS
1845 cterm_normal_fg_gui_color = INVALCOLOR;
1846 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001847 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001848# endif
1849#endif
1850}
1851
1852/*
1853 * Return TRUE if highlight group "idx" has any settings.
1854 * When "check_link" is TRUE also check for an existing link.
1855 */
1856 static int
1857hl_has_settings(int idx, int check_link)
1858{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001859 return HL_TABLE()[idx].sg_cleared == 0
1860 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001861 || HL_TABLE()[idx].sg_cterm_attr != 0
1862 || HL_TABLE()[idx].sg_cterm_fg != 0
1863 || HL_TABLE()[idx].sg_cterm_bg != 0
1864#ifdef FEAT_GUI
1865 || HL_TABLE()[idx].sg_gui_attr != 0
1866 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1867 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1868 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1869 || HL_TABLE()[idx].sg_font_name != NULL
1870#endif
1871 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1872}
1873
1874/*
1875 * Clear highlighting for one group.
1876 */
1877 static void
1878highlight_clear(int idx)
1879{
1880 HL_TABLE()[idx].sg_cleared = TRUE;
1881
1882 HL_TABLE()[idx].sg_term = 0;
1883 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1884 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1885 HL_TABLE()[idx].sg_term_attr = 0;
1886 HL_TABLE()[idx].sg_cterm = 0;
1887 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1888 HL_TABLE()[idx].sg_cterm_fg = 0;
1889 HL_TABLE()[idx].sg_cterm_bg = 0;
1890 HL_TABLE()[idx].sg_cterm_attr = 0;
1891#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1892 HL_TABLE()[idx].sg_gui = 0;
1893 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1894 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1895 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1896#endif
1897#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1898 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1899 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001900 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001901#endif
1902#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001903 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1904 HL_TABLE()[idx].sg_font = NOFONT;
1905# ifdef FEAT_XFONTSET
1906 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1907 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1908# endif
1909 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1910 HL_TABLE()[idx].sg_gui_attr = 0;
1911#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001912 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001913 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001914#ifdef FEAT_EVAL
1915 // Since we set the default link, set the location to where the default
1916 // link was set.
1917 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001918#endif
1919}
1920
1921#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1922/*
1923 * Set the normal foreground and background colors according to the "Normal"
1924 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1925 * "Tooltip" colors.
1926 */
1927 void
1928set_normal_colors(void)
1929{
1930# ifdef FEAT_GUI
1931# ifdef FEAT_TERMGUICOLORS
1932 if (gui.in_use)
1933# endif
1934 {
1935 if (set_group_colors((char_u *)"Normal",
1936 &gui.norm_pixel, &gui.back_pixel,
1937 FALSE, TRUE, FALSE))
1938 {
1939 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001940 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001941 }
1942# ifdef FEAT_GUI_X11
1943 if (set_group_colors((char_u *)"Menu",
1944 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1945 TRUE, FALSE, FALSE))
1946 {
1947# ifdef FEAT_MENU
1948 gui_mch_new_menu_colors();
1949# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001950 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001951 }
1952# ifdef FEAT_BEVAL_GUI
1953 if (set_group_colors((char_u *)"Tooltip",
1954 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1955 FALSE, FALSE, TRUE))
1956 {
1957# ifdef FEAT_TOOLBAR
1958 gui_mch_new_tooltip_colors();
1959# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001960 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001961 }
1962# endif
1963 if (set_group_colors((char_u *)"Scrollbar",
1964 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1965 FALSE, FALSE, FALSE))
1966 {
1967 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001968 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001969 }
1970# endif
1971 }
1972# endif
1973# ifdef FEAT_TERMGUICOLORS
1974# ifdef FEAT_GUI
1975 else
1976# endif
1977 {
1978 int idx;
1979
1980 idx = syn_name2id((char_u *)"Normal") - 1;
1981 if (idx >= 0)
1982 {
1983 gui_do_one_color(idx, FALSE, FALSE);
1984
1985 // If the normal fg or bg color changed a complete redraw is
1986 // required.
1987 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1988 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1989 {
1990 // if the GUI color is INVALCOLOR then we use the default cterm
1991 // color
1992 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1993 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001994 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001995 }
1996 }
1997 }
1998# endif
1999}
2000#endif
2001
2002#if defined(FEAT_GUI) || defined(PROTO)
2003/*
2004 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2005 */
2006 static int
2007set_group_colors(
2008 char_u *name,
2009 guicolor_T *fgp,
2010 guicolor_T *bgp,
2011 int do_menu,
2012 int use_norm,
2013 int do_tooltip)
2014{
2015 int idx;
2016
2017 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002018 if (idx < 0)
2019 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002020
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002021 gui_do_one_color(idx, do_menu, do_tooltip);
2022
2023 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2024 *fgp = HL_TABLE()[idx].sg_gui_fg;
2025 else if (use_norm)
2026 *fgp = gui.def_norm_pixel;
2027 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2028 *bgp = HL_TABLE()[idx].sg_gui_bg;
2029 else if (use_norm)
2030 *bgp = gui.def_back_pixel;
2031 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002032}
2033
2034/*
2035 * Get the font of the "Normal" group.
2036 * Returns "" when it's not found or not set.
2037 */
2038 char_u *
2039hl_get_font_name(void)
2040{
2041 int id;
2042 char_u *s;
2043
2044 id = syn_name2id((char_u *)"Normal");
2045 if (id > 0)
2046 {
2047 s = HL_TABLE()[id - 1].sg_font_name;
2048 if (s != NULL)
2049 return s;
2050 }
2051 return (char_u *)"";
2052}
2053
2054/*
2055 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2056 * actually chosen to be used.
2057 */
2058 void
2059hl_set_font_name(char_u *font_name)
2060{
2061 int id;
2062
2063 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002064 if (id <= 0)
2065 return;
2066
2067 vim_free(HL_TABLE()[id - 1].sg_font_name);
2068 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002069}
2070
2071/*
2072 * Set background color for "Normal" group. Called by gui_set_bg_color()
2073 * when the color is known.
2074 */
2075 void
2076hl_set_bg_color_name(
2077 char_u *name) // must have been allocated
2078{
2079 int id;
2080
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002081 if (name == NULL)
2082 return;
2083
2084 id = syn_name2id((char_u *)"Normal");
2085 if (id <= 0)
2086 return;
2087
2088 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2089 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002090}
2091
2092/*
2093 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2094 * when the color is known.
2095 */
2096 void
2097hl_set_fg_color_name(
2098 char_u *name) // must have been allocated
2099{
2100 int id;
2101
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002102 if (name == NULL)
2103 return;
2104
2105 id = syn_name2id((char_u *)"Normal");
2106 if (id <= 0)
2107 return;
2108
2109 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2110 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002111}
2112
2113/*
2114 * Return the handle for a font name.
2115 * Returns NOFONT when failed.
2116 */
2117 static GuiFont
2118font_name2handle(char_u *name)
2119{
2120 if (STRCMP(name, "NONE") == 0)
2121 return NOFONT;
2122
2123 return gui_mch_get_font(name, TRUE);
2124}
2125
2126# ifdef FEAT_XFONTSET
2127/*
2128 * Return the handle for a fontset name.
2129 * Returns NOFONTSET when failed.
2130 */
2131 static GuiFontset
2132fontset_name2handle(char_u *name, int fixed_width)
2133{
2134 if (STRCMP(name, "NONE") == 0)
2135 return NOFONTSET;
2136
2137 return gui_mch_get_fontset(name, TRUE, fixed_width);
2138}
2139# endif
2140
2141/*
2142 * Get the font or fontset for one highlight group.
2143 */
2144 static void
2145hl_do_font(
2146 int idx,
2147 char_u *arg,
2148 int do_normal, // set normal font
2149 int do_menu UNUSED, // set menu font
2150 int do_tooltip UNUSED, // set tooltip font
2151 int free_font) // free current font/fontset
2152{
2153# ifdef FEAT_XFONTSET
2154 // If 'guifontset' is not empty, first try using the name as a
2155 // fontset. If that doesn't work, use it as a font name.
2156 if (*p_guifontset != NUL
2157# ifdef FONTSET_ALWAYS
2158 || do_menu
2159# endif
2160# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002161 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002162 || do_tooltip
2163# endif
2164 )
2165 {
2166 if (free_font)
2167 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2168 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2169# ifdef FONTSET_ALWAYS
2170 || do_menu
2171# endif
2172# ifdef FEAT_BEVAL_TIP
2173 || do_tooltip
2174# endif
2175 );
2176 }
2177 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2178 {
2179 // If it worked and it's the Normal group, use it as the normal
2180 // fontset. Same for the Menu group.
2181 if (do_normal)
2182 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002183# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002184 if (do_menu)
2185 {
2186# ifdef FONTSET_ALWAYS
2187 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2188# else
2189 // YIKES! This is a bug waiting to crash the program
2190 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2191# endif
2192 gui_mch_new_menu_font();
2193 }
2194# ifdef FEAT_BEVAL_GUI
2195 if (do_tooltip)
2196 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002197 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002198 // displaying a single font and a fontset.
2199 // If the XtNinternational resource is set to True at widget
2200 // creation, then a fontset is always used, otherwise an
2201 // XFontStruct is used.
2202 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2203 gui_mch_new_tooltip_font();
2204 }
2205# endif
2206# endif
2207 }
2208 else
2209# endif
2210 {
2211 if (free_font)
2212 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2213 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2214 // If it worked and it's the Normal group, use it as the
2215 // normal font. Same for the Menu group.
2216 if (HL_TABLE()[idx].sg_font != NOFONT)
2217 {
2218 if (do_normal)
2219 gui_init_font(arg, FALSE);
2220#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002221# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002222 if (do_menu)
2223 {
2224 gui.menu_font = HL_TABLE()[idx].sg_font;
2225 gui_mch_new_menu_font();
2226 }
2227# endif
2228#endif
2229 }
2230 }
2231}
2232
2233#endif // FEAT_GUI
2234
2235#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2236/*
2237 * Return the handle for a color name.
2238 * Returns INVALCOLOR when failed.
2239 */
2240 guicolor_T
2241color_name2handle(char_u *name)
2242{
2243 if (STRCMP(name, "NONE") == 0)
2244 return INVALCOLOR;
2245
2246 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2247 {
2248#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2249 if (gui.in_use)
2250#endif
2251#ifdef FEAT_GUI
2252 return gui.norm_pixel;
2253#endif
2254#ifdef FEAT_TERMGUICOLORS
2255 if (cterm_normal_fg_gui_color != INVALCOLOR)
2256 return cterm_normal_fg_gui_color;
2257 // Guess that the foreground is black or white.
2258 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2259#endif
2260 }
2261 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2262 {
2263#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2264 if (gui.in_use)
2265#endif
2266#ifdef FEAT_GUI
2267 return gui.back_pixel;
2268#endif
2269#ifdef FEAT_TERMGUICOLORS
2270 if (cterm_normal_bg_gui_color != INVALCOLOR)
2271 return cterm_normal_bg_gui_color;
2272 // Guess that the background is white or black.
2273 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2274#endif
2275 }
2276
2277 return GUI_GET_COLOR(name);
2278}
Drew Vogele30d1022021-10-24 20:35:07 +01002279
2280// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2281// values as used by the MS-Windows GDI api. It should be used only for
2282// MS-Windows GDI builds.
2283# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2284# undef RGB
2285# endif
2286# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002287# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002288# endif
2289
2290# ifdef VIMDLL
2291 static guicolor_T
2292gui_adjust_rgb(guicolor_T c)
2293{
2294 if (gui.in_use)
2295 return c;
2296 else
2297 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2298}
2299# else
2300# define gui_adjust_rgb(c) (c)
2301# endif
2302
2303 static int
2304hex_digit(int c)
2305{
2306 if (isdigit(c))
2307 return c - '0';
2308 c = TOLOWER_ASC(c);
2309 if (c >= 'a' && c <= 'f')
2310 return c - 'a' + 10;
2311 return 0x1ffffff;
2312}
2313
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002314 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002315decode_hex_color(char_u *hex)
2316{
2317 guicolor_T color;
2318
2319 if (hex[0] != '#' || STRLEN(hex) != 7)
2320 return INVALCOLOR;
2321
2322 // Name is in "#rrggbb" format
2323 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2324 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2325 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2326 if (color > 0xffffff)
2327 return INVALCOLOR;
2328 return gui_adjust_rgb(color);
2329}
2330
Bram Moolenaar2a521962021-10-25 10:30:14 +01002331#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002332// Returns the color currently mapped to the given name or INVALCOLOR if no
2333// such name exists in the color table. The convention is to use lowercase for
2334// all keys in the v:colornames dictionary. The value can be either a string in
2335// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002336 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002337colorname2rgb(char_u *name)
2338{
2339 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2340 char_u *lc_name;
2341 dictitem_T *colentry;
2342 char_u *colstr;
2343 varnumber_T colnum;
2344
2345 lc_name = strlow_save(name);
2346 if (lc_name == NULL)
2347 return INVALCOLOR;
2348
2349 colentry = dict_find(colornames_table, lc_name, -1);
2350 vim_free(lc_name);
2351 if (colentry == NULL)
2352 return INVALCOLOR;
2353
2354 if (colentry->di_tv.v_type == VAR_STRING)
2355 {
2356 colstr = tv_get_string_strict(&colentry->di_tv);
2357 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2358 {
2359 return decode_hex_color(colstr);
2360 }
2361 else
2362 {
2363 semsg(_(e_bad_color_string_str), colstr);
2364 return INVALCOLOR;
2365 }
2366 }
2367
2368 if (colentry->di_tv.v_type == VAR_NUMBER)
2369 {
2370 colnum = tv_get_number(&colentry->di_tv);
2371 return (guicolor_T)colnum;
2372 }
2373
2374 return INVALCOLOR;
2375}
2376
Drew Vogele30d1022021-10-24 20:35:07 +01002377#endif
2378
2379 guicolor_T
2380gui_get_color_cmn(char_u *name)
2381{
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002382 int i;
Drew Vogele30d1022021-10-24 20:35:07 +01002383 guicolor_T color;
2384
2385 struct rgbcolor_table_S {
2386 char_u *color_name;
2387 guicolor_T color;
2388 };
2389
2390 // Only non X11 colors (not present in rgb.txt) and colors in
2391 // color_names[], useful when $VIMRUNTIME is not found,.
2392 static struct rgbcolor_table_S rgb_table[] = {
2393 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2394 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2395 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2396 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2397 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2398 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2399 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2400 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2401 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2402 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2403 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2404 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2405 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2406 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2407 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2408 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2409 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2410 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2411 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2412 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2413 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2414 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2415 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2416 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2417 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2418 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2419 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2420 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2421 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2422 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2423 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2424 };
2425
2426 color = decode_hex_color(name);
2427 if (color != INVALCOLOR)
2428 return color;
2429
2430 // Check if the name is one of the colors we know
2431 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2432 if (STRICMP(name, rgb_table[i].color_name) == 0)
2433 return gui_adjust_rgb(rgb_table[i].color);
2434
2435#if defined(FEAT_EVAL)
2436 /*
2437 * Not a traditional color. Load additional color aliases and then consult the alias table.
2438 */
2439
2440 color = colorname2rgb(name);
2441 if (color == INVALCOLOR)
2442 {
2443 load_default_colors_lists();
2444 color = colorname2rgb(name);
2445 }
2446
2447 return color;
2448#else
2449 return INVALCOLOR;
2450#endif
2451}
2452
2453 guicolor_T
2454gui_get_rgb_color_cmn(int r, int g, int b)
2455{
2456 guicolor_T color = RGB(r, g, b);
2457
2458 if (color > 0xffffff)
2459 return INVALCOLOR;
2460 return gui_adjust_rgb(color);
2461}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002462#endif
2463
2464/*
2465 * Table with the specifications for an attribute number.
2466 * Note that this table is used by ALL buffers. This is required because the
2467 * GUI can redraw at any time for any buffer.
2468 */
2469static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2470
2471#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2472
2473static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2474
2475#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2476
2477#ifdef FEAT_GUI
2478static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2479
2480#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2481#endif
2482
2483/*
2484 * Return the attr number for a set of colors and font.
2485 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2486 * if the combination is new.
2487 * Return 0 for error (no more room).
2488 */
2489 static int
2490get_attr_entry(garray_T *table, attrentry_T *aep)
2491{
2492 int i;
2493 attrentry_T *taep;
2494 static int recursive = FALSE;
2495
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002496 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002497 table->ga_itemsize = sizeof(attrentry_T);
2498 table->ga_growsize = 7;
2499
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002500 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002501 for (i = 0; i < table->ga_len; ++i)
2502 {
2503 taep = &(((attrentry_T *)table->ga_data)[i]);
2504 if ( aep->ae_attr == taep->ae_attr
2505 && (
2506#ifdef FEAT_GUI
2507 (table == &gui_attr_table
2508 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2509 && aep->ae_u.gui.bg_color
2510 == taep->ae_u.gui.bg_color
2511 && aep->ae_u.gui.sp_color
2512 == taep->ae_u.gui.sp_color
2513 && aep->ae_u.gui.font == taep->ae_u.gui.font
2514# ifdef FEAT_XFONTSET
2515 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2516# endif
2517 ))
2518 ||
2519#endif
2520 (table == &term_attr_table
2521 && (aep->ae_u.term.start == NULL)
2522 == (taep->ae_u.term.start == NULL)
2523 && (aep->ae_u.term.start == NULL
2524 || STRCMP(aep->ae_u.term.start,
2525 taep->ae_u.term.start) == 0)
2526 && (aep->ae_u.term.stop == NULL)
2527 == (taep->ae_u.term.stop == NULL)
2528 && (aep->ae_u.term.stop == NULL
2529 || STRCMP(aep->ae_u.term.stop,
2530 taep->ae_u.term.stop) == 0))
2531 || (table == &cterm_attr_table
2532 && aep->ae_u.cterm.fg_color
2533 == taep->ae_u.cterm.fg_color
2534 && aep->ae_u.cterm.bg_color
2535 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002536 && aep->ae_u.cterm.ul_color
2537 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002538#ifdef FEAT_TERMGUICOLORS
2539 && aep->ae_u.cterm.fg_rgb
2540 == taep->ae_u.cterm.fg_rgb
2541 && aep->ae_u.cterm.bg_rgb
2542 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002543 && aep->ae_u.cterm.ul_rgb
2544 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002545#endif
2546 )))
2547
2548 return i + ATTR_OFF;
2549 }
2550
2551 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2552 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002553 // Running out of attribute entries! remove all attributes, and
2554 // compute new ones for all groups.
2555 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002556 if (recursive)
2557 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002558 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002559 return 0;
2560 }
2561 recursive = TRUE;
2562
2563 clear_hl_tables();
2564
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002565 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002566
2567 for (i = 0; i < highlight_ga.ga_len; ++i)
2568 set_hl_attr(i);
2569
2570 recursive = FALSE;
2571 }
2572
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002573 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002574 if (ga_grow(table, 1) == FAIL)
2575 return 0;
2576
2577 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002578 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002579 taep->ae_attr = aep->ae_attr;
2580#ifdef FEAT_GUI
2581 if (table == &gui_attr_table)
2582 {
2583 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2584 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2585 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2586 taep->ae_u.gui.font = aep->ae_u.gui.font;
2587# ifdef FEAT_XFONTSET
2588 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2589# endif
2590 }
2591#endif
2592 if (table == &term_attr_table)
2593 {
2594 if (aep->ae_u.term.start == NULL)
2595 taep->ae_u.term.start = NULL;
2596 else
2597 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2598 if (aep->ae_u.term.stop == NULL)
2599 taep->ae_u.term.stop = NULL;
2600 else
2601 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2602 }
2603 else if (table == &cterm_attr_table)
2604 {
2605 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2606 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002607 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002608#ifdef FEAT_TERMGUICOLORS
2609 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2610 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002611 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002612#endif
2613 }
2614 ++table->ga_len;
2615 return (table->ga_len - 1 + ATTR_OFF);
2616}
2617
2618#if defined(FEAT_TERMINAL) || defined(PROTO)
2619/*
2620 * Get an attribute index for a cterm entry.
2621 * Uses an existing entry when possible or adds one when needed.
2622 */
2623 int
2624get_cterm_attr_idx(int attr, int fg, int bg)
2625{
2626 attrentry_T at_en;
2627
Bram Moolenaara80faa82020-04-12 19:37:17 +02002628 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002629#ifdef FEAT_TERMGUICOLORS
2630 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2631 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002632 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002633#endif
2634 at_en.ae_attr = attr;
2635 at_en.ae_u.cterm.fg_color = fg;
2636 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002637 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002638 return get_attr_entry(&cterm_attr_table, &at_en);
2639}
2640#endif
2641
2642#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2643/*
2644 * Get an attribute index for a 'termguicolors' entry.
2645 * Uses an existing entry when possible or adds one when needed.
2646 */
2647 int
2648get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2649{
2650 attrentry_T at_en;
2651
Bram Moolenaara80faa82020-04-12 19:37:17 +02002652 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002653 at_en.ae_attr = attr;
2654 if (fg == INVALCOLOR && bg == INVALCOLOR)
2655 {
2656 // If both GUI colors are not set fall back to the cterm colors. Helps
2657 // if the GUI only has an attribute, such as undercurl.
2658 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2659 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2660 }
2661 else
2662 {
2663 at_en.ae_u.cterm.fg_rgb = fg;
2664 at_en.ae_u.cterm.bg_rgb = bg;
2665 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002666 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002667 return get_attr_entry(&cterm_attr_table, &at_en);
2668}
2669#endif
2670
2671#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2672/*
2673 * Get an attribute index for a cterm entry.
2674 * Uses an existing entry when possible or adds one when needed.
2675 */
2676 int
2677get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2678{
2679 attrentry_T at_en;
2680
Bram Moolenaara80faa82020-04-12 19:37:17 +02002681 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002682 at_en.ae_attr = attr;
2683 at_en.ae_u.gui.fg_color = fg;
2684 at_en.ae_u.gui.bg_color = bg;
2685 return get_attr_entry(&gui_attr_table, &at_en);
2686}
2687#endif
2688
2689/*
2690 * Clear all highlight tables.
2691 */
2692 void
2693clear_hl_tables(void)
2694{
2695 int i;
2696 attrentry_T *taep;
2697
2698#ifdef FEAT_GUI
2699 ga_clear(&gui_attr_table);
2700#endif
2701 for (i = 0; i < term_attr_table.ga_len; ++i)
2702 {
2703 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2704 vim_free(taep->ae_u.term.start);
2705 vim_free(taep->ae_u.term.stop);
2706 }
2707 ga_clear(&term_attr_table);
2708 ga_clear(&cterm_attr_table);
2709}
2710
2711/*
2712 * Combine special attributes (e.g., for spelling) with other attributes
2713 * (e.g., for syntax highlighting).
2714 * "prim_attr" overrules "char_attr".
2715 * This creates a new group when required.
2716 * Since we expect there to be few spelling mistakes we don't cache the
2717 * result.
2718 * Return the resulting attributes.
2719 */
2720 int
2721hl_combine_attr(int char_attr, int prim_attr)
2722{
2723 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002724 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002725 attrentry_T new_en;
2726
2727 if (char_attr == 0)
2728 return prim_attr;
2729 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2730 return ATTR_COMBINE(char_attr, prim_attr);
2731#ifdef FEAT_GUI
2732 if (gui.in_use)
2733 {
2734 if (char_attr > HL_ALL)
2735 char_aep = syn_gui_attr2entry(char_attr);
2736 if (char_aep != NULL)
2737 new_en = *char_aep;
2738 else
2739 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002740 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002741 new_en.ae_u.gui.fg_color = INVALCOLOR;
2742 new_en.ae_u.gui.bg_color = INVALCOLOR;
2743 new_en.ae_u.gui.sp_color = INVALCOLOR;
2744 if (char_attr <= HL_ALL)
2745 new_en.ae_attr = char_attr;
2746 }
2747
2748 if (prim_attr <= HL_ALL)
2749 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2750 else
2751 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002752 prim_aep = syn_gui_attr2entry(prim_attr);
2753 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002754 {
2755 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002756 prim_aep->ae_attr);
2757 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2758 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2759 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2760 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2761 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2762 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2763 if (prim_aep->ae_u.gui.font != NOFONT)
2764 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002765# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002766 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2767 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002768# endif
2769 }
2770 }
2771 return get_attr_entry(&gui_attr_table, &new_en);
2772 }
2773#endif
2774
2775 if (IS_CTERM)
2776 {
2777 if (char_attr > HL_ALL)
2778 char_aep = syn_cterm_attr2entry(char_attr);
2779 if (char_aep != NULL)
2780 new_en = *char_aep;
2781 else
2782 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002783 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002784#ifdef FEAT_TERMGUICOLORS
2785 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2786 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002787 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002788#endif
2789 if (char_attr <= HL_ALL)
2790 new_en.ae_attr = char_attr;
2791 }
2792
2793 if (prim_attr <= HL_ALL)
2794 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2795 else
2796 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002797 prim_aep = syn_cterm_attr2entry(prim_attr);
2798 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002799 {
2800 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002801 prim_aep->ae_attr);
2802 if (prim_aep->ae_u.cterm.fg_color > 0)
2803 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2804 if (prim_aep->ae_u.cterm.bg_color > 0)
2805 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2806 if (prim_aep->ae_u.cterm.ul_color > 0)
2807 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002808#ifdef FEAT_TERMGUICOLORS
2809 // If both fg and bg are not set fall back to cterm colors.
2810 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002811 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2812 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002813 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002814 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002815 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002816 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002817 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2818 }
2819 else
2820 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002821 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2822 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2823 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2824 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002825 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002826 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2827 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002828#endif
2829 }
2830 }
2831 return get_attr_entry(&cterm_attr_table, &new_en);
2832 }
2833
2834 if (char_attr > HL_ALL)
2835 char_aep = syn_term_attr2entry(char_attr);
2836 if (char_aep != NULL)
2837 new_en = *char_aep;
2838 else
2839 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002840 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002841 if (char_attr <= HL_ALL)
2842 new_en.ae_attr = char_attr;
2843 }
2844
2845 if (prim_attr <= HL_ALL)
2846 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2847 else
2848 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002849 prim_aep = syn_term_attr2entry(prim_attr);
2850 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002851 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002852 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2853 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002854 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002855 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2856 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002857 }
2858 }
2859 }
2860 return get_attr_entry(&term_attr_table, &new_en);
2861}
2862
2863#ifdef FEAT_GUI
2864 attrentry_T *
2865syn_gui_attr2entry(int attr)
2866{
2867 attr -= ATTR_OFF;
2868 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2869 return NULL;
2870 return &(GUI_ATTR_ENTRY(attr));
2871}
2872#endif
2873
2874/*
2875 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2876 * Only to be used when "attr" > HL_ALL.
2877 */
2878 int
2879syn_attr2attr(int attr)
2880{
2881 attrentry_T *aep;
2882
2883#ifdef FEAT_GUI
2884 if (gui.in_use)
2885 aep = syn_gui_attr2entry(attr);
2886 else
2887#endif
2888 if (IS_CTERM)
2889 aep = syn_cterm_attr2entry(attr);
2890 else
2891 aep = syn_term_attr2entry(attr);
2892
2893 if (aep == NULL) // highlighting not set
2894 return 0;
2895 return aep->ae_attr;
2896}
2897
2898
2899 attrentry_T *
2900syn_term_attr2entry(int attr)
2901{
2902 attr -= ATTR_OFF;
2903 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2904 return NULL;
2905 return &(TERM_ATTR_ENTRY(attr));
2906}
2907
2908 attrentry_T *
2909syn_cterm_attr2entry(int attr)
2910{
2911 attr -= ATTR_OFF;
2912 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2913 return NULL;
2914 return &(CTERM_ATTR_ENTRY(attr));
2915}
2916
2917#define LIST_ATTR 1
2918#define LIST_STRING 2
2919#define LIST_INT 3
2920
2921 static void
2922highlight_list_one(int id)
2923{
2924 hl_group_T *sgp;
2925 int didh = FALSE;
2926
2927 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2928
2929 if (message_filtered(sgp->sg_name))
2930 return;
2931
2932 didh = highlight_list_arg(id, didh, LIST_ATTR,
2933 sgp->sg_term, NULL, "term");
2934 didh = highlight_list_arg(id, didh, LIST_STRING,
2935 0, sgp->sg_start, "start");
2936 didh = highlight_list_arg(id, didh, LIST_STRING,
2937 0, sgp->sg_stop, "stop");
2938
2939 didh = highlight_list_arg(id, didh, LIST_ATTR,
2940 sgp->sg_cterm, NULL, "cterm");
2941 didh = highlight_list_arg(id, didh, LIST_INT,
2942 sgp->sg_cterm_fg, NULL, "ctermfg");
2943 didh = highlight_list_arg(id, didh, LIST_INT,
2944 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002945 didh = highlight_list_arg(id, didh, LIST_INT,
2946 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002947
2948#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2949 didh = highlight_list_arg(id, didh, LIST_ATTR,
2950 sgp->sg_gui, NULL, "gui");
2951 didh = highlight_list_arg(id, didh, LIST_STRING,
2952 0, sgp->sg_gui_fg_name, "guifg");
2953 didh = highlight_list_arg(id, didh, LIST_STRING,
2954 0, sgp->sg_gui_bg_name, "guibg");
2955 didh = highlight_list_arg(id, didh, LIST_STRING,
2956 0, sgp->sg_gui_sp_name, "guisp");
2957#endif
2958#ifdef FEAT_GUI
2959 didh = highlight_list_arg(id, didh, LIST_STRING,
2960 0, sgp->sg_font_name, "font");
2961#endif
2962
2963 if (sgp->sg_link && !got_int)
2964 {
2965 (void)syn_list_header(didh, 9999, id);
2966 didh = TRUE;
2967 msg_puts_attr("links to", HL_ATTR(HLF_D));
2968 msg_putchar(' ');
2969 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2970 }
2971
2972 if (!didh)
2973 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2974#ifdef FEAT_EVAL
2975 if (p_verbose > 0)
2976 last_set_msg(sgp->sg_script_ctx);
2977#endif
2978}
2979
2980 static int
2981highlight_list_arg(
2982 int id,
2983 int didh,
2984 int type,
2985 int iarg,
2986 char_u *sarg,
2987 char *name)
2988{
Bram Moolenaar84f54632022-06-29 18:39:11 +01002989 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002990 char_u *ts;
2991 int i;
2992
2993 if (got_int)
2994 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002995
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002996 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
2997 return didh;
2998
2999 ts = buf;
3000 if (type == LIST_INT)
3001 sprintf((char *)buf, "%d", iarg - 1);
3002 else if (type == LIST_STRING)
3003 ts = sarg;
3004 else // type == LIST_ATTR
3005 {
3006 buf[0] = NUL;
3007 for (i = 0; hl_attr_table[i] != 0; ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003008 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003009 if (iarg & hl_attr_table[i])
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003010 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003011 if (buf[0] != NUL)
3012 vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
3013 vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
3014 iarg &= ~hl_attr_table[i]; // don't want "inverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003015 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003016 }
3017 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003018
3019 (void)syn_list_header(didh,
3020 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3021 didh = TRUE;
3022 if (!got_int)
3023 {
3024 if (*name != NUL)
3025 {
3026 msg_puts_attr(name, HL_ATTR(HLF_D));
3027 msg_puts_attr("=", HL_ATTR(HLF_D));
3028 }
3029 msg_outtrans(ts);
3030 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003031 return didh;
3032}
3033
3034#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3035/*
3036 * Return "1" if highlight group "id" has attribute "flag".
3037 * Return NULL otherwise.
3038 */
3039 char_u *
3040highlight_has_attr(
3041 int id,
3042 int flag,
3043 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3044{
3045 int attr;
3046
3047 if (id <= 0 || id > highlight_ga.ga_len)
3048 return NULL;
3049
3050#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3051 if (modec == 'g')
3052 attr = HL_TABLE()[id - 1].sg_gui;
3053 else
3054#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003055 {
3056 if (modec == 'c')
3057 attr = HL_TABLE()[id - 1].sg_cterm;
3058 else
3059 attr = HL_TABLE()[id - 1].sg_term;
3060 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003061
3062 if (attr & flag)
3063 return (char_u *)"1";
3064 return NULL;
3065}
3066#endif
3067
3068#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3069/*
3070 * Return color name of highlight group "id".
3071 */
3072 char_u *
3073highlight_color(
3074 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003075 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003076 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3077{
3078 static char_u name[20];
3079 int n;
3080 int fg = FALSE;
3081 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003082 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003083 int font = FALSE;
3084
3085 if (id <= 0 || id > highlight_ga.ga_len)
3086 return NULL;
3087
3088 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3089 fg = TRUE;
3090 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3091 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3092 font = TRUE;
3093 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3094 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003095 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3096 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003097 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3098 return NULL;
3099 if (modec == 'g')
3100 {
3101# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3102# ifdef FEAT_GUI
3103 // return font name
3104 if (font)
3105 return HL_TABLE()[id - 1].sg_font_name;
3106# endif
3107
3108 // return #RRGGBB form (only possible when GUI is running)
3109 if ((USE_24BIT) && what[2] == '#')
3110 {
3111 guicolor_T color;
3112 long_u rgb;
3113 static char_u buf[10];
3114
3115 if (fg)
3116 color = HL_TABLE()[id - 1].sg_gui_fg;
3117 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003118 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003119 else
3120 color = HL_TABLE()[id - 1].sg_gui_bg;
3121 if (color == INVALCOLOR)
3122 return NULL;
3123 rgb = (long_u)GUI_MCH_GET_RGB(color);
3124 sprintf((char *)buf, "#%02x%02x%02x",
3125 (unsigned)(rgb >> 16),
3126 (unsigned)(rgb >> 8) & 255,
3127 (unsigned)rgb & 255);
3128 return buf;
3129 }
3130# endif
3131 if (fg)
3132 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3133 if (sp)
3134 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3135 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3136 }
3137 if (font || sp)
3138 return NULL;
3139 if (modec == 'c')
3140 {
3141 if (fg)
3142 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003143 else if (ul)
3144 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003145 else
3146 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3147 if (n < 0)
3148 return NULL;
3149 sprintf((char *)name, "%d", n);
3150 return name;
3151 }
3152 // term doesn't have color
3153 return NULL;
3154}
3155#endif
3156
3157#if (defined(FEAT_SYN_HL) \
3158 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3159 && defined(FEAT_PRINTER)) || defined(PROTO)
3160/*
3161 * Return color name of highlight group "id" as RGB value.
3162 */
3163 long_u
3164highlight_gui_color_rgb(
3165 int id,
3166 int fg) // TRUE = fg, FALSE = bg
3167{
3168 guicolor_T color;
3169
3170 if (id <= 0 || id > highlight_ga.ga_len)
3171 return 0L;
3172
3173 if (fg)
3174 color = HL_TABLE()[id - 1].sg_gui_fg;
3175 else
3176 color = HL_TABLE()[id - 1].sg_gui_bg;
3177
3178 if (color == INVALCOLOR)
3179 return 0L;
3180
3181 return GUI_MCH_GET_RGB(color);
3182}
3183#endif
3184
3185/*
3186 * Output the syntax list header.
3187 * Return TRUE when started a new line.
3188 */
3189 int
3190syn_list_header(
3191 int did_header, // did header already
3192 int outlen, // length of string that comes
3193 int id) // highlight group id
3194{
3195 int endcol = 19;
3196 int newline = TRUE;
3197 int name_col = 0;
3198
3199 if (!did_header)
3200 {
3201 msg_putchar('\n');
3202 if (got_int)
3203 return TRUE;
3204 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3205 name_col = msg_col;
3206 endcol = 15;
3207 }
3208 else if (msg_col + outlen + 1 >= Columns)
3209 {
3210 msg_putchar('\n');
3211 if (got_int)
3212 return TRUE;
3213 }
3214 else
3215 {
3216 if (msg_col >= endcol) // wrap around is like starting a new line
3217 newline = FALSE;
3218 }
3219
3220 if (msg_col >= endcol) // output at least one space
3221 endcol = msg_col + 1;
3222 if (Columns <= endcol) // avoid hang for tiny window
3223 endcol = Columns - 1;
3224
3225 msg_advance(endcol);
3226
3227 // Show "xxx" with the attributes.
3228 if (!did_header)
3229 {
3230 if (endcol == Columns - 1 && endcol <= name_col)
3231 msg_putchar(' ');
3232 msg_puts_attr("xxx", syn_id2attr(id));
3233 msg_putchar(' ');
3234 }
3235
3236 return newline;
3237}
3238
3239/*
3240 * Set the attribute numbers for a highlight group.
3241 * Called after one of the attributes has changed.
3242 */
3243 static void
3244set_hl_attr(
3245 int idx) // index in array
3246{
3247 attrentry_T at_en;
3248 hl_group_T *sgp = HL_TABLE() + idx;
3249
3250 // The "Normal" group doesn't need an attribute number
3251 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3252 return;
3253
3254#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003255 // For the GUI mode: If there are other than "normal" highlighting
3256 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003257 if (sgp->sg_gui_fg == INVALCOLOR
3258 && sgp->sg_gui_bg == INVALCOLOR
3259 && sgp->sg_gui_sp == INVALCOLOR
3260 && sgp->sg_font == NOFONT
3261# ifdef FEAT_XFONTSET
3262 && sgp->sg_fontset == NOFONTSET
3263# endif
3264 )
3265 {
3266 sgp->sg_gui_attr = sgp->sg_gui;
3267 }
3268 else
3269 {
3270 at_en.ae_attr = sgp->sg_gui;
3271 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3272 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3273 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3274 at_en.ae_u.gui.font = sgp->sg_font;
3275# ifdef FEAT_XFONTSET
3276 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3277# endif
3278 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3279 }
3280#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003281 // For the term mode: If there are other than "normal" highlighting
3282 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003283 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3284 sgp->sg_term_attr = sgp->sg_term;
3285 else
3286 {
3287 at_en.ae_attr = sgp->sg_term;
3288 at_en.ae_u.term.start = sgp->sg_start;
3289 at_en.ae_u.term.stop = sgp->sg_stop;
3290 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3291 }
3292
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003293 // For the color term mode: If there are other than "normal"
3294 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003295 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003296# ifdef FEAT_TERMGUICOLORS
3297 && sgp->sg_gui_fg == INVALCOLOR
3298 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003299 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003300# endif
3301 )
3302 sgp->sg_cterm_attr = sgp->sg_cterm;
3303 else
3304 {
3305 at_en.ae_attr = sgp->sg_cterm;
3306 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3307 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003308 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003309# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003310 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3311 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003312 // Only use the underline/undercurl color when used, it may clear the
3313 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003314 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3315 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003316 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3317 else
3318 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003319 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3320 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3321 {
3322 // If both fg and bg are invalid fall back to the cterm colors.
3323 // Helps when the GUI only uses an attribute, e.g. undercurl.
3324 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3325 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3326 }
3327# endif
3328 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3329 }
3330}
3331
3332/*
3333 * Lookup a highlight group name and return its ID.
3334 * If it is not found, 0 is returned.
3335 */
3336 int
3337syn_name2id(char_u *name)
3338{
3339 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003340 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003341
3342 // Avoid using stricmp() too much, it's slow on some systems
3343 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3344 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003345 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003346 vim_strup(name_u);
3347 for (i = highlight_ga.ga_len; --i >= 0; )
3348 if (HL_TABLE()[i].sg_name_u != NULL
3349 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3350 break;
3351 return i + 1;
3352}
3353
3354/*
3355 * Lookup a highlight group name and return its attributes.
3356 * Return zero if not found.
3357 */
3358 int
3359syn_name2attr(char_u *name)
3360{
3361 int id = syn_name2id(name);
3362
3363 if (id != 0)
3364 return syn_id2attr(id);
3365 return 0;
3366}
3367
3368#if defined(FEAT_EVAL) || defined(PROTO)
3369/*
3370 * Return TRUE if highlight group "name" exists.
3371 */
3372 int
3373highlight_exists(char_u *name)
3374{
3375 return (syn_name2id(name) > 0);
3376}
3377
3378# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3379/*
3380 * Return the name of highlight group "id".
3381 * When not a valid ID return an empty string.
3382 */
3383 char_u *
3384syn_id2name(int id)
3385{
3386 if (id <= 0 || id > highlight_ga.ga_len)
3387 return (char_u *)"";
3388 return HL_TABLE()[id - 1].sg_name;
3389}
3390# endif
3391#endif
3392
3393/*
3394 * Like syn_name2id(), but take a pointer + length argument.
3395 */
3396 int
3397syn_namen2id(char_u *linep, int len)
3398{
3399 char_u *name;
3400 int id = 0;
3401
3402 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003403 if (name == NULL)
3404 return 0;
3405
3406 id = syn_name2id(name);
3407 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003408 return id;
3409}
3410
3411/*
3412 * Find highlight group name in the table and return its ID.
3413 * The argument is a pointer to the name and the length of the name.
3414 * If it doesn't exist yet, a new entry is created.
3415 * Return 0 for failure.
3416 */
3417 int
3418syn_check_group(char_u *pp, int len)
3419{
3420 int id;
3421 char_u *name;
3422
erw7f7f7aaf2021-12-07 21:29:20 +00003423 if (len > MAX_SYN_NAME)
3424 {
3425 emsg(_(e_highlight_group_name_too_long));
3426 return 0;
3427 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003428 name = vim_strnsave(pp, len);
3429 if (name == NULL)
3430 return 0;
3431
3432 id = syn_name2id(name);
3433 if (id == 0) // doesn't exist yet
3434 id = syn_add_group(name);
3435 else
3436 vim_free(name);
3437 return id;
3438}
3439
3440/*
3441 * Add new highlight group and return its ID.
3442 * "name" must be an allocated string, it will be consumed.
3443 * Return 0 for failure.
3444 */
3445 static int
3446syn_add_group(char_u *name)
3447{
3448 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003449 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003450
3451 // Check that the name is ASCII letters, digits and underscore.
3452 for (p = name; *p != NUL; ++p)
3453 {
3454 if (!vim_isprintc(*p))
3455 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003456 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003457 vim_free(name);
3458 return 0;
3459 }
3460 else if (!ASCII_ISALNUM(*p) && *p != '_')
3461 {
3462 // This is an error, but since there previously was no check only
3463 // give a warning.
3464 msg_source(HL_ATTR(HLF_W));
3465 msg(_("W18: Invalid character in group name"));
3466 break;
3467 }
3468 }
3469
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003470 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003471 if (highlight_ga.ga_data == NULL)
3472 {
3473 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3474 highlight_ga.ga_growsize = 10;
3475 }
3476
3477 if (highlight_ga.ga_len >= MAX_HL_ID)
3478 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003479 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003480 vim_free(name);
3481 return 0;
3482 }
3483
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003484 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003485 if (ga_grow(&highlight_ga, 1) == FAIL)
3486 {
3487 vim_free(name);
3488 return 0;
3489 }
3490
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003491 name_up = vim_strsave_up(name);
3492 if (name_up == NULL)
3493 {
3494 vim_free(name);
3495 return 0;
3496 }
3497
Bram Moolenaara80faa82020-04-12 19:37:17 +02003498 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003499 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003500 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003501#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3502 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3503 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003504 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003505#endif
3506 ++highlight_ga.ga_len;
3507
3508 return highlight_ga.ga_len; // ID is index plus one
3509}
3510
3511/*
3512 * When, just after calling syn_add_group(), an error is discovered, this
3513 * function deletes the new name.
3514 */
3515 static void
3516syn_unadd_group(void)
3517{
3518 --highlight_ga.ga_len;
3519 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3520 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3521}
3522
3523/*
3524 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003525 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003526 */
3527 int
3528syn_id2attr(int hl_id)
3529{
3530 int attr;
3531 hl_group_T *sgp;
3532
3533 hl_id = syn_get_final_id(hl_id);
3534 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3535
3536#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003537 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003538 if (gui.in_use)
3539 attr = sgp->sg_gui_attr;
3540 else
3541#endif
3542 if (IS_CTERM)
3543 attr = sgp->sg_cterm_attr;
3544 else
3545 attr = sgp->sg_term_attr;
3546
3547 return attr;
3548}
3549
3550#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3551/*
3552 * Get the GUI colors and attributes for a group ID.
3553 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3554 */
3555 int
3556syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3557{
3558 hl_group_T *sgp;
3559
3560 hl_id = syn_get_final_id(hl_id);
3561 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3562
3563 *fgp = sgp->sg_gui_fg;
3564 *bgp = sgp->sg_gui_bg;
3565 return sgp->sg_gui;
3566}
3567#endif
3568
3569#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003570 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3571 && defined(FEAT_TERMGUICOLORS)) \
3572 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003573 void
3574syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3575{
3576 hl_group_T *sgp;
3577
3578 hl_id = syn_get_final_id(hl_id);
3579 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3580 *fgp = sgp->sg_cterm_fg - 1;
3581 *bgp = sgp->sg_cterm_bg - 1;
3582}
3583#endif
3584
3585/*
3586 * Translate a group ID to the final group ID (following links).
3587 */
3588 int
3589syn_get_final_id(int hl_id)
3590{
3591 int count;
3592 hl_group_T *sgp;
3593
3594 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3595 return 0; // Can be called from eval!!
3596
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003597 // Follow links until there is no more.
3598 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003599 for (count = 100; --count >= 0; )
3600 {
3601 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3602 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3603 break;
3604 hl_id = sgp->sg_link;
3605 }
3606
3607 return hl_id;
3608}
3609
3610#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3611/*
3612 * Call this function just after the GUI has started.
3613 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3614 * It finds the font and color handles for the highlighting groups.
3615 */
3616 void
3617highlight_gui_started(void)
3618{
3619 int idx;
3620
3621 // First get the colors from the "Normal" and "Menu" group, if set
3622 if (USE_24BIT)
3623 set_normal_colors();
3624
3625 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3626 gui_do_one_color(idx, FALSE, FALSE);
3627
3628 highlight_changed();
3629}
3630
3631 static void
3632gui_do_one_color(
3633 int idx,
3634 int do_menu UNUSED, // TRUE: might set the menu font
3635 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3636{
3637 int didit = FALSE;
3638
3639# ifdef FEAT_GUI
3640# ifdef FEAT_TERMGUICOLORS
3641 if (gui.in_use)
3642# endif
3643 if (HL_TABLE()[idx].sg_font_name != NULL)
3644 {
3645 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3646 do_tooltip, TRUE);
3647 didit = TRUE;
3648 }
3649# endif
3650 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3651 {
3652 HL_TABLE()[idx].sg_gui_fg =
3653 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3654 didit = TRUE;
3655 }
3656 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3657 {
3658 HL_TABLE()[idx].sg_gui_bg =
3659 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3660 didit = TRUE;
3661 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003662 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3663 {
3664 HL_TABLE()[idx].sg_gui_sp =
3665 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3666 didit = TRUE;
3667 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003668 if (didit) // need to get a new attr number
3669 set_hl_attr(idx);
3670}
3671#endif
3672
3673#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3674/*
3675 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3676 */
3677 static void
3678combine_stl_hlt(
3679 int id,
3680 int id_S,
3681 int id_alt,
3682 int hlcnt,
3683 int i,
3684 int hlf,
3685 int *table)
3686{
3687 hl_group_T *hlt = HL_TABLE();
3688
3689 if (id_alt == 0)
3690 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003691 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003692 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3693 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3694# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3695 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3696# endif
3697 }
3698 else
3699 mch_memmove(&hlt[hlcnt + i],
3700 &hlt[id_alt - 1],
3701 sizeof(hl_group_T));
3702 hlt[hlcnt + i].sg_link = 0;
3703
3704 hlt[hlcnt + i].sg_term ^=
3705 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3706 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3707 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3708 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3709 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3710 hlt[hlcnt + i].sg_cterm ^=
3711 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3712 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3713 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3714 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3715 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3716# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3717 hlt[hlcnt + i].sg_gui ^=
3718 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3719# endif
3720# ifdef FEAT_GUI
3721 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3722 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3723 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3724 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3725 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3726 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3727 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3728 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3729# ifdef FEAT_XFONTSET
3730 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3731 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3732# endif
3733# endif
3734 highlight_ga.ga_len = hlcnt + i + 1;
3735 set_hl_attr(hlcnt + i); // At long last we can apply
3736 table[i] = syn_id2attr(hlcnt + i + 1);
3737}
3738#endif
3739
3740/*
3741 * Translate the 'highlight' option into attributes in highlight_attr[] and
3742 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3743 * corresponding highlights to use on top of HLF_SNC is computed.
3744 * Called only when the 'highlight' option has been changed and upon first
3745 * screen redraw after any :highlight command.
3746 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3747 */
3748 int
3749highlight_changed(void)
3750{
3751 int hlf;
3752 int i;
3753 char_u *p;
3754 int attr;
3755 char_u *end;
3756 int id;
3757#ifdef USER_HIGHLIGHT
3758 char_u userhl[30]; // use 30 to avoid compiler warning
3759# ifdef FEAT_STL_OPT
3760 int id_S = -1;
3761 int id_SNC = 0;
3762# ifdef FEAT_TERMINAL
3763 int id_ST = 0;
3764 int id_STNC = 0;
3765# endif
3766 int hlcnt;
3767# endif
3768#endif
3769 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3770
3771 need_highlight_changed = FALSE;
3772
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003773#ifdef FEAT_TERMINAL
3774 term_update_colors_all();
3775 term_update_wincolor_all();
3776#endif
3777
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003778 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003779 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3780 highlight_attr[hlf] = 0;
3781
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003782 // First set all attributes to their default value.
3783 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003784 for (i = 0; i < 2; ++i)
3785 {
3786 if (i)
3787 p = p_hl;
3788 else
3789 p = get_highlight_default();
3790 if (p == NULL) // just in case
3791 continue;
3792
3793 while (*p)
3794 {
3795 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3796 if (hl_flags[hlf] == *p)
3797 break;
3798 ++p;
3799 if (hlf == (int)HLF_COUNT || *p == NUL)
3800 return FAIL;
3801
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003802 // Allow several hl_flags to be combined, like "bu" for
3803 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003804 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003805 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003806 {
3807 if (VIM_ISWHITE(*p)) // ignore white space
3808 continue;
3809
3810 if (attr > HL_ALL) // Combination with ':' is not allowed.
3811 return FAIL;
3812
3813 switch (*p)
3814 {
3815 case 'b': attr |= HL_BOLD;
3816 break;
3817 case 'i': attr |= HL_ITALIC;
3818 break;
3819 case '-':
3820 case 'n': // no highlighting
3821 break;
3822 case 'r': attr |= HL_INVERSE;
3823 break;
3824 case 's': attr |= HL_STANDOUT;
3825 break;
3826 case 'u': attr |= HL_UNDERLINE;
3827 break;
3828 case 'c': attr |= HL_UNDERCURL;
3829 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003830 case '2': attr |= HL_UNDERDOUBLE;
3831 break;
3832 case 'd': attr |= HL_UNDERDOTTED;
3833 break;
3834 case '=': attr |= HL_UNDERDASHED;
3835 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003836 case 't': attr |= HL_STRIKETHROUGH;
3837 break;
3838 case ':': ++p; // highlight group name
3839 if (attr || *p == NUL) // no combinations
3840 return FAIL;
3841 end = vim_strchr(p, ',');
3842 if (end == NULL)
3843 end = p + STRLEN(p);
3844 id = syn_check_group(p, (int)(end - p));
3845 if (id == 0)
3846 return FAIL;
3847 attr = syn_id2attr(id);
3848 p = end - 1;
3849#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3850 if (hlf == (int)HLF_SNC)
3851 id_SNC = syn_get_final_id(id);
3852# ifdef FEAT_TERMINAL
3853 else if (hlf == (int)HLF_ST)
3854 id_ST = syn_get_final_id(id);
3855 else if (hlf == (int)HLF_STNC)
3856 id_STNC = syn_get_final_id(id);
3857# endif
3858 else if (hlf == (int)HLF_S)
3859 id_S = syn_get_final_id(id);
3860#endif
3861 break;
3862 default: return FAIL;
3863 }
3864 }
3865 highlight_attr[hlf] = attr;
3866
3867 p = skip_to_option_part(p); // skip comma and spaces
3868 }
3869 }
3870
3871#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003872 // Setup the user highlights
3873 //
3874 // Temporarily utilize 28 more hl entries:
3875 // 9 for User1-User9 combined with StatusLineNC
3876 // 9 for User1-User9 combined with StatusLineTerm
3877 // 9 for User1-User9 combined with StatusLineTermNC
3878 // 1 for StatusLine default
3879 // Have to be in there simultaneously in case of table overflows in
3880 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003881# ifdef FEAT_STL_OPT
3882 if (ga_grow(&highlight_ga, 28) == FAIL)
3883 return FAIL;
3884 hlcnt = highlight_ga.ga_len;
3885 if (id_S == -1)
3886 {
3887 // Make sure id_S is always valid to simplify code below. Use the last
3888 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003889 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003890 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3891 id_S = hlcnt + 19;
3892 }
3893# endif
3894 for (i = 0; i < 9; i++)
3895 {
3896 sprintf((char *)userhl, "User%d", i + 1);
3897 id = syn_name2id(userhl);
3898 if (id == 0)
3899 {
3900 highlight_user[i] = 0;
3901# ifdef FEAT_STL_OPT
3902 highlight_stlnc[i] = 0;
3903# ifdef FEAT_TERMINAL
3904 highlight_stlterm[i] = 0;
3905 highlight_stltermnc[i] = 0;
3906# endif
3907# endif
3908 }
3909 else
3910 {
3911 highlight_user[i] = syn_id2attr(id);
3912# ifdef FEAT_STL_OPT
3913 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3914 HLF_SNC, highlight_stlnc);
3915# ifdef FEAT_TERMINAL
3916 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3917 HLF_ST, highlight_stlterm);
3918 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3919 HLF_STNC, highlight_stltermnc);
3920# endif
3921# endif
3922 }
3923 }
3924# ifdef FEAT_STL_OPT
3925 highlight_ga.ga_len = hlcnt;
3926# endif
3927
3928#endif // USER_HIGHLIGHT
3929
3930 return OK;
3931}
3932
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003933static void highlight_list(void);
3934static void highlight_list_two(int cnt, int attr);
3935
3936/*
3937 * Handle command line completion for :highlight command.
3938 */
3939 void
3940set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3941{
3942 char_u *p;
3943
3944 // Default: expand group names
3945 xp->xp_context = EXPAND_HIGHLIGHT;
3946 xp->xp_pattern = arg;
3947 include_link = 2;
3948 include_default = 1;
3949
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003950 if (*arg == NUL)
3951 return;
3952
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003953 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003954 p = skiptowhite(arg);
3955 if (*p == NUL)
3956 return;
3957
3958 // past "default" or group name
3959 include_default = 0;
3960 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003961 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003962 arg = skipwhite(p);
3963 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003964 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003965 }
3966 if (*p == NUL)
3967 return;
3968
3969 // past group name
3970 include_link = 0;
3971 if (arg[1] == 'i' && arg[0] == 'N')
3972 highlight_list();
3973 if (STRNCMP("link", arg, p - arg) == 0
3974 || STRNCMP("clear", arg, p - arg) == 0)
3975 {
3976 xp->xp_pattern = skipwhite(p);
3977 p = skiptowhite(xp->xp_pattern);
3978 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003979 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003980 xp->xp_pattern = skipwhite(p);
3981 p = skiptowhite(xp->xp_pattern);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003982 }
3983 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003984 if (*p != NUL) // past group name(s)
3985 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003986}
3987
3988/*
3989 * List highlighting matches in a nice way.
3990 */
3991 static void
3992highlight_list(void)
3993{
3994 int i;
3995
3996 for (i = 10; --i >= 0; )
3997 highlight_list_two(i, HL_ATTR(HLF_D));
3998 for (i = 40; --i >= 0; )
3999 highlight_list_two(99, 0);
4000}
4001
4002 static void
4003highlight_list_two(int cnt, int attr)
4004{
4005 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4006 msg_clr_eos();
4007 out_flush();
4008 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4009}
4010
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004011/*
4012 * Function given to ExpandGeneric() to obtain the list of group names.
4013 */
4014 char_u *
4015get_highlight_name(expand_T *xp UNUSED, int idx)
4016{
4017 return get_highlight_name_ext(xp, idx, TRUE);
4018}
4019
4020/*
4021 * Obtain a highlight group name.
4022 * When "skip_cleared" is TRUE don't return a cleared entry.
4023 */
4024 char_u *
4025get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4026{
4027 if (idx < 0)
4028 return NULL;
4029
4030 // Items are never removed from the table, skip the ones that were
4031 // cleared.
4032 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4033 return (char_u *)"";
4034
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004035 if (idx == highlight_ga.ga_len && include_none != 0)
4036 return (char_u *)"none";
4037 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4038 return (char_u *)"default";
4039 if (idx == highlight_ga.ga_len + include_none + include_default
4040 && include_link != 0)
4041 return (char_u *)"link";
4042 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4043 && include_link != 0)
4044 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004045 if (idx >= highlight_ga.ga_len)
4046 return NULL;
4047 return HL_TABLE()[idx].sg_name;
4048}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004049
4050#if defined(FEAT_GUI) || defined(PROTO)
4051/*
4052 * Free all the highlight group fonts.
4053 * Used when quitting for systems which need it.
4054 */
4055 void
4056free_highlight_fonts(void)
4057{
4058 int idx;
4059
4060 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4061 {
4062 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4063 HL_TABLE()[idx].sg_font = NOFONT;
4064# ifdef FEAT_XFONTSET
4065 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4066 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4067# endif
4068 }
4069
4070 gui_mch_free_font(gui.norm_font);
4071# ifdef FEAT_XFONTSET
4072 gui_mch_free_fontset(gui.fontset);
4073# endif
4074# ifndef FEAT_GUI_GTK
4075 gui_mch_free_font(gui.bold_font);
4076 gui_mch_free_font(gui.ital_font);
4077 gui_mch_free_font(gui.boldital_font);
4078# endif
4079}
4080#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004081
4082#if defined(FEAT_EVAL) || defined(PROTO)
4083/*
4084 * Convert each of the highlight attribute bits (bold, standout, underline,
4085 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4086 * the attribute name as the key.
4087 */
4088 static dict_T *
4089highlight_get_attr_dict(int hlattr)
4090{
4091 dict_T *dict;
4092 int i;
4093
4094 dict = dict_alloc();
4095 if (dict == NULL)
4096 return NULL;
4097
4098 for (i = 0; hl_attr_table[i] != 0; ++i)
4099 {
4100 if (hlattr & hl_attr_table[i])
4101 {
4102 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4103 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4104 }
4105 }
4106
4107 return dict;
4108}
4109
4110/*
4111 * Return the attributes of the highlight group at index 'hl_idx' as a
4112 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4113 * links recursively.
4114 */
4115 static dict_T *
4116highlight_get_info(int hl_idx, int resolve_link)
4117{
4118 dict_T *dict;
4119 hl_group_T *sgp;
4120 dict_T *attr_dict;
4121 int hlgid;
4122
4123 dict = dict_alloc();
4124 if (dict == NULL)
4125 return dict;
4126
4127 sgp = &HL_TABLE()[hl_idx];
4128 // highlight group id is 1-based
4129 hlgid = hl_idx + 1;
4130
4131 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4132 goto error;
4133 if (dict_add_number(dict, "id", hlgid) == FAIL)
4134 goto error;
4135
4136 if (sgp->sg_link && resolve_link)
4137 {
4138 // resolve the highlight group link recursively
4139 while (sgp->sg_link)
4140 {
4141 hlgid = sgp->sg_link;
4142 sgp = &HL_TABLE()[sgp->sg_link - 1];
4143 }
4144 }
4145
4146 if (sgp->sg_term != 0)
4147 {
4148 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4149 if (attr_dict != NULL)
4150 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4151 goto error;
4152 }
4153 if (sgp->sg_start != NULL)
4154 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4155 goto error;
4156 if (sgp->sg_stop != NULL)
4157 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4158 goto error;
4159 if (sgp->sg_cterm != 0)
4160 {
4161 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4162 if (attr_dict != NULL)
4163 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4164 goto error;
4165 }
4166 if (sgp->sg_cterm_fg != 0)
4167 if (dict_add_string(dict, "ctermfg",
4168 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4169 goto error;
4170 if (sgp->sg_cterm_bg != 0)
4171 if (dict_add_string(dict, "ctermbg",
4172 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4173 goto error;
4174 if (sgp->sg_cterm_ul != 0)
4175 if (dict_add_string(dict, "ctermul",
4176 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4177 goto error;
4178 if (sgp->sg_gui != 0)
4179 {
4180 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4181 if (attr_dict != NULL)
4182 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4183 goto error;
4184 }
4185 if (sgp->sg_gui_fg_name != NULL)
4186 if (dict_add_string(dict, "guifg",
4187 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4188 goto error;
4189 if (sgp->sg_gui_bg_name != NULL)
4190 if (dict_add_string(dict, "guibg",
4191 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4192 goto error;
4193 if (sgp->sg_gui_sp_name != NULL)
4194 if (dict_add_string(dict, "guisp",
4195 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4196 goto error;
4197# ifdef FEAT_GUI
4198 if (sgp->sg_font_name != NULL)
4199 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4200 goto error;
4201# endif
4202 if (sgp->sg_link)
4203 {
4204 char_u *link;
4205
4206 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4207 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4208 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004209
4210 if (sgp->sg_deflink)
4211 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004212 }
4213 if (dict_len(dict) == 2)
4214 // If only 'name' is present, then the highlight group is cleared.
4215 dict_add_bool(dict, "cleared", VVAL_TRUE);
4216
4217 return dict;
4218
4219error:
4220 vim_free(dict);
4221 return NULL;
4222}
4223
4224/*
4225 * "hlget([name])" function
4226 * Return the attributes of a specific highlight group (if specified) or all
4227 * the highlight groups.
4228 */
4229 void
4230f_hlget(typval_T *argvars, typval_T *rettv)
4231{
4232 list_T *list;
4233 dict_T *dict;
4234 int i;
4235 char_u *hlarg = NULL;
4236 int resolve_link = FALSE;
4237
4238 if (rettv_list_alloc(rettv) == FAIL)
4239 return;
4240
4241 if (check_for_opt_string_arg(argvars, 0) == FAIL
4242 || (argvars[0].v_type != VAR_UNKNOWN
4243 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4244 return;
4245
4246 if (argvars[0].v_type != VAR_UNKNOWN)
4247 {
4248 // highlight group name supplied
4249 hlarg = tv_get_string_chk(&argvars[0]);
4250 if (hlarg == NULL)
4251 return;
4252
4253 if (argvars[1].v_type != VAR_UNKNOWN)
4254 {
4255 int error = FALSE;
4256
4257 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4258 if (error)
4259 return;
4260 }
4261 }
4262
4263 list = rettv->vval.v_list;
4264 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4265 {
4266 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4267 {
4268 dict = highlight_get_info(i, resolve_link);
4269 if (dict != NULL)
4270 list_append_dict(list, dict);
4271 }
4272 }
4273}
4274
4275/*
4276 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4277 * 'dict' or the value is not a string type. If the value is not a string type
4278 * or is NULL, then 'error' is set to TRUE.
4279 */
4280 static char_u *
4281hldict_get_string(dict_T *dict, char_u *key, int *error)
4282{
4283 dictitem_T *di;
4284
4285 *error = FALSE;
4286 di = dict_find(dict, key, -1);
4287 if (di == NULL)
4288 return NULL;
4289
4290 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4291 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004292 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004293 *error = TRUE;
4294 return NULL;
4295 }
4296
4297 return di->di_tv.vval.v_string;
4298}
4299
4300/*
4301 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4302 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4303 * Dictionary or is NULL.
4304 */
4305 static int
4306hldict_attr_to_str(
4307 dict_T *dict,
4308 char_u *key,
4309 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004310 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004311{
4312 dictitem_T *di;
4313 dict_T *attrdict;
4314 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004315 char_u *p;
4316 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004317
4318 attr_str[0] = NUL;
4319 di = dict_find(dict, key, -1);
4320 if (di == NULL)
4321 return TRUE;
4322
4323 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4324 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004325 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004326 return FALSE;
4327 }
4328
4329 attrdict = di->di_tv.vval.v_dict;
4330
4331 // If the attribute dict is empty, then return NONE to clear the attributes
4332 if (dict_len(attrdict) == 0)
4333 {
4334 vim_strcat(attr_str, (char_u *)"NONE", len);
4335 return TRUE;
4336 }
4337
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004338 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004339 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4340 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01004341 if (dict_get_bool(attrdict, hl_name_table[i], VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004342 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004343 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4344 STRCPY(p, (char_u *)",");
4345 sz = STRLEN(hl_name_table[i]);
4346 if (p - attr_str + sz + 1 < len)
4347 {
4348 STRCPY(p, (char_u *)hl_name_table[i]);
4349 p += sz;
4350 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004351 }
4352 }
4353
4354 return TRUE;
4355}
4356
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004357// Temporary buffer used to store the command string produced by hlset().
4358// IObuff cannot be used for this as the error messages produced by hlset()
4359// internally use IObuff.
4360#define HLSETBUFSZ 512
4361static char_u hlsetBuf[HLSETBUFSZ + 1];
4362
4363/*
4364 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4365 * "dptr", which points into "hlsetBuf".
4366 * Returns the updated pointer.
4367 */
4368 static char_u *
4369add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4370{
4371 size_t vallen;
4372
4373 // Do nothing if the value is not specified or is empty
4374 if (value == NULL || *value == NUL)
4375 return dptr;
4376
4377 vallen = STRLEN(value);
4378 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4379 {
4380 STRCPY(dptr, attr);
4381 dptr += attrlen;
4382 STRCPY(dptr, value);
4383 dptr += vallen;
4384 }
4385
4386 return dptr;
4387}
4388
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004389/*
4390 * Add or update a highlight group using 'dict' items. Returns TRUE if
4391 * successfully updated the highlight group.
4392 */
4393 static int
4394hlg_add_or_update(dict_T *dict)
4395{
4396 char_u *name;
4397 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004398 char_u term_attr[MAX_ATTR_LEN];
4399 char_u cterm_attr[MAX_ATTR_LEN];
4400 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004401 char_u *start;
4402 char_u *stop;
4403 char_u *ctermfg;
4404 char_u *ctermbg;
4405 char_u *ctermul;
4406 char_u *guifg;
4407 char_u *guibg;
4408 char_u *guisp;
4409# ifdef FEAT_GUI
4410 char_u *font;
4411# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004412 int forceit = FALSE;
4413 int dodefault = FALSE;
4414 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004415 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004416
4417 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004418 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004419 return FALSE;
4420
Bram Moolenaard61efa52022-07-23 09:52:04 +01004421 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004422 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004423
Bram Moolenaard61efa52022-07-23 09:52:04 +01004424 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004425 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004426
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004427 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004428 {
4429 varnumber_T cleared;
4430
4431 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004432 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004433 if (cleared == TRUE)
4434 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004435 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4436 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004437 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004438 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004439 }
4440
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004441 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004442 {
4443 char_u *linksto;
4444
4445 // link highlight groups
4446 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004447 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004448 return FALSE;
4449
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004450 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004451 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004452 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004453
4454 done = TRUE;
4455 }
4456
4457 // If 'cleared' or 'linksto' are specified, then don't process the other
4458 // attributes.
4459 if (done)
4460 return TRUE;
4461
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004462 start = hldict_get_string(dict, (char_u *)"start", &error);
4463 if (error)
4464 return FALSE;
4465
4466 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4467 if (error)
4468 return FALSE;
4469
4470 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004471 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004472 return FALSE;
4473
4474 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004475 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004476 return FALSE;
4477
4478 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4479 if (error)
4480 return FALSE;
4481
4482 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4483 if (error)
4484 return FALSE;
4485
4486 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4487 if (error)
4488 return FALSE;
4489
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004490 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004491 return FALSE;
4492
4493 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4494 if (error)
4495 return FALSE;
4496
4497 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4498 if (error)
4499 return FALSE;
4500
4501 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4502 if (error)
4503 return FALSE;
4504
4505# ifdef FEAT_GUI
4506 font = hldict_get_string(dict, (char_u *)"font", &error);
4507 if (error)
4508 return FALSE;
4509# endif
4510
4511 // If none of the attributes are specified, then do nothing.
4512 if (term_attr[0] == NUL && start == NULL && stop == NULL
4513 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4514 && ctermul == NULL && gui_attr[0] == NUL
4515# ifdef FEAT_GUI
4516 && font == NULL
4517# endif
4518 && guifg == NULL && guibg == NULL && guisp == NULL
4519 )
4520 return TRUE;
4521
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004522 hlsetBuf[0] = NUL;
4523 p = hlsetBuf;
4524 if (dodefault)
4525 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4526 p = add_attr_and_value(p, (char_u *)"", 0, name);
4527 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4528 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4529 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4530 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4531 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4532 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4533 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4534 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004535# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004536 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004537# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004538 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4539 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004540 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004541
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004542 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004543
4544 return TRUE;
4545}
4546
4547/*
4548 * "hlset([{highlight_attr}])" function
4549 * Add or modify highlight groups
4550 */
4551 void
4552f_hlset(typval_T *argvars, typval_T *rettv)
4553{
4554 listitem_T *li;
4555 dict_T *dict;
4556
4557 rettv->vval.v_number = -1;
4558
4559 if (check_for_list_arg(argvars, 0) == FAIL)
4560 return;
4561
4562 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4563 {
4564 if (li->li_tv.v_type != VAR_DICT)
4565 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004566 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004567 return;
4568 }
4569
4570 dict = li->li_tv.vval.v_dict;
4571 if (!hlg_add_or_update(dict))
4572 return;
4573 }
4574
4575 rettv->vval.v_number = 0;
4576}
4577#endif