blob: dc11df4d4a22bb36eb88ed1975879b782853bc27 [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"),
207#ifdef FEAT_WILDMENU
208 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
209 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
210#endif
211#ifdef FEAT_FOLDING
212 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
213 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
214 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
215 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
216#endif
217#ifdef FEAT_SIGNS
218 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
219 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
220#endif
221 CENT("Visual term=reverse",
222 "Visual term=reverse guibg=LightGrey"),
223#ifdef FEAT_DIFF
224 CENT("DiffAdd term=bold ctermbg=LightBlue",
225 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
226 CENT("DiffChange term=bold ctermbg=LightMagenta",
227 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
228 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
229 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
230#endif
231 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
232 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
233#ifdef FEAT_SYN_HL
234 CENT("CursorColumn term=reverse ctermbg=LightGrey",
235 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
236 CENT("CursorLine term=underline cterm=underline",
237 "CursorLine term=underline cterm=underline guibg=Grey90"),
238 CENT("ColorColumn term=reverse ctermbg=LightRed",
239 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
240#endif
241#ifdef FEAT_CONCEAL
242 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
243 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
244#endif
245 CENT("MatchParen term=reverse ctermbg=Cyan",
246 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
247#ifdef FEAT_TERMINAL
248 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
249 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
250 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
251 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
252#endif
253#ifdef FEAT_MENU
254 CENT("ToolbarLine term=underline ctermbg=LightGrey",
255 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
256 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
257 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
258#endif
259 NULL
260};
261
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200262// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200263static char *(highlight_init_dark[]) = {
264 CENT("Directory term=bold ctermfg=LightCyan",
265 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
266 CENT("LineNr term=underline ctermfg=Yellow",
267 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200268 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
269 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200270 CENT("MoreMsg term=bold ctermfg=LightGreen",
271 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
272 CENT("Question term=standout ctermfg=LightGreen",
273 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
274 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
275 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
276 CENT("SpecialKey term=bold ctermfg=LightBlue",
277 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
278#ifdef FEAT_SPELL
279 CENT("SpellBad term=reverse ctermbg=Red",
280 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
281 CENT("SpellCap term=reverse ctermbg=Blue",
282 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
283 CENT("SpellRare term=reverse ctermbg=Magenta",
284 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
285 CENT("SpellLocal term=underline ctermbg=Cyan",
286 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
287#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200288 CENT("PmenuThumb ctermbg=White",
289 "PmenuThumb ctermbg=White guibg=White"),
290 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
291 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
292 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
293 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200294 CENT("Title term=bold ctermfg=LightMagenta",
295 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
296 CENT("WarningMsg term=standout ctermfg=LightRed",
297 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
298#ifdef FEAT_WILDMENU
299 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
300 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
301#endif
302#ifdef FEAT_FOLDING
303 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
304 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
305 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
306 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
307#endif
308#ifdef FEAT_SIGNS
309 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
310 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
311#endif
312 CENT("Visual term=reverse",
313 "Visual term=reverse guibg=DarkGrey"),
314#ifdef FEAT_DIFF
315 CENT("DiffAdd term=bold ctermbg=DarkBlue",
316 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
317 CENT("DiffChange term=bold ctermbg=DarkMagenta",
318 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
319 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
320 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
321#endif
322 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
323 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
324#ifdef FEAT_SYN_HL
325 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
326 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
327 CENT("CursorLine term=underline cterm=underline",
328 "CursorLine term=underline cterm=underline guibg=Grey40"),
329 CENT("ColorColumn term=reverse ctermbg=DarkRed",
330 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
331#endif
332 CENT("MatchParen term=reverse ctermbg=DarkCyan",
333 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
334#ifdef FEAT_CONCEAL
335 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
336 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
337#endif
338#ifdef FEAT_TERMINAL
339 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
340 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
341 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
342 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
343#endif
344#ifdef FEAT_MENU
345 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
346 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
347 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
348 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
349#endif
350 NULL
351};
352
Dominique Pelle748b3082022-01-08 12:41:16 +0000353#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200354/*
355 * Returns the number of highlight groups.
356 */
357 int
358highlight_num_groups(void)
359{
360 return highlight_ga.ga_len;
361}
362
363/*
364 * Returns the name of a highlight group.
365 */
366 char_u *
367highlight_group_name(int id)
368{
369 return HL_TABLE()[id].sg_name;
370}
371
372/*
373 * Returns the ID of the link to a highlight group.
374 */
375 int
376highlight_link_id(int id)
377{
378 return HL_TABLE()[id].sg_link;
379}
Dominique Pelle748b3082022-01-08 12:41:16 +0000380#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200381
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200382 void
383init_highlight(
384 int both, // include groups where 'bg' doesn't matter
385 int reset) // clear group first
386{
387 int i;
388 char **pp;
389 static int had_both = FALSE;
390#ifdef FEAT_EVAL
391 char_u *p;
392
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100393 // Try finding the color scheme file. Used when a color file was loaded
394 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200395 p = get_var_value((char_u *)"g:colors_name");
396 if (p != NULL)
397 {
398 // The value of g:colors_name could be freed when sourcing the script,
399 // making "p" invalid, so copy it.
400 char_u *copy_p = vim_strsave(p);
401 int r;
402
403 if (copy_p != NULL)
404 {
405 r = load_colors(copy_p);
406 vim_free(copy_p);
407 if (r == OK)
408 return;
409 }
410 }
411
412#endif
413
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100414 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200415 if (both)
416 {
417 had_both = TRUE;
418 pp = highlight_init_both;
419 for (i = 0; pp[i] != NULL; ++i)
420 do_highlight((char_u *)pp[i], reset, TRUE);
421 }
422 else if (!had_both)
423 // Don't do anything before the call with both == TRUE from main().
424 // Not everything has been setup then, and that call will overrule
425 // everything anyway.
426 return;
427
428 if (*p_bg == 'l')
429 pp = highlight_init_light;
430 else
431 pp = highlight_init_dark;
432 for (i = 0; pp[i] != NULL; ++i)
433 do_highlight((char_u *)pp[i], reset, TRUE);
434
435 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
436 // depend on the number of colors available.
437 // With 8 colors brown is equal to yellow, need to use black for Search fg
438 // to avoid Statement highlighted text disappears.
439 // Clear the attributes, needed when changing the t_Co value.
440 if (t_colors > 8)
441 do_highlight((char_u *)(*p_bg == 'l'
442 ? "Visual cterm=NONE ctermbg=LightGrey"
443 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
444 else
445 {
446 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
447 FALSE, TRUE);
448 if (*p_bg == 'l')
449 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
450 }
451
452#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100453 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200454 if (get_var_value((char_u *)"g:syntax_on") != NULL)
455 {
456 static int recursive = 0;
457
458 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000459 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200460 else
461 {
462 ++recursive;
463 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
464 --recursive;
465 }
466 }
467#endif
468}
469
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000470#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
471/*
472 * Load a default color list. Intended to support legacy color names but allows
473 * the user to override the color values. Only loaded once.
474 */
475 static void
476load_default_colors_lists()
477{
478 // Lacking a default color list isn't the end of the world but it is likely
479 // an inconvenience so users should know when it is missing.
480 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
481 msg("failed to load colors/lists/default.vim");
482}
483#endif
484
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200485/*
486 * Load color file "name".
487 * Return OK for success, FAIL for failure.
488 */
489 int
490load_colors(char_u *name)
491{
492 char_u *buf;
493 int retval = FAIL;
494 static int recursive = FALSE;
495
496 // When being called recursively, this is probably because setting
497 // 'background' caused the highlighting to be reloaded. This means it is
498 // working, thus we should return OK.
499 if (recursive)
500 return OK;
501
502 recursive = TRUE;
503 buf = alloc(STRLEN(name) + 12);
504 if (buf != NULL)
505 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100506#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100507 load_default_colors_lists();
508#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200509 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
510 curbuf->b_fname, FALSE, curbuf);
511 sprintf((char *)buf, "colors/%s.vim", name);
512 retval = source_runtime(buf, DIP_START + DIP_OPT);
513 vim_free(buf);
514 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
515 }
516 recursive = FALSE;
517
518 return retval;
519}
520
521static char *(color_names[28]) = {
522 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
523 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
524 "Gray", "Grey", "LightGray", "LightGrey",
525 "DarkGray", "DarkGrey",
526 "Blue", "LightBlue", "Green", "LightGreen",
527 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
528 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
529 // indices:
530 // 0, 1, 2, 3,
531 // 4, 5, 6, 7,
532 // 8, 9, 10, 11,
533 // 12, 13,
534 // 14, 15, 16, 17,
535 // 18, 19, 20, 21, 22,
536 // 23, 24, 25, 26, 27
537static int color_numbers_16[28] = {0, 1, 2, 3,
538 4, 5, 6, 6,
539 7, 7, 7, 7,
540 8, 8,
541 9, 9, 10, 10,
542 11, 11, 12, 12, 13,
543 13, 14, 14, 15, -1};
544// for xterm with 88 colors...
545static int color_numbers_88[28] = {0, 4, 2, 6,
546 1, 5, 32, 72,
547 84, 84, 7, 7,
548 82, 82,
549 12, 43, 10, 61,
550 14, 63, 9, 74, 13,
551 75, 11, 78, 15, -1};
552// for xterm with 256 colors...
553static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200554 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200555 248, 248, 7, 7,
556 242, 242,
557 12, 81, 10, 121,
558 14, 159, 9, 224, 13,
559 225, 11, 229, 15, -1};
560// for terminals with less than 16 colors...
561static int color_numbers_8[28] = {0, 4, 2, 6,
562 1, 5, 3, 3,
563 7, 7, 7, 7,
564 0+8, 0+8,
565 4+8, 4+8, 2+8, 2+8,
566 6+8, 6+8, 1+8, 1+8, 5+8,
567 5+8, 3+8, 3+8, 7+8, -1};
568
569/*
570 * Lookup the "cterm" value to be used for color with index "idx" in
571 * color_names[].
572 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
573 * colors, otherwise it will be unchanged.
574 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100575 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200576lookup_color(int idx, int foreground, int *boldp)
577{
578 int color = color_numbers_16[idx];
579 char_u *p;
580
581 // Use the _16 table to check if it's a valid color name.
582 if (color < 0)
583 return -1;
584
585 if (t_colors == 8)
586 {
587 // t_Co is 8: use the 8 colors table
588#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100589 // On qnx, the 8 & 16 color arrays are the same
590 if (STRNCMP(T_NAME, "qansi", 5) == 0)
591 color = color_numbers_16[idx];
592 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200593#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100594 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200595 if (foreground)
596 {
597 // set/reset bold attribute to get light foreground
598 // colors (on some terminals, e.g. "linux")
599 if (color & 8)
600 *boldp = TRUE;
601 else
602 *boldp = FALSE;
603 }
604 color &= 7; // truncate to 8 colors
605 }
606 else if (t_colors == 16 || t_colors == 88
607 || t_colors >= 256)
608 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100609 // Guess: if the termcap entry ends in 'm', it is
610 // probably an xterm-like terminal. Use the changed
611 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200612 if (*T_CAF != NUL)
613 p = T_CAF;
614 else
615 p = T_CSF;
616 if (*p != NUL && (t_colors > 256
617 || *(p + STRLEN(p) - 1) == 'm'))
618 {
619 if (t_colors == 88)
620 color = color_numbers_88[idx];
621 else if (t_colors >= 256)
622 color = color_numbers_256[idx];
623 else
624 color = color_numbers_8[idx];
625 }
626#ifdef FEAT_TERMRESPONSE
627 if (t_colors >= 256 && color == 15 && is_mac_terminal)
628 // Terminal.app has a bug: 15 is light grey. Use white
629 // from the color cube instead.
630 color = 231;
631#endif
632 }
633 return color;
634}
635
636/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100637 * Link highlight group 'from_hg' to 'to_hg'.
638 * 'dodefault' is set to TRUE for ":highlight default link".
639 * 'forceit' is set to TRUE for ":higlight! link"
640 * 'init' is set to TRUE when initializing all the highlight groups.
641 */
642 static void
643highlight_group_link(
644 char_u *from_hg,
645 int from_len,
646 char_u *to_hg,
647 int to_len,
648 int dodefault,
649 int forceit,
650 int init)
651{
652 int from_id;
653 int to_id;
654 hl_group_T *hlgroup = NULL;
655
656 from_id = syn_check_group(from_hg, from_len);
657 if (STRNCMP(to_hg, "NONE", 4) == 0)
658 to_id = 0;
659 else
660 to_id = syn_check_group(to_hg, to_len);
661
662 if (from_id > 0)
663 {
664 hlgroup = &HL_TABLE()[from_id - 1];
665 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
666 {
667 hlgroup->sg_deflink = to_id;
668#ifdef FEAT_EVAL
669 hlgroup->sg_deflink_sctx = current_sctx;
670 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
671#endif
672 }
673 }
674
675 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
676 {
677 // Don't allow a link when there already is some highlighting
678 // for the group, unless '!' is used
679 if (to_id > 0 && !forceit && !init
680 && hl_has_settings(from_id - 1, dodefault))
681 {
682 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000683 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100684 }
685 else if (hlgroup->sg_link != to_id
686#ifdef FEAT_EVAL
687 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
688#endif
689 || hlgroup->sg_cleared)
690 {
691 if (!init)
692 hlgroup->sg_set |= SG_LINK;
693 hlgroup->sg_link = to_id;
694#ifdef FEAT_EVAL
695 hlgroup->sg_script_ctx = current_sctx;
696 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
697#endif
698 hlgroup->sg_cleared = FALSE;
699 redraw_all_later(SOME_VALID);
700
701 // Only call highlight_changed() once after multiple changes.
702 need_highlight_changed = TRUE;
703 }
704 }
705
706}
707
708/*
709 * Reset all highlighting to the defaults. Removes all highlighting for the
710 * groups added by the user.
711 */
712 static void
713highlight_reset_all(void)
714{
715 int idx;
716
717#ifdef FEAT_GUI
718 // First, we do not destroy the old values, but allocate the new
719 // ones and update the display. THEN we destroy the old values.
720 // If we destroy the old values first, then the old values
721 // (such as GuiFont's or GuiFontset's) will still be displayed but
722 // invalid because they were free'd.
723 if (gui.in_use)
724 {
725# ifdef FEAT_BEVAL_TIP
726 gui_init_tooltip_font();
727# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100728# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100729 gui_init_menu_font();
730# endif
731 }
732# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
733 gui_mch_def_colors();
734# endif
735# ifdef FEAT_GUI_X11
736# ifdef FEAT_MENU
737
738 // This only needs to be done when there is no Menu highlight
739 // group defined by default, which IS currently the case.
740 gui_mch_new_menu_colors();
741# endif
742 if (gui.in_use)
743 {
744 gui_new_scrollbar_colors();
745# ifdef FEAT_BEVAL_GUI
746 gui_mch_new_tooltip_colors();
747# endif
748# ifdef FEAT_MENU
749 gui_mch_new_menu_font();
750# endif
751 }
752# endif
753
754 // Ok, we're done allocating the new default graphics items.
755 // The screen should already be refreshed at this point.
756 // It is now Ok to clear out the old data.
757#endif
758#ifdef FEAT_EVAL
759 do_unlet((char_u *)"g:colors_name", TRUE);
760#endif
761 restore_cterm_colors();
762
763 // Clear all default highlight groups and load the defaults.
764 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
765 highlight_clear(idx);
766 init_highlight(TRUE, TRUE);
767#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
768 if (USE_24BIT)
769 highlight_gui_started();
770 else
771#endif
772 highlight_changed();
773 redraw_later_clear();
774}
775
776/*
777 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
778 * index 'idx'.
779 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
780 * 'arg' is the list of attribute names separated by comma.
781 * 'init' is set to TRUE when initializing all the highlight groups.
782 * Returns TRUE if the attributes are set.
783 */
784 static int
785highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
786{
787 int attr;
788 int off;
789 long i;
790 int len;
791
792 attr = 0;
793 off = 0;
794 while (arg[off] != NUL)
795 {
796 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
797 {
798 len = (int)STRLEN(hl_name_table[i]);
799 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
800 {
801 attr |= hl_attr_table[i];
802 off += len;
803 break;
804 }
805 }
806 if (i < 0)
807 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000808 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100809 return FALSE;
810 }
811 if (arg[off] == ',') // another one follows
812 ++off;
813 }
814 if (*key == 'T')
815 {
816 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
817 {
818 if (!init)
819 HL_TABLE()[idx].sg_set |= SG_TERM;
820 HL_TABLE()[idx].sg_term = attr;
821 }
822 }
823 else if (*key == 'C')
824 {
825 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
826 {
827 if (!init)
828 HL_TABLE()[idx].sg_set |= SG_CTERM;
829 HL_TABLE()[idx].sg_cterm = attr;
830 HL_TABLE()[idx].sg_cterm_bold = FALSE;
831 }
832 }
833#if defined(FEAT_GUI) || defined(FEAT_EVAL)
834 else
835 {
836 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
837 {
838 if (!init)
839 HL_TABLE()[idx].sg_set |= SG_GUI;
840 HL_TABLE()[idx].sg_gui = attr;
841 }
842 }
843#endif
844
845 return TRUE;
846}
847
848#ifdef FEAT_GUI
849/*
850 * Set the font for the highlight group at 'idx'.
851 * 'arg' is the font name.
852 * Returns TRUE if the font is changed.
853 */
854 static int
855highlight_set_font(
856 int idx,
857 char_u *arg,
858 int is_normal_group,
859 int is_menu_group,
860 int is_tooltip_group)
861{
862 int did_change = FALSE;
863
864 // in non-GUI fonts are simply ignored
865 if (HL_TABLE()[idx].sg_font_name != NULL
866 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
867 {
868 // Font name didn't change, ignore.
869 }
870 else if (!gui.shell_created)
871 {
872 // GUI not started yet, always accept the name.
873 vim_free(HL_TABLE()[idx].sg_font_name);
874 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
875 did_change = TRUE;
876 }
877 else
878 {
879 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
880# ifdef FEAT_XFONTSET
881 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
882# endif
883 // First, save the current font/fontset.
884 // Then try to allocate the font/fontset.
885 // If the allocation fails, HL_TABLE()[idx].sg_font OR
886 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
887
888 HL_TABLE()[idx].sg_font = NOFONT;
889# ifdef FEAT_XFONTSET
890 HL_TABLE()[idx].sg_fontset = NOFONTSET;
891# endif
892 hl_do_font(idx, arg, is_normal_group, is_menu_group,
893 is_tooltip_group, FALSE);
894
895# ifdef FEAT_XFONTSET
896 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
897 {
898 // New fontset was accepted. Free the old one, if there
899 // was one.
900 gui_mch_free_fontset(temp_sg_fontset);
901 vim_free(HL_TABLE()[idx].sg_font_name);
902 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
903 did_change = TRUE;
904 }
905 else
906 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
907# endif
908 if (HL_TABLE()[idx].sg_font != NOFONT)
909 {
910 // New font was accepted. Free the old one, if there was
911 // one.
912 gui_mch_free_font(temp_sg_font);
913 vim_free(HL_TABLE()[idx].sg_font_name);
914 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
915 did_change = TRUE;
916 }
917 else
918 HL_TABLE()[idx].sg_font = temp_sg_font;
919 }
920
921 return did_change;
922}
923#endif
924
925/*
926 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
927 * Returns TRUE if the foreground color is set.
928 */
929 static void
930highlight_set_ctermfg(int idx, int color, int is_normal_group)
931{
932 HL_TABLE()[idx].sg_cterm_fg = color + 1;
933 if (is_normal_group)
934 {
935 cterm_normal_fg_color = color + 1;
936 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
937#ifdef FEAT_GUI
938 // Don't do this if the GUI is used.
939 if (!gui.in_use && !gui.starting)
940#endif
941 {
942 must_redraw = CLEAR;
943 if (termcap_active && color >= 0)
944 term_fg_color(color);
945 }
946 }
947}
948
949/*
950 * Set the cterm background color for the highlight group at 'idx' to 'color'.
951 * Returns TRUE if the background color is set.
952 */
953 static void
954highlight_set_ctermbg(int idx, int color, int is_normal_group)
955{
956 HL_TABLE()[idx].sg_cterm_bg = color + 1;
957 if (is_normal_group)
958 {
959 cterm_normal_bg_color = color + 1;
960#ifdef FEAT_GUI
961 // Don't mess with 'background' if the GUI is used.
962 if (!gui.in_use && !gui.starting)
963#endif
964 {
965 must_redraw = CLEAR;
966 if (color >= 0)
967 {
968 int dark = -1;
969
970 if (termcap_active)
971 term_bg_color(color);
972 if (t_colors < 16)
973 dark = (color == 0 || color == 4);
974 // Limit the heuristic to the standard 16 colors
975 else if (color < 16)
976 dark = (color < 7 || color == 8);
977 // Set the 'background' option if the value is
978 // wrong.
979 if (dark != -1
980 && dark != (*p_bg == 'd')
981 && !option_was_set((char_u *)"bg"))
982 {
Bram Moolenaar31e5c602022-04-15 13:53:33 +0100983 set_option_value_give_err((char_u *)"bg",
984 0L, (char_u *)(dark ? "dark" : "light"), 0);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100985 reset_option_was_set((char_u *)"bg");
986 }
987 }
988 }
989 }
990}
991
992/*
993 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
994 * Returns TRUE if the underline color is set.
995 */
996 static void
997highlight_set_ctermul(int idx, int color, int is_normal_group)
998{
999 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1000 if (is_normal_group)
1001 {
1002 cterm_normal_ul_color = color + 1;
1003#ifdef FEAT_GUI
1004 // Don't do this if the GUI is used.
1005 if (!gui.in_use && !gui.starting)
1006#endif
1007 {
1008 must_redraw = CLEAR;
1009 if (termcap_active && color >= 0)
1010 term_ul_color(color);
1011 }
1012 }
1013}
1014
1015/*
1016 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1017 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1018 * 'keystart' is the color name/value.
1019 * 'arg' is the color name or the numeric value as a string.
1020 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1021 * 'init' is set to TRUE when initializing highlighting.
1022 * Called for the ":highlight" command and the "hlset()" function.
1023 *
1024 * Returns TRUE if the color is set.
1025 */
1026 static int
1027highlight_set_cterm_color(
1028 int idx,
1029 char_u *key,
1030 char_u *key_start,
1031 char_u *arg,
1032 int is_normal_group,
1033 int init)
1034{
1035 int color;
1036 long i;
1037 int off;
1038
1039 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1040 {
1041 if (!init)
1042 HL_TABLE()[idx].sg_set |= SG_CTERM;
1043
1044 // When setting the foreground color, and previously the "bold"
1045 // flag was set for a light color, reset it now
1046 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1047 {
1048 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1049 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1050 }
1051
1052 if (VIM_ISDIGIT(*arg))
1053 color = atoi((char *)arg);
1054 else if (STRICMP(arg, "fg") == 0)
1055 {
1056 if (cterm_normal_fg_color)
1057 color = cterm_normal_fg_color - 1;
1058 else
1059 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001060 emsg(_(e_fg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001061 return FALSE;
1062 }
1063 }
1064 else if (STRICMP(arg, "bg") == 0)
1065 {
1066 if (cterm_normal_bg_color > 0)
1067 color = cterm_normal_bg_color - 1;
1068 else
1069 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001070 emsg(_(e_bg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001071 return FALSE;
1072 }
1073 }
1074 else if (STRICMP(arg, "ul") == 0)
1075 {
1076 if (cterm_normal_ul_color > 0)
1077 color = cterm_normal_ul_color - 1;
1078 else
1079 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00001080 emsg(_(e_ul_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001081 return FALSE;
1082 }
1083 }
1084 else
1085 {
1086 int bold = MAYBE;
1087
1088 // reduce calls to STRICMP a bit, it can be slow
1089 off = TOUPPER_ASC(*arg);
1090 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1091 if (off == color_names[i][0]
1092 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1093 break;
1094 if (i < 0)
1095 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001096 semsg(_(e_color_name_or_number_not_recognized), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001097 return FALSE;
1098 }
1099
1100 color = lookup_color(i, key[5] == 'F', &bold);
1101
1102 // set/reset bold attribute to get light foreground
1103 // colors (on some terminals, e.g. "linux")
1104 if (bold == TRUE)
1105 {
1106 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1107 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1108 }
1109 else if (bold == FALSE)
1110 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1111 }
1112
1113 // Add one to the argument, to avoid zero. Zero is used for
1114 // "NONE", then "color" is -1.
1115 if (key[5] == 'F')
1116 highlight_set_ctermfg(idx, color, is_normal_group);
1117 else if (key[5] == 'B')
1118 highlight_set_ctermbg(idx, color, is_normal_group);
1119 else // ctermul
1120 highlight_set_ctermul(idx, color, is_normal_group);
1121 }
1122
1123 return TRUE;
1124}
1125
1126#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1127/*
1128 * Set the GUI foreground color for the highlight group at 'idx'.
1129 * Returns TRUE if the color is set.
1130 */
1131 static int
1132highlight_set_guifg(
1133 int idx,
1134 char_u *arg,
1135 int is_menu_group UNUSED,
1136 int is_scrollbar_group UNUSED,
1137 int is_tooltip_group UNUSED,
1138 int *do_colors UNUSED,
1139 int init)
1140{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001141# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001142 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001143# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001144 char_u **namep;
1145 int did_change = FALSE;
1146
1147 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1148 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1149 {
1150 if (!init)
1151 HL_TABLE()[idx].sg_set |= SG_GUI;
1152
1153# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1154 // In GUI guifg colors are only used when recognized
1155 i = color_name2handle(arg);
1156 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1157 {
1158 HL_TABLE()[idx].sg_gui_fg = i;
1159# endif
1160 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1161 {
1162 vim_free(*namep);
1163 if (STRCMP(arg, "NONE") != 0)
1164 *namep = vim_strsave(arg);
1165 else
1166 *namep = NULL;
1167 did_change = TRUE;
1168 }
1169# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1170# ifdef FEAT_GUI_X11
1171 if (is_menu_group && gui.menu_fg_pixel != i)
1172 {
1173 gui.menu_fg_pixel = i;
1174 *do_colors = TRUE;
1175 }
1176 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1177 {
1178 gui.scroll_fg_pixel = i;
1179 *do_colors = TRUE;
1180 }
1181# ifdef FEAT_BEVAL_GUI
1182 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1183 {
1184 gui.tooltip_fg_pixel = i;
1185 *do_colors = TRUE;
1186 }
1187# endif
1188# endif
1189 }
1190# endif
1191 }
1192
1193 return did_change;
1194}
1195
1196/*
1197 * Set the GUI background color for the highlight group at 'idx'.
1198 * Returns TRUE if the color is set.
1199 */
1200 static int
1201highlight_set_guibg(
1202 int idx,
1203 char_u *arg,
1204 int is_menu_group UNUSED,
1205 int is_scrollbar_group UNUSED,
1206 int is_tooltip_group UNUSED,
1207 int *do_colors UNUSED,
1208 int init)
1209{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001210# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001211 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001212# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001213 char_u **namep;
1214 int did_change = FALSE;
1215
1216 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1217 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1218 {
1219 if (!init)
1220 HL_TABLE()[idx].sg_set |= SG_GUI;
1221
1222# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Drew Vogele30d1022021-10-24 20:35:07 +01001223 // In GUI guibg colors are only used when recognized
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001224 i = color_name2handle(arg);
1225 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1226 {
1227 HL_TABLE()[idx].sg_gui_bg = i;
1228# endif
1229 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1230 {
1231 vim_free(*namep);
1232 if (STRCMP(arg, "NONE") != 0)
1233 *namep = vim_strsave(arg);
1234 else
1235 *namep = NULL;
1236 did_change = TRUE;
1237 }
1238# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1239# ifdef FEAT_GUI_X11
1240 if (is_menu_group && gui.menu_bg_pixel != i)
1241 {
1242 gui.menu_bg_pixel = i;
1243 *do_colors = TRUE;
1244 }
1245 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1246 {
1247 gui.scroll_bg_pixel = i;
1248 *do_colors = TRUE;
1249 }
1250# ifdef FEAT_BEVAL_GUI
1251 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1252 {
1253 gui.tooltip_bg_pixel = i;
1254 *do_colors = TRUE;
1255 }
1256# endif
1257# endif
1258 }
1259# endif
1260 }
1261
1262 return did_change;
1263}
1264
1265/*
1266 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1267 * Returns TRUE if the color is set.
1268 */
1269 static int
1270highlight_set_guisp(int idx, char_u *arg, int init)
1271{
1272# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1273 int i;
1274# endif
1275 int did_change = FALSE;
1276 char_u **namep;
1277
1278 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1279 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1280 {
1281 if (!init)
1282 HL_TABLE()[idx].sg_set |= SG_GUI;
1283
1284# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1285 // In GUI guisp colors are only used when recognized
1286 i = color_name2handle(arg);
1287 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1288 {
1289 HL_TABLE()[idx].sg_gui_sp = i;
1290# endif
1291 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1292 {
1293 vim_free(*namep);
1294 if (STRCMP(arg, "NONE") != 0)
1295 *namep = vim_strsave(arg);
1296 else
1297 *namep = NULL;
1298 did_change = TRUE;
1299 }
1300# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1301 }
1302# endif
1303 }
1304
1305 return did_change;
1306}
1307#endif
1308
1309/*
1310 * Set the start/stop terminal codes for a highlight group.
1311 * Returns TRUE if the terminal code is set.
1312 */
1313 static int
1314highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1315{
1316 int off;
1317 char_u buf[100];
1318 int len;
1319 char_u *tname;
1320 char_u *p;
1321
1322 if (!init)
1323 HL_TABLE()[idx].sg_set |= SG_TERM;
1324
1325 // The "start" and "stop" arguments can be a literal escape
1326 // sequence, or a comma separated list of terminal codes.
1327 if (STRNCMP(arg, "t_", 2) == 0)
1328 {
1329 off = 0;
1330 buf[0] = 0;
1331 while (arg[off] != NUL)
1332 {
1333 // Isolate one termcap name
1334 for (len = 0; arg[off + len] &&
1335 arg[off + len] != ','; ++len)
1336 ;
1337 tname = vim_strnsave(arg + off, len);
1338 if (tname == NULL) // out of memory
1339 return FALSE;
1340 // lookup the escape sequence for the item
1341 p = get_term_code(tname);
1342 vim_free(tname);
1343 if (p == NULL) // ignore non-existing things
1344 p = (char_u *)"";
1345
1346 // Append it to the already found stuff
1347 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1348 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001349 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001350 return FALSE;
1351 }
1352 STRCAT(buf, p);
1353
1354 // Advance to the next item
1355 off += len;
1356 if (arg[off] == ',') // another one follows
1357 ++off;
1358 }
1359 }
1360 else
1361 {
1362 // Copy characters from arg[] to buf[], translating <> codes.
1363 for (p = arg, off = 0; off < 100 - 6 && *p; )
1364 {
zeertzjqdb088872022-05-02 22:53:45 +01001365 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001366 if (len > 0) // recognized special char
1367 off += len;
1368 else // copy as normal char
1369 buf[off++] = *p++;
1370 }
1371 buf[off] = NUL;
1372 }
1373
1374 if (STRCMP(buf, "NONE") == 0) // resetting the value
1375 p = NULL;
1376 else
1377 p = vim_strsave(buf);
1378 if (key[2] == 'A')
1379 {
1380 vim_free(HL_TABLE()[idx].sg_start);
1381 HL_TABLE()[idx].sg_start = p;
1382 }
1383 else
1384 {
1385 vim_free(HL_TABLE()[idx].sg_stop);
1386 HL_TABLE()[idx].sg_stop = p;
1387 }
1388 return TRUE;
1389}
1390
1391/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001392 * Handle the ":highlight .." command.
1393 * When using ":hi clear" this is called recursively for each group with
1394 * "forceit" and "init" both TRUE.
1395 */
1396 void
1397do_highlight(
1398 char_u *line,
1399 int forceit,
1400 int init) // TRUE when called for initializing
1401{
1402 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001403 char_u *linep;
1404 char_u *key_start;
1405 char_u *arg_start;
1406 char_u *key = NULL, *arg = NULL;
1407 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001408 int id;
1409 int idx;
1410 hl_group_T item_before;
1411 int did_change = FALSE;
1412 int dodefault = FALSE;
1413 int doclear = FALSE;
1414 int dolink = FALSE;
1415 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001416 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001417#ifdef FEAT_GUI_X11
1418 int is_menu_group = FALSE; // "Menu" group
1419 int is_scrollbar_group = FALSE; // "Scrollbar" group
1420 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001421#else
1422# define is_menu_group 0
1423# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001424# define is_scrollbar_group 0
1425#endif
1426#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1427 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001428#endif
1429#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1430 int did_highlight_changed = FALSE;
1431#endif
1432
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001433 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001434 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001435 {
1436 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1437 // TODO: only call when the group has attributes set
1438 highlight_list_one((int)i);
1439 return;
1440 }
1441
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001442 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001443 name_end = skiptowhite(line);
1444 linep = skipwhite(name_end);
1445
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001446 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001447 if (STRNCMP(line, "default", name_end - line) == 0)
1448 {
1449 dodefault = TRUE;
1450 line = linep;
1451 name_end = skiptowhite(line);
1452 linep = skipwhite(name_end);
1453 }
1454
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001455 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001456 if (STRNCMP(line, "clear", name_end - line) == 0)
1457 doclear = TRUE;
1458 if (STRNCMP(line, "link", name_end - line) == 0)
1459 dolink = TRUE;
1460
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001461 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001462 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001463 {
1464 id = syn_namen2id(line, (int)(name_end - line));
1465 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001466 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001467 else
1468 highlight_list_one(id);
1469 return;
1470 }
1471
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001472 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001473 if (dolink)
1474 {
1475 char_u *from_start = linep;
1476 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001477 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001478 char_u *to_start;
1479 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001480 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001481
1482 from_end = skiptowhite(from_start);
1483 to_start = skipwhite(from_end);
1484 to_end = skiptowhite(to_start);
1485
Bram Moolenaar1966c242020-04-20 22:42:32 +02001486 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001487 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001488 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001489 return;
1490 }
1491
Bram Moolenaar1966c242020-04-20 22:42:32 +02001492 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001493 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001494 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001495 return;
1496 }
1497
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001498 from_len = (int)(from_end - from_start);
1499 to_len = (int)(to_end - to_start);
1500 highlight_group_link(from_start, from_len, to_start, to_len,
1501 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001502 return;
1503 }
1504
1505 if (doclear)
1506 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001507 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001508 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001509 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001510 // ":highlight clear" without group name
1511 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001512 return;
1513 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001514 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001515 name_end = skiptowhite(line);
1516 linep = skipwhite(name_end);
1517 }
1518
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001519 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001520 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001521 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001522 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001523 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001524
1525 // Return if "default" was used and the group already has settings.
1526 if (dodefault && hl_has_settings(idx, TRUE))
1527 return;
1528
1529 // Make a copy so we can check if any attribute actually changed.
1530 item_before = HL_TABLE()[idx];
1531
1532 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1533 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001534#ifdef FEAT_GUI_X11
1535 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1536 is_menu_group = TRUE;
1537 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1538 is_scrollbar_group = TRUE;
1539 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1540 is_tooltip_group = TRUE;
1541#endif
1542
1543 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1544 if (doclear || (forceit && init))
1545 {
1546 highlight_clear(idx);
1547 if (!doclear)
1548 HL_TABLE()[idx].sg_set = 0;
1549 }
1550
1551 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001552 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001553 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001554 key_start = linep;
1555 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001556 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001557 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001558 error = TRUE;
1559 break;
1560 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001561
1562 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1563 // or "guibg").
1564 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1565 ++linep;
1566 vim_free(key);
1567 key = vim_strnsave_up(key_start, linep - key_start);
1568 if (key == NULL)
1569 {
1570 error = TRUE;
1571 break;
1572 }
1573 linep = skipwhite(linep);
1574
1575 if (STRCMP(key, "NONE") == 0)
1576 {
1577 if (!init || HL_TABLE()[idx].sg_set == 0)
1578 {
1579 if (!init)
1580 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1581 highlight_clear(idx);
1582 }
1583 continue;
1584 }
1585
1586 // Check for the equal sign.
1587 if (*linep != '=')
1588 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001589 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001590 error = TRUE;
1591 break;
1592 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001593 ++linep;
1594
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001595 // Isolate the argument.
1596 linep = skipwhite(linep);
1597 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001598 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001599 arg_start = ++linep;
1600 linep = vim_strchr(linep, '\'');
1601 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001602 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001603 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001604 error = TRUE;
1605 break;
1606 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001607 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001608 else
1609 {
1610 arg_start = linep;
1611 linep = skiptowhite(linep);
1612 }
1613 if (linep == arg_start)
1614 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001615 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001616 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001617 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001618 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001619 vim_free(arg);
1620 arg = vim_strnsave(arg_start, linep - arg_start);
1621 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001622 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001623 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001624 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001625 }
1626 if (*linep == '\'')
1627 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001628
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001629 // Store the argument.
1630 if (STRCMP(key, "TERM") == 0
1631 || STRCMP(key, "CTERM") == 0
1632 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001633 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001634 if (!highlight_set_termgui_attr(idx, key, arg, init))
1635 {
1636 error = TRUE;
1637 break;
1638 }
1639 }
1640 else if (STRCMP(key, "FONT") == 0)
1641 {
1642 // in non-GUI fonts are simply ignored
1643#ifdef FEAT_GUI
1644 if (highlight_set_font(idx, arg, is_normal_group,
1645 is_menu_group, is_tooltip_group))
1646 did_change = TRUE;
1647#endif
1648 }
1649 else if (STRCMP(key, "CTERMFG") == 0
1650 || STRCMP(key, "CTERMBG") == 0
1651 || STRCMP(key, "CTERMUL") == 0)
1652 {
1653 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1654 is_normal_group, init))
1655 {
1656 error = TRUE;
1657 break;
1658 }
1659 }
1660 else if (STRCMP(key, "GUIFG") == 0)
1661 {
1662#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1663 if (highlight_set_guifg(idx, arg, is_menu_group,
1664 is_scrollbar_group, is_tooltip_group,
1665 &do_colors, init))
1666 did_change = TRUE;
1667#endif
1668 }
1669 else if (STRCMP(key, "GUIBG") == 0)
1670 {
1671#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1672 if (highlight_set_guibg(idx, arg, is_menu_group,
1673 is_scrollbar_group, is_tooltip_group,
1674 &do_colors, init))
1675 did_change = TRUE;
1676#endif
1677 }
1678 else if (STRCMP(key, "GUISP") == 0)
1679 {
1680#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1681 if (highlight_set_guisp(idx, arg, init))
1682 did_change = TRUE;
1683#endif
1684 }
1685 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1686 {
1687 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1688 {
1689 error = TRUE;
1690 break;
1691 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001692 }
1693 else
1694 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001695 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001696 error = TRUE;
1697 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001698 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001699 HL_TABLE()[idx].sg_cleared = FALSE;
1700
1701 // When highlighting has been given for a group, don't link it.
1702 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1703 HL_TABLE()[idx].sg_link = 0;
1704
1705 // Continue with next argument.
1706 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001707 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001708
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001709 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001710 if (error && idx == highlight_ga.ga_len)
1711 syn_unadd_group();
1712 else
1713 {
1714 if (is_normal_group)
1715 {
1716 HL_TABLE()[idx].sg_term_attr = 0;
1717 HL_TABLE()[idx].sg_cterm_attr = 0;
1718#ifdef FEAT_GUI
1719 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001720 // Need to update all groups, because they might be using "bg"
1721 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001722#endif
1723#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1724 if (USE_24BIT)
1725 {
1726 highlight_gui_started();
1727 did_highlight_changed = TRUE;
1728 redraw_all_later(NOT_VALID);
1729 }
1730#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001731#ifdef FEAT_VTP
1732 control_console_color_rgb();
1733#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001734 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001735#ifdef FEAT_GUI_X11
1736# ifdef FEAT_MENU
1737 else if (is_menu_group)
1738 {
1739 if (gui.in_use && do_colors)
1740 gui_mch_new_menu_colors();
1741 }
1742# endif
1743 else if (is_scrollbar_group)
1744 {
1745 if (gui.in_use && do_colors)
1746 gui_new_scrollbar_colors();
1747 else
1748 set_hl_attr(idx);
1749 }
1750# ifdef FEAT_BEVAL_GUI
1751 else if (is_tooltip_group)
1752 {
1753 if (gui.in_use && do_colors)
1754 gui_mch_new_tooltip_colors();
1755 }
1756# endif
1757#endif
1758 else
1759 set_hl_attr(idx);
1760#ifdef FEAT_EVAL
1761 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001762 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001763#endif
1764 }
1765
1766 vim_free(key);
1767 vim_free(arg);
1768
1769 // Only call highlight_changed() once, after a sequence of highlight
1770 // commands, and only if an attribute actually changed.
1771 if ((did_change
1772 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1773#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1774 && !did_highlight_changed
1775#endif
1776 )
1777 {
1778 // Do not trigger a redraw when highlighting is changed while
1779 // redrawing. This may happen when evaluating 'statusline' changes the
1780 // StatusLine group.
1781 if (!updating_screen)
1782 redraw_all_later(NOT_VALID);
1783 need_highlight_changed = TRUE;
1784 }
1785}
1786
1787#if defined(EXITFREE) || defined(PROTO)
1788 void
1789free_highlight(void)
1790{
1791 int i;
1792
1793 for (i = 0; i < highlight_ga.ga_len; ++i)
1794 {
1795 highlight_clear(i);
1796 vim_free(HL_TABLE()[i].sg_name);
1797 vim_free(HL_TABLE()[i].sg_name_u);
1798 }
1799 ga_clear(&highlight_ga);
1800}
1801#endif
1802
1803/*
1804 * Reset the cterm colors to what they were before Vim was started, if
1805 * possible. Otherwise reset them to zero.
1806 */
1807 void
1808restore_cterm_colors(void)
1809{
1810#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1811 // Since t_me has been set, this probably means that the user
1812 // wants to use this as default colors. Need to reset default
1813 // background/foreground colors.
1814 mch_set_normal_colors();
1815#else
1816# ifdef VIMDLL
1817 if (!gui.in_use)
1818 {
1819 mch_set_normal_colors();
1820 return;
1821 }
1822# endif
1823 cterm_normal_fg_color = 0;
1824 cterm_normal_fg_bold = 0;
1825 cterm_normal_bg_color = 0;
1826# ifdef FEAT_TERMGUICOLORS
1827 cterm_normal_fg_gui_color = INVALCOLOR;
1828 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001829 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001830# endif
1831#endif
1832}
1833
1834/*
1835 * Return TRUE if highlight group "idx" has any settings.
1836 * When "check_link" is TRUE also check for an existing link.
1837 */
1838 static int
1839hl_has_settings(int idx, int check_link)
1840{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001841 return HL_TABLE()[idx].sg_cleared == 0
1842 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001843 || HL_TABLE()[idx].sg_cterm_attr != 0
1844 || HL_TABLE()[idx].sg_cterm_fg != 0
1845 || HL_TABLE()[idx].sg_cterm_bg != 0
1846#ifdef FEAT_GUI
1847 || HL_TABLE()[idx].sg_gui_attr != 0
1848 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1849 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1850 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1851 || HL_TABLE()[idx].sg_font_name != NULL
1852#endif
1853 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1854}
1855
1856/*
1857 * Clear highlighting for one group.
1858 */
1859 static void
1860highlight_clear(int idx)
1861{
1862 HL_TABLE()[idx].sg_cleared = TRUE;
1863
1864 HL_TABLE()[idx].sg_term = 0;
1865 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1866 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1867 HL_TABLE()[idx].sg_term_attr = 0;
1868 HL_TABLE()[idx].sg_cterm = 0;
1869 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1870 HL_TABLE()[idx].sg_cterm_fg = 0;
1871 HL_TABLE()[idx].sg_cterm_bg = 0;
1872 HL_TABLE()[idx].sg_cterm_attr = 0;
1873#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1874 HL_TABLE()[idx].sg_gui = 0;
1875 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1876 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1877 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1878#endif
1879#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1880 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1881 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001882 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001883#endif
1884#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001885 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1886 HL_TABLE()[idx].sg_font = NOFONT;
1887# ifdef FEAT_XFONTSET
1888 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1889 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1890# endif
1891 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1892 HL_TABLE()[idx].sg_gui_attr = 0;
1893#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001894 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001895 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001896#ifdef FEAT_EVAL
1897 // Since we set the default link, set the location to where the default
1898 // link was set.
1899 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001900#endif
1901}
1902
1903#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1904/*
1905 * Set the normal foreground and background colors according to the "Normal"
1906 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1907 * "Tooltip" colors.
1908 */
1909 void
1910set_normal_colors(void)
1911{
1912# ifdef FEAT_GUI
1913# ifdef FEAT_TERMGUICOLORS
1914 if (gui.in_use)
1915# endif
1916 {
1917 if (set_group_colors((char_u *)"Normal",
1918 &gui.norm_pixel, &gui.back_pixel,
1919 FALSE, TRUE, FALSE))
1920 {
1921 gui_mch_new_colors();
1922 must_redraw = CLEAR;
1923 }
1924# ifdef FEAT_GUI_X11
1925 if (set_group_colors((char_u *)"Menu",
1926 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1927 TRUE, FALSE, FALSE))
1928 {
1929# ifdef FEAT_MENU
1930 gui_mch_new_menu_colors();
1931# endif
1932 must_redraw = CLEAR;
1933 }
1934# ifdef FEAT_BEVAL_GUI
1935 if (set_group_colors((char_u *)"Tooltip",
1936 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1937 FALSE, FALSE, TRUE))
1938 {
1939# ifdef FEAT_TOOLBAR
1940 gui_mch_new_tooltip_colors();
1941# endif
1942 must_redraw = CLEAR;
1943 }
1944# endif
1945 if (set_group_colors((char_u *)"Scrollbar",
1946 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1947 FALSE, FALSE, FALSE))
1948 {
1949 gui_new_scrollbar_colors();
1950 must_redraw = CLEAR;
1951 }
1952# endif
1953 }
1954# endif
1955# ifdef FEAT_TERMGUICOLORS
1956# ifdef FEAT_GUI
1957 else
1958# endif
1959 {
1960 int idx;
1961
1962 idx = syn_name2id((char_u *)"Normal") - 1;
1963 if (idx >= 0)
1964 {
1965 gui_do_one_color(idx, FALSE, FALSE);
1966
1967 // If the normal fg or bg color changed a complete redraw is
1968 // required.
1969 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1970 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1971 {
1972 // if the GUI color is INVALCOLOR then we use the default cterm
1973 // color
1974 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1975 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1976 must_redraw = CLEAR;
1977 }
1978 }
1979 }
1980# endif
1981}
1982#endif
1983
1984#if defined(FEAT_GUI) || defined(PROTO)
1985/*
1986 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1987 */
1988 static int
1989set_group_colors(
1990 char_u *name,
1991 guicolor_T *fgp,
1992 guicolor_T *bgp,
1993 int do_menu,
1994 int use_norm,
1995 int do_tooltip)
1996{
1997 int idx;
1998
1999 idx = syn_name2id(name) - 1;
2000 if (idx >= 0)
2001 {
2002 gui_do_one_color(idx, do_menu, do_tooltip);
2003
2004 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2005 *fgp = HL_TABLE()[idx].sg_gui_fg;
2006 else if (use_norm)
2007 *fgp = gui.def_norm_pixel;
2008 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2009 *bgp = HL_TABLE()[idx].sg_gui_bg;
2010 else if (use_norm)
2011 *bgp = gui.def_back_pixel;
2012 return TRUE;
2013 }
2014 return FALSE;
2015}
2016
2017/*
2018 * Get the font of the "Normal" group.
2019 * Returns "" when it's not found or not set.
2020 */
2021 char_u *
2022hl_get_font_name(void)
2023{
2024 int id;
2025 char_u *s;
2026
2027 id = syn_name2id((char_u *)"Normal");
2028 if (id > 0)
2029 {
2030 s = HL_TABLE()[id - 1].sg_font_name;
2031 if (s != NULL)
2032 return s;
2033 }
2034 return (char_u *)"";
2035}
2036
2037/*
2038 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2039 * actually chosen to be used.
2040 */
2041 void
2042hl_set_font_name(char_u *font_name)
2043{
2044 int id;
2045
2046 id = syn_name2id((char_u *)"Normal");
2047 if (id > 0)
2048 {
2049 vim_free(HL_TABLE()[id - 1].sg_font_name);
2050 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2051 }
2052}
2053
2054/*
2055 * Set background color for "Normal" group. Called by gui_set_bg_color()
2056 * when the color is known.
2057 */
2058 void
2059hl_set_bg_color_name(
2060 char_u *name) // must have been allocated
2061{
2062 int id;
2063
2064 if (name != NULL)
2065 {
2066 id = syn_name2id((char_u *)"Normal");
2067 if (id > 0)
2068 {
2069 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2070 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2071 }
2072 }
2073}
2074
2075/*
2076 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2077 * when the color is known.
2078 */
2079 void
2080hl_set_fg_color_name(
2081 char_u *name) // must have been allocated
2082{
2083 int id;
2084
2085 if (name != NULL)
2086 {
2087 id = syn_name2id((char_u *)"Normal");
2088 if (id > 0)
2089 {
2090 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2091 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2092 }
2093 }
2094}
2095
2096/*
2097 * Return the handle for a font name.
2098 * Returns NOFONT when failed.
2099 */
2100 static GuiFont
2101font_name2handle(char_u *name)
2102{
2103 if (STRCMP(name, "NONE") == 0)
2104 return NOFONT;
2105
2106 return gui_mch_get_font(name, TRUE);
2107}
2108
2109# ifdef FEAT_XFONTSET
2110/*
2111 * Return the handle for a fontset name.
2112 * Returns NOFONTSET when failed.
2113 */
2114 static GuiFontset
2115fontset_name2handle(char_u *name, int fixed_width)
2116{
2117 if (STRCMP(name, "NONE") == 0)
2118 return NOFONTSET;
2119
2120 return gui_mch_get_fontset(name, TRUE, fixed_width);
2121}
2122# endif
2123
2124/*
2125 * Get the font or fontset for one highlight group.
2126 */
2127 static void
2128hl_do_font(
2129 int idx,
2130 char_u *arg,
2131 int do_normal, // set normal font
2132 int do_menu UNUSED, // set menu font
2133 int do_tooltip UNUSED, // set tooltip font
2134 int free_font) // free current font/fontset
2135{
2136# ifdef FEAT_XFONTSET
2137 // If 'guifontset' is not empty, first try using the name as a
2138 // fontset. If that doesn't work, use it as a font name.
2139 if (*p_guifontset != NUL
2140# ifdef FONTSET_ALWAYS
2141 || do_menu
2142# endif
2143# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002144 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002145 || do_tooltip
2146# endif
2147 )
2148 {
2149 if (free_font)
2150 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2151 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2152# ifdef FONTSET_ALWAYS
2153 || do_menu
2154# endif
2155# ifdef FEAT_BEVAL_TIP
2156 || do_tooltip
2157# endif
2158 );
2159 }
2160 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2161 {
2162 // If it worked and it's the Normal group, use it as the normal
2163 // fontset. Same for the Menu group.
2164 if (do_normal)
2165 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002166# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002167 if (do_menu)
2168 {
2169# ifdef FONTSET_ALWAYS
2170 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2171# else
2172 // YIKES! This is a bug waiting to crash the program
2173 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2174# endif
2175 gui_mch_new_menu_font();
2176 }
2177# ifdef FEAT_BEVAL_GUI
2178 if (do_tooltip)
2179 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002180 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002181 // displaying a single font and a fontset.
2182 // If the XtNinternational resource is set to True at widget
2183 // creation, then a fontset is always used, otherwise an
2184 // XFontStruct is used.
2185 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2186 gui_mch_new_tooltip_font();
2187 }
2188# endif
2189# endif
2190 }
2191 else
2192# endif
2193 {
2194 if (free_font)
2195 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2196 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2197 // If it worked and it's the Normal group, use it as the
2198 // normal font. Same for the Menu group.
2199 if (HL_TABLE()[idx].sg_font != NOFONT)
2200 {
2201 if (do_normal)
2202 gui_init_font(arg, FALSE);
2203#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002204# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002205 if (do_menu)
2206 {
2207 gui.menu_font = HL_TABLE()[idx].sg_font;
2208 gui_mch_new_menu_font();
2209 }
2210# endif
2211#endif
2212 }
2213 }
2214}
2215
2216#endif // FEAT_GUI
2217
2218#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2219/*
2220 * Return the handle for a color name.
2221 * Returns INVALCOLOR when failed.
2222 */
2223 guicolor_T
2224color_name2handle(char_u *name)
2225{
2226 if (STRCMP(name, "NONE") == 0)
2227 return INVALCOLOR;
2228
2229 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2230 {
2231#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2232 if (gui.in_use)
2233#endif
2234#ifdef FEAT_GUI
2235 return gui.norm_pixel;
2236#endif
2237#ifdef FEAT_TERMGUICOLORS
2238 if (cterm_normal_fg_gui_color != INVALCOLOR)
2239 return cterm_normal_fg_gui_color;
2240 // Guess that the foreground is black or white.
2241 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2242#endif
2243 }
2244 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2245 {
2246#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2247 if (gui.in_use)
2248#endif
2249#ifdef FEAT_GUI
2250 return gui.back_pixel;
2251#endif
2252#ifdef FEAT_TERMGUICOLORS
2253 if (cterm_normal_bg_gui_color != INVALCOLOR)
2254 return cterm_normal_bg_gui_color;
2255 // Guess that the background is white or black.
2256 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2257#endif
2258 }
2259
2260 return GUI_GET_COLOR(name);
2261}
Drew Vogele30d1022021-10-24 20:35:07 +01002262
2263// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2264// values as used by the MS-Windows GDI api. It should be used only for
2265// MS-Windows GDI builds.
2266# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2267# undef RGB
2268# endif
2269# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002270# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002271# endif
2272
2273# ifdef VIMDLL
2274 static guicolor_T
2275gui_adjust_rgb(guicolor_T c)
2276{
2277 if (gui.in_use)
2278 return c;
2279 else
2280 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2281}
2282# else
2283# define gui_adjust_rgb(c) (c)
2284# endif
2285
2286 static int
2287hex_digit(int c)
2288{
2289 if (isdigit(c))
2290 return c - '0';
2291 c = TOLOWER_ASC(c);
2292 if (c >= 'a' && c <= 'f')
2293 return c - 'a' + 10;
2294 return 0x1ffffff;
2295}
2296
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002297 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002298decode_hex_color(char_u *hex)
2299{
2300 guicolor_T color;
2301
2302 if (hex[0] != '#' || STRLEN(hex) != 7)
2303 return INVALCOLOR;
2304
2305 // Name is in "#rrggbb" format
2306 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2307 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2308 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2309 if (color > 0xffffff)
2310 return INVALCOLOR;
2311 return gui_adjust_rgb(color);
2312}
2313
Bram Moolenaar2a521962021-10-25 10:30:14 +01002314#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002315// Returns the color currently mapped to the given name or INVALCOLOR if no
2316// such name exists in the color table. The convention is to use lowercase for
2317// all keys in the v:colornames dictionary. The value can be either a string in
2318// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002319 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002320colorname2rgb(char_u *name)
2321{
2322 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2323 char_u *lc_name;
2324 dictitem_T *colentry;
2325 char_u *colstr;
2326 varnumber_T colnum;
2327
2328 lc_name = strlow_save(name);
2329 if (lc_name == NULL)
2330 return INVALCOLOR;
2331
2332 colentry = dict_find(colornames_table, lc_name, -1);
2333 vim_free(lc_name);
2334 if (colentry == NULL)
2335 return INVALCOLOR;
2336
2337 if (colentry->di_tv.v_type == VAR_STRING)
2338 {
2339 colstr = tv_get_string_strict(&colentry->di_tv);
2340 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2341 {
2342 return decode_hex_color(colstr);
2343 }
2344 else
2345 {
2346 semsg(_(e_bad_color_string_str), colstr);
2347 return INVALCOLOR;
2348 }
2349 }
2350
2351 if (colentry->di_tv.v_type == VAR_NUMBER)
2352 {
2353 colnum = tv_get_number(&colentry->di_tv);
2354 return (guicolor_T)colnum;
2355 }
2356
2357 return INVALCOLOR;
2358}
2359
Drew Vogele30d1022021-10-24 20:35:07 +01002360#endif
2361
2362 guicolor_T
2363gui_get_color_cmn(char_u *name)
2364{
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002365 int i;
Drew Vogele30d1022021-10-24 20:35:07 +01002366 guicolor_T color;
2367
2368 struct rgbcolor_table_S {
2369 char_u *color_name;
2370 guicolor_T color;
2371 };
2372
2373 // Only non X11 colors (not present in rgb.txt) and colors in
2374 // color_names[], useful when $VIMRUNTIME is not found,.
2375 static struct rgbcolor_table_S rgb_table[] = {
2376 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2377 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2378 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2379 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2380 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2381 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2382 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2383 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2384 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2385 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2386 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2387 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2388 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2389 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2390 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2391 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2392 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2393 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2394 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2395 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2396 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2397 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2398 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2399 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2400 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2401 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2402 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2403 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2404 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2405 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2406 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2407 };
2408
2409 color = decode_hex_color(name);
2410 if (color != INVALCOLOR)
2411 return color;
2412
2413 // Check if the name is one of the colors we know
2414 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2415 if (STRICMP(name, rgb_table[i].color_name) == 0)
2416 return gui_adjust_rgb(rgb_table[i].color);
2417
2418#if defined(FEAT_EVAL)
2419 /*
2420 * Not a traditional color. Load additional color aliases and then consult the alias table.
2421 */
2422
2423 color = colorname2rgb(name);
2424 if (color == INVALCOLOR)
2425 {
2426 load_default_colors_lists();
2427 color = colorname2rgb(name);
2428 }
2429
2430 return color;
2431#else
2432 return INVALCOLOR;
2433#endif
2434}
2435
2436 guicolor_T
2437gui_get_rgb_color_cmn(int r, int g, int b)
2438{
2439 guicolor_T color = RGB(r, g, b);
2440
2441 if (color > 0xffffff)
2442 return INVALCOLOR;
2443 return gui_adjust_rgb(color);
2444}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002445#endif
2446
2447/*
2448 * Table with the specifications for an attribute number.
2449 * Note that this table is used by ALL buffers. This is required because the
2450 * GUI can redraw at any time for any buffer.
2451 */
2452static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2453
2454#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2455
2456static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2457
2458#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2459
2460#ifdef FEAT_GUI
2461static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2462
2463#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2464#endif
2465
2466/*
2467 * Return the attr number for a set of colors and font.
2468 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2469 * if the combination is new.
2470 * Return 0 for error (no more room).
2471 */
2472 static int
2473get_attr_entry(garray_T *table, attrentry_T *aep)
2474{
2475 int i;
2476 attrentry_T *taep;
2477 static int recursive = FALSE;
2478
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002479 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002480 table->ga_itemsize = sizeof(attrentry_T);
2481 table->ga_growsize = 7;
2482
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002483 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002484 for (i = 0; i < table->ga_len; ++i)
2485 {
2486 taep = &(((attrentry_T *)table->ga_data)[i]);
2487 if ( aep->ae_attr == taep->ae_attr
2488 && (
2489#ifdef FEAT_GUI
2490 (table == &gui_attr_table
2491 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2492 && aep->ae_u.gui.bg_color
2493 == taep->ae_u.gui.bg_color
2494 && aep->ae_u.gui.sp_color
2495 == taep->ae_u.gui.sp_color
2496 && aep->ae_u.gui.font == taep->ae_u.gui.font
2497# ifdef FEAT_XFONTSET
2498 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2499# endif
2500 ))
2501 ||
2502#endif
2503 (table == &term_attr_table
2504 && (aep->ae_u.term.start == NULL)
2505 == (taep->ae_u.term.start == NULL)
2506 && (aep->ae_u.term.start == NULL
2507 || STRCMP(aep->ae_u.term.start,
2508 taep->ae_u.term.start) == 0)
2509 && (aep->ae_u.term.stop == NULL)
2510 == (taep->ae_u.term.stop == NULL)
2511 && (aep->ae_u.term.stop == NULL
2512 || STRCMP(aep->ae_u.term.stop,
2513 taep->ae_u.term.stop) == 0))
2514 || (table == &cterm_attr_table
2515 && aep->ae_u.cterm.fg_color
2516 == taep->ae_u.cterm.fg_color
2517 && aep->ae_u.cterm.bg_color
2518 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002519 && aep->ae_u.cterm.ul_color
2520 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002521#ifdef FEAT_TERMGUICOLORS
2522 && aep->ae_u.cterm.fg_rgb
2523 == taep->ae_u.cterm.fg_rgb
2524 && aep->ae_u.cterm.bg_rgb
2525 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002526 && aep->ae_u.cterm.ul_rgb
2527 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002528#endif
2529 )))
2530
2531 return i + ATTR_OFF;
2532 }
2533
2534 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2535 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002536 // Running out of attribute entries! remove all attributes, and
2537 // compute new ones for all groups.
2538 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002539 if (recursive)
2540 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002541 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002542 return 0;
2543 }
2544 recursive = TRUE;
2545
2546 clear_hl_tables();
2547
2548 must_redraw = CLEAR;
2549
2550 for (i = 0; i < highlight_ga.ga_len; ++i)
2551 set_hl_attr(i);
2552
2553 recursive = FALSE;
2554 }
2555
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002556 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002557 if (ga_grow(table, 1) == FAIL)
2558 return 0;
2559
2560 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002561 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002562 taep->ae_attr = aep->ae_attr;
2563#ifdef FEAT_GUI
2564 if (table == &gui_attr_table)
2565 {
2566 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2567 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2568 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2569 taep->ae_u.gui.font = aep->ae_u.gui.font;
2570# ifdef FEAT_XFONTSET
2571 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2572# endif
2573 }
2574#endif
2575 if (table == &term_attr_table)
2576 {
2577 if (aep->ae_u.term.start == NULL)
2578 taep->ae_u.term.start = NULL;
2579 else
2580 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2581 if (aep->ae_u.term.stop == NULL)
2582 taep->ae_u.term.stop = NULL;
2583 else
2584 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2585 }
2586 else if (table == &cterm_attr_table)
2587 {
2588 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2589 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002590 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002591#ifdef FEAT_TERMGUICOLORS
2592 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2593 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002594 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002595#endif
2596 }
2597 ++table->ga_len;
2598 return (table->ga_len - 1 + ATTR_OFF);
2599}
2600
2601#if defined(FEAT_TERMINAL) || defined(PROTO)
2602/*
2603 * Get an attribute index for a cterm entry.
2604 * Uses an existing entry when possible or adds one when needed.
2605 */
2606 int
2607get_cterm_attr_idx(int attr, int fg, int bg)
2608{
2609 attrentry_T at_en;
2610
Bram Moolenaara80faa82020-04-12 19:37:17 +02002611 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002612#ifdef FEAT_TERMGUICOLORS
2613 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2614 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002615 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002616#endif
2617 at_en.ae_attr = attr;
2618 at_en.ae_u.cterm.fg_color = fg;
2619 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002620 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002621 return get_attr_entry(&cterm_attr_table, &at_en);
2622}
2623#endif
2624
2625#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2626/*
2627 * Get an attribute index for a 'termguicolors' entry.
2628 * Uses an existing entry when possible or adds one when needed.
2629 */
2630 int
2631get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2632{
2633 attrentry_T at_en;
2634
Bram Moolenaara80faa82020-04-12 19:37:17 +02002635 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002636 at_en.ae_attr = attr;
2637 if (fg == INVALCOLOR && bg == INVALCOLOR)
2638 {
2639 // If both GUI colors are not set fall back to the cterm colors. Helps
2640 // if the GUI only has an attribute, such as undercurl.
2641 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2642 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2643 }
2644 else
2645 {
2646 at_en.ae_u.cterm.fg_rgb = fg;
2647 at_en.ae_u.cterm.bg_rgb = bg;
2648 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002649 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002650 return get_attr_entry(&cterm_attr_table, &at_en);
2651}
2652#endif
2653
2654#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2655/*
2656 * Get an attribute index for a cterm entry.
2657 * Uses an existing entry when possible or adds one when needed.
2658 */
2659 int
2660get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2661{
2662 attrentry_T at_en;
2663
Bram Moolenaara80faa82020-04-12 19:37:17 +02002664 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002665 at_en.ae_attr = attr;
2666 at_en.ae_u.gui.fg_color = fg;
2667 at_en.ae_u.gui.bg_color = bg;
2668 return get_attr_entry(&gui_attr_table, &at_en);
2669}
2670#endif
2671
2672/*
2673 * Clear all highlight tables.
2674 */
2675 void
2676clear_hl_tables(void)
2677{
2678 int i;
2679 attrentry_T *taep;
2680
2681#ifdef FEAT_GUI
2682 ga_clear(&gui_attr_table);
2683#endif
2684 for (i = 0; i < term_attr_table.ga_len; ++i)
2685 {
2686 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2687 vim_free(taep->ae_u.term.start);
2688 vim_free(taep->ae_u.term.stop);
2689 }
2690 ga_clear(&term_attr_table);
2691 ga_clear(&cterm_attr_table);
2692}
2693
2694/*
2695 * Combine special attributes (e.g., for spelling) with other attributes
2696 * (e.g., for syntax highlighting).
2697 * "prim_attr" overrules "char_attr".
2698 * This creates a new group when required.
2699 * Since we expect there to be few spelling mistakes we don't cache the
2700 * result.
2701 * Return the resulting attributes.
2702 */
2703 int
2704hl_combine_attr(int char_attr, int prim_attr)
2705{
2706 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002707 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002708 attrentry_T new_en;
2709
2710 if (char_attr == 0)
2711 return prim_attr;
2712 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2713 return ATTR_COMBINE(char_attr, prim_attr);
2714#ifdef FEAT_GUI
2715 if (gui.in_use)
2716 {
2717 if (char_attr > HL_ALL)
2718 char_aep = syn_gui_attr2entry(char_attr);
2719 if (char_aep != NULL)
2720 new_en = *char_aep;
2721 else
2722 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002723 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002724 new_en.ae_u.gui.fg_color = INVALCOLOR;
2725 new_en.ae_u.gui.bg_color = INVALCOLOR;
2726 new_en.ae_u.gui.sp_color = INVALCOLOR;
2727 if (char_attr <= HL_ALL)
2728 new_en.ae_attr = char_attr;
2729 }
2730
2731 if (prim_attr <= HL_ALL)
2732 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2733 else
2734 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002735 prim_aep = syn_gui_attr2entry(prim_attr);
2736 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002737 {
2738 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002739 prim_aep->ae_attr);
2740 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2741 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2742 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2743 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2744 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2745 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2746 if (prim_aep->ae_u.gui.font != NOFONT)
2747 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002748# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002749 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2750 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002751# endif
2752 }
2753 }
2754 return get_attr_entry(&gui_attr_table, &new_en);
2755 }
2756#endif
2757
2758 if (IS_CTERM)
2759 {
2760 if (char_attr > HL_ALL)
2761 char_aep = syn_cterm_attr2entry(char_attr);
2762 if (char_aep != NULL)
2763 new_en = *char_aep;
2764 else
2765 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002766 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002767#ifdef FEAT_TERMGUICOLORS
2768 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2769 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002770 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002771#endif
2772 if (char_attr <= HL_ALL)
2773 new_en.ae_attr = char_attr;
2774 }
2775
2776 if (prim_attr <= HL_ALL)
2777 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2778 else
2779 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002780 prim_aep = syn_cterm_attr2entry(prim_attr);
2781 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002782 {
2783 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002784 prim_aep->ae_attr);
2785 if (prim_aep->ae_u.cterm.fg_color > 0)
2786 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2787 if (prim_aep->ae_u.cterm.bg_color > 0)
2788 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2789 if (prim_aep->ae_u.cterm.ul_color > 0)
2790 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002791#ifdef FEAT_TERMGUICOLORS
2792 // If both fg and bg are not set fall back to cterm colors.
2793 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002794 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2795 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002796 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002797 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002798 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002799 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002800 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2801 }
2802 else
2803 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002804 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2805 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2806 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2807 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002808 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002809 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2810 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002811#endif
2812 }
2813 }
2814 return get_attr_entry(&cterm_attr_table, &new_en);
2815 }
2816
2817 if (char_attr > HL_ALL)
2818 char_aep = syn_term_attr2entry(char_attr);
2819 if (char_aep != NULL)
2820 new_en = *char_aep;
2821 else
2822 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002823 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002824 if (char_attr <= HL_ALL)
2825 new_en.ae_attr = char_attr;
2826 }
2827
2828 if (prim_attr <= HL_ALL)
2829 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2830 else
2831 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002832 prim_aep = syn_term_attr2entry(prim_attr);
2833 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002834 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002835 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2836 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002837 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002838 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2839 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002840 }
2841 }
2842 }
2843 return get_attr_entry(&term_attr_table, &new_en);
2844}
2845
2846#ifdef FEAT_GUI
2847 attrentry_T *
2848syn_gui_attr2entry(int attr)
2849{
2850 attr -= ATTR_OFF;
2851 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2852 return NULL;
2853 return &(GUI_ATTR_ENTRY(attr));
2854}
2855#endif
2856
2857/*
2858 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2859 * Only to be used when "attr" > HL_ALL.
2860 */
2861 int
2862syn_attr2attr(int attr)
2863{
2864 attrentry_T *aep;
2865
2866#ifdef FEAT_GUI
2867 if (gui.in_use)
2868 aep = syn_gui_attr2entry(attr);
2869 else
2870#endif
2871 if (IS_CTERM)
2872 aep = syn_cterm_attr2entry(attr);
2873 else
2874 aep = syn_term_attr2entry(attr);
2875
2876 if (aep == NULL) // highlighting not set
2877 return 0;
2878 return aep->ae_attr;
2879}
2880
2881
2882 attrentry_T *
2883syn_term_attr2entry(int attr)
2884{
2885 attr -= ATTR_OFF;
2886 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2887 return NULL;
2888 return &(TERM_ATTR_ENTRY(attr));
2889}
2890
2891 attrentry_T *
2892syn_cterm_attr2entry(int attr)
2893{
2894 attr -= ATTR_OFF;
2895 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2896 return NULL;
2897 return &(CTERM_ATTR_ENTRY(attr));
2898}
2899
2900#define LIST_ATTR 1
2901#define LIST_STRING 2
2902#define LIST_INT 3
2903
2904 static void
2905highlight_list_one(int id)
2906{
2907 hl_group_T *sgp;
2908 int didh = FALSE;
2909
2910 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2911
2912 if (message_filtered(sgp->sg_name))
2913 return;
2914
2915 didh = highlight_list_arg(id, didh, LIST_ATTR,
2916 sgp->sg_term, NULL, "term");
2917 didh = highlight_list_arg(id, didh, LIST_STRING,
2918 0, sgp->sg_start, "start");
2919 didh = highlight_list_arg(id, didh, LIST_STRING,
2920 0, sgp->sg_stop, "stop");
2921
2922 didh = highlight_list_arg(id, didh, LIST_ATTR,
2923 sgp->sg_cterm, NULL, "cterm");
2924 didh = highlight_list_arg(id, didh, LIST_INT,
2925 sgp->sg_cterm_fg, NULL, "ctermfg");
2926 didh = highlight_list_arg(id, didh, LIST_INT,
2927 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002928 didh = highlight_list_arg(id, didh, LIST_INT,
2929 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002930
2931#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2932 didh = highlight_list_arg(id, didh, LIST_ATTR,
2933 sgp->sg_gui, NULL, "gui");
2934 didh = highlight_list_arg(id, didh, LIST_STRING,
2935 0, sgp->sg_gui_fg_name, "guifg");
2936 didh = highlight_list_arg(id, didh, LIST_STRING,
2937 0, sgp->sg_gui_bg_name, "guibg");
2938 didh = highlight_list_arg(id, didh, LIST_STRING,
2939 0, sgp->sg_gui_sp_name, "guisp");
2940#endif
2941#ifdef FEAT_GUI
2942 didh = highlight_list_arg(id, didh, LIST_STRING,
2943 0, sgp->sg_font_name, "font");
2944#endif
2945
2946 if (sgp->sg_link && !got_int)
2947 {
2948 (void)syn_list_header(didh, 9999, id);
2949 didh = TRUE;
2950 msg_puts_attr("links to", HL_ATTR(HLF_D));
2951 msg_putchar(' ');
2952 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2953 }
2954
2955 if (!didh)
2956 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2957#ifdef FEAT_EVAL
2958 if (p_verbose > 0)
2959 last_set_msg(sgp->sg_script_ctx);
2960#endif
2961}
2962
2963 static int
2964highlight_list_arg(
2965 int id,
2966 int didh,
2967 int type,
2968 int iarg,
2969 char_u *sarg,
2970 char *name)
2971{
Bram Moolenaar84f54632022-06-29 18:39:11 +01002972 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002973 char_u *ts;
2974 int i;
2975
2976 if (got_int)
2977 return FALSE;
2978 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2979 {
2980 ts = buf;
2981 if (type == LIST_INT)
2982 sprintf((char *)buf, "%d", iarg - 1);
2983 else if (type == LIST_STRING)
2984 ts = sarg;
2985 else // type == LIST_ATTR
2986 {
2987 buf[0] = NUL;
2988 for (i = 0; hl_attr_table[i] != 0; ++i)
2989 {
2990 if (iarg & hl_attr_table[i])
2991 {
2992 if (buf[0] != NUL)
Bram Moolenaar84f54632022-06-29 18:39:11 +01002993 vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
2994 vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002995 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2996 }
2997 }
2998 }
2999
3000 (void)syn_list_header(didh,
3001 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3002 didh = TRUE;
3003 if (!got_int)
3004 {
3005 if (*name != NUL)
3006 {
3007 msg_puts_attr(name, HL_ATTR(HLF_D));
3008 msg_puts_attr("=", HL_ATTR(HLF_D));
3009 }
3010 msg_outtrans(ts);
3011 }
3012 }
3013 return didh;
3014}
3015
3016#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3017/*
3018 * Return "1" if highlight group "id" has attribute "flag".
3019 * Return NULL otherwise.
3020 */
3021 char_u *
3022highlight_has_attr(
3023 int id,
3024 int flag,
3025 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3026{
3027 int attr;
3028
3029 if (id <= 0 || id > highlight_ga.ga_len)
3030 return NULL;
3031
3032#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3033 if (modec == 'g')
3034 attr = HL_TABLE()[id - 1].sg_gui;
3035 else
3036#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003037 {
3038 if (modec == 'c')
3039 attr = HL_TABLE()[id - 1].sg_cterm;
3040 else
3041 attr = HL_TABLE()[id - 1].sg_term;
3042 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003043
3044 if (attr & flag)
3045 return (char_u *)"1";
3046 return NULL;
3047}
3048#endif
3049
3050#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3051/*
3052 * Return color name of highlight group "id".
3053 */
3054 char_u *
3055highlight_color(
3056 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003057 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003058 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3059{
3060 static char_u name[20];
3061 int n;
3062 int fg = FALSE;
3063 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003064 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003065 int font = FALSE;
3066
3067 if (id <= 0 || id > highlight_ga.ga_len)
3068 return NULL;
3069
3070 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3071 fg = TRUE;
3072 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3073 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3074 font = TRUE;
3075 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3076 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003077 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3078 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003079 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3080 return NULL;
3081 if (modec == 'g')
3082 {
3083# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3084# ifdef FEAT_GUI
3085 // return font name
3086 if (font)
3087 return HL_TABLE()[id - 1].sg_font_name;
3088# endif
3089
3090 // return #RRGGBB form (only possible when GUI is running)
3091 if ((USE_24BIT) && what[2] == '#')
3092 {
3093 guicolor_T color;
3094 long_u rgb;
3095 static char_u buf[10];
3096
3097 if (fg)
3098 color = HL_TABLE()[id - 1].sg_gui_fg;
3099 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003100 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003101 else
3102 color = HL_TABLE()[id - 1].sg_gui_bg;
3103 if (color == INVALCOLOR)
3104 return NULL;
3105 rgb = (long_u)GUI_MCH_GET_RGB(color);
3106 sprintf((char *)buf, "#%02x%02x%02x",
3107 (unsigned)(rgb >> 16),
3108 (unsigned)(rgb >> 8) & 255,
3109 (unsigned)rgb & 255);
3110 return buf;
3111 }
3112# endif
3113 if (fg)
3114 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3115 if (sp)
3116 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3117 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3118 }
3119 if (font || sp)
3120 return NULL;
3121 if (modec == 'c')
3122 {
3123 if (fg)
3124 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003125 else if (ul)
3126 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003127 else
3128 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3129 if (n < 0)
3130 return NULL;
3131 sprintf((char *)name, "%d", n);
3132 return name;
3133 }
3134 // term doesn't have color
3135 return NULL;
3136}
3137#endif
3138
3139#if (defined(FEAT_SYN_HL) \
3140 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3141 && defined(FEAT_PRINTER)) || defined(PROTO)
3142/*
3143 * Return color name of highlight group "id" as RGB value.
3144 */
3145 long_u
3146highlight_gui_color_rgb(
3147 int id,
3148 int fg) // TRUE = fg, FALSE = bg
3149{
3150 guicolor_T color;
3151
3152 if (id <= 0 || id > highlight_ga.ga_len)
3153 return 0L;
3154
3155 if (fg)
3156 color = HL_TABLE()[id - 1].sg_gui_fg;
3157 else
3158 color = HL_TABLE()[id - 1].sg_gui_bg;
3159
3160 if (color == INVALCOLOR)
3161 return 0L;
3162
3163 return GUI_MCH_GET_RGB(color);
3164}
3165#endif
3166
3167/*
3168 * Output the syntax list header.
3169 * Return TRUE when started a new line.
3170 */
3171 int
3172syn_list_header(
3173 int did_header, // did header already
3174 int outlen, // length of string that comes
3175 int id) // highlight group id
3176{
3177 int endcol = 19;
3178 int newline = TRUE;
3179 int name_col = 0;
3180
3181 if (!did_header)
3182 {
3183 msg_putchar('\n');
3184 if (got_int)
3185 return TRUE;
3186 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3187 name_col = msg_col;
3188 endcol = 15;
3189 }
3190 else if (msg_col + outlen + 1 >= Columns)
3191 {
3192 msg_putchar('\n');
3193 if (got_int)
3194 return TRUE;
3195 }
3196 else
3197 {
3198 if (msg_col >= endcol) // wrap around is like starting a new line
3199 newline = FALSE;
3200 }
3201
3202 if (msg_col >= endcol) // output at least one space
3203 endcol = msg_col + 1;
3204 if (Columns <= endcol) // avoid hang for tiny window
3205 endcol = Columns - 1;
3206
3207 msg_advance(endcol);
3208
3209 // Show "xxx" with the attributes.
3210 if (!did_header)
3211 {
3212 if (endcol == Columns - 1 && endcol <= name_col)
3213 msg_putchar(' ');
3214 msg_puts_attr("xxx", syn_id2attr(id));
3215 msg_putchar(' ');
3216 }
3217
3218 return newline;
3219}
3220
3221/*
3222 * Set the attribute numbers for a highlight group.
3223 * Called after one of the attributes has changed.
3224 */
3225 static void
3226set_hl_attr(
3227 int idx) // index in array
3228{
3229 attrentry_T at_en;
3230 hl_group_T *sgp = HL_TABLE() + idx;
3231
3232 // The "Normal" group doesn't need an attribute number
3233 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3234 return;
3235
3236#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003237 // For the GUI mode: If there are other than "normal" highlighting
3238 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003239 if (sgp->sg_gui_fg == INVALCOLOR
3240 && sgp->sg_gui_bg == INVALCOLOR
3241 && sgp->sg_gui_sp == INVALCOLOR
3242 && sgp->sg_font == NOFONT
3243# ifdef FEAT_XFONTSET
3244 && sgp->sg_fontset == NOFONTSET
3245# endif
3246 )
3247 {
3248 sgp->sg_gui_attr = sgp->sg_gui;
3249 }
3250 else
3251 {
3252 at_en.ae_attr = sgp->sg_gui;
3253 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3254 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3255 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3256 at_en.ae_u.gui.font = sgp->sg_font;
3257# ifdef FEAT_XFONTSET
3258 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3259# endif
3260 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3261 }
3262#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003263 // For the term mode: If there are other than "normal" highlighting
3264 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003265 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3266 sgp->sg_term_attr = sgp->sg_term;
3267 else
3268 {
3269 at_en.ae_attr = sgp->sg_term;
3270 at_en.ae_u.term.start = sgp->sg_start;
3271 at_en.ae_u.term.stop = sgp->sg_stop;
3272 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3273 }
3274
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003275 // For the color term mode: If there are other than "normal"
3276 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003277 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003278# ifdef FEAT_TERMGUICOLORS
3279 && sgp->sg_gui_fg == INVALCOLOR
3280 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003281 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003282# endif
3283 )
3284 sgp->sg_cterm_attr = sgp->sg_cterm;
3285 else
3286 {
3287 at_en.ae_attr = sgp->sg_cterm;
3288 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3289 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003290 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003291# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003292 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3293 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003294 // Only use the underline/undercurl color when used, it may clear the
3295 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003296 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3297 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003298 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3299 else
3300 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003301 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3302 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3303 {
3304 // If both fg and bg are invalid fall back to the cterm colors.
3305 // Helps when the GUI only uses an attribute, e.g. undercurl.
3306 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3307 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3308 }
3309# endif
3310 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3311 }
3312}
3313
3314/*
3315 * Lookup a highlight group name and return its ID.
3316 * If it is not found, 0 is returned.
3317 */
3318 int
3319syn_name2id(char_u *name)
3320{
3321 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003322 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003323
3324 // Avoid using stricmp() too much, it's slow on some systems
3325 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3326 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003327 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003328 vim_strup(name_u);
3329 for (i = highlight_ga.ga_len; --i >= 0; )
3330 if (HL_TABLE()[i].sg_name_u != NULL
3331 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3332 break;
3333 return i + 1;
3334}
3335
3336/*
3337 * Lookup a highlight group name and return its attributes.
3338 * Return zero if not found.
3339 */
3340 int
3341syn_name2attr(char_u *name)
3342{
3343 int id = syn_name2id(name);
3344
3345 if (id != 0)
3346 return syn_id2attr(id);
3347 return 0;
3348}
3349
3350#if defined(FEAT_EVAL) || defined(PROTO)
3351/*
3352 * Return TRUE if highlight group "name" exists.
3353 */
3354 int
3355highlight_exists(char_u *name)
3356{
3357 return (syn_name2id(name) > 0);
3358}
3359
3360# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3361/*
3362 * Return the name of highlight group "id".
3363 * When not a valid ID return an empty string.
3364 */
3365 char_u *
3366syn_id2name(int id)
3367{
3368 if (id <= 0 || id > highlight_ga.ga_len)
3369 return (char_u *)"";
3370 return HL_TABLE()[id - 1].sg_name;
3371}
3372# endif
3373#endif
3374
3375/*
3376 * Like syn_name2id(), but take a pointer + length argument.
3377 */
3378 int
3379syn_namen2id(char_u *linep, int len)
3380{
3381 char_u *name;
3382 int id = 0;
3383
3384 name = vim_strnsave(linep, len);
3385 if (name != NULL)
3386 {
3387 id = syn_name2id(name);
3388 vim_free(name);
3389 }
3390 return id;
3391}
3392
3393/*
3394 * Find highlight group name in the table and return its ID.
3395 * The argument is a pointer to the name and the length of the name.
3396 * If it doesn't exist yet, a new entry is created.
3397 * Return 0 for failure.
3398 */
3399 int
3400syn_check_group(char_u *pp, int len)
3401{
3402 int id;
3403 char_u *name;
3404
erw7f7f7aaf2021-12-07 21:29:20 +00003405 if (len > MAX_SYN_NAME)
3406 {
3407 emsg(_(e_highlight_group_name_too_long));
3408 return 0;
3409 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003410 name = vim_strnsave(pp, len);
3411 if (name == NULL)
3412 return 0;
3413
3414 id = syn_name2id(name);
3415 if (id == 0) // doesn't exist yet
3416 id = syn_add_group(name);
3417 else
3418 vim_free(name);
3419 return id;
3420}
3421
3422/*
3423 * Add new highlight group and return its ID.
3424 * "name" must be an allocated string, it will be consumed.
3425 * Return 0 for failure.
3426 */
3427 static int
3428syn_add_group(char_u *name)
3429{
3430 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003431 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003432
3433 // Check that the name is ASCII letters, digits and underscore.
3434 for (p = name; *p != NUL; ++p)
3435 {
3436 if (!vim_isprintc(*p))
3437 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003438 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003439 vim_free(name);
3440 return 0;
3441 }
3442 else if (!ASCII_ISALNUM(*p) && *p != '_')
3443 {
3444 // This is an error, but since there previously was no check only
3445 // give a warning.
3446 msg_source(HL_ATTR(HLF_W));
3447 msg(_("W18: Invalid character in group name"));
3448 break;
3449 }
3450 }
3451
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003452 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003453 if (highlight_ga.ga_data == NULL)
3454 {
3455 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3456 highlight_ga.ga_growsize = 10;
3457 }
3458
3459 if (highlight_ga.ga_len >= MAX_HL_ID)
3460 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003461 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003462 vim_free(name);
3463 return 0;
3464 }
3465
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003466 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003467 if (ga_grow(&highlight_ga, 1) == FAIL)
3468 {
3469 vim_free(name);
3470 return 0;
3471 }
3472
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003473 name_up = vim_strsave_up(name);
3474 if (name_up == NULL)
3475 {
3476 vim_free(name);
3477 return 0;
3478 }
3479
Bram Moolenaara80faa82020-04-12 19:37:17 +02003480 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003481 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003482 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003483#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3484 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3485 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003486 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003487#endif
3488 ++highlight_ga.ga_len;
3489
3490 return highlight_ga.ga_len; // ID is index plus one
3491}
3492
3493/*
3494 * When, just after calling syn_add_group(), an error is discovered, this
3495 * function deletes the new name.
3496 */
3497 static void
3498syn_unadd_group(void)
3499{
3500 --highlight_ga.ga_len;
3501 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3502 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3503}
3504
3505/*
3506 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003507 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003508 */
3509 int
3510syn_id2attr(int hl_id)
3511{
3512 int attr;
3513 hl_group_T *sgp;
3514
3515 hl_id = syn_get_final_id(hl_id);
3516 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3517
3518#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003519 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003520 if (gui.in_use)
3521 attr = sgp->sg_gui_attr;
3522 else
3523#endif
3524 if (IS_CTERM)
3525 attr = sgp->sg_cterm_attr;
3526 else
3527 attr = sgp->sg_term_attr;
3528
3529 return attr;
3530}
3531
3532#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3533/*
3534 * Get the GUI colors and attributes for a group ID.
3535 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3536 */
3537 int
3538syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3539{
3540 hl_group_T *sgp;
3541
3542 hl_id = syn_get_final_id(hl_id);
3543 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3544
3545 *fgp = sgp->sg_gui_fg;
3546 *bgp = sgp->sg_gui_bg;
3547 return sgp->sg_gui;
3548}
3549#endif
3550
3551#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003552 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3553 && defined(FEAT_TERMGUICOLORS)) \
3554 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003555 void
3556syn_id2cterm_bg(int hl_id, int *fgp, int *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 *fgp = sgp->sg_cterm_fg - 1;
3563 *bgp = sgp->sg_cterm_bg - 1;
3564}
3565#endif
3566
3567/*
3568 * Translate a group ID to the final group ID (following links).
3569 */
3570 int
3571syn_get_final_id(int hl_id)
3572{
3573 int count;
3574 hl_group_T *sgp;
3575
3576 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3577 return 0; // Can be called from eval!!
3578
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003579 // Follow links until there is no more.
3580 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003581 for (count = 100; --count >= 0; )
3582 {
3583 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3584 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3585 break;
3586 hl_id = sgp->sg_link;
3587 }
3588
3589 return hl_id;
3590}
3591
3592#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3593/*
3594 * Call this function just after the GUI has started.
3595 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3596 * It finds the font and color handles for the highlighting groups.
3597 */
3598 void
3599highlight_gui_started(void)
3600{
3601 int idx;
3602
3603 // First get the colors from the "Normal" and "Menu" group, if set
3604 if (USE_24BIT)
3605 set_normal_colors();
3606
3607 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3608 gui_do_one_color(idx, FALSE, FALSE);
3609
3610 highlight_changed();
3611}
3612
3613 static void
3614gui_do_one_color(
3615 int idx,
3616 int do_menu UNUSED, // TRUE: might set the menu font
3617 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3618{
3619 int didit = FALSE;
3620
3621# ifdef FEAT_GUI
3622# ifdef FEAT_TERMGUICOLORS
3623 if (gui.in_use)
3624# endif
3625 if (HL_TABLE()[idx].sg_font_name != NULL)
3626 {
3627 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3628 do_tooltip, TRUE);
3629 didit = TRUE;
3630 }
3631# endif
3632 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3633 {
3634 HL_TABLE()[idx].sg_gui_fg =
3635 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3636 didit = TRUE;
3637 }
3638 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3639 {
3640 HL_TABLE()[idx].sg_gui_bg =
3641 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3642 didit = TRUE;
3643 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003644 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3645 {
3646 HL_TABLE()[idx].sg_gui_sp =
3647 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3648 didit = TRUE;
3649 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003650 if (didit) // need to get a new attr number
3651 set_hl_attr(idx);
3652}
3653#endif
3654
3655#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3656/*
3657 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3658 */
3659 static void
3660combine_stl_hlt(
3661 int id,
3662 int id_S,
3663 int id_alt,
3664 int hlcnt,
3665 int i,
3666 int hlf,
3667 int *table)
3668{
3669 hl_group_T *hlt = HL_TABLE();
3670
3671 if (id_alt == 0)
3672 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003673 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003674 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3675 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3676# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3677 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3678# endif
3679 }
3680 else
3681 mch_memmove(&hlt[hlcnt + i],
3682 &hlt[id_alt - 1],
3683 sizeof(hl_group_T));
3684 hlt[hlcnt + i].sg_link = 0;
3685
3686 hlt[hlcnt + i].sg_term ^=
3687 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3688 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3689 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3690 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3691 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3692 hlt[hlcnt + i].sg_cterm ^=
3693 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3694 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3695 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3696 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3697 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3698# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3699 hlt[hlcnt + i].sg_gui ^=
3700 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3701# endif
3702# ifdef FEAT_GUI
3703 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3704 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3705 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3706 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3707 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3708 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3709 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3710 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3711# ifdef FEAT_XFONTSET
3712 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3713 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3714# endif
3715# endif
3716 highlight_ga.ga_len = hlcnt + i + 1;
3717 set_hl_attr(hlcnt + i); // At long last we can apply
3718 table[i] = syn_id2attr(hlcnt + i + 1);
3719}
3720#endif
3721
3722/*
3723 * Translate the 'highlight' option into attributes in highlight_attr[] and
3724 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3725 * corresponding highlights to use on top of HLF_SNC is computed.
3726 * Called only when the 'highlight' option has been changed and upon first
3727 * screen redraw after any :highlight command.
3728 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3729 */
3730 int
3731highlight_changed(void)
3732{
3733 int hlf;
3734 int i;
3735 char_u *p;
3736 int attr;
3737 char_u *end;
3738 int id;
3739#ifdef USER_HIGHLIGHT
3740 char_u userhl[30]; // use 30 to avoid compiler warning
3741# ifdef FEAT_STL_OPT
3742 int id_S = -1;
3743 int id_SNC = 0;
3744# ifdef FEAT_TERMINAL
3745 int id_ST = 0;
3746 int id_STNC = 0;
3747# endif
3748 int hlcnt;
3749# endif
3750#endif
3751 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3752
3753 need_highlight_changed = FALSE;
3754
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003755#ifdef FEAT_TERMINAL
3756 term_update_colors_all();
3757 term_update_wincolor_all();
3758#endif
3759
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003760 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003761 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3762 highlight_attr[hlf] = 0;
3763
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003764 // First set all attributes to their default value.
3765 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003766 for (i = 0; i < 2; ++i)
3767 {
3768 if (i)
3769 p = p_hl;
3770 else
3771 p = get_highlight_default();
3772 if (p == NULL) // just in case
3773 continue;
3774
3775 while (*p)
3776 {
3777 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3778 if (hl_flags[hlf] == *p)
3779 break;
3780 ++p;
3781 if (hlf == (int)HLF_COUNT || *p == NUL)
3782 return FAIL;
3783
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003784 // Allow several hl_flags to be combined, like "bu" for
3785 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003786 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003787 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003788 {
3789 if (VIM_ISWHITE(*p)) // ignore white space
3790 continue;
3791
3792 if (attr > HL_ALL) // Combination with ':' is not allowed.
3793 return FAIL;
3794
3795 switch (*p)
3796 {
3797 case 'b': attr |= HL_BOLD;
3798 break;
3799 case 'i': attr |= HL_ITALIC;
3800 break;
3801 case '-':
3802 case 'n': // no highlighting
3803 break;
3804 case 'r': attr |= HL_INVERSE;
3805 break;
3806 case 's': attr |= HL_STANDOUT;
3807 break;
3808 case 'u': attr |= HL_UNDERLINE;
3809 break;
3810 case 'c': attr |= HL_UNDERCURL;
3811 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003812 case '2': attr |= HL_UNDERDOUBLE;
3813 break;
3814 case 'd': attr |= HL_UNDERDOTTED;
3815 break;
3816 case '=': attr |= HL_UNDERDASHED;
3817 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003818 case 't': attr |= HL_STRIKETHROUGH;
3819 break;
3820 case ':': ++p; // highlight group name
3821 if (attr || *p == NUL) // no combinations
3822 return FAIL;
3823 end = vim_strchr(p, ',');
3824 if (end == NULL)
3825 end = p + STRLEN(p);
3826 id = syn_check_group(p, (int)(end - p));
3827 if (id == 0)
3828 return FAIL;
3829 attr = syn_id2attr(id);
3830 p = end - 1;
3831#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3832 if (hlf == (int)HLF_SNC)
3833 id_SNC = syn_get_final_id(id);
3834# ifdef FEAT_TERMINAL
3835 else if (hlf == (int)HLF_ST)
3836 id_ST = syn_get_final_id(id);
3837 else if (hlf == (int)HLF_STNC)
3838 id_STNC = syn_get_final_id(id);
3839# endif
3840 else if (hlf == (int)HLF_S)
3841 id_S = syn_get_final_id(id);
3842#endif
3843 break;
3844 default: return FAIL;
3845 }
3846 }
3847 highlight_attr[hlf] = attr;
3848
3849 p = skip_to_option_part(p); // skip comma and spaces
3850 }
3851 }
3852
3853#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003854 // Setup the user highlights
3855 //
3856 // Temporarily utilize 28 more hl entries:
3857 // 9 for User1-User9 combined with StatusLineNC
3858 // 9 for User1-User9 combined with StatusLineTerm
3859 // 9 for User1-User9 combined with StatusLineTermNC
3860 // 1 for StatusLine default
3861 // Have to be in there simultaneously in case of table overflows in
3862 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003863# ifdef FEAT_STL_OPT
3864 if (ga_grow(&highlight_ga, 28) == FAIL)
3865 return FAIL;
3866 hlcnt = highlight_ga.ga_len;
3867 if (id_S == -1)
3868 {
3869 // Make sure id_S is always valid to simplify code below. Use the last
3870 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003871 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003872 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3873 id_S = hlcnt + 19;
3874 }
3875# endif
3876 for (i = 0; i < 9; i++)
3877 {
3878 sprintf((char *)userhl, "User%d", i + 1);
3879 id = syn_name2id(userhl);
3880 if (id == 0)
3881 {
3882 highlight_user[i] = 0;
3883# ifdef FEAT_STL_OPT
3884 highlight_stlnc[i] = 0;
3885# ifdef FEAT_TERMINAL
3886 highlight_stlterm[i] = 0;
3887 highlight_stltermnc[i] = 0;
3888# endif
3889# endif
3890 }
3891 else
3892 {
3893 highlight_user[i] = syn_id2attr(id);
3894# ifdef FEAT_STL_OPT
3895 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3896 HLF_SNC, highlight_stlnc);
3897# ifdef FEAT_TERMINAL
3898 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3899 HLF_ST, highlight_stlterm);
3900 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3901 HLF_STNC, highlight_stltermnc);
3902# endif
3903# endif
3904 }
3905 }
3906# ifdef FEAT_STL_OPT
3907 highlight_ga.ga_len = hlcnt;
3908# endif
3909
3910#endif // USER_HIGHLIGHT
3911
3912 return OK;
3913}
3914
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003915static void highlight_list(void);
3916static void highlight_list_two(int cnt, int attr);
3917
3918/*
3919 * Handle command line completion for :highlight command.
3920 */
3921 void
3922set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3923{
3924 char_u *p;
3925
3926 // Default: expand group names
3927 xp->xp_context = EXPAND_HIGHLIGHT;
3928 xp->xp_pattern = arg;
3929 include_link = 2;
3930 include_default = 1;
3931
3932 // (part of) subcommand already typed
3933 if (*arg != NUL)
3934 {
3935 p = skiptowhite(arg);
3936 if (*p != NUL) // past "default" or group name
3937 {
3938 include_default = 0;
3939 if (STRNCMP("default", arg, p - arg) == 0)
3940 {
3941 arg = skipwhite(p);
3942 xp->xp_pattern = arg;
3943 p = skiptowhite(arg);
3944 }
3945 if (*p != NUL) // past group name
3946 {
3947 include_link = 0;
3948 if (arg[1] == 'i' && arg[0] == 'N')
3949 highlight_list();
3950 if (STRNCMP("link", arg, p - arg) == 0
3951 || STRNCMP("clear", arg, p - arg) == 0)
3952 {
3953 xp->xp_pattern = skipwhite(p);
3954 p = skiptowhite(xp->xp_pattern);
3955 if (*p != NUL) // past first group name
3956 {
3957 xp->xp_pattern = skipwhite(p);
3958 p = skiptowhite(xp->xp_pattern);
3959 }
3960 }
3961 if (*p != NUL) // past group name(s)
3962 xp->xp_context = EXPAND_NOTHING;
3963 }
3964 }
3965 }
3966}
3967
3968/*
3969 * List highlighting matches in a nice way.
3970 */
3971 static void
3972highlight_list(void)
3973{
3974 int i;
3975
3976 for (i = 10; --i >= 0; )
3977 highlight_list_two(i, HL_ATTR(HLF_D));
3978 for (i = 40; --i >= 0; )
3979 highlight_list_two(99, 0);
3980}
3981
3982 static void
3983highlight_list_two(int cnt, int attr)
3984{
3985 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3986 msg_clr_eos();
3987 out_flush();
3988 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3989}
3990
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003991/*
3992 * Function given to ExpandGeneric() to obtain the list of group names.
3993 */
3994 char_u *
3995get_highlight_name(expand_T *xp UNUSED, int idx)
3996{
3997 return get_highlight_name_ext(xp, idx, TRUE);
3998}
3999
4000/*
4001 * Obtain a highlight group name.
4002 * When "skip_cleared" is TRUE don't return a cleared entry.
4003 */
4004 char_u *
4005get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4006{
4007 if (idx < 0)
4008 return NULL;
4009
4010 // Items are never removed from the table, skip the ones that were
4011 // cleared.
4012 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4013 return (char_u *)"";
4014
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004015 if (idx == highlight_ga.ga_len && include_none != 0)
4016 return (char_u *)"none";
4017 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4018 return (char_u *)"default";
4019 if (idx == highlight_ga.ga_len + include_none + include_default
4020 && include_link != 0)
4021 return (char_u *)"link";
4022 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4023 && include_link != 0)
4024 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004025 if (idx >= highlight_ga.ga_len)
4026 return NULL;
4027 return HL_TABLE()[idx].sg_name;
4028}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004029
4030#if defined(FEAT_GUI) || defined(PROTO)
4031/*
4032 * Free all the highlight group fonts.
4033 * Used when quitting for systems which need it.
4034 */
4035 void
4036free_highlight_fonts(void)
4037{
4038 int idx;
4039
4040 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4041 {
4042 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4043 HL_TABLE()[idx].sg_font = NOFONT;
4044# ifdef FEAT_XFONTSET
4045 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4046 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4047# endif
4048 }
4049
4050 gui_mch_free_font(gui.norm_font);
4051# ifdef FEAT_XFONTSET
4052 gui_mch_free_fontset(gui.fontset);
4053# endif
4054# ifndef FEAT_GUI_GTK
4055 gui_mch_free_font(gui.bold_font);
4056 gui_mch_free_font(gui.ital_font);
4057 gui_mch_free_font(gui.boldital_font);
4058# endif
4059}
4060#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004061
4062#if defined(FEAT_EVAL) || defined(PROTO)
4063/*
4064 * Convert each of the highlight attribute bits (bold, standout, underline,
4065 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4066 * the attribute name as the key.
4067 */
4068 static dict_T *
4069highlight_get_attr_dict(int hlattr)
4070{
4071 dict_T *dict;
4072 int i;
4073
4074 dict = dict_alloc();
4075 if (dict == NULL)
4076 return NULL;
4077
4078 for (i = 0; hl_attr_table[i] != 0; ++i)
4079 {
4080 if (hlattr & hl_attr_table[i])
4081 {
4082 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4083 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4084 }
4085 }
4086
4087 return dict;
4088}
4089
4090/*
4091 * Return the attributes of the highlight group at index 'hl_idx' as a
4092 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4093 * links recursively.
4094 */
4095 static dict_T *
4096highlight_get_info(int hl_idx, int resolve_link)
4097{
4098 dict_T *dict;
4099 hl_group_T *sgp;
4100 dict_T *attr_dict;
4101 int hlgid;
4102
4103 dict = dict_alloc();
4104 if (dict == NULL)
4105 return dict;
4106
4107 sgp = &HL_TABLE()[hl_idx];
4108 // highlight group id is 1-based
4109 hlgid = hl_idx + 1;
4110
4111 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4112 goto error;
4113 if (dict_add_number(dict, "id", hlgid) == FAIL)
4114 goto error;
4115
4116 if (sgp->sg_link && resolve_link)
4117 {
4118 // resolve the highlight group link recursively
4119 while (sgp->sg_link)
4120 {
4121 hlgid = sgp->sg_link;
4122 sgp = &HL_TABLE()[sgp->sg_link - 1];
4123 }
4124 }
4125
4126 if (sgp->sg_term != 0)
4127 {
4128 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4129 if (attr_dict != NULL)
4130 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4131 goto error;
4132 }
4133 if (sgp->sg_start != NULL)
4134 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4135 goto error;
4136 if (sgp->sg_stop != NULL)
4137 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4138 goto error;
4139 if (sgp->sg_cterm != 0)
4140 {
4141 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4142 if (attr_dict != NULL)
4143 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4144 goto error;
4145 }
4146 if (sgp->sg_cterm_fg != 0)
4147 if (dict_add_string(dict, "ctermfg",
4148 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4149 goto error;
4150 if (sgp->sg_cterm_bg != 0)
4151 if (dict_add_string(dict, "ctermbg",
4152 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4153 goto error;
4154 if (sgp->sg_cterm_ul != 0)
4155 if (dict_add_string(dict, "ctermul",
4156 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4157 goto error;
4158 if (sgp->sg_gui != 0)
4159 {
4160 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4161 if (attr_dict != NULL)
4162 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4163 goto error;
4164 }
4165 if (sgp->sg_gui_fg_name != NULL)
4166 if (dict_add_string(dict, "guifg",
4167 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4168 goto error;
4169 if (sgp->sg_gui_bg_name != NULL)
4170 if (dict_add_string(dict, "guibg",
4171 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4172 goto error;
4173 if (sgp->sg_gui_sp_name != NULL)
4174 if (dict_add_string(dict, "guisp",
4175 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4176 goto error;
4177# ifdef FEAT_GUI
4178 if (sgp->sg_font_name != NULL)
4179 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4180 goto error;
4181# endif
4182 if (sgp->sg_link)
4183 {
4184 char_u *link;
4185
4186 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4187 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4188 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004189
4190 if (sgp->sg_deflink)
4191 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004192 }
4193 if (dict_len(dict) == 2)
4194 // If only 'name' is present, then the highlight group is cleared.
4195 dict_add_bool(dict, "cleared", VVAL_TRUE);
4196
4197 return dict;
4198
4199error:
4200 vim_free(dict);
4201 return NULL;
4202}
4203
4204/*
4205 * "hlget([name])" function
4206 * Return the attributes of a specific highlight group (if specified) or all
4207 * the highlight groups.
4208 */
4209 void
4210f_hlget(typval_T *argvars, typval_T *rettv)
4211{
4212 list_T *list;
4213 dict_T *dict;
4214 int i;
4215 char_u *hlarg = NULL;
4216 int resolve_link = FALSE;
4217
4218 if (rettv_list_alloc(rettv) == FAIL)
4219 return;
4220
4221 if (check_for_opt_string_arg(argvars, 0) == FAIL
4222 || (argvars[0].v_type != VAR_UNKNOWN
4223 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4224 return;
4225
4226 if (argvars[0].v_type != VAR_UNKNOWN)
4227 {
4228 // highlight group name supplied
4229 hlarg = tv_get_string_chk(&argvars[0]);
4230 if (hlarg == NULL)
4231 return;
4232
4233 if (argvars[1].v_type != VAR_UNKNOWN)
4234 {
4235 int error = FALSE;
4236
4237 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4238 if (error)
4239 return;
4240 }
4241 }
4242
4243 list = rettv->vval.v_list;
4244 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4245 {
4246 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4247 {
4248 dict = highlight_get_info(i, resolve_link);
4249 if (dict != NULL)
4250 list_append_dict(list, dict);
4251 }
4252 }
4253}
4254
4255/*
4256 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4257 * 'dict' or the value is not a string type. If the value is not a string type
4258 * or is NULL, then 'error' is set to TRUE.
4259 */
4260 static char_u *
4261hldict_get_string(dict_T *dict, char_u *key, int *error)
4262{
4263 dictitem_T *di;
4264
4265 *error = FALSE;
4266 di = dict_find(dict, key, -1);
4267 if (di == NULL)
4268 return NULL;
4269
4270 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4271 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004272 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004273 *error = TRUE;
4274 return NULL;
4275 }
4276
4277 return di->di_tv.vval.v_string;
4278}
4279
4280/*
4281 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4282 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4283 * Dictionary or is NULL.
4284 */
4285 static int
4286hldict_attr_to_str(
4287 dict_T *dict,
4288 char_u *key,
4289 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004290 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004291{
4292 dictitem_T *di;
4293 dict_T *attrdict;
4294 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004295 char_u *p;
4296 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004297
4298 attr_str[0] = NUL;
4299 di = dict_find(dict, key, -1);
4300 if (di == NULL)
4301 return TRUE;
4302
4303 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4304 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004305 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004306 return FALSE;
4307 }
4308
4309 attrdict = di->di_tv.vval.v_dict;
4310
4311 // If the attribute dict is empty, then return NONE to clear the attributes
4312 if (dict_len(attrdict) == 0)
4313 {
4314 vim_strcat(attr_str, (char_u *)"NONE", len);
4315 return TRUE;
4316 }
4317
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004318 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004319 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4320 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01004321 if (dict_get_bool(attrdict, hl_name_table[i], VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004322 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004323 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4324 STRCPY(p, (char_u *)",");
4325 sz = STRLEN(hl_name_table[i]);
4326 if (p - attr_str + sz + 1 < len)
4327 {
4328 STRCPY(p, (char_u *)hl_name_table[i]);
4329 p += sz;
4330 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004331 }
4332 }
4333
4334 return TRUE;
4335}
4336
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004337// Temporary buffer used to store the command string produced by hlset().
4338// IObuff cannot be used for this as the error messages produced by hlset()
4339// internally use IObuff.
4340#define HLSETBUFSZ 512
4341static char_u hlsetBuf[HLSETBUFSZ + 1];
4342
4343/*
4344 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4345 * "dptr", which points into "hlsetBuf".
4346 * Returns the updated pointer.
4347 */
4348 static char_u *
4349add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4350{
4351 size_t vallen;
4352
4353 // Do nothing if the value is not specified or is empty
4354 if (value == NULL || *value == NUL)
4355 return dptr;
4356
4357 vallen = STRLEN(value);
4358 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4359 {
4360 STRCPY(dptr, attr);
4361 dptr += attrlen;
4362 STRCPY(dptr, value);
4363 dptr += vallen;
4364 }
4365
4366 return dptr;
4367}
4368
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004369/*
4370 * Add or update a highlight group using 'dict' items. Returns TRUE if
4371 * successfully updated the highlight group.
4372 */
4373 static int
4374hlg_add_or_update(dict_T *dict)
4375{
4376 char_u *name;
4377 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004378 char_u term_attr[MAX_ATTR_LEN];
4379 char_u cterm_attr[MAX_ATTR_LEN];
4380 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004381 char_u *start;
4382 char_u *stop;
4383 char_u *ctermfg;
4384 char_u *ctermbg;
4385 char_u *ctermul;
4386 char_u *guifg;
4387 char_u *guibg;
4388 char_u *guisp;
4389# ifdef FEAT_GUI
4390 char_u *font;
4391# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004392 int forceit = FALSE;
4393 int dodefault = FALSE;
4394 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004395 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004396
4397 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004398 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004399 return FALSE;
4400
Bram Moolenaard61efa52022-07-23 09:52:04 +01004401 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004402 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004403
Bram Moolenaard61efa52022-07-23 09:52:04 +01004404 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004405 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004406
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004407 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004408 {
4409 varnumber_T cleared;
4410
4411 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004412 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004413 if (cleared == TRUE)
4414 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004415 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4416 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004417 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004418 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004419 }
4420
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004421 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004422 {
4423 char_u *linksto;
4424
4425 // link highlight groups
4426 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004427 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004428 return FALSE;
4429
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004430 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004431 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004432 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004433
4434 done = TRUE;
4435 }
4436
4437 // If 'cleared' or 'linksto' are specified, then don't process the other
4438 // attributes.
4439 if (done)
4440 return TRUE;
4441
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004442 start = hldict_get_string(dict, (char_u *)"start", &error);
4443 if (error)
4444 return FALSE;
4445
4446 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4447 if (error)
4448 return FALSE;
4449
4450 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004451 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004452 return FALSE;
4453
4454 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004455 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004456 return FALSE;
4457
4458 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4459 if (error)
4460 return FALSE;
4461
4462 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4463 if (error)
4464 return FALSE;
4465
4466 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4467 if (error)
4468 return FALSE;
4469
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004470 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004471 return FALSE;
4472
4473 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4474 if (error)
4475 return FALSE;
4476
4477 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4478 if (error)
4479 return FALSE;
4480
4481 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4482 if (error)
4483 return FALSE;
4484
4485# ifdef FEAT_GUI
4486 font = hldict_get_string(dict, (char_u *)"font", &error);
4487 if (error)
4488 return FALSE;
4489# endif
4490
4491 // If none of the attributes are specified, then do nothing.
4492 if (term_attr[0] == NUL && start == NULL && stop == NULL
4493 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4494 && ctermul == NULL && gui_attr[0] == NUL
4495# ifdef FEAT_GUI
4496 && font == NULL
4497# endif
4498 && guifg == NULL && guibg == NULL && guisp == NULL
4499 )
4500 return TRUE;
4501
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004502 hlsetBuf[0] = NUL;
4503 p = hlsetBuf;
4504 if (dodefault)
4505 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4506 p = add_attr_and_value(p, (char_u *)"", 0, name);
4507 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4508 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4509 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4510 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4511 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4512 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4513 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4514 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004515# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004516 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004517# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004518 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4519 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
4520 p = add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004521
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004522 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004523
4524 return TRUE;
4525}
4526
4527/*
4528 * "hlset([{highlight_attr}])" function
4529 * Add or modify highlight groups
4530 */
4531 void
4532f_hlset(typval_T *argvars, typval_T *rettv)
4533{
4534 listitem_T *li;
4535 dict_T *dict;
4536
4537 rettv->vval.v_number = -1;
4538
4539 if (check_for_list_arg(argvars, 0) == FAIL)
4540 return;
4541
4542 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4543 {
4544 if (li->li_tv.v_type != VAR_DICT)
4545 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004546 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004547 return;
4548 }
4549
4550 dict = li->li_tv.vval.v_dict;
4551 if (!hlg_add_or_update(dict))
4552 return;
4553 }
4554
4555 rettv->vval.v_number = 0;
4556}
4557#endif