blob: 724c4ba8d7b8a96c3b964b0428b7f6dde2491328 [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[]) =
28 {"bold", "standout", "underline", "undercurl",
29 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
30static int hl_attr_table[] =
31 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
kylo252ae6f1d82022-02-16 19:24:07 +000032#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? (attr_b) : (attr_a)) | (attr_b))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020033
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020034/*
35 * Structure that stores information about a highlight group.
36 * The ID of a highlight group is also called group ID. It is the index in
37 * the highlight_ga array PLUS ONE.
38 */
39typedef struct
40{
41 char_u *sg_name; // highlight group name
42 char_u *sg_name_u; // uppercase of sg_name
43 int sg_cleared; // "hi clear" was used
44// for normal terminals
45 int sg_term; // "term=" highlighting attributes
46 char_u *sg_start; // terminal string for start highl
47 char_u *sg_stop; // terminal string for stop highl
48 int sg_term_attr; // Screen attr for term mode
49// for color terminals
50 int sg_cterm; // "cterm=" highlighting attr
51 int sg_cterm_bold; // bold attr was set for light color
52 int sg_cterm_fg; // terminal fg color number + 1
53 int sg_cterm_bg; // terminal bg color number + 1
Bram Moolenaare023e882020-05-31 16:42:30 +020054 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020055 int sg_cterm_attr; // Screen attr for color term mode
56// for when using the GUI
57#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
58 guicolor_T sg_gui_fg; // GUI foreground color handle
59 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020060 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020061#endif
62#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020063 GuiFont sg_font; // GUI font handle
64#ifdef FEAT_XFONTSET
65 GuiFontset sg_fontset; // GUI fontset handle
66#endif
67 char_u *sg_font_name; // GUI font or fontset name
68 int sg_gui_attr; // Screen attr for GUI mode
69#endif
70#if defined(FEAT_GUI) || defined(FEAT_EVAL)
71// Store the sp color name for the GUI or synIDattr()
72 int sg_gui; // "gui=" highlighting attributes
73 char_u *sg_gui_fg_name;// GUI foreground color name
74 char_u *sg_gui_bg_name;// GUI background color name
75 char_u *sg_gui_sp_name;// GUI special color name
76#endif
77 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +020078 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020079 int sg_set; // combination of SG_* flags
80#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +020081 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020082 sctx_T sg_script_ctx; // script in which the group was last set
83#endif
84} hl_group_T;
85
86// highlight groups for 'highlight' option
87static garray_T highlight_ga;
88#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
89
90/*
91 * An attribute number is the index in attr_table plus ATTR_OFF.
92 */
93#define ATTR_OFF (HL_ALL + 1)
94
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020095static void syn_unadd_group(void);
96static void set_hl_attr(int idx);
97static void highlight_list_one(int id);
98static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
99static int syn_add_group(char_u *name);
100static int hl_has_settings(int idx, int check_link);
101static void highlight_clear(int idx);
102
103#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
104static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
105#endif
106#ifdef FEAT_GUI
107static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
108static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
109#endif
110
111/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200112 * The default highlight groups. These are compiled-in for fast startup and
113 * they still work when the runtime files can't be found.
114 * When making changes here, also change runtime/colors/default.vim!
115 * The #ifdefs are needed to reduce the amount of static data. Helps to make
116 * the 16 bit DOS (museum) version compile.
117 */
118#if defined(FEAT_GUI) || defined(FEAT_EVAL)
119# define CENT(a, b) b
120#else
121# define CENT(a, b) a
122#endif
123static char *(highlight_init_both[]) = {
124 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
125 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
126 CENT("IncSearch term=reverse cterm=reverse",
127 "IncSearch term=reverse cterm=reverse gui=reverse"),
128 CENT("ModeMsg term=bold cterm=bold",
129 "ModeMsg term=bold cterm=bold gui=bold"),
130 CENT("NonText term=bold ctermfg=Blue",
131 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
132 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
133 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
134 CENT("StatusLineNC term=reverse cterm=reverse",
135 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
136 "default link EndOfBuffer NonText",
137 CENT("VertSplit term=reverse cterm=reverse",
138 "VertSplit term=reverse cterm=reverse gui=reverse"),
139#ifdef FEAT_CLIPBOARD
140 CENT("VisualNOS term=underline,bold cterm=underline,bold",
141 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
142#endif
143#ifdef FEAT_DIFF
144 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
145 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
146#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200147 CENT("PmenuSbar ctermbg=Grey",
148 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200149 CENT("TabLineSel term=bold cterm=bold",
150 "TabLineSel term=bold cterm=bold gui=bold"),
151 CENT("TabLineFill term=reverse cterm=reverse",
152 "TabLineFill term=reverse cterm=reverse gui=reverse"),
153#ifdef FEAT_GUI
154 "Cursor guibg=fg guifg=bg",
155 "lCursor guibg=fg guifg=bg", // should be different, but what?
156#endif
157 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000158 "default link CursorLineSign SignColumn",
159 "default link CursorLineFold FoldColumn",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200160 CENT("Normal cterm=NONE", "Normal gui=NONE"),
161 NULL
162};
163
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200164// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200165static char *(highlight_init_light[]) = {
166 CENT("Directory term=bold ctermfg=DarkBlue",
167 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
168 CENT("LineNr term=underline ctermfg=Brown",
169 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200170 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
171 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200172 CENT("MoreMsg term=bold ctermfg=DarkGreen",
173 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
174 CENT("Question term=standout ctermfg=DarkGreen",
175 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
176 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
177 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
178#ifdef FEAT_SPELL
179 CENT("SpellBad term=reverse ctermbg=LightRed",
180 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
181 CENT("SpellCap term=reverse ctermbg=LightBlue",
182 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
183 CENT("SpellRare term=reverse ctermbg=LightMagenta",
184 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
185 CENT("SpellLocal term=underline ctermbg=Cyan",
186 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
187#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200188 CENT("PmenuThumb ctermbg=Black",
189 "PmenuThumb ctermbg=Black guibg=Black"),
190 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
191 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
192 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
193 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200194 CENT("SpecialKey term=bold ctermfg=DarkBlue",
195 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
196 CENT("Title term=bold ctermfg=DarkMagenta",
197 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
198 CENT("WarningMsg term=standout ctermfg=DarkRed",
199 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
200#ifdef FEAT_WILDMENU
201 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
202 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
203#endif
204#ifdef FEAT_FOLDING
205 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
206 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
207 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
208 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
209#endif
210#ifdef FEAT_SIGNS
211 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
212 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
213#endif
214 CENT("Visual term=reverse",
215 "Visual term=reverse guibg=LightGrey"),
216#ifdef FEAT_DIFF
217 CENT("DiffAdd term=bold ctermbg=LightBlue",
218 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
219 CENT("DiffChange term=bold ctermbg=LightMagenta",
220 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
221 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
222 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
223#endif
224 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
225 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
226#ifdef FEAT_SYN_HL
227 CENT("CursorColumn term=reverse ctermbg=LightGrey",
228 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
229 CENT("CursorLine term=underline cterm=underline",
230 "CursorLine term=underline cterm=underline guibg=Grey90"),
231 CENT("ColorColumn term=reverse ctermbg=LightRed",
232 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
233#endif
234#ifdef FEAT_CONCEAL
235 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
236 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
237#endif
238 CENT("MatchParen term=reverse ctermbg=Cyan",
239 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
240#ifdef FEAT_TERMINAL
241 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
242 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
243 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
244 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
245#endif
246#ifdef FEAT_MENU
247 CENT("ToolbarLine term=underline ctermbg=LightGrey",
248 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
249 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
250 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
251#endif
252 NULL
253};
254
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200255// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200256static char *(highlight_init_dark[]) = {
257 CENT("Directory term=bold ctermfg=LightCyan",
258 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
259 CENT("LineNr term=underline ctermfg=Yellow",
260 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200261 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
262 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200263 CENT("MoreMsg term=bold ctermfg=LightGreen",
264 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
265 CENT("Question term=standout ctermfg=LightGreen",
266 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
267 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
268 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
269 CENT("SpecialKey term=bold ctermfg=LightBlue",
270 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
271#ifdef FEAT_SPELL
272 CENT("SpellBad term=reverse ctermbg=Red",
273 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
274 CENT("SpellCap term=reverse ctermbg=Blue",
275 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
276 CENT("SpellRare term=reverse ctermbg=Magenta",
277 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
278 CENT("SpellLocal term=underline ctermbg=Cyan",
279 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
280#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200281 CENT("PmenuThumb ctermbg=White",
282 "PmenuThumb ctermbg=White guibg=White"),
283 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
284 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
285 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
286 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200287 CENT("Title term=bold ctermfg=LightMagenta",
288 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
289 CENT("WarningMsg term=standout ctermfg=LightRed",
290 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
291#ifdef FEAT_WILDMENU
292 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
293 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
294#endif
295#ifdef FEAT_FOLDING
296 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
297 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
298 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
299 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
300#endif
301#ifdef FEAT_SIGNS
302 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
303 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
304#endif
305 CENT("Visual term=reverse",
306 "Visual term=reverse guibg=DarkGrey"),
307#ifdef FEAT_DIFF
308 CENT("DiffAdd term=bold ctermbg=DarkBlue",
309 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
310 CENT("DiffChange term=bold ctermbg=DarkMagenta",
311 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
312 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
313 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
314#endif
315 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
316 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
317#ifdef FEAT_SYN_HL
318 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
319 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
320 CENT("CursorLine term=underline cterm=underline",
321 "CursorLine term=underline cterm=underline guibg=Grey40"),
322 CENT("ColorColumn term=reverse ctermbg=DarkRed",
323 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
324#endif
325 CENT("MatchParen term=reverse ctermbg=DarkCyan",
326 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
327#ifdef FEAT_CONCEAL
328 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
329 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
330#endif
331#ifdef FEAT_TERMINAL
332 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
333 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
334 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
335 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
336#endif
337#ifdef FEAT_MENU
338 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
339 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
340 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
341 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
342#endif
343 NULL
344};
345
Dominique Pelle748b3082022-01-08 12:41:16 +0000346#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200347/*
348 * Returns the number of highlight groups.
349 */
350 int
351highlight_num_groups(void)
352{
353 return highlight_ga.ga_len;
354}
355
356/*
357 * Returns the name of a highlight group.
358 */
359 char_u *
360highlight_group_name(int id)
361{
362 return HL_TABLE()[id].sg_name;
363}
364
365/*
366 * Returns the ID of the link to a highlight group.
367 */
368 int
369highlight_link_id(int id)
370{
371 return HL_TABLE()[id].sg_link;
372}
Dominique Pelle748b3082022-01-08 12:41:16 +0000373#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200374
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200375 void
376init_highlight(
377 int both, // include groups where 'bg' doesn't matter
378 int reset) // clear group first
379{
380 int i;
381 char **pp;
382 static int had_both = FALSE;
383#ifdef FEAT_EVAL
384 char_u *p;
385
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100386 // Try finding the color scheme file. Used when a color file was loaded
387 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200388 p = get_var_value((char_u *)"g:colors_name");
389 if (p != NULL)
390 {
391 // The value of g:colors_name could be freed when sourcing the script,
392 // making "p" invalid, so copy it.
393 char_u *copy_p = vim_strsave(p);
394 int r;
395
396 if (copy_p != NULL)
397 {
398 r = load_colors(copy_p);
399 vim_free(copy_p);
400 if (r == OK)
401 return;
402 }
403 }
404
405#endif
406
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100407 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200408 if (both)
409 {
410 had_both = TRUE;
411 pp = highlight_init_both;
412 for (i = 0; pp[i] != NULL; ++i)
413 do_highlight((char_u *)pp[i], reset, TRUE);
414 }
415 else if (!had_both)
416 // Don't do anything before the call with both == TRUE from main().
417 // Not everything has been setup then, and that call will overrule
418 // everything anyway.
419 return;
420
421 if (*p_bg == 'l')
422 pp = highlight_init_light;
423 else
424 pp = highlight_init_dark;
425 for (i = 0; pp[i] != NULL; ++i)
426 do_highlight((char_u *)pp[i], reset, TRUE);
427
428 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
429 // depend on the number of colors available.
430 // With 8 colors brown is equal to yellow, need to use black for Search fg
431 // to avoid Statement highlighted text disappears.
432 // Clear the attributes, needed when changing the t_Co value.
433 if (t_colors > 8)
434 do_highlight((char_u *)(*p_bg == 'l'
435 ? "Visual cterm=NONE ctermbg=LightGrey"
436 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
437 else
438 {
439 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
440 FALSE, TRUE);
441 if (*p_bg == 'l')
442 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
443 }
444
445#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100446 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200447 if (get_var_value((char_u *)"g:syntax_on") != NULL)
448 {
449 static int recursive = 0;
450
451 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000452 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200453 else
454 {
455 ++recursive;
456 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
457 --recursive;
458 }
459 }
460#endif
461}
462
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000463#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
464/*
465 * Load a default color list. Intended to support legacy color names but allows
466 * the user to override the color values. Only loaded once.
467 */
468 static void
469load_default_colors_lists()
470{
471 // Lacking a default color list isn't the end of the world but it is likely
472 // an inconvenience so users should know when it is missing.
473 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
474 msg("failed to load colors/lists/default.vim");
475}
476#endif
477
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200478/*
479 * Load color file "name".
480 * Return OK for success, FAIL for failure.
481 */
482 int
483load_colors(char_u *name)
484{
485 char_u *buf;
486 int retval = FAIL;
487 static int recursive = FALSE;
488
489 // When being called recursively, this is probably because setting
490 // 'background' caused the highlighting to be reloaded. This means it is
491 // working, thus we should return OK.
492 if (recursive)
493 return OK;
494
495 recursive = TRUE;
496 buf = alloc(STRLEN(name) + 12);
497 if (buf != NULL)
498 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100499#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100500 load_default_colors_lists();
501#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200502 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
503 curbuf->b_fname, FALSE, curbuf);
504 sprintf((char *)buf, "colors/%s.vim", name);
505 retval = source_runtime(buf, DIP_START + DIP_OPT);
506 vim_free(buf);
507 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
508 }
509 recursive = FALSE;
510
511 return retval;
512}
513
514static char *(color_names[28]) = {
515 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
516 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
517 "Gray", "Grey", "LightGray", "LightGrey",
518 "DarkGray", "DarkGrey",
519 "Blue", "LightBlue", "Green", "LightGreen",
520 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
521 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
522 // indices:
523 // 0, 1, 2, 3,
524 // 4, 5, 6, 7,
525 // 8, 9, 10, 11,
526 // 12, 13,
527 // 14, 15, 16, 17,
528 // 18, 19, 20, 21, 22,
529 // 23, 24, 25, 26, 27
530static int color_numbers_16[28] = {0, 1, 2, 3,
531 4, 5, 6, 6,
532 7, 7, 7, 7,
533 8, 8,
534 9, 9, 10, 10,
535 11, 11, 12, 12, 13,
536 13, 14, 14, 15, -1};
537// for xterm with 88 colors...
538static int color_numbers_88[28] = {0, 4, 2, 6,
539 1, 5, 32, 72,
540 84, 84, 7, 7,
541 82, 82,
542 12, 43, 10, 61,
543 14, 63, 9, 74, 13,
544 75, 11, 78, 15, -1};
545// for xterm with 256 colors...
546static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200547 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200548 248, 248, 7, 7,
549 242, 242,
550 12, 81, 10, 121,
551 14, 159, 9, 224, 13,
552 225, 11, 229, 15, -1};
553// for terminals with less than 16 colors...
554static int color_numbers_8[28] = {0, 4, 2, 6,
555 1, 5, 3, 3,
556 7, 7, 7, 7,
557 0+8, 0+8,
558 4+8, 4+8, 2+8, 2+8,
559 6+8, 6+8, 1+8, 1+8, 5+8,
560 5+8, 3+8, 3+8, 7+8, -1};
561
562/*
563 * Lookup the "cterm" value to be used for color with index "idx" in
564 * color_names[].
565 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
566 * colors, otherwise it will be unchanged.
567 */
568 int
569lookup_color(int idx, int foreground, int *boldp)
570{
571 int color = color_numbers_16[idx];
572 char_u *p;
573
574 // Use the _16 table to check if it's a valid color name.
575 if (color < 0)
576 return -1;
577
578 if (t_colors == 8)
579 {
580 // t_Co is 8: use the 8 colors table
581#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100582 // On qnx, the 8 & 16 color arrays are the same
583 if (STRNCMP(T_NAME, "qansi", 5) == 0)
584 color = color_numbers_16[idx];
585 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200586#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100587 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200588 if (foreground)
589 {
590 // set/reset bold attribute to get light foreground
591 // colors (on some terminals, e.g. "linux")
592 if (color & 8)
593 *boldp = TRUE;
594 else
595 *boldp = FALSE;
596 }
597 color &= 7; // truncate to 8 colors
598 }
599 else if (t_colors == 16 || t_colors == 88
600 || t_colors >= 256)
601 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100602 // Guess: if the termcap entry ends in 'm', it is
603 // probably an xterm-like terminal. Use the changed
604 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200605 if (*T_CAF != NUL)
606 p = T_CAF;
607 else
608 p = T_CSF;
609 if (*p != NUL && (t_colors > 256
610 || *(p + STRLEN(p) - 1) == 'm'))
611 {
612 if (t_colors == 88)
613 color = color_numbers_88[idx];
614 else if (t_colors >= 256)
615 color = color_numbers_256[idx];
616 else
617 color = color_numbers_8[idx];
618 }
619#ifdef FEAT_TERMRESPONSE
620 if (t_colors >= 256 && color == 15 && is_mac_terminal)
621 // Terminal.app has a bug: 15 is light grey. Use white
622 // from the color cube instead.
623 color = 231;
624#endif
625 }
626 return color;
627}
628
629/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100630 * Link highlight group 'from_hg' to 'to_hg'.
631 * 'dodefault' is set to TRUE for ":highlight default link".
632 * 'forceit' is set to TRUE for ":higlight! link"
633 * 'init' is set to TRUE when initializing all the highlight groups.
634 */
635 static void
636highlight_group_link(
637 char_u *from_hg,
638 int from_len,
639 char_u *to_hg,
640 int to_len,
641 int dodefault,
642 int forceit,
643 int init)
644{
645 int from_id;
646 int to_id;
647 hl_group_T *hlgroup = NULL;
648
649 from_id = syn_check_group(from_hg, from_len);
650 if (STRNCMP(to_hg, "NONE", 4) == 0)
651 to_id = 0;
652 else
653 to_id = syn_check_group(to_hg, to_len);
654
655 if (from_id > 0)
656 {
657 hlgroup = &HL_TABLE()[from_id - 1];
658 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
659 {
660 hlgroup->sg_deflink = to_id;
661#ifdef FEAT_EVAL
662 hlgroup->sg_deflink_sctx = current_sctx;
663 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
664#endif
665 }
666 }
667
668 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
669 {
670 // Don't allow a link when there already is some highlighting
671 // for the group, unless '!' is used
672 if (to_id > 0 && !forceit && !init
673 && hl_has_settings(from_id - 1, dodefault))
674 {
675 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000676 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100677 }
678 else if (hlgroup->sg_link != to_id
679#ifdef FEAT_EVAL
680 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
681#endif
682 || hlgroup->sg_cleared)
683 {
684 if (!init)
685 hlgroup->sg_set |= SG_LINK;
686 hlgroup->sg_link = to_id;
687#ifdef FEAT_EVAL
688 hlgroup->sg_script_ctx = current_sctx;
689 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
690#endif
691 hlgroup->sg_cleared = FALSE;
692 redraw_all_later(SOME_VALID);
693
694 // Only call highlight_changed() once after multiple changes.
695 need_highlight_changed = TRUE;
696 }
697 }
698
699}
700
701/*
702 * Reset all highlighting to the defaults. Removes all highlighting for the
703 * groups added by the user.
704 */
705 static void
706highlight_reset_all(void)
707{
708 int idx;
709
710#ifdef FEAT_GUI
711 // First, we do not destroy the old values, but allocate the new
712 // ones and update the display. THEN we destroy the old values.
713 // If we destroy the old values first, then the old values
714 // (such as GuiFont's or GuiFontset's) will still be displayed but
715 // invalid because they were free'd.
716 if (gui.in_use)
717 {
718# ifdef FEAT_BEVAL_TIP
719 gui_init_tooltip_font();
720# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100721# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100722 gui_init_menu_font();
723# endif
724 }
725# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
726 gui_mch_def_colors();
727# endif
728# ifdef FEAT_GUI_X11
729# ifdef FEAT_MENU
730
731 // This only needs to be done when there is no Menu highlight
732 // group defined by default, which IS currently the case.
733 gui_mch_new_menu_colors();
734# endif
735 if (gui.in_use)
736 {
737 gui_new_scrollbar_colors();
738# ifdef FEAT_BEVAL_GUI
739 gui_mch_new_tooltip_colors();
740# endif
741# ifdef FEAT_MENU
742 gui_mch_new_menu_font();
743# endif
744 }
745# endif
746
747 // Ok, we're done allocating the new default graphics items.
748 // The screen should already be refreshed at this point.
749 // It is now Ok to clear out the old data.
750#endif
751#ifdef FEAT_EVAL
752 do_unlet((char_u *)"g:colors_name", TRUE);
753#endif
754 restore_cterm_colors();
755
756 // Clear all default highlight groups and load the defaults.
757 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
758 highlight_clear(idx);
759 init_highlight(TRUE, TRUE);
760#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
761 if (USE_24BIT)
762 highlight_gui_started();
763 else
764#endif
765 highlight_changed();
766 redraw_later_clear();
767}
768
769/*
770 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
771 * index 'idx'.
772 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
773 * 'arg' is the list of attribute names separated by comma.
774 * 'init' is set to TRUE when initializing all the highlight groups.
775 * Returns TRUE if the attributes are set.
776 */
777 static int
778highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
779{
780 int attr;
781 int off;
782 long i;
783 int len;
784
785 attr = 0;
786 off = 0;
787 while (arg[off] != NUL)
788 {
789 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
790 {
791 len = (int)STRLEN(hl_name_table[i]);
792 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
793 {
794 attr |= hl_attr_table[i];
795 off += len;
796 break;
797 }
798 }
799 if (i < 0)
800 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000801 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100802 return FALSE;
803 }
804 if (arg[off] == ',') // another one follows
805 ++off;
806 }
807 if (*key == 'T')
808 {
809 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
810 {
811 if (!init)
812 HL_TABLE()[idx].sg_set |= SG_TERM;
813 HL_TABLE()[idx].sg_term = attr;
814 }
815 }
816 else if (*key == 'C')
817 {
818 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
819 {
820 if (!init)
821 HL_TABLE()[idx].sg_set |= SG_CTERM;
822 HL_TABLE()[idx].sg_cterm = attr;
823 HL_TABLE()[idx].sg_cterm_bold = FALSE;
824 }
825 }
826#if defined(FEAT_GUI) || defined(FEAT_EVAL)
827 else
828 {
829 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
830 {
831 if (!init)
832 HL_TABLE()[idx].sg_set |= SG_GUI;
833 HL_TABLE()[idx].sg_gui = attr;
834 }
835 }
836#endif
837
838 return TRUE;
839}
840
841#ifdef FEAT_GUI
842/*
843 * Set the font for the highlight group at 'idx'.
844 * 'arg' is the font name.
845 * Returns TRUE if the font is changed.
846 */
847 static int
848highlight_set_font(
849 int idx,
850 char_u *arg,
851 int is_normal_group,
852 int is_menu_group,
853 int is_tooltip_group)
854{
855 int did_change = FALSE;
856
857 // in non-GUI fonts are simply ignored
858 if (HL_TABLE()[idx].sg_font_name != NULL
859 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
860 {
861 // Font name didn't change, ignore.
862 }
863 else if (!gui.shell_created)
864 {
865 // GUI not started yet, always accept the name.
866 vim_free(HL_TABLE()[idx].sg_font_name);
867 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
868 did_change = TRUE;
869 }
870 else
871 {
872 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
873# ifdef FEAT_XFONTSET
874 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
875# endif
876 // First, save the current font/fontset.
877 // Then try to allocate the font/fontset.
878 // If the allocation fails, HL_TABLE()[idx].sg_font OR
879 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
880
881 HL_TABLE()[idx].sg_font = NOFONT;
882# ifdef FEAT_XFONTSET
883 HL_TABLE()[idx].sg_fontset = NOFONTSET;
884# endif
885 hl_do_font(idx, arg, is_normal_group, is_menu_group,
886 is_tooltip_group, FALSE);
887
888# ifdef FEAT_XFONTSET
889 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
890 {
891 // New fontset was accepted. Free the old one, if there
892 // was one.
893 gui_mch_free_fontset(temp_sg_fontset);
894 vim_free(HL_TABLE()[idx].sg_font_name);
895 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
896 did_change = TRUE;
897 }
898 else
899 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
900# endif
901 if (HL_TABLE()[idx].sg_font != NOFONT)
902 {
903 // New font was accepted. Free the old one, if there was
904 // one.
905 gui_mch_free_font(temp_sg_font);
906 vim_free(HL_TABLE()[idx].sg_font_name);
907 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
908 did_change = TRUE;
909 }
910 else
911 HL_TABLE()[idx].sg_font = temp_sg_font;
912 }
913
914 return did_change;
915}
916#endif
917
918/*
919 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
920 * Returns TRUE if the foreground color is set.
921 */
922 static void
923highlight_set_ctermfg(int idx, int color, int is_normal_group)
924{
925 HL_TABLE()[idx].sg_cterm_fg = color + 1;
926 if (is_normal_group)
927 {
928 cterm_normal_fg_color = color + 1;
929 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
930#ifdef FEAT_GUI
931 // Don't do this if the GUI is used.
932 if (!gui.in_use && !gui.starting)
933#endif
934 {
935 must_redraw = CLEAR;
936 if (termcap_active && color >= 0)
937 term_fg_color(color);
938 }
939 }
940}
941
942/*
943 * Set the cterm background color for the highlight group at 'idx' to 'color'.
944 * Returns TRUE if the background color is set.
945 */
946 static void
947highlight_set_ctermbg(int idx, int color, int is_normal_group)
948{
949 HL_TABLE()[idx].sg_cterm_bg = color + 1;
950 if (is_normal_group)
951 {
952 cterm_normal_bg_color = color + 1;
953#ifdef FEAT_GUI
954 // Don't mess with 'background' if the GUI is used.
955 if (!gui.in_use && !gui.starting)
956#endif
957 {
958 must_redraw = CLEAR;
959 if (color >= 0)
960 {
961 int dark = -1;
962
963 if (termcap_active)
964 term_bg_color(color);
965 if (t_colors < 16)
966 dark = (color == 0 || color == 4);
967 // Limit the heuristic to the standard 16 colors
968 else if (color < 16)
969 dark = (color < 7 || color == 8);
970 // Set the 'background' option if the value is
971 // wrong.
972 if (dark != -1
973 && dark != (*p_bg == 'd')
974 && !option_was_set((char_u *)"bg"))
975 {
976 set_option_value((char_u *)"bg", 0L,
977 (char_u *)(dark ? "dark" : "light"), 0);
978 reset_option_was_set((char_u *)"bg");
979 }
980 }
981 }
982 }
983}
984
985/*
986 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
987 * Returns TRUE if the underline color is set.
988 */
989 static void
990highlight_set_ctermul(int idx, int color, int is_normal_group)
991{
992 HL_TABLE()[idx].sg_cterm_ul = color + 1;
993 if (is_normal_group)
994 {
995 cterm_normal_ul_color = color + 1;
996#ifdef FEAT_GUI
997 // Don't do this if the GUI is used.
998 if (!gui.in_use && !gui.starting)
999#endif
1000 {
1001 must_redraw = CLEAR;
1002 if (termcap_active && color >= 0)
1003 term_ul_color(color);
1004 }
1005 }
1006}
1007
1008/*
1009 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1010 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1011 * 'keystart' is the color name/value.
1012 * 'arg' is the color name or the numeric value as a string.
1013 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1014 * 'init' is set to TRUE when initializing highlighting.
1015 * Called for the ":highlight" command and the "hlset()" function.
1016 *
1017 * Returns TRUE if the color is set.
1018 */
1019 static int
1020highlight_set_cterm_color(
1021 int idx,
1022 char_u *key,
1023 char_u *key_start,
1024 char_u *arg,
1025 int is_normal_group,
1026 int init)
1027{
1028 int color;
1029 long i;
1030 int off;
1031
1032 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1033 {
1034 if (!init)
1035 HL_TABLE()[idx].sg_set |= SG_CTERM;
1036
1037 // When setting the foreground color, and previously the "bold"
1038 // flag was set for a light color, reset it now
1039 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1040 {
1041 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1042 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1043 }
1044
1045 if (VIM_ISDIGIT(*arg))
1046 color = atoi((char *)arg);
1047 else if (STRICMP(arg, "fg") == 0)
1048 {
1049 if (cterm_normal_fg_color)
1050 color = cterm_normal_fg_color - 1;
1051 else
1052 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001053 emsg(_(e_fg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001054 return FALSE;
1055 }
1056 }
1057 else if (STRICMP(arg, "bg") == 0)
1058 {
1059 if (cterm_normal_bg_color > 0)
1060 color = cterm_normal_bg_color - 1;
1061 else
1062 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001063 emsg(_(e_bg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001064 return FALSE;
1065 }
1066 }
1067 else if (STRICMP(arg, "ul") == 0)
1068 {
1069 if (cterm_normal_ul_color > 0)
1070 color = cterm_normal_ul_color - 1;
1071 else
1072 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00001073 emsg(_(e_ul_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001074 return FALSE;
1075 }
1076 }
1077 else
1078 {
1079 int bold = MAYBE;
1080
1081 // reduce calls to STRICMP a bit, it can be slow
1082 off = TOUPPER_ASC(*arg);
1083 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1084 if (off == color_names[i][0]
1085 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1086 break;
1087 if (i < 0)
1088 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001089 semsg(_(e_color_name_or_number_not_recognized), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001090 return FALSE;
1091 }
1092
1093 color = lookup_color(i, key[5] == 'F', &bold);
1094
1095 // set/reset bold attribute to get light foreground
1096 // colors (on some terminals, e.g. "linux")
1097 if (bold == TRUE)
1098 {
1099 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1100 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1101 }
1102 else if (bold == FALSE)
1103 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1104 }
1105
1106 // Add one to the argument, to avoid zero. Zero is used for
1107 // "NONE", then "color" is -1.
1108 if (key[5] == 'F')
1109 highlight_set_ctermfg(idx, color, is_normal_group);
1110 else if (key[5] == 'B')
1111 highlight_set_ctermbg(idx, color, is_normal_group);
1112 else // ctermul
1113 highlight_set_ctermul(idx, color, is_normal_group);
1114 }
1115
1116 return TRUE;
1117}
1118
1119#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1120/*
1121 * Set the GUI foreground color for the highlight group at 'idx'.
1122 * Returns TRUE if the color is set.
1123 */
1124 static int
1125highlight_set_guifg(
1126 int idx,
1127 char_u *arg,
1128 int is_menu_group UNUSED,
1129 int is_scrollbar_group UNUSED,
1130 int is_tooltip_group UNUSED,
1131 int *do_colors UNUSED,
1132 int init)
1133{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001134# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001135 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001136# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001137 char_u **namep;
1138 int did_change = FALSE;
1139
1140 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1141 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1142 {
1143 if (!init)
1144 HL_TABLE()[idx].sg_set |= SG_GUI;
1145
1146# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1147 // In GUI guifg colors are only used when recognized
1148 i = color_name2handle(arg);
1149 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1150 {
1151 HL_TABLE()[idx].sg_gui_fg = i;
1152# endif
1153 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1154 {
1155 vim_free(*namep);
1156 if (STRCMP(arg, "NONE") != 0)
1157 *namep = vim_strsave(arg);
1158 else
1159 *namep = NULL;
1160 did_change = TRUE;
1161 }
1162# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1163# ifdef FEAT_GUI_X11
1164 if (is_menu_group && gui.menu_fg_pixel != i)
1165 {
1166 gui.menu_fg_pixel = i;
1167 *do_colors = TRUE;
1168 }
1169 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1170 {
1171 gui.scroll_fg_pixel = i;
1172 *do_colors = TRUE;
1173 }
1174# ifdef FEAT_BEVAL_GUI
1175 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1176 {
1177 gui.tooltip_fg_pixel = i;
1178 *do_colors = TRUE;
1179 }
1180# endif
1181# endif
1182 }
1183# endif
1184 }
1185
1186 return did_change;
1187}
1188
1189/*
1190 * Set the GUI background color for the highlight group at 'idx'.
1191 * Returns TRUE if the color is set.
1192 */
1193 static int
1194highlight_set_guibg(
1195 int idx,
1196 char_u *arg,
1197 int is_menu_group UNUSED,
1198 int is_scrollbar_group UNUSED,
1199 int is_tooltip_group UNUSED,
1200 int *do_colors UNUSED,
1201 int init)
1202{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001203# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001204 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001205# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001206 char_u **namep;
1207 int did_change = FALSE;
1208
1209 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1210 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1211 {
1212 if (!init)
1213 HL_TABLE()[idx].sg_set |= SG_GUI;
1214
1215# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Drew Vogele30d1022021-10-24 20:35:07 +01001216 // In GUI guibg colors are only used when recognized
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001217 i = color_name2handle(arg);
1218 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1219 {
1220 HL_TABLE()[idx].sg_gui_bg = i;
1221# endif
1222 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1223 {
1224 vim_free(*namep);
1225 if (STRCMP(arg, "NONE") != 0)
1226 *namep = vim_strsave(arg);
1227 else
1228 *namep = NULL;
1229 did_change = TRUE;
1230 }
1231# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1232# ifdef FEAT_GUI_X11
1233 if (is_menu_group && gui.menu_bg_pixel != i)
1234 {
1235 gui.menu_bg_pixel = i;
1236 *do_colors = TRUE;
1237 }
1238 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1239 {
1240 gui.scroll_bg_pixel = i;
1241 *do_colors = TRUE;
1242 }
1243# ifdef FEAT_BEVAL_GUI
1244 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1245 {
1246 gui.tooltip_bg_pixel = i;
1247 *do_colors = TRUE;
1248 }
1249# endif
1250# endif
1251 }
1252# endif
1253 }
1254
1255 return did_change;
1256}
1257
1258/*
1259 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1260 * Returns TRUE if the color is set.
1261 */
1262 static int
1263highlight_set_guisp(int idx, char_u *arg, int init)
1264{
1265# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1266 int i;
1267# endif
1268 int did_change = FALSE;
1269 char_u **namep;
1270
1271 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1272 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1273 {
1274 if (!init)
1275 HL_TABLE()[idx].sg_set |= SG_GUI;
1276
1277# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1278 // In GUI guisp colors are only used when recognized
1279 i = color_name2handle(arg);
1280 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1281 {
1282 HL_TABLE()[idx].sg_gui_sp = i;
1283# endif
1284 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1285 {
1286 vim_free(*namep);
1287 if (STRCMP(arg, "NONE") != 0)
1288 *namep = vim_strsave(arg);
1289 else
1290 *namep = NULL;
1291 did_change = TRUE;
1292 }
1293# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1294 }
1295# endif
1296 }
1297
1298 return did_change;
1299}
1300#endif
1301
1302/*
1303 * Set the start/stop terminal codes for a highlight group.
1304 * Returns TRUE if the terminal code is set.
1305 */
1306 static int
1307highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1308{
1309 int off;
1310 char_u buf[100];
1311 int len;
1312 char_u *tname;
1313 char_u *p;
1314
1315 if (!init)
1316 HL_TABLE()[idx].sg_set |= SG_TERM;
1317
1318 // The "start" and "stop" arguments can be a literal escape
1319 // sequence, or a comma separated list of terminal codes.
1320 if (STRNCMP(arg, "t_", 2) == 0)
1321 {
1322 off = 0;
1323 buf[0] = 0;
1324 while (arg[off] != NUL)
1325 {
1326 // Isolate one termcap name
1327 for (len = 0; arg[off + len] &&
1328 arg[off + len] != ','; ++len)
1329 ;
1330 tname = vim_strnsave(arg + off, len);
1331 if (tname == NULL) // out of memory
1332 return FALSE;
1333 // lookup the escape sequence for the item
1334 p = get_term_code(tname);
1335 vim_free(tname);
1336 if (p == NULL) // ignore non-existing things
1337 p = (char_u *)"";
1338
1339 // Append it to the already found stuff
1340 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1341 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001342 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001343 return FALSE;
1344 }
1345 STRCAT(buf, p);
1346
1347 // Advance to the next item
1348 off += len;
1349 if (arg[off] == ',') // another one follows
1350 ++off;
1351 }
1352 }
1353 else
1354 {
1355 // Copy characters from arg[] to buf[], translating <> codes.
1356 for (p = arg, off = 0; off < 100 - 6 && *p; )
1357 {
1358 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
1359 if (len > 0) // recognized special char
1360 off += len;
1361 else // copy as normal char
1362 buf[off++] = *p++;
1363 }
1364 buf[off] = NUL;
1365 }
1366
1367 if (STRCMP(buf, "NONE") == 0) // resetting the value
1368 p = NULL;
1369 else
1370 p = vim_strsave(buf);
1371 if (key[2] == 'A')
1372 {
1373 vim_free(HL_TABLE()[idx].sg_start);
1374 HL_TABLE()[idx].sg_start = p;
1375 }
1376 else
1377 {
1378 vim_free(HL_TABLE()[idx].sg_stop);
1379 HL_TABLE()[idx].sg_stop = p;
1380 }
1381 return TRUE;
1382}
1383
1384/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001385 * Handle the ":highlight .." command.
1386 * When using ":hi clear" this is called recursively for each group with
1387 * "forceit" and "init" both TRUE.
1388 */
1389 void
1390do_highlight(
1391 char_u *line,
1392 int forceit,
1393 int init) // TRUE when called for initializing
1394{
1395 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001396 char_u *linep;
1397 char_u *key_start;
1398 char_u *arg_start;
1399 char_u *key = NULL, *arg = NULL;
1400 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001401 int id;
1402 int idx;
1403 hl_group_T item_before;
1404 int did_change = FALSE;
1405 int dodefault = FALSE;
1406 int doclear = FALSE;
1407 int dolink = FALSE;
1408 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001409 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001410#ifdef FEAT_GUI_X11
1411 int is_menu_group = FALSE; // "Menu" group
1412 int is_scrollbar_group = FALSE; // "Scrollbar" group
1413 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001414#else
1415# define is_menu_group 0
1416# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001417# define is_scrollbar_group 0
1418#endif
1419#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1420 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001421#endif
1422#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1423 int did_highlight_changed = FALSE;
1424#endif
1425
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001426 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001427 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001428 {
1429 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1430 // TODO: only call when the group has attributes set
1431 highlight_list_one((int)i);
1432 return;
1433 }
1434
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001435 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001436 name_end = skiptowhite(line);
1437 linep = skipwhite(name_end);
1438
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001439 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001440 if (STRNCMP(line, "default", name_end - line) == 0)
1441 {
1442 dodefault = TRUE;
1443 line = linep;
1444 name_end = skiptowhite(line);
1445 linep = skipwhite(name_end);
1446 }
1447
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001448 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001449 if (STRNCMP(line, "clear", name_end - line) == 0)
1450 doclear = TRUE;
1451 if (STRNCMP(line, "link", name_end - line) == 0)
1452 dolink = TRUE;
1453
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001454 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001455 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001456 {
1457 id = syn_namen2id(line, (int)(name_end - line));
1458 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001459 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001460 else
1461 highlight_list_one(id);
1462 return;
1463 }
1464
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001465 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001466 if (dolink)
1467 {
1468 char_u *from_start = linep;
1469 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001470 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001471 char_u *to_start;
1472 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001473 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001474
1475 from_end = skiptowhite(from_start);
1476 to_start = skipwhite(from_end);
1477 to_end = skiptowhite(to_start);
1478
Bram Moolenaar1966c242020-04-20 22:42:32 +02001479 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001480 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001481 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001482 return;
1483 }
1484
Bram Moolenaar1966c242020-04-20 22:42:32 +02001485 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001486 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001487 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001488 return;
1489 }
1490
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001491 from_len = (int)(from_end - from_start);
1492 to_len = (int)(to_end - to_start);
1493 highlight_group_link(from_start, from_len, to_start, to_len,
1494 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001495 return;
1496 }
1497
1498 if (doclear)
1499 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001500 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001501 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001502 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001503 // ":highlight clear" without group name
1504 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001505 return;
1506 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001507 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001508 name_end = skiptowhite(line);
1509 linep = skipwhite(name_end);
1510 }
1511
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001512 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001513 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001514 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001515 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001516 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001517
1518 // Return if "default" was used and the group already has settings.
1519 if (dodefault && hl_has_settings(idx, TRUE))
1520 return;
1521
1522 // Make a copy so we can check if any attribute actually changed.
1523 item_before = HL_TABLE()[idx];
1524
1525 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1526 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001527#ifdef FEAT_GUI_X11
1528 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1529 is_menu_group = TRUE;
1530 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1531 is_scrollbar_group = TRUE;
1532 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1533 is_tooltip_group = TRUE;
1534#endif
1535
1536 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1537 if (doclear || (forceit && init))
1538 {
1539 highlight_clear(idx);
1540 if (!doclear)
1541 HL_TABLE()[idx].sg_set = 0;
1542 }
1543
1544 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001545 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001546 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001547 key_start = linep;
1548 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001549 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001550 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001551 error = TRUE;
1552 break;
1553 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001554
1555 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1556 // or "guibg").
1557 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1558 ++linep;
1559 vim_free(key);
1560 key = vim_strnsave_up(key_start, linep - key_start);
1561 if (key == NULL)
1562 {
1563 error = TRUE;
1564 break;
1565 }
1566 linep = skipwhite(linep);
1567
1568 if (STRCMP(key, "NONE") == 0)
1569 {
1570 if (!init || HL_TABLE()[idx].sg_set == 0)
1571 {
1572 if (!init)
1573 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1574 highlight_clear(idx);
1575 }
1576 continue;
1577 }
1578
1579 // Check for the equal sign.
1580 if (*linep != '=')
1581 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001582 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001583 error = TRUE;
1584 break;
1585 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001586 ++linep;
1587
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001588 // Isolate the argument.
1589 linep = skipwhite(linep);
1590 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001591 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001592 arg_start = ++linep;
1593 linep = vim_strchr(linep, '\'');
1594 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001595 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001596 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001597 error = TRUE;
1598 break;
1599 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001600 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001601 else
1602 {
1603 arg_start = linep;
1604 linep = skiptowhite(linep);
1605 }
1606 if (linep == arg_start)
1607 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001608 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001609 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001610 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001611 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001612 vim_free(arg);
1613 arg = vim_strnsave(arg_start, linep - arg_start);
1614 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001615 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001616 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001617 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001618 }
1619 if (*linep == '\'')
1620 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001621
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001622 // Store the argument.
1623 if (STRCMP(key, "TERM") == 0
1624 || STRCMP(key, "CTERM") == 0
1625 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001626 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001627 if (!highlight_set_termgui_attr(idx, key, arg, init))
1628 {
1629 error = TRUE;
1630 break;
1631 }
1632 }
1633 else if (STRCMP(key, "FONT") == 0)
1634 {
1635 // in non-GUI fonts are simply ignored
1636#ifdef FEAT_GUI
1637 if (highlight_set_font(idx, arg, is_normal_group,
1638 is_menu_group, is_tooltip_group))
1639 did_change = TRUE;
1640#endif
1641 }
1642 else if (STRCMP(key, "CTERMFG") == 0
1643 || STRCMP(key, "CTERMBG") == 0
1644 || STRCMP(key, "CTERMUL") == 0)
1645 {
1646 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1647 is_normal_group, init))
1648 {
1649 error = TRUE;
1650 break;
1651 }
1652 }
1653 else if (STRCMP(key, "GUIFG") == 0)
1654 {
1655#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1656 if (highlight_set_guifg(idx, arg, is_menu_group,
1657 is_scrollbar_group, is_tooltip_group,
1658 &do_colors, init))
1659 did_change = TRUE;
1660#endif
1661 }
1662 else if (STRCMP(key, "GUIBG") == 0)
1663 {
1664#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1665 if (highlight_set_guibg(idx, arg, is_menu_group,
1666 is_scrollbar_group, is_tooltip_group,
1667 &do_colors, init))
1668 did_change = TRUE;
1669#endif
1670 }
1671 else if (STRCMP(key, "GUISP") == 0)
1672 {
1673#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1674 if (highlight_set_guisp(idx, arg, init))
1675 did_change = TRUE;
1676#endif
1677 }
1678 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1679 {
1680 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1681 {
1682 error = TRUE;
1683 break;
1684 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001685 }
1686 else
1687 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001688 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001689 error = TRUE;
1690 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001691 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001692 HL_TABLE()[idx].sg_cleared = FALSE;
1693
1694 // When highlighting has been given for a group, don't link it.
1695 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1696 HL_TABLE()[idx].sg_link = 0;
1697
1698 // Continue with next argument.
1699 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001700 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001701
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001702 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001703 if (error && idx == highlight_ga.ga_len)
1704 syn_unadd_group();
1705 else
1706 {
1707 if (is_normal_group)
1708 {
1709 HL_TABLE()[idx].sg_term_attr = 0;
1710 HL_TABLE()[idx].sg_cterm_attr = 0;
1711#ifdef FEAT_GUI
1712 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001713 // Need to update all groups, because they might be using "bg"
1714 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001715#endif
1716#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1717 if (USE_24BIT)
1718 {
1719 highlight_gui_started();
1720 did_highlight_changed = TRUE;
1721 redraw_all_later(NOT_VALID);
1722 }
1723#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001724#ifdef FEAT_VTP
1725 control_console_color_rgb();
1726#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001727 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001728#ifdef FEAT_GUI_X11
1729# ifdef FEAT_MENU
1730 else if (is_menu_group)
1731 {
1732 if (gui.in_use && do_colors)
1733 gui_mch_new_menu_colors();
1734 }
1735# endif
1736 else if (is_scrollbar_group)
1737 {
1738 if (gui.in_use && do_colors)
1739 gui_new_scrollbar_colors();
1740 else
1741 set_hl_attr(idx);
1742 }
1743# ifdef FEAT_BEVAL_GUI
1744 else if (is_tooltip_group)
1745 {
1746 if (gui.in_use && do_colors)
1747 gui_mch_new_tooltip_colors();
1748 }
1749# endif
1750#endif
1751 else
1752 set_hl_attr(idx);
1753#ifdef FEAT_EVAL
1754 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001755 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001756#endif
1757 }
1758
1759 vim_free(key);
1760 vim_free(arg);
1761
1762 // Only call highlight_changed() once, after a sequence of highlight
1763 // commands, and only if an attribute actually changed.
1764 if ((did_change
1765 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1766#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1767 && !did_highlight_changed
1768#endif
1769 )
1770 {
1771 // Do not trigger a redraw when highlighting is changed while
1772 // redrawing. This may happen when evaluating 'statusline' changes the
1773 // StatusLine group.
1774 if (!updating_screen)
1775 redraw_all_later(NOT_VALID);
1776 need_highlight_changed = TRUE;
1777 }
1778}
1779
1780#if defined(EXITFREE) || defined(PROTO)
1781 void
1782free_highlight(void)
1783{
1784 int i;
1785
1786 for (i = 0; i < highlight_ga.ga_len; ++i)
1787 {
1788 highlight_clear(i);
1789 vim_free(HL_TABLE()[i].sg_name);
1790 vim_free(HL_TABLE()[i].sg_name_u);
1791 }
1792 ga_clear(&highlight_ga);
1793}
1794#endif
1795
1796/*
1797 * Reset the cterm colors to what they were before Vim was started, if
1798 * possible. Otherwise reset them to zero.
1799 */
1800 void
1801restore_cterm_colors(void)
1802{
1803#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1804 // Since t_me has been set, this probably means that the user
1805 // wants to use this as default colors. Need to reset default
1806 // background/foreground colors.
1807 mch_set_normal_colors();
1808#else
1809# ifdef VIMDLL
1810 if (!gui.in_use)
1811 {
1812 mch_set_normal_colors();
1813 return;
1814 }
1815# endif
1816 cterm_normal_fg_color = 0;
1817 cterm_normal_fg_bold = 0;
1818 cterm_normal_bg_color = 0;
1819# ifdef FEAT_TERMGUICOLORS
1820 cterm_normal_fg_gui_color = INVALCOLOR;
1821 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001822 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001823# endif
1824#endif
1825}
1826
1827/*
1828 * Return TRUE if highlight group "idx" has any settings.
1829 * When "check_link" is TRUE also check for an existing link.
1830 */
1831 static int
1832hl_has_settings(int idx, int check_link)
1833{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001834 return HL_TABLE()[idx].sg_cleared == 0
1835 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001836 || HL_TABLE()[idx].sg_cterm_attr != 0
1837 || HL_TABLE()[idx].sg_cterm_fg != 0
1838 || HL_TABLE()[idx].sg_cterm_bg != 0
1839#ifdef FEAT_GUI
1840 || HL_TABLE()[idx].sg_gui_attr != 0
1841 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1842 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1843 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1844 || HL_TABLE()[idx].sg_font_name != NULL
1845#endif
1846 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1847}
1848
1849/*
1850 * Clear highlighting for one group.
1851 */
1852 static void
1853highlight_clear(int idx)
1854{
1855 HL_TABLE()[idx].sg_cleared = TRUE;
1856
1857 HL_TABLE()[idx].sg_term = 0;
1858 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1859 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1860 HL_TABLE()[idx].sg_term_attr = 0;
1861 HL_TABLE()[idx].sg_cterm = 0;
1862 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1863 HL_TABLE()[idx].sg_cterm_fg = 0;
1864 HL_TABLE()[idx].sg_cterm_bg = 0;
1865 HL_TABLE()[idx].sg_cterm_attr = 0;
1866#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1867 HL_TABLE()[idx].sg_gui = 0;
1868 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1869 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1870 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1871#endif
1872#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1873 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1874 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001875 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001876#endif
1877#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001878 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1879 HL_TABLE()[idx].sg_font = NOFONT;
1880# ifdef FEAT_XFONTSET
1881 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1882 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1883# endif
1884 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1885 HL_TABLE()[idx].sg_gui_attr = 0;
1886#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001887 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001888 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001889#ifdef FEAT_EVAL
1890 // Since we set the default link, set the location to where the default
1891 // link was set.
1892 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001893#endif
1894}
1895
1896#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1897/*
1898 * Set the normal foreground and background colors according to the "Normal"
1899 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1900 * "Tooltip" colors.
1901 */
1902 void
1903set_normal_colors(void)
1904{
1905# ifdef FEAT_GUI
1906# ifdef FEAT_TERMGUICOLORS
1907 if (gui.in_use)
1908# endif
1909 {
1910 if (set_group_colors((char_u *)"Normal",
1911 &gui.norm_pixel, &gui.back_pixel,
1912 FALSE, TRUE, FALSE))
1913 {
1914 gui_mch_new_colors();
1915 must_redraw = CLEAR;
1916 }
1917# ifdef FEAT_GUI_X11
1918 if (set_group_colors((char_u *)"Menu",
1919 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1920 TRUE, FALSE, FALSE))
1921 {
1922# ifdef FEAT_MENU
1923 gui_mch_new_menu_colors();
1924# endif
1925 must_redraw = CLEAR;
1926 }
1927# ifdef FEAT_BEVAL_GUI
1928 if (set_group_colors((char_u *)"Tooltip",
1929 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1930 FALSE, FALSE, TRUE))
1931 {
1932# ifdef FEAT_TOOLBAR
1933 gui_mch_new_tooltip_colors();
1934# endif
1935 must_redraw = CLEAR;
1936 }
1937# endif
1938 if (set_group_colors((char_u *)"Scrollbar",
1939 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1940 FALSE, FALSE, FALSE))
1941 {
1942 gui_new_scrollbar_colors();
1943 must_redraw = CLEAR;
1944 }
1945# endif
1946 }
1947# endif
1948# ifdef FEAT_TERMGUICOLORS
1949# ifdef FEAT_GUI
1950 else
1951# endif
1952 {
1953 int idx;
1954
1955 idx = syn_name2id((char_u *)"Normal") - 1;
1956 if (idx >= 0)
1957 {
1958 gui_do_one_color(idx, FALSE, FALSE);
1959
1960 // If the normal fg or bg color changed a complete redraw is
1961 // required.
1962 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1963 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1964 {
1965 // if the GUI color is INVALCOLOR then we use the default cterm
1966 // color
1967 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1968 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1969 must_redraw = CLEAR;
1970 }
1971 }
1972 }
1973# endif
1974}
1975#endif
1976
1977#if defined(FEAT_GUI) || defined(PROTO)
1978/*
1979 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1980 */
1981 static int
1982set_group_colors(
1983 char_u *name,
1984 guicolor_T *fgp,
1985 guicolor_T *bgp,
1986 int do_menu,
1987 int use_norm,
1988 int do_tooltip)
1989{
1990 int idx;
1991
1992 idx = syn_name2id(name) - 1;
1993 if (idx >= 0)
1994 {
1995 gui_do_one_color(idx, do_menu, do_tooltip);
1996
1997 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1998 *fgp = HL_TABLE()[idx].sg_gui_fg;
1999 else if (use_norm)
2000 *fgp = gui.def_norm_pixel;
2001 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2002 *bgp = HL_TABLE()[idx].sg_gui_bg;
2003 else if (use_norm)
2004 *bgp = gui.def_back_pixel;
2005 return TRUE;
2006 }
2007 return FALSE;
2008}
2009
2010/*
2011 * Get the font of the "Normal" group.
2012 * Returns "" when it's not found or not set.
2013 */
2014 char_u *
2015hl_get_font_name(void)
2016{
2017 int id;
2018 char_u *s;
2019
2020 id = syn_name2id((char_u *)"Normal");
2021 if (id > 0)
2022 {
2023 s = HL_TABLE()[id - 1].sg_font_name;
2024 if (s != NULL)
2025 return s;
2026 }
2027 return (char_u *)"";
2028}
2029
2030/*
2031 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2032 * actually chosen to be used.
2033 */
2034 void
2035hl_set_font_name(char_u *font_name)
2036{
2037 int id;
2038
2039 id = syn_name2id((char_u *)"Normal");
2040 if (id > 0)
2041 {
2042 vim_free(HL_TABLE()[id - 1].sg_font_name);
2043 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2044 }
2045}
2046
2047/*
2048 * Set background color for "Normal" group. Called by gui_set_bg_color()
2049 * when the color is known.
2050 */
2051 void
2052hl_set_bg_color_name(
2053 char_u *name) // must have been allocated
2054{
2055 int id;
2056
2057 if (name != NULL)
2058 {
2059 id = syn_name2id((char_u *)"Normal");
2060 if (id > 0)
2061 {
2062 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2063 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2064 }
2065 }
2066}
2067
2068/*
2069 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2070 * when the color is known.
2071 */
2072 void
2073hl_set_fg_color_name(
2074 char_u *name) // must have been allocated
2075{
2076 int id;
2077
2078 if (name != NULL)
2079 {
2080 id = syn_name2id((char_u *)"Normal");
2081 if (id > 0)
2082 {
2083 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2084 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2085 }
2086 }
2087}
2088
2089/*
2090 * Return the handle for a font name.
2091 * Returns NOFONT when failed.
2092 */
2093 static GuiFont
2094font_name2handle(char_u *name)
2095{
2096 if (STRCMP(name, "NONE") == 0)
2097 return NOFONT;
2098
2099 return gui_mch_get_font(name, TRUE);
2100}
2101
2102# ifdef FEAT_XFONTSET
2103/*
2104 * Return the handle for a fontset name.
2105 * Returns NOFONTSET when failed.
2106 */
2107 static GuiFontset
2108fontset_name2handle(char_u *name, int fixed_width)
2109{
2110 if (STRCMP(name, "NONE") == 0)
2111 return NOFONTSET;
2112
2113 return gui_mch_get_fontset(name, TRUE, fixed_width);
2114}
2115# endif
2116
2117/*
2118 * Get the font or fontset for one highlight group.
2119 */
2120 static void
2121hl_do_font(
2122 int idx,
2123 char_u *arg,
2124 int do_normal, // set normal font
2125 int do_menu UNUSED, // set menu font
2126 int do_tooltip UNUSED, // set tooltip font
2127 int free_font) // free current font/fontset
2128{
2129# ifdef FEAT_XFONTSET
2130 // If 'guifontset' is not empty, first try using the name as a
2131 // fontset. If that doesn't work, use it as a font name.
2132 if (*p_guifontset != NUL
2133# ifdef FONTSET_ALWAYS
2134 || do_menu
2135# endif
2136# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002137 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002138 || do_tooltip
2139# endif
2140 )
2141 {
2142 if (free_font)
2143 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2144 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2145# ifdef FONTSET_ALWAYS
2146 || do_menu
2147# endif
2148# ifdef FEAT_BEVAL_TIP
2149 || do_tooltip
2150# endif
2151 );
2152 }
2153 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2154 {
2155 // If it worked and it's the Normal group, use it as the normal
2156 // fontset. Same for the Menu group.
2157 if (do_normal)
2158 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002159# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002160 if (do_menu)
2161 {
2162# ifdef FONTSET_ALWAYS
2163 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2164# else
2165 // YIKES! This is a bug waiting to crash the program
2166 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2167# endif
2168 gui_mch_new_menu_font();
2169 }
2170# ifdef FEAT_BEVAL_GUI
2171 if (do_tooltip)
2172 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002173 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002174 // displaying a single font and a fontset.
2175 // If the XtNinternational resource is set to True at widget
2176 // creation, then a fontset is always used, otherwise an
2177 // XFontStruct is used.
2178 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2179 gui_mch_new_tooltip_font();
2180 }
2181# endif
2182# endif
2183 }
2184 else
2185# endif
2186 {
2187 if (free_font)
2188 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2189 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2190 // If it worked and it's the Normal group, use it as the
2191 // normal font. Same for the Menu group.
2192 if (HL_TABLE()[idx].sg_font != NOFONT)
2193 {
2194 if (do_normal)
2195 gui_init_font(arg, FALSE);
2196#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002197# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002198 if (do_menu)
2199 {
2200 gui.menu_font = HL_TABLE()[idx].sg_font;
2201 gui_mch_new_menu_font();
2202 }
2203# endif
2204#endif
2205 }
2206 }
2207}
2208
2209#endif // FEAT_GUI
2210
2211#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2212/*
2213 * Return the handle for a color name.
2214 * Returns INVALCOLOR when failed.
2215 */
2216 guicolor_T
2217color_name2handle(char_u *name)
2218{
2219 if (STRCMP(name, "NONE") == 0)
2220 return INVALCOLOR;
2221
2222 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2223 {
2224#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2225 if (gui.in_use)
2226#endif
2227#ifdef FEAT_GUI
2228 return gui.norm_pixel;
2229#endif
2230#ifdef FEAT_TERMGUICOLORS
2231 if (cterm_normal_fg_gui_color != INVALCOLOR)
2232 return cterm_normal_fg_gui_color;
2233 // Guess that the foreground is black or white.
2234 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2235#endif
2236 }
2237 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2238 {
2239#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2240 if (gui.in_use)
2241#endif
2242#ifdef FEAT_GUI
2243 return gui.back_pixel;
2244#endif
2245#ifdef FEAT_TERMGUICOLORS
2246 if (cterm_normal_bg_gui_color != INVALCOLOR)
2247 return cterm_normal_bg_gui_color;
2248 // Guess that the background is white or black.
2249 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2250#endif
2251 }
2252
2253 return GUI_GET_COLOR(name);
2254}
Drew Vogele30d1022021-10-24 20:35:07 +01002255
2256// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2257// values as used by the MS-Windows GDI api. It should be used only for
2258// MS-Windows GDI builds.
2259# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2260# undef RGB
2261# endif
2262# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002263# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002264# endif
2265
2266# ifdef VIMDLL
2267 static guicolor_T
2268gui_adjust_rgb(guicolor_T c)
2269{
2270 if (gui.in_use)
2271 return c;
2272 else
2273 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2274}
2275# else
2276# define gui_adjust_rgb(c) (c)
2277# endif
2278
2279 static int
2280hex_digit(int c)
2281{
2282 if (isdigit(c))
2283 return c - '0';
2284 c = TOLOWER_ASC(c);
2285 if (c >= 'a' && c <= 'f')
2286 return c - 'a' + 10;
2287 return 0x1ffffff;
2288}
2289
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002290 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002291decode_hex_color(char_u *hex)
2292{
2293 guicolor_T color;
2294
2295 if (hex[0] != '#' || STRLEN(hex) != 7)
2296 return INVALCOLOR;
2297
2298 // Name is in "#rrggbb" format
2299 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2300 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2301 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2302 if (color > 0xffffff)
2303 return INVALCOLOR;
2304 return gui_adjust_rgb(color);
2305}
2306
Bram Moolenaar2a521962021-10-25 10:30:14 +01002307#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002308// Returns the color currently mapped to the given name or INVALCOLOR if no
2309// such name exists in the color table. The convention is to use lowercase for
2310// all keys in the v:colornames dictionary. The value can be either a string in
2311// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002312 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002313colorname2rgb(char_u *name)
2314{
2315 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2316 char_u *lc_name;
2317 dictitem_T *colentry;
2318 char_u *colstr;
2319 varnumber_T colnum;
2320
2321 lc_name = strlow_save(name);
2322 if (lc_name == NULL)
2323 return INVALCOLOR;
2324
2325 colentry = dict_find(colornames_table, lc_name, -1);
2326 vim_free(lc_name);
2327 if (colentry == NULL)
2328 return INVALCOLOR;
2329
2330 if (colentry->di_tv.v_type == VAR_STRING)
2331 {
2332 colstr = tv_get_string_strict(&colentry->di_tv);
2333 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2334 {
2335 return decode_hex_color(colstr);
2336 }
2337 else
2338 {
2339 semsg(_(e_bad_color_string_str), colstr);
2340 return INVALCOLOR;
2341 }
2342 }
2343
2344 if (colentry->di_tv.v_type == VAR_NUMBER)
2345 {
2346 colnum = tv_get_number(&colentry->di_tv);
2347 return (guicolor_T)colnum;
2348 }
2349
2350 return INVALCOLOR;
2351}
2352
Drew Vogele30d1022021-10-24 20:35:07 +01002353#endif
2354
2355 guicolor_T
2356gui_get_color_cmn(char_u *name)
2357{
2358 int i;
2359 guicolor_T color;
2360
2361 struct rgbcolor_table_S {
2362 char_u *color_name;
2363 guicolor_T color;
2364 };
2365
2366 // Only non X11 colors (not present in rgb.txt) and colors in
2367 // color_names[], useful when $VIMRUNTIME is not found,.
2368 static struct rgbcolor_table_S rgb_table[] = {
2369 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2370 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2371 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2372 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2373 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2374 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2375 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2376 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2377 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2378 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2379 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2380 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2381 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2382 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2383 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2384 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2385 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2386 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2387 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2388 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2389 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2390 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2391 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2392 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2393 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2394 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2395 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2396 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2397 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2398 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2399 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2400 };
2401
2402 color = decode_hex_color(name);
2403 if (color != INVALCOLOR)
2404 return color;
2405
2406 // Check if the name is one of the colors we know
2407 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2408 if (STRICMP(name, rgb_table[i].color_name) == 0)
2409 return gui_adjust_rgb(rgb_table[i].color);
2410
2411#if defined(FEAT_EVAL)
2412 /*
2413 * Not a traditional color. Load additional color aliases and then consult the alias table.
2414 */
2415
2416 color = colorname2rgb(name);
2417 if (color == INVALCOLOR)
2418 {
2419 load_default_colors_lists();
2420 color = colorname2rgb(name);
2421 }
2422
2423 return color;
2424#else
2425 return INVALCOLOR;
2426#endif
2427}
2428
2429 guicolor_T
2430gui_get_rgb_color_cmn(int r, int g, int b)
2431{
2432 guicolor_T color = RGB(r, g, b);
2433
2434 if (color > 0xffffff)
2435 return INVALCOLOR;
2436 return gui_adjust_rgb(color);
2437}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002438#endif
2439
2440/*
2441 * Table with the specifications for an attribute number.
2442 * Note that this table is used by ALL buffers. This is required because the
2443 * GUI can redraw at any time for any buffer.
2444 */
2445static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2446
2447#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2448
2449static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2450
2451#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2452
2453#ifdef FEAT_GUI
2454static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2455
2456#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2457#endif
2458
2459/*
2460 * Return the attr number for a set of colors and font.
2461 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2462 * if the combination is new.
2463 * Return 0 for error (no more room).
2464 */
2465 static int
2466get_attr_entry(garray_T *table, attrentry_T *aep)
2467{
2468 int i;
2469 attrentry_T *taep;
2470 static int recursive = FALSE;
2471
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002472 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002473 table->ga_itemsize = sizeof(attrentry_T);
2474 table->ga_growsize = 7;
2475
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002476 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002477 for (i = 0; i < table->ga_len; ++i)
2478 {
2479 taep = &(((attrentry_T *)table->ga_data)[i]);
2480 if ( aep->ae_attr == taep->ae_attr
2481 && (
2482#ifdef FEAT_GUI
2483 (table == &gui_attr_table
2484 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2485 && aep->ae_u.gui.bg_color
2486 == taep->ae_u.gui.bg_color
2487 && aep->ae_u.gui.sp_color
2488 == taep->ae_u.gui.sp_color
2489 && aep->ae_u.gui.font == taep->ae_u.gui.font
2490# ifdef FEAT_XFONTSET
2491 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2492# endif
2493 ))
2494 ||
2495#endif
2496 (table == &term_attr_table
2497 && (aep->ae_u.term.start == NULL)
2498 == (taep->ae_u.term.start == NULL)
2499 && (aep->ae_u.term.start == NULL
2500 || STRCMP(aep->ae_u.term.start,
2501 taep->ae_u.term.start) == 0)
2502 && (aep->ae_u.term.stop == NULL)
2503 == (taep->ae_u.term.stop == NULL)
2504 && (aep->ae_u.term.stop == NULL
2505 || STRCMP(aep->ae_u.term.stop,
2506 taep->ae_u.term.stop) == 0))
2507 || (table == &cterm_attr_table
2508 && aep->ae_u.cterm.fg_color
2509 == taep->ae_u.cterm.fg_color
2510 && aep->ae_u.cterm.bg_color
2511 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002512 && aep->ae_u.cterm.ul_color
2513 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002514#ifdef FEAT_TERMGUICOLORS
2515 && aep->ae_u.cterm.fg_rgb
2516 == taep->ae_u.cterm.fg_rgb
2517 && aep->ae_u.cterm.bg_rgb
2518 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002519 && aep->ae_u.cterm.ul_rgb
2520 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002521#endif
2522 )))
2523
2524 return i + ATTR_OFF;
2525 }
2526
2527 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2528 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002529 // Running out of attribute entries! remove all attributes, and
2530 // compute new ones for all groups.
2531 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002532 if (recursive)
2533 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002534 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002535 return 0;
2536 }
2537 recursive = TRUE;
2538
2539 clear_hl_tables();
2540
2541 must_redraw = CLEAR;
2542
2543 for (i = 0; i < highlight_ga.ga_len; ++i)
2544 set_hl_attr(i);
2545
2546 recursive = FALSE;
2547 }
2548
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002549 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002550 if (ga_grow(table, 1) == FAIL)
2551 return 0;
2552
2553 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002554 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002555 taep->ae_attr = aep->ae_attr;
2556#ifdef FEAT_GUI
2557 if (table == &gui_attr_table)
2558 {
2559 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2560 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2561 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2562 taep->ae_u.gui.font = aep->ae_u.gui.font;
2563# ifdef FEAT_XFONTSET
2564 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2565# endif
2566 }
2567#endif
2568 if (table == &term_attr_table)
2569 {
2570 if (aep->ae_u.term.start == NULL)
2571 taep->ae_u.term.start = NULL;
2572 else
2573 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2574 if (aep->ae_u.term.stop == NULL)
2575 taep->ae_u.term.stop = NULL;
2576 else
2577 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2578 }
2579 else if (table == &cterm_attr_table)
2580 {
2581 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2582 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002583 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002584#ifdef FEAT_TERMGUICOLORS
2585 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2586 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002587 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002588#endif
2589 }
2590 ++table->ga_len;
2591 return (table->ga_len - 1 + ATTR_OFF);
2592}
2593
2594#if defined(FEAT_TERMINAL) || defined(PROTO)
2595/*
2596 * Get an attribute index for a cterm entry.
2597 * Uses an existing entry when possible or adds one when needed.
2598 */
2599 int
2600get_cterm_attr_idx(int attr, int fg, int bg)
2601{
2602 attrentry_T at_en;
2603
Bram Moolenaara80faa82020-04-12 19:37:17 +02002604 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002605#ifdef FEAT_TERMGUICOLORS
2606 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2607 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002608 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002609#endif
2610 at_en.ae_attr = attr;
2611 at_en.ae_u.cterm.fg_color = fg;
2612 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002613 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002614 return get_attr_entry(&cterm_attr_table, &at_en);
2615}
2616#endif
2617
2618#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2619/*
2620 * Get an attribute index for a 'termguicolors' entry.
2621 * Uses an existing entry when possible or adds one when needed.
2622 */
2623 int
2624get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2625{
2626 attrentry_T at_en;
2627
Bram Moolenaara80faa82020-04-12 19:37:17 +02002628 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002629 at_en.ae_attr = attr;
2630 if (fg == INVALCOLOR && bg == INVALCOLOR)
2631 {
2632 // If both GUI colors are not set fall back to the cterm colors. Helps
2633 // if the GUI only has an attribute, such as undercurl.
2634 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2635 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2636 }
2637 else
2638 {
2639 at_en.ae_u.cterm.fg_rgb = fg;
2640 at_en.ae_u.cterm.bg_rgb = bg;
2641 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002642 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002643 return get_attr_entry(&cterm_attr_table, &at_en);
2644}
2645#endif
2646
2647#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2648/*
2649 * Get an attribute index for a cterm entry.
2650 * Uses an existing entry when possible or adds one when needed.
2651 */
2652 int
2653get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2654{
2655 attrentry_T at_en;
2656
Bram Moolenaara80faa82020-04-12 19:37:17 +02002657 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002658 at_en.ae_attr = attr;
2659 at_en.ae_u.gui.fg_color = fg;
2660 at_en.ae_u.gui.bg_color = bg;
2661 return get_attr_entry(&gui_attr_table, &at_en);
2662}
2663#endif
2664
2665/*
2666 * Clear all highlight tables.
2667 */
2668 void
2669clear_hl_tables(void)
2670{
2671 int i;
2672 attrentry_T *taep;
2673
2674#ifdef FEAT_GUI
2675 ga_clear(&gui_attr_table);
2676#endif
2677 for (i = 0; i < term_attr_table.ga_len; ++i)
2678 {
2679 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2680 vim_free(taep->ae_u.term.start);
2681 vim_free(taep->ae_u.term.stop);
2682 }
2683 ga_clear(&term_attr_table);
2684 ga_clear(&cterm_attr_table);
2685}
2686
2687/*
2688 * Combine special attributes (e.g., for spelling) with other attributes
2689 * (e.g., for syntax highlighting).
2690 * "prim_attr" overrules "char_attr".
2691 * This creates a new group when required.
2692 * Since we expect there to be few spelling mistakes we don't cache the
2693 * result.
2694 * Return the resulting attributes.
2695 */
2696 int
2697hl_combine_attr(int char_attr, int prim_attr)
2698{
2699 attrentry_T *char_aep = NULL;
2700 attrentry_T *spell_aep;
2701 attrentry_T new_en;
2702
2703 if (char_attr == 0)
2704 return prim_attr;
2705 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2706 return ATTR_COMBINE(char_attr, prim_attr);
2707#ifdef FEAT_GUI
2708 if (gui.in_use)
2709 {
2710 if (char_attr > HL_ALL)
2711 char_aep = syn_gui_attr2entry(char_attr);
2712 if (char_aep != NULL)
2713 new_en = *char_aep;
2714 else
2715 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002716 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002717 new_en.ae_u.gui.fg_color = INVALCOLOR;
2718 new_en.ae_u.gui.bg_color = INVALCOLOR;
2719 new_en.ae_u.gui.sp_color = INVALCOLOR;
2720 if (char_attr <= HL_ALL)
2721 new_en.ae_attr = char_attr;
2722 }
2723
2724 if (prim_attr <= HL_ALL)
2725 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2726 else
2727 {
2728 spell_aep = syn_gui_attr2entry(prim_attr);
2729 if (spell_aep != NULL)
2730 {
2731 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2732 spell_aep->ae_attr);
2733 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2734 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2735 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2736 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2737 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2738 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2739 if (spell_aep->ae_u.gui.font != NOFONT)
2740 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2741# ifdef FEAT_XFONTSET
2742 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2743 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2744# endif
2745 }
2746 }
2747 return get_attr_entry(&gui_attr_table, &new_en);
2748 }
2749#endif
2750
2751 if (IS_CTERM)
2752 {
2753 if (char_attr > HL_ALL)
2754 char_aep = syn_cterm_attr2entry(char_attr);
2755 if (char_aep != NULL)
2756 new_en = *char_aep;
2757 else
2758 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002759 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002760#ifdef FEAT_TERMGUICOLORS
2761 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2762 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002763 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002764#endif
2765 if (char_attr <= HL_ALL)
2766 new_en.ae_attr = char_attr;
2767 }
2768
2769 if (prim_attr <= HL_ALL)
2770 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2771 else
2772 {
2773 spell_aep = syn_cterm_attr2entry(prim_attr);
2774 if (spell_aep != NULL)
2775 {
2776 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2777 spell_aep->ae_attr);
2778 if (spell_aep->ae_u.cterm.fg_color > 0)
2779 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2780 if (spell_aep->ae_u.cterm.bg_color > 0)
2781 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002782 if (spell_aep->ae_u.cterm.ul_color > 0)
2783 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002784#ifdef FEAT_TERMGUICOLORS
2785 // If both fg and bg are not set fall back to cterm colors.
2786 // Helps for SpellBad which uses undercurl in the GUI.
2787 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2788 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2789 {
2790 if (spell_aep->ae_u.cterm.fg_color > 0)
2791 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2792 if (spell_aep->ae_u.cterm.bg_color > 0)
2793 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2794 }
2795 else
2796 {
2797 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2798 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2799 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2800 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2801 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002802 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2803 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002804#endif
2805 }
2806 }
2807 return get_attr_entry(&cterm_attr_table, &new_en);
2808 }
2809
2810 if (char_attr > HL_ALL)
2811 char_aep = syn_term_attr2entry(char_attr);
2812 if (char_aep != NULL)
2813 new_en = *char_aep;
2814 else
2815 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002816 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002817 if (char_attr <= HL_ALL)
2818 new_en.ae_attr = char_attr;
2819 }
2820
2821 if (prim_attr <= HL_ALL)
2822 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2823 else
2824 {
2825 spell_aep = syn_term_attr2entry(prim_attr);
2826 if (spell_aep != NULL)
2827 {
2828 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2829 if (spell_aep->ae_u.term.start != NULL)
2830 {
2831 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2832 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2833 }
2834 }
2835 }
2836 return get_attr_entry(&term_attr_table, &new_en);
2837}
2838
2839#ifdef FEAT_GUI
2840 attrentry_T *
2841syn_gui_attr2entry(int attr)
2842{
2843 attr -= ATTR_OFF;
2844 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2845 return NULL;
2846 return &(GUI_ATTR_ENTRY(attr));
2847}
2848#endif
2849
2850/*
2851 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2852 * Only to be used when "attr" > HL_ALL.
2853 */
2854 int
2855syn_attr2attr(int attr)
2856{
2857 attrentry_T *aep;
2858
2859#ifdef FEAT_GUI
2860 if (gui.in_use)
2861 aep = syn_gui_attr2entry(attr);
2862 else
2863#endif
2864 if (IS_CTERM)
2865 aep = syn_cterm_attr2entry(attr);
2866 else
2867 aep = syn_term_attr2entry(attr);
2868
2869 if (aep == NULL) // highlighting not set
2870 return 0;
2871 return aep->ae_attr;
2872}
2873
2874
2875 attrentry_T *
2876syn_term_attr2entry(int attr)
2877{
2878 attr -= ATTR_OFF;
2879 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2880 return NULL;
2881 return &(TERM_ATTR_ENTRY(attr));
2882}
2883
2884 attrentry_T *
2885syn_cterm_attr2entry(int attr)
2886{
2887 attr -= ATTR_OFF;
2888 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2889 return NULL;
2890 return &(CTERM_ATTR_ENTRY(attr));
2891}
2892
2893#define LIST_ATTR 1
2894#define LIST_STRING 2
2895#define LIST_INT 3
2896
2897 static void
2898highlight_list_one(int id)
2899{
2900 hl_group_T *sgp;
2901 int didh = FALSE;
2902
2903 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2904
2905 if (message_filtered(sgp->sg_name))
2906 return;
2907
2908 didh = highlight_list_arg(id, didh, LIST_ATTR,
2909 sgp->sg_term, NULL, "term");
2910 didh = highlight_list_arg(id, didh, LIST_STRING,
2911 0, sgp->sg_start, "start");
2912 didh = highlight_list_arg(id, didh, LIST_STRING,
2913 0, sgp->sg_stop, "stop");
2914
2915 didh = highlight_list_arg(id, didh, LIST_ATTR,
2916 sgp->sg_cterm, NULL, "cterm");
2917 didh = highlight_list_arg(id, didh, LIST_INT,
2918 sgp->sg_cterm_fg, NULL, "ctermfg");
2919 didh = highlight_list_arg(id, didh, LIST_INT,
2920 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002921 didh = highlight_list_arg(id, didh, LIST_INT,
2922 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002923
2924#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2925 didh = highlight_list_arg(id, didh, LIST_ATTR,
2926 sgp->sg_gui, NULL, "gui");
2927 didh = highlight_list_arg(id, didh, LIST_STRING,
2928 0, sgp->sg_gui_fg_name, "guifg");
2929 didh = highlight_list_arg(id, didh, LIST_STRING,
2930 0, sgp->sg_gui_bg_name, "guibg");
2931 didh = highlight_list_arg(id, didh, LIST_STRING,
2932 0, sgp->sg_gui_sp_name, "guisp");
2933#endif
2934#ifdef FEAT_GUI
2935 didh = highlight_list_arg(id, didh, LIST_STRING,
2936 0, sgp->sg_font_name, "font");
2937#endif
2938
2939 if (sgp->sg_link && !got_int)
2940 {
2941 (void)syn_list_header(didh, 9999, id);
2942 didh = TRUE;
2943 msg_puts_attr("links to", HL_ATTR(HLF_D));
2944 msg_putchar(' ');
2945 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2946 }
2947
2948 if (!didh)
2949 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2950#ifdef FEAT_EVAL
2951 if (p_verbose > 0)
2952 last_set_msg(sgp->sg_script_ctx);
2953#endif
2954}
2955
2956 static int
2957highlight_list_arg(
2958 int id,
2959 int didh,
2960 int type,
2961 int iarg,
2962 char_u *sarg,
2963 char *name)
2964{
2965 char_u buf[100];
2966 char_u *ts;
2967 int i;
2968
2969 if (got_int)
2970 return FALSE;
2971 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2972 {
2973 ts = buf;
2974 if (type == LIST_INT)
2975 sprintf((char *)buf, "%d", iarg - 1);
2976 else if (type == LIST_STRING)
2977 ts = sarg;
2978 else // type == LIST_ATTR
2979 {
2980 buf[0] = NUL;
2981 for (i = 0; hl_attr_table[i] != 0; ++i)
2982 {
2983 if (iarg & hl_attr_table[i])
2984 {
2985 if (buf[0] != NUL)
2986 vim_strcat(buf, (char_u *)",", 100);
2987 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2988 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2989 }
2990 }
2991 }
2992
2993 (void)syn_list_header(didh,
2994 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2995 didh = TRUE;
2996 if (!got_int)
2997 {
2998 if (*name != NUL)
2999 {
3000 msg_puts_attr(name, HL_ATTR(HLF_D));
3001 msg_puts_attr("=", HL_ATTR(HLF_D));
3002 }
3003 msg_outtrans(ts);
3004 }
3005 }
3006 return didh;
3007}
3008
3009#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3010/*
3011 * Return "1" if highlight group "id" has attribute "flag".
3012 * Return NULL otherwise.
3013 */
3014 char_u *
3015highlight_has_attr(
3016 int id,
3017 int flag,
3018 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3019{
3020 int attr;
3021
3022 if (id <= 0 || id > highlight_ga.ga_len)
3023 return NULL;
3024
3025#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3026 if (modec == 'g')
3027 attr = HL_TABLE()[id - 1].sg_gui;
3028 else
3029#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003030 {
3031 if (modec == 'c')
3032 attr = HL_TABLE()[id - 1].sg_cterm;
3033 else
3034 attr = HL_TABLE()[id - 1].sg_term;
3035 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003036
3037 if (attr & flag)
3038 return (char_u *)"1";
3039 return NULL;
3040}
3041#endif
3042
3043#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3044/*
3045 * Return color name of highlight group "id".
3046 */
3047 char_u *
3048highlight_color(
3049 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003050 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003051 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3052{
3053 static char_u name[20];
3054 int n;
3055 int fg = FALSE;
3056 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003057 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003058 int font = FALSE;
3059
3060 if (id <= 0 || id > highlight_ga.ga_len)
3061 return NULL;
3062
3063 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3064 fg = TRUE;
3065 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3066 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3067 font = TRUE;
3068 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3069 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003070 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3071 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003072 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3073 return NULL;
3074 if (modec == 'g')
3075 {
3076# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3077# ifdef FEAT_GUI
3078 // return font name
3079 if (font)
3080 return HL_TABLE()[id - 1].sg_font_name;
3081# endif
3082
3083 // return #RRGGBB form (only possible when GUI is running)
3084 if ((USE_24BIT) && what[2] == '#')
3085 {
3086 guicolor_T color;
3087 long_u rgb;
3088 static char_u buf[10];
3089
3090 if (fg)
3091 color = HL_TABLE()[id - 1].sg_gui_fg;
3092 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003093 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003094 else
3095 color = HL_TABLE()[id - 1].sg_gui_bg;
3096 if (color == INVALCOLOR)
3097 return NULL;
3098 rgb = (long_u)GUI_MCH_GET_RGB(color);
3099 sprintf((char *)buf, "#%02x%02x%02x",
3100 (unsigned)(rgb >> 16),
3101 (unsigned)(rgb >> 8) & 255,
3102 (unsigned)rgb & 255);
3103 return buf;
3104 }
3105# endif
3106 if (fg)
3107 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3108 if (sp)
3109 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3110 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3111 }
3112 if (font || sp)
3113 return NULL;
3114 if (modec == 'c')
3115 {
3116 if (fg)
3117 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003118 else if (ul)
3119 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003120 else
3121 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3122 if (n < 0)
3123 return NULL;
3124 sprintf((char *)name, "%d", n);
3125 return name;
3126 }
3127 // term doesn't have color
3128 return NULL;
3129}
3130#endif
3131
3132#if (defined(FEAT_SYN_HL) \
3133 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3134 && defined(FEAT_PRINTER)) || defined(PROTO)
3135/*
3136 * Return color name of highlight group "id" as RGB value.
3137 */
3138 long_u
3139highlight_gui_color_rgb(
3140 int id,
3141 int fg) // TRUE = fg, FALSE = bg
3142{
3143 guicolor_T color;
3144
3145 if (id <= 0 || id > highlight_ga.ga_len)
3146 return 0L;
3147
3148 if (fg)
3149 color = HL_TABLE()[id - 1].sg_gui_fg;
3150 else
3151 color = HL_TABLE()[id - 1].sg_gui_bg;
3152
3153 if (color == INVALCOLOR)
3154 return 0L;
3155
3156 return GUI_MCH_GET_RGB(color);
3157}
3158#endif
3159
3160/*
3161 * Output the syntax list header.
3162 * Return TRUE when started a new line.
3163 */
3164 int
3165syn_list_header(
3166 int did_header, // did header already
3167 int outlen, // length of string that comes
3168 int id) // highlight group id
3169{
3170 int endcol = 19;
3171 int newline = TRUE;
3172 int name_col = 0;
3173
3174 if (!did_header)
3175 {
3176 msg_putchar('\n');
3177 if (got_int)
3178 return TRUE;
3179 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3180 name_col = msg_col;
3181 endcol = 15;
3182 }
3183 else if (msg_col + outlen + 1 >= Columns)
3184 {
3185 msg_putchar('\n');
3186 if (got_int)
3187 return TRUE;
3188 }
3189 else
3190 {
3191 if (msg_col >= endcol) // wrap around is like starting a new line
3192 newline = FALSE;
3193 }
3194
3195 if (msg_col >= endcol) // output at least one space
3196 endcol = msg_col + 1;
3197 if (Columns <= endcol) // avoid hang for tiny window
3198 endcol = Columns - 1;
3199
3200 msg_advance(endcol);
3201
3202 // Show "xxx" with the attributes.
3203 if (!did_header)
3204 {
3205 if (endcol == Columns - 1 && endcol <= name_col)
3206 msg_putchar(' ');
3207 msg_puts_attr("xxx", syn_id2attr(id));
3208 msg_putchar(' ');
3209 }
3210
3211 return newline;
3212}
3213
3214/*
3215 * Set the attribute numbers for a highlight group.
3216 * Called after one of the attributes has changed.
3217 */
3218 static void
3219set_hl_attr(
3220 int idx) // index in array
3221{
3222 attrentry_T at_en;
3223 hl_group_T *sgp = HL_TABLE() + idx;
3224
3225 // The "Normal" group doesn't need an attribute number
3226 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3227 return;
3228
3229#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003230 // For the GUI mode: If there are other than "normal" highlighting
3231 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003232 if (sgp->sg_gui_fg == INVALCOLOR
3233 && sgp->sg_gui_bg == INVALCOLOR
3234 && sgp->sg_gui_sp == INVALCOLOR
3235 && sgp->sg_font == NOFONT
3236# ifdef FEAT_XFONTSET
3237 && sgp->sg_fontset == NOFONTSET
3238# endif
3239 )
3240 {
3241 sgp->sg_gui_attr = sgp->sg_gui;
3242 }
3243 else
3244 {
3245 at_en.ae_attr = sgp->sg_gui;
3246 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3247 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3248 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3249 at_en.ae_u.gui.font = sgp->sg_font;
3250# ifdef FEAT_XFONTSET
3251 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3252# endif
3253 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3254 }
3255#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003256 // For the term mode: If there are other than "normal" highlighting
3257 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003258 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3259 sgp->sg_term_attr = sgp->sg_term;
3260 else
3261 {
3262 at_en.ae_attr = sgp->sg_term;
3263 at_en.ae_u.term.start = sgp->sg_start;
3264 at_en.ae_u.term.stop = sgp->sg_stop;
3265 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3266 }
3267
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003268 // For the color term mode: If there are other than "normal"
3269 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003270 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003271# ifdef FEAT_TERMGUICOLORS
3272 && sgp->sg_gui_fg == INVALCOLOR
3273 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003274 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003275# endif
3276 )
3277 sgp->sg_cterm_attr = sgp->sg_cterm;
3278 else
3279 {
3280 at_en.ae_attr = sgp->sg_cterm;
3281 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3282 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003283 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003284# ifdef FEAT_TERMGUICOLORS
3285# ifdef MSWIN
3286# ifdef VIMDLL
3287 // Only when not using the GUI.
3288 if (!gui.in_use && !gui.starting)
3289# endif
3290 {
3291 int id;
3292 guicolor_T fg, bg;
3293
3294 id = syn_name2id((char_u *)"Normal");
3295 if (id > 0)
3296 {
3297 syn_id2colors(id, &fg, &bg);
3298 if (sgp->sg_gui_fg == INVALCOLOR)
3299 sgp->sg_gui_fg = fg;
3300 if (sgp->sg_gui_bg == INVALCOLOR)
3301 sgp->sg_gui_bg = bg;
3302 }
3303
3304 }
3305# endif
3306 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3307 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003308 // Only use the underline/undercurl color when used, it may clear the
3309 // background color if not supported.
3310 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
3311 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3312 else
3313 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003314 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3315 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3316 {
3317 // If both fg and bg are invalid fall back to the cterm colors.
3318 // Helps when the GUI only uses an attribute, e.g. undercurl.
3319 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3320 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3321 }
3322# endif
3323 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3324 }
3325}
3326
3327/*
3328 * Lookup a highlight group name and return its ID.
3329 * If it is not found, 0 is returned.
3330 */
3331 int
3332syn_name2id(char_u *name)
3333{
3334 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003335 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003336
3337 // Avoid using stricmp() too much, it's slow on some systems
3338 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3339 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003340 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003341 vim_strup(name_u);
3342 for (i = highlight_ga.ga_len; --i >= 0; )
3343 if (HL_TABLE()[i].sg_name_u != NULL
3344 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3345 break;
3346 return i + 1;
3347}
3348
3349/*
3350 * Lookup a highlight group name and return its attributes.
3351 * Return zero if not found.
3352 */
3353 int
3354syn_name2attr(char_u *name)
3355{
3356 int id = syn_name2id(name);
3357
3358 if (id != 0)
3359 return syn_id2attr(id);
3360 return 0;
3361}
3362
3363#if defined(FEAT_EVAL) || defined(PROTO)
3364/*
3365 * Return TRUE if highlight group "name" exists.
3366 */
3367 int
3368highlight_exists(char_u *name)
3369{
3370 return (syn_name2id(name) > 0);
3371}
3372
3373# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3374/*
3375 * Return the name of highlight group "id".
3376 * When not a valid ID return an empty string.
3377 */
3378 char_u *
3379syn_id2name(int id)
3380{
3381 if (id <= 0 || id > highlight_ga.ga_len)
3382 return (char_u *)"";
3383 return HL_TABLE()[id - 1].sg_name;
3384}
3385# endif
3386#endif
3387
3388/*
3389 * Like syn_name2id(), but take a pointer + length argument.
3390 */
3391 int
3392syn_namen2id(char_u *linep, int len)
3393{
3394 char_u *name;
3395 int id = 0;
3396
3397 name = vim_strnsave(linep, len);
3398 if (name != NULL)
3399 {
3400 id = syn_name2id(name);
3401 vim_free(name);
3402 }
3403 return id;
3404}
3405
3406/*
3407 * Find highlight group name in the table and return its ID.
3408 * The argument is a pointer to the name and the length of the name.
3409 * If it doesn't exist yet, a new entry is created.
3410 * Return 0 for failure.
3411 */
3412 int
3413syn_check_group(char_u *pp, int len)
3414{
3415 int id;
3416 char_u *name;
3417
erw7f7f7aaf2021-12-07 21:29:20 +00003418 if (len > MAX_SYN_NAME)
3419 {
3420 emsg(_(e_highlight_group_name_too_long));
3421 return 0;
3422 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003423 name = vim_strnsave(pp, len);
3424 if (name == NULL)
3425 return 0;
3426
3427 id = syn_name2id(name);
3428 if (id == 0) // doesn't exist yet
3429 id = syn_add_group(name);
3430 else
3431 vim_free(name);
3432 return id;
3433}
3434
3435/*
3436 * Add new highlight group and return its ID.
3437 * "name" must be an allocated string, it will be consumed.
3438 * Return 0 for failure.
3439 */
3440 static int
3441syn_add_group(char_u *name)
3442{
3443 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003444 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003445
3446 // Check that the name is ASCII letters, digits and underscore.
3447 for (p = name; *p != NUL; ++p)
3448 {
3449 if (!vim_isprintc(*p))
3450 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003451 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003452 vim_free(name);
3453 return 0;
3454 }
3455 else if (!ASCII_ISALNUM(*p) && *p != '_')
3456 {
3457 // This is an error, but since there previously was no check only
3458 // give a warning.
3459 msg_source(HL_ATTR(HLF_W));
3460 msg(_("W18: Invalid character in group name"));
3461 break;
3462 }
3463 }
3464
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003465 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003466 if (highlight_ga.ga_data == NULL)
3467 {
3468 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3469 highlight_ga.ga_growsize = 10;
3470 }
3471
3472 if (highlight_ga.ga_len >= MAX_HL_ID)
3473 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003474 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003475 vim_free(name);
3476 return 0;
3477 }
3478
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003479 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003480 if (ga_grow(&highlight_ga, 1) == FAIL)
3481 {
3482 vim_free(name);
3483 return 0;
3484 }
3485
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003486 name_up = vim_strsave_up(name);
3487 if (name_up == NULL)
3488 {
3489 vim_free(name);
3490 return 0;
3491 }
3492
Bram Moolenaara80faa82020-04-12 19:37:17 +02003493 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003494 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003495 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003496#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3497 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3498 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003499 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003500#endif
3501 ++highlight_ga.ga_len;
3502
3503 return highlight_ga.ga_len; // ID is index plus one
3504}
3505
3506/*
3507 * When, just after calling syn_add_group(), an error is discovered, this
3508 * function deletes the new name.
3509 */
3510 static void
3511syn_unadd_group(void)
3512{
3513 --highlight_ga.ga_len;
3514 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3515 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3516}
3517
3518/*
3519 * Translate a group ID to highlight attributes.
3520 */
3521 int
3522syn_id2attr(int hl_id)
3523{
3524 int attr;
3525 hl_group_T *sgp;
3526
3527 hl_id = syn_get_final_id(hl_id);
3528 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3529
3530#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003531 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003532 if (gui.in_use)
3533 attr = sgp->sg_gui_attr;
3534 else
3535#endif
3536 if (IS_CTERM)
3537 attr = sgp->sg_cterm_attr;
3538 else
3539 attr = sgp->sg_term_attr;
3540
3541 return attr;
3542}
3543
3544#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3545/*
3546 * Get the GUI colors and attributes for a group ID.
3547 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3548 */
3549 int
3550syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3551{
3552 hl_group_T *sgp;
3553
3554 hl_id = syn_get_final_id(hl_id);
3555 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3556
3557 *fgp = sgp->sg_gui_fg;
3558 *bgp = sgp->sg_gui_bg;
3559 return sgp->sg_gui;
3560}
3561#endif
3562
3563#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003564 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3565 && defined(FEAT_TERMGUICOLORS)) \
3566 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003567 void
3568syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3569{
3570 hl_group_T *sgp;
3571
3572 hl_id = syn_get_final_id(hl_id);
3573 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3574 *fgp = sgp->sg_cterm_fg - 1;
3575 *bgp = sgp->sg_cterm_bg - 1;
3576}
3577#endif
3578
3579/*
3580 * Translate a group ID to the final group ID (following links).
3581 */
3582 int
3583syn_get_final_id(int hl_id)
3584{
3585 int count;
3586 hl_group_T *sgp;
3587
3588 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3589 return 0; // Can be called from eval!!
3590
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003591 // Follow links until there is no more.
3592 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003593 for (count = 100; --count >= 0; )
3594 {
3595 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3596 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3597 break;
3598 hl_id = sgp->sg_link;
3599 }
3600
3601 return hl_id;
3602}
3603
3604#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3605/*
3606 * Call this function just after the GUI has started.
3607 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3608 * It finds the font and color handles for the highlighting groups.
3609 */
3610 void
3611highlight_gui_started(void)
3612{
3613 int idx;
3614
3615 // First get the colors from the "Normal" and "Menu" group, if set
3616 if (USE_24BIT)
3617 set_normal_colors();
3618
3619 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3620 gui_do_one_color(idx, FALSE, FALSE);
3621
3622 highlight_changed();
3623}
3624
3625 static void
3626gui_do_one_color(
3627 int idx,
3628 int do_menu UNUSED, // TRUE: might set the menu font
3629 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3630{
3631 int didit = FALSE;
3632
3633# ifdef FEAT_GUI
3634# ifdef FEAT_TERMGUICOLORS
3635 if (gui.in_use)
3636# endif
3637 if (HL_TABLE()[idx].sg_font_name != NULL)
3638 {
3639 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3640 do_tooltip, TRUE);
3641 didit = TRUE;
3642 }
3643# endif
3644 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3645 {
3646 HL_TABLE()[idx].sg_gui_fg =
3647 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3648 didit = TRUE;
3649 }
3650 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3651 {
3652 HL_TABLE()[idx].sg_gui_bg =
3653 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3654 didit = TRUE;
3655 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003656 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3657 {
3658 HL_TABLE()[idx].sg_gui_sp =
3659 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3660 didit = TRUE;
3661 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003662 if (didit) // need to get a new attr number
3663 set_hl_attr(idx);
3664}
3665#endif
3666
3667#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3668/*
3669 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3670 */
3671 static void
3672combine_stl_hlt(
3673 int id,
3674 int id_S,
3675 int id_alt,
3676 int hlcnt,
3677 int i,
3678 int hlf,
3679 int *table)
3680{
3681 hl_group_T *hlt = HL_TABLE();
3682
3683 if (id_alt == 0)
3684 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003685 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003686 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3687 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3688# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3689 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3690# endif
3691 }
3692 else
3693 mch_memmove(&hlt[hlcnt + i],
3694 &hlt[id_alt - 1],
3695 sizeof(hl_group_T));
3696 hlt[hlcnt + i].sg_link = 0;
3697
3698 hlt[hlcnt + i].sg_term ^=
3699 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3700 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3701 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3702 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3703 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3704 hlt[hlcnt + i].sg_cterm ^=
3705 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3706 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3707 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3708 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3709 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3710# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3711 hlt[hlcnt + i].sg_gui ^=
3712 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3713# endif
3714# ifdef FEAT_GUI
3715 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3716 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3717 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3718 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3719 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3720 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3721 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3722 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3723# ifdef FEAT_XFONTSET
3724 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3725 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3726# endif
3727# endif
3728 highlight_ga.ga_len = hlcnt + i + 1;
3729 set_hl_attr(hlcnt + i); // At long last we can apply
3730 table[i] = syn_id2attr(hlcnt + i + 1);
3731}
3732#endif
3733
3734/*
3735 * Translate the 'highlight' option into attributes in highlight_attr[] and
3736 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3737 * corresponding highlights to use on top of HLF_SNC is computed.
3738 * Called only when the 'highlight' option has been changed and upon first
3739 * screen redraw after any :highlight command.
3740 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3741 */
3742 int
3743highlight_changed(void)
3744{
3745 int hlf;
3746 int i;
3747 char_u *p;
3748 int attr;
3749 char_u *end;
3750 int id;
3751#ifdef USER_HIGHLIGHT
3752 char_u userhl[30]; // use 30 to avoid compiler warning
3753# ifdef FEAT_STL_OPT
3754 int id_S = -1;
3755 int id_SNC = 0;
3756# ifdef FEAT_TERMINAL
3757 int id_ST = 0;
3758 int id_STNC = 0;
3759# endif
3760 int hlcnt;
3761# endif
3762#endif
3763 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3764
3765 need_highlight_changed = FALSE;
3766
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003767#ifdef FEAT_TERMINAL
3768 term_update_colors_all();
3769 term_update_wincolor_all();
3770#endif
3771
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003772 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003773 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3774 highlight_attr[hlf] = 0;
3775
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003776 // First set all attributes to their default value.
3777 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003778 for (i = 0; i < 2; ++i)
3779 {
3780 if (i)
3781 p = p_hl;
3782 else
3783 p = get_highlight_default();
3784 if (p == NULL) // just in case
3785 continue;
3786
3787 while (*p)
3788 {
3789 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3790 if (hl_flags[hlf] == *p)
3791 break;
3792 ++p;
3793 if (hlf == (int)HLF_COUNT || *p == NUL)
3794 return FAIL;
3795
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003796 // Allow several hl_flags to be combined, like "bu" for
3797 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003798 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003799 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003800 {
3801 if (VIM_ISWHITE(*p)) // ignore white space
3802 continue;
3803
3804 if (attr > HL_ALL) // Combination with ':' is not allowed.
3805 return FAIL;
3806
3807 switch (*p)
3808 {
3809 case 'b': attr |= HL_BOLD;
3810 break;
3811 case 'i': attr |= HL_ITALIC;
3812 break;
3813 case '-':
3814 case 'n': // no highlighting
3815 break;
3816 case 'r': attr |= HL_INVERSE;
3817 break;
3818 case 's': attr |= HL_STANDOUT;
3819 break;
3820 case 'u': attr |= HL_UNDERLINE;
3821 break;
3822 case 'c': attr |= HL_UNDERCURL;
3823 break;
3824 case 't': attr |= HL_STRIKETHROUGH;
3825 break;
3826 case ':': ++p; // highlight group name
3827 if (attr || *p == NUL) // no combinations
3828 return FAIL;
3829 end = vim_strchr(p, ',');
3830 if (end == NULL)
3831 end = p + STRLEN(p);
3832 id = syn_check_group(p, (int)(end - p));
3833 if (id == 0)
3834 return FAIL;
3835 attr = syn_id2attr(id);
3836 p = end - 1;
3837#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3838 if (hlf == (int)HLF_SNC)
3839 id_SNC = syn_get_final_id(id);
3840# ifdef FEAT_TERMINAL
3841 else if (hlf == (int)HLF_ST)
3842 id_ST = syn_get_final_id(id);
3843 else if (hlf == (int)HLF_STNC)
3844 id_STNC = syn_get_final_id(id);
3845# endif
3846 else if (hlf == (int)HLF_S)
3847 id_S = syn_get_final_id(id);
3848#endif
3849 break;
3850 default: return FAIL;
3851 }
3852 }
3853 highlight_attr[hlf] = attr;
3854
3855 p = skip_to_option_part(p); // skip comma and spaces
3856 }
3857 }
3858
3859#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003860 // Setup the user highlights
3861 //
3862 // Temporarily utilize 28 more hl entries:
3863 // 9 for User1-User9 combined with StatusLineNC
3864 // 9 for User1-User9 combined with StatusLineTerm
3865 // 9 for User1-User9 combined with StatusLineTermNC
3866 // 1 for StatusLine default
3867 // Have to be in there simultaneously in case of table overflows in
3868 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003869# ifdef FEAT_STL_OPT
3870 if (ga_grow(&highlight_ga, 28) == FAIL)
3871 return FAIL;
3872 hlcnt = highlight_ga.ga_len;
3873 if (id_S == -1)
3874 {
3875 // Make sure id_S is always valid to simplify code below. Use the last
3876 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003877 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003878 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3879 id_S = hlcnt + 19;
3880 }
3881# endif
3882 for (i = 0; i < 9; i++)
3883 {
3884 sprintf((char *)userhl, "User%d", i + 1);
3885 id = syn_name2id(userhl);
3886 if (id == 0)
3887 {
3888 highlight_user[i] = 0;
3889# ifdef FEAT_STL_OPT
3890 highlight_stlnc[i] = 0;
3891# ifdef FEAT_TERMINAL
3892 highlight_stlterm[i] = 0;
3893 highlight_stltermnc[i] = 0;
3894# endif
3895# endif
3896 }
3897 else
3898 {
3899 highlight_user[i] = syn_id2attr(id);
3900# ifdef FEAT_STL_OPT
3901 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3902 HLF_SNC, highlight_stlnc);
3903# ifdef FEAT_TERMINAL
3904 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3905 HLF_ST, highlight_stlterm);
3906 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3907 HLF_STNC, highlight_stltermnc);
3908# endif
3909# endif
3910 }
3911 }
3912# ifdef FEAT_STL_OPT
3913 highlight_ga.ga_len = hlcnt;
3914# endif
3915
3916#endif // USER_HIGHLIGHT
3917
3918 return OK;
3919}
3920
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003921static void highlight_list(void);
3922static void highlight_list_two(int cnt, int attr);
3923
3924/*
3925 * Handle command line completion for :highlight command.
3926 */
3927 void
3928set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3929{
3930 char_u *p;
3931
3932 // Default: expand group names
3933 xp->xp_context = EXPAND_HIGHLIGHT;
3934 xp->xp_pattern = arg;
3935 include_link = 2;
3936 include_default = 1;
3937
3938 // (part of) subcommand already typed
3939 if (*arg != NUL)
3940 {
3941 p = skiptowhite(arg);
3942 if (*p != NUL) // past "default" or group name
3943 {
3944 include_default = 0;
3945 if (STRNCMP("default", arg, p - arg) == 0)
3946 {
3947 arg = skipwhite(p);
3948 xp->xp_pattern = arg;
3949 p = skiptowhite(arg);
3950 }
3951 if (*p != NUL) // past group name
3952 {
3953 include_link = 0;
3954 if (arg[1] == 'i' && arg[0] == 'N')
3955 highlight_list();
3956 if (STRNCMP("link", arg, p - arg) == 0
3957 || STRNCMP("clear", arg, p - arg) == 0)
3958 {
3959 xp->xp_pattern = skipwhite(p);
3960 p = skiptowhite(xp->xp_pattern);
3961 if (*p != NUL) // past first group name
3962 {
3963 xp->xp_pattern = skipwhite(p);
3964 p = skiptowhite(xp->xp_pattern);
3965 }
3966 }
3967 if (*p != NUL) // past group name(s)
3968 xp->xp_context = EXPAND_NOTHING;
3969 }
3970 }
3971 }
3972}
3973
3974/*
3975 * List highlighting matches in a nice way.
3976 */
3977 static void
3978highlight_list(void)
3979{
3980 int i;
3981
3982 for (i = 10; --i >= 0; )
3983 highlight_list_two(i, HL_ATTR(HLF_D));
3984 for (i = 40; --i >= 0; )
3985 highlight_list_two(99, 0);
3986}
3987
3988 static void
3989highlight_list_two(int cnt, int attr)
3990{
3991 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3992 msg_clr_eos();
3993 out_flush();
3994 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3995}
3996
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003997/*
3998 * Function given to ExpandGeneric() to obtain the list of group names.
3999 */
4000 char_u *
4001get_highlight_name(expand_T *xp UNUSED, int idx)
4002{
4003 return get_highlight_name_ext(xp, idx, TRUE);
4004}
4005
4006/*
4007 * Obtain a highlight group name.
4008 * When "skip_cleared" is TRUE don't return a cleared entry.
4009 */
4010 char_u *
4011get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4012{
4013 if (idx < 0)
4014 return NULL;
4015
4016 // Items are never removed from the table, skip the ones that were
4017 // cleared.
4018 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4019 return (char_u *)"";
4020
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004021 if (idx == highlight_ga.ga_len && include_none != 0)
4022 return (char_u *)"none";
4023 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4024 return (char_u *)"default";
4025 if (idx == highlight_ga.ga_len + include_none + include_default
4026 && include_link != 0)
4027 return (char_u *)"link";
4028 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4029 && include_link != 0)
4030 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004031 if (idx >= highlight_ga.ga_len)
4032 return NULL;
4033 return HL_TABLE()[idx].sg_name;
4034}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004035
4036#if defined(FEAT_GUI) || defined(PROTO)
4037/*
4038 * Free all the highlight group fonts.
4039 * Used when quitting for systems which need it.
4040 */
4041 void
4042free_highlight_fonts(void)
4043{
4044 int idx;
4045
4046 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4047 {
4048 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4049 HL_TABLE()[idx].sg_font = NOFONT;
4050# ifdef FEAT_XFONTSET
4051 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4052 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4053# endif
4054 }
4055
4056 gui_mch_free_font(gui.norm_font);
4057# ifdef FEAT_XFONTSET
4058 gui_mch_free_fontset(gui.fontset);
4059# endif
4060# ifndef FEAT_GUI_GTK
4061 gui_mch_free_font(gui.bold_font);
4062 gui_mch_free_font(gui.ital_font);
4063 gui_mch_free_font(gui.boldital_font);
4064# endif
4065}
4066#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004067
4068#if defined(FEAT_EVAL) || defined(PROTO)
4069/*
4070 * Convert each of the highlight attribute bits (bold, standout, underline,
4071 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4072 * the attribute name as the key.
4073 */
4074 static dict_T *
4075highlight_get_attr_dict(int hlattr)
4076{
4077 dict_T *dict;
4078 int i;
4079
4080 dict = dict_alloc();
4081 if (dict == NULL)
4082 return NULL;
4083
4084 for (i = 0; hl_attr_table[i] != 0; ++i)
4085 {
4086 if (hlattr & hl_attr_table[i])
4087 {
4088 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4089 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4090 }
4091 }
4092
4093 return dict;
4094}
4095
4096/*
4097 * Return the attributes of the highlight group at index 'hl_idx' as a
4098 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4099 * links recursively.
4100 */
4101 static dict_T *
4102highlight_get_info(int hl_idx, int resolve_link)
4103{
4104 dict_T *dict;
4105 hl_group_T *sgp;
4106 dict_T *attr_dict;
4107 int hlgid;
4108
4109 dict = dict_alloc();
4110 if (dict == NULL)
4111 return dict;
4112
4113 sgp = &HL_TABLE()[hl_idx];
4114 // highlight group id is 1-based
4115 hlgid = hl_idx + 1;
4116
4117 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4118 goto error;
4119 if (dict_add_number(dict, "id", hlgid) == FAIL)
4120 goto error;
4121
4122 if (sgp->sg_link && resolve_link)
4123 {
4124 // resolve the highlight group link recursively
4125 while (sgp->sg_link)
4126 {
4127 hlgid = sgp->sg_link;
4128 sgp = &HL_TABLE()[sgp->sg_link - 1];
4129 }
4130 }
4131
4132 if (sgp->sg_term != 0)
4133 {
4134 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4135 if (attr_dict != NULL)
4136 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4137 goto error;
4138 }
4139 if (sgp->sg_start != NULL)
4140 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4141 goto error;
4142 if (sgp->sg_stop != NULL)
4143 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4144 goto error;
4145 if (sgp->sg_cterm != 0)
4146 {
4147 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4148 if (attr_dict != NULL)
4149 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4150 goto error;
4151 }
4152 if (sgp->sg_cterm_fg != 0)
4153 if (dict_add_string(dict, "ctermfg",
4154 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4155 goto error;
4156 if (sgp->sg_cterm_bg != 0)
4157 if (dict_add_string(dict, "ctermbg",
4158 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4159 goto error;
4160 if (sgp->sg_cterm_ul != 0)
4161 if (dict_add_string(dict, "ctermul",
4162 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4163 goto error;
4164 if (sgp->sg_gui != 0)
4165 {
4166 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4167 if (attr_dict != NULL)
4168 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4169 goto error;
4170 }
4171 if (sgp->sg_gui_fg_name != NULL)
4172 if (dict_add_string(dict, "guifg",
4173 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4174 goto error;
4175 if (sgp->sg_gui_bg_name != NULL)
4176 if (dict_add_string(dict, "guibg",
4177 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4178 goto error;
4179 if (sgp->sg_gui_sp_name != NULL)
4180 if (dict_add_string(dict, "guisp",
4181 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4182 goto error;
4183# ifdef FEAT_GUI
4184 if (sgp->sg_font_name != NULL)
4185 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4186 goto error;
4187# endif
4188 if (sgp->sg_link)
4189 {
4190 char_u *link;
4191
4192 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4193 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4194 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004195
4196 if (sgp->sg_deflink)
4197 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004198 }
4199 if (dict_len(dict) == 2)
4200 // If only 'name' is present, then the highlight group is cleared.
4201 dict_add_bool(dict, "cleared", VVAL_TRUE);
4202
4203 return dict;
4204
4205error:
4206 vim_free(dict);
4207 return NULL;
4208}
4209
4210/*
4211 * "hlget([name])" function
4212 * Return the attributes of a specific highlight group (if specified) or all
4213 * the highlight groups.
4214 */
4215 void
4216f_hlget(typval_T *argvars, typval_T *rettv)
4217{
4218 list_T *list;
4219 dict_T *dict;
4220 int i;
4221 char_u *hlarg = NULL;
4222 int resolve_link = FALSE;
4223
4224 if (rettv_list_alloc(rettv) == FAIL)
4225 return;
4226
4227 if (check_for_opt_string_arg(argvars, 0) == FAIL
4228 || (argvars[0].v_type != VAR_UNKNOWN
4229 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4230 return;
4231
4232 if (argvars[0].v_type != VAR_UNKNOWN)
4233 {
4234 // highlight group name supplied
4235 hlarg = tv_get_string_chk(&argvars[0]);
4236 if (hlarg == NULL)
4237 return;
4238
4239 if (argvars[1].v_type != VAR_UNKNOWN)
4240 {
4241 int error = FALSE;
4242
4243 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4244 if (error)
4245 return;
4246 }
4247 }
4248
4249 list = rettv->vval.v_list;
4250 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4251 {
4252 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4253 {
4254 dict = highlight_get_info(i, resolve_link);
4255 if (dict != NULL)
4256 list_append_dict(list, dict);
4257 }
4258 }
4259}
4260
4261/*
4262 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4263 * 'dict' or the value is not a string type. If the value is not a string type
4264 * or is NULL, then 'error' is set to TRUE.
4265 */
4266 static char_u *
4267hldict_get_string(dict_T *dict, char_u *key, int *error)
4268{
4269 dictitem_T *di;
4270
4271 *error = FALSE;
4272 di = dict_find(dict, key, -1);
4273 if (di == NULL)
4274 return NULL;
4275
4276 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4277 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004278 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004279 *error = TRUE;
4280 return NULL;
4281 }
4282
4283 return di->di_tv.vval.v_string;
4284}
4285
4286/*
4287 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4288 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4289 * Dictionary or is NULL.
4290 */
4291 static int
4292hldict_attr_to_str(
4293 dict_T *dict,
4294 char_u *key,
4295 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004296 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004297{
4298 dictitem_T *di;
4299 dict_T *attrdict;
4300 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004301 char_u *p;
4302 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004303
4304 attr_str[0] = NUL;
4305 di = dict_find(dict, key, -1);
4306 if (di == NULL)
4307 return TRUE;
4308
4309 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4310 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004311 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004312 return FALSE;
4313 }
4314
4315 attrdict = di->di_tv.vval.v_dict;
4316
4317 // If the attribute dict is empty, then return NONE to clear the attributes
4318 if (dict_len(attrdict) == 0)
4319 {
4320 vim_strcat(attr_str, (char_u *)"NONE", len);
4321 return TRUE;
4322 }
4323
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004324 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004325 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4326 {
4327 if (dict_get_bool(attrdict, (char_u *)hl_name_table[i],
4328 VVAL_FALSE) == VVAL_TRUE)
4329 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004330 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4331 STRCPY(p, (char_u *)",");
4332 sz = STRLEN(hl_name_table[i]);
4333 if (p - attr_str + sz + 1 < len)
4334 {
4335 STRCPY(p, (char_u *)hl_name_table[i]);
4336 p += sz;
4337 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004338 }
4339 }
4340
4341 return TRUE;
4342}
4343
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004344// Temporary buffer used to store the command string produced by hlset().
4345// IObuff cannot be used for this as the error messages produced by hlset()
4346// internally use IObuff.
4347#define HLSETBUFSZ 512
4348static char_u hlsetBuf[HLSETBUFSZ + 1];
4349
4350/*
4351 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4352 * "dptr", which points into "hlsetBuf".
4353 * Returns the updated pointer.
4354 */
4355 static char_u *
4356add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4357{
4358 size_t vallen;
4359
4360 // Do nothing if the value is not specified or is empty
4361 if (value == NULL || *value == NUL)
4362 return dptr;
4363
4364 vallen = STRLEN(value);
4365 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4366 {
4367 STRCPY(dptr, attr);
4368 dptr += attrlen;
4369 STRCPY(dptr, value);
4370 dptr += vallen;
4371 }
4372
4373 return dptr;
4374}
4375
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004376/*
4377 * Add or update a highlight group using 'dict' items. Returns TRUE if
4378 * successfully updated the highlight group.
4379 */
4380 static int
4381hlg_add_or_update(dict_T *dict)
4382{
4383 char_u *name;
4384 int error;
4385 char_u term_attr[80];
4386 char_u cterm_attr[80];
4387 char_u gui_attr[80];
4388 char_u *start;
4389 char_u *stop;
4390 char_u *ctermfg;
4391 char_u *ctermbg;
4392 char_u *ctermul;
4393 char_u *guifg;
4394 char_u *guibg;
4395 char_u *guisp;
4396# ifdef FEAT_GUI
4397 char_u *font;
4398# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004399 int forceit = FALSE;
4400 int dodefault = FALSE;
4401 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004402 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004403
4404 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004405 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004406 return FALSE;
4407
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004408 if (dict_get_bool(dict, (char_u *)"force", VVAL_FALSE) == VVAL_TRUE)
4409 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004410
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004411 if (dict_get_bool(dict, (char_u *)"default", VVAL_FALSE) == VVAL_TRUE)
4412 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004413
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004414 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004415 {
4416 varnumber_T cleared;
4417
4418 // clear a highlight group
4419 cleared = dict_get_bool(dict, (char_u *)"cleared", FALSE);
4420 if (cleared == TRUE)
4421 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004422 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4423 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004424 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004425 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004426 }
4427
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004428 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004429 {
4430 char_u *linksto;
4431
4432 // link highlight groups
4433 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004434 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004435 return FALSE;
4436
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004437 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004438 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004439 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004440
4441 done = TRUE;
4442 }
4443
4444 // If 'cleared' or 'linksto' are specified, then don't process the other
4445 // attributes.
4446 if (done)
4447 return TRUE;
4448
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004449 start = hldict_get_string(dict, (char_u *)"start", &error);
4450 if (error)
4451 return FALSE;
4452
4453 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4454 if (error)
4455 return FALSE;
4456
4457 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004458 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004459 return FALSE;
4460
4461 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004462 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004463 return FALSE;
4464
4465 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4466 if (error)
4467 return FALSE;
4468
4469 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4470 if (error)
4471 return FALSE;
4472
4473 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4474 if (error)
4475 return FALSE;
4476
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004477 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004478 return FALSE;
4479
4480 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4481 if (error)
4482 return FALSE;
4483
4484 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4485 if (error)
4486 return FALSE;
4487
4488 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4489 if (error)
4490 return FALSE;
4491
4492# ifdef FEAT_GUI
4493 font = hldict_get_string(dict, (char_u *)"font", &error);
4494 if (error)
4495 return FALSE;
4496# endif
4497
4498 // If none of the attributes are specified, then do nothing.
4499 if (term_attr[0] == NUL && start == NULL && stop == NULL
4500 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4501 && ctermul == NULL && gui_attr[0] == NUL
4502# ifdef FEAT_GUI
4503 && font == NULL
4504# endif
4505 && guifg == NULL && guibg == NULL && guisp == NULL
4506 )
4507 return TRUE;
4508
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004509 hlsetBuf[0] = NUL;
4510 p = hlsetBuf;
4511 if (dodefault)
4512 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4513 p = add_attr_and_value(p, (char_u *)"", 0, name);
4514 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4515 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4516 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4517 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4518 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4519 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4520 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4521 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004522# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004523 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004524# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004525 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4526 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
4527 p = add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004528
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004529 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004530
4531 return TRUE;
4532}
4533
4534/*
4535 * "hlset([{highlight_attr}])" function
4536 * Add or modify highlight groups
4537 */
4538 void
4539f_hlset(typval_T *argvars, typval_T *rettv)
4540{
4541 listitem_T *li;
4542 dict_T *dict;
4543
4544 rettv->vval.v_number = -1;
4545
4546 if (check_for_list_arg(argvars, 0) == FAIL)
4547 return;
4548
4549 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4550 {
4551 if (li->li_tv.v_type != VAR_DICT)
4552 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004553 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004554 return;
4555 }
4556
4557 dict = li->li_tv.vval.v_dict;
4558 if (!hlg_add_or_update(dict))
4559 return;
4560 }
4561
4562 rettv->vval.v_number = 0;
4563}
4564#endif