blob: efbc5b10e29b6ecbc7f1f3d421016c96d837028e [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};
32#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
33
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
463/*
464 * Load color file "name".
465 * Return OK for success, FAIL for failure.
466 */
467 int
468load_colors(char_u *name)
469{
470 char_u *buf;
471 int retval = FAIL;
472 static int recursive = FALSE;
473
474 // When being called recursively, this is probably because setting
475 // 'background' caused the highlighting to be reloaded. This means it is
476 // working, thus we should return OK.
477 if (recursive)
478 return OK;
479
480 recursive = TRUE;
481 buf = alloc(STRLEN(name) + 12);
482 if (buf != NULL)
483 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100484#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100485 load_default_colors_lists();
486#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200487 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
488 curbuf->b_fname, FALSE, curbuf);
489 sprintf((char *)buf, "colors/%s.vim", name);
490 retval = source_runtime(buf, DIP_START + DIP_OPT);
491 vim_free(buf);
492 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
493 }
494 recursive = FALSE;
495
496 return retval;
497}
498
499static char *(color_names[28]) = {
500 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
501 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
502 "Gray", "Grey", "LightGray", "LightGrey",
503 "DarkGray", "DarkGrey",
504 "Blue", "LightBlue", "Green", "LightGreen",
505 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
506 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
507 // indices:
508 // 0, 1, 2, 3,
509 // 4, 5, 6, 7,
510 // 8, 9, 10, 11,
511 // 12, 13,
512 // 14, 15, 16, 17,
513 // 18, 19, 20, 21, 22,
514 // 23, 24, 25, 26, 27
515static int color_numbers_16[28] = {0, 1, 2, 3,
516 4, 5, 6, 6,
517 7, 7, 7, 7,
518 8, 8,
519 9, 9, 10, 10,
520 11, 11, 12, 12, 13,
521 13, 14, 14, 15, -1};
522// for xterm with 88 colors...
523static int color_numbers_88[28] = {0, 4, 2, 6,
524 1, 5, 32, 72,
525 84, 84, 7, 7,
526 82, 82,
527 12, 43, 10, 61,
528 14, 63, 9, 74, 13,
529 75, 11, 78, 15, -1};
530// for xterm with 256 colors...
531static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200532 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200533 248, 248, 7, 7,
534 242, 242,
535 12, 81, 10, 121,
536 14, 159, 9, 224, 13,
537 225, 11, 229, 15, -1};
538// for terminals with less than 16 colors...
539static int color_numbers_8[28] = {0, 4, 2, 6,
540 1, 5, 3, 3,
541 7, 7, 7, 7,
542 0+8, 0+8,
543 4+8, 4+8, 2+8, 2+8,
544 6+8, 6+8, 1+8, 1+8, 5+8,
545 5+8, 3+8, 3+8, 7+8, -1};
546
547/*
548 * Lookup the "cterm" value to be used for color with index "idx" in
549 * color_names[].
550 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
551 * colors, otherwise it will be unchanged.
552 */
553 int
554lookup_color(int idx, int foreground, int *boldp)
555{
556 int color = color_numbers_16[idx];
557 char_u *p;
558
559 // Use the _16 table to check if it's a valid color name.
560 if (color < 0)
561 return -1;
562
563 if (t_colors == 8)
564 {
565 // t_Co is 8: use the 8 colors table
566#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100567 // On qnx, the 8 & 16 color arrays are the same
568 if (STRNCMP(T_NAME, "qansi", 5) == 0)
569 color = color_numbers_16[idx];
570 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200571#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100572 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200573 if (foreground)
574 {
575 // set/reset bold attribute to get light foreground
576 // colors (on some terminals, e.g. "linux")
577 if (color & 8)
578 *boldp = TRUE;
579 else
580 *boldp = FALSE;
581 }
582 color &= 7; // truncate to 8 colors
583 }
584 else if (t_colors == 16 || t_colors == 88
585 || t_colors >= 256)
586 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100587 // Guess: if the termcap entry ends in 'm', it is
588 // probably an xterm-like terminal. Use the changed
589 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200590 if (*T_CAF != NUL)
591 p = T_CAF;
592 else
593 p = T_CSF;
594 if (*p != NUL && (t_colors > 256
595 || *(p + STRLEN(p) - 1) == 'm'))
596 {
597 if (t_colors == 88)
598 color = color_numbers_88[idx];
599 else if (t_colors >= 256)
600 color = color_numbers_256[idx];
601 else
602 color = color_numbers_8[idx];
603 }
604#ifdef FEAT_TERMRESPONSE
605 if (t_colors >= 256 && color == 15 && is_mac_terminal)
606 // Terminal.app has a bug: 15 is light grey. Use white
607 // from the color cube instead.
608 color = 231;
609#endif
610 }
611 return color;
612}
613
614/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100615 * Link highlight group 'from_hg' to 'to_hg'.
616 * 'dodefault' is set to TRUE for ":highlight default link".
617 * 'forceit' is set to TRUE for ":higlight! link"
618 * 'init' is set to TRUE when initializing all the highlight groups.
619 */
620 static void
621highlight_group_link(
622 char_u *from_hg,
623 int from_len,
624 char_u *to_hg,
625 int to_len,
626 int dodefault,
627 int forceit,
628 int init)
629{
630 int from_id;
631 int to_id;
632 hl_group_T *hlgroup = NULL;
633
634 from_id = syn_check_group(from_hg, from_len);
635 if (STRNCMP(to_hg, "NONE", 4) == 0)
636 to_id = 0;
637 else
638 to_id = syn_check_group(to_hg, to_len);
639
640 if (from_id > 0)
641 {
642 hlgroup = &HL_TABLE()[from_id - 1];
643 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
644 {
645 hlgroup->sg_deflink = to_id;
646#ifdef FEAT_EVAL
647 hlgroup->sg_deflink_sctx = current_sctx;
648 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
649#endif
650 }
651 }
652
653 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
654 {
655 // Don't allow a link when there already is some highlighting
656 // for the group, unless '!' is used
657 if (to_id > 0 && !forceit && !init
658 && hl_has_settings(from_id - 1, dodefault))
659 {
660 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000661 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100662 }
663 else if (hlgroup->sg_link != to_id
664#ifdef FEAT_EVAL
665 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
666#endif
667 || hlgroup->sg_cleared)
668 {
669 if (!init)
670 hlgroup->sg_set |= SG_LINK;
671 hlgroup->sg_link = to_id;
672#ifdef FEAT_EVAL
673 hlgroup->sg_script_ctx = current_sctx;
674 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
675#endif
676 hlgroup->sg_cleared = FALSE;
677 redraw_all_later(SOME_VALID);
678
679 // Only call highlight_changed() once after multiple changes.
680 need_highlight_changed = TRUE;
681 }
682 }
683
684}
685
686/*
687 * Reset all highlighting to the defaults. Removes all highlighting for the
688 * groups added by the user.
689 */
690 static void
691highlight_reset_all(void)
692{
693 int idx;
694
695#ifdef FEAT_GUI
696 // First, we do not destroy the old values, but allocate the new
697 // ones and update the display. THEN we destroy the old values.
698 // If we destroy the old values first, then the old values
699 // (such as GuiFont's or GuiFontset's) will still be displayed but
700 // invalid because they were free'd.
701 if (gui.in_use)
702 {
703# ifdef FEAT_BEVAL_TIP
704 gui_init_tooltip_font();
705# endif
706# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
707 gui_init_menu_font();
708# endif
709 }
710# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
711 gui_mch_def_colors();
712# endif
713# ifdef FEAT_GUI_X11
714# ifdef FEAT_MENU
715
716 // This only needs to be done when there is no Menu highlight
717 // group defined by default, which IS currently the case.
718 gui_mch_new_menu_colors();
719# endif
720 if (gui.in_use)
721 {
722 gui_new_scrollbar_colors();
723# ifdef FEAT_BEVAL_GUI
724 gui_mch_new_tooltip_colors();
725# endif
726# ifdef FEAT_MENU
727 gui_mch_new_menu_font();
728# endif
729 }
730# endif
731
732 // Ok, we're done allocating the new default graphics items.
733 // The screen should already be refreshed at this point.
734 // It is now Ok to clear out the old data.
735#endif
736#ifdef FEAT_EVAL
737 do_unlet((char_u *)"g:colors_name", TRUE);
738#endif
739 restore_cterm_colors();
740
741 // Clear all default highlight groups and load the defaults.
742 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
743 highlight_clear(idx);
744 init_highlight(TRUE, TRUE);
745#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
746 if (USE_24BIT)
747 highlight_gui_started();
748 else
749#endif
750 highlight_changed();
751 redraw_later_clear();
752}
753
754/*
755 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
756 * index 'idx'.
757 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
758 * 'arg' is the list of attribute names separated by comma.
759 * 'init' is set to TRUE when initializing all the highlight groups.
760 * Returns TRUE if the attributes are set.
761 */
762 static int
763highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
764{
765 int attr;
766 int off;
767 long i;
768 int len;
769
770 attr = 0;
771 off = 0;
772 while (arg[off] != NUL)
773 {
774 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
775 {
776 len = (int)STRLEN(hl_name_table[i]);
777 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
778 {
779 attr |= hl_attr_table[i];
780 off += len;
781 break;
782 }
783 }
784 if (i < 0)
785 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000786 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100787 return FALSE;
788 }
789 if (arg[off] == ',') // another one follows
790 ++off;
791 }
792 if (*key == 'T')
793 {
794 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
795 {
796 if (!init)
797 HL_TABLE()[idx].sg_set |= SG_TERM;
798 HL_TABLE()[idx].sg_term = attr;
799 }
800 }
801 else if (*key == 'C')
802 {
803 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
804 {
805 if (!init)
806 HL_TABLE()[idx].sg_set |= SG_CTERM;
807 HL_TABLE()[idx].sg_cterm = attr;
808 HL_TABLE()[idx].sg_cterm_bold = FALSE;
809 }
810 }
811#if defined(FEAT_GUI) || defined(FEAT_EVAL)
812 else
813 {
814 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
815 {
816 if (!init)
817 HL_TABLE()[idx].sg_set |= SG_GUI;
818 HL_TABLE()[idx].sg_gui = attr;
819 }
820 }
821#endif
822
823 return TRUE;
824}
825
826#ifdef FEAT_GUI
827/*
828 * Set the font for the highlight group at 'idx'.
829 * 'arg' is the font name.
830 * Returns TRUE if the font is changed.
831 */
832 static int
833highlight_set_font(
834 int idx,
835 char_u *arg,
836 int is_normal_group,
837 int is_menu_group,
838 int is_tooltip_group)
839{
840 int did_change = FALSE;
841
842 // in non-GUI fonts are simply ignored
843 if (HL_TABLE()[idx].sg_font_name != NULL
844 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
845 {
846 // Font name didn't change, ignore.
847 }
848 else if (!gui.shell_created)
849 {
850 // GUI not started yet, always accept the name.
851 vim_free(HL_TABLE()[idx].sg_font_name);
852 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
853 did_change = TRUE;
854 }
855 else
856 {
857 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
858# ifdef FEAT_XFONTSET
859 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
860# endif
861 // First, save the current font/fontset.
862 // Then try to allocate the font/fontset.
863 // If the allocation fails, HL_TABLE()[idx].sg_font OR
864 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
865
866 HL_TABLE()[idx].sg_font = NOFONT;
867# ifdef FEAT_XFONTSET
868 HL_TABLE()[idx].sg_fontset = NOFONTSET;
869# endif
870 hl_do_font(idx, arg, is_normal_group, is_menu_group,
871 is_tooltip_group, FALSE);
872
873# ifdef FEAT_XFONTSET
874 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
875 {
876 // New fontset was accepted. Free the old one, if there
877 // was one.
878 gui_mch_free_fontset(temp_sg_fontset);
879 vim_free(HL_TABLE()[idx].sg_font_name);
880 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
881 did_change = TRUE;
882 }
883 else
884 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
885# endif
886 if (HL_TABLE()[idx].sg_font != NOFONT)
887 {
888 // New font was accepted. Free the old one, if there was
889 // one.
890 gui_mch_free_font(temp_sg_font);
891 vim_free(HL_TABLE()[idx].sg_font_name);
892 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
893 did_change = TRUE;
894 }
895 else
896 HL_TABLE()[idx].sg_font = temp_sg_font;
897 }
898
899 return did_change;
900}
901#endif
902
903/*
904 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
905 * Returns TRUE if the foreground color is set.
906 */
907 static void
908highlight_set_ctermfg(int idx, int color, int is_normal_group)
909{
910 HL_TABLE()[idx].sg_cterm_fg = color + 1;
911 if (is_normal_group)
912 {
913 cterm_normal_fg_color = color + 1;
914 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
915#ifdef FEAT_GUI
916 // Don't do this if the GUI is used.
917 if (!gui.in_use && !gui.starting)
918#endif
919 {
920 must_redraw = CLEAR;
921 if (termcap_active && color >= 0)
922 term_fg_color(color);
923 }
924 }
925}
926
927/*
928 * Set the cterm background color for the highlight group at 'idx' to 'color'.
929 * Returns TRUE if the background color is set.
930 */
931 static void
932highlight_set_ctermbg(int idx, int color, int is_normal_group)
933{
934 HL_TABLE()[idx].sg_cterm_bg = color + 1;
935 if (is_normal_group)
936 {
937 cterm_normal_bg_color = color + 1;
938#ifdef FEAT_GUI
939 // Don't mess with 'background' if the GUI is used.
940 if (!gui.in_use && !gui.starting)
941#endif
942 {
943 must_redraw = CLEAR;
944 if (color >= 0)
945 {
946 int dark = -1;
947
948 if (termcap_active)
949 term_bg_color(color);
950 if (t_colors < 16)
951 dark = (color == 0 || color == 4);
952 // Limit the heuristic to the standard 16 colors
953 else if (color < 16)
954 dark = (color < 7 || color == 8);
955 // Set the 'background' option if the value is
956 // wrong.
957 if (dark != -1
958 && dark != (*p_bg == 'd')
959 && !option_was_set((char_u *)"bg"))
960 {
961 set_option_value((char_u *)"bg", 0L,
962 (char_u *)(dark ? "dark" : "light"), 0);
963 reset_option_was_set((char_u *)"bg");
964 }
965 }
966 }
967 }
968}
969
970/*
971 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
972 * Returns TRUE if the underline color is set.
973 */
974 static void
975highlight_set_ctermul(int idx, int color, int is_normal_group)
976{
977 HL_TABLE()[idx].sg_cterm_ul = color + 1;
978 if (is_normal_group)
979 {
980 cterm_normal_ul_color = color + 1;
981#ifdef FEAT_GUI
982 // Don't do this if the GUI is used.
983 if (!gui.in_use && !gui.starting)
984#endif
985 {
986 must_redraw = CLEAR;
987 if (termcap_active && color >= 0)
988 term_ul_color(color);
989 }
990 }
991}
992
993/*
994 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
995 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
996 * 'keystart' is the color name/value.
997 * 'arg' is the color name or the numeric value as a string.
998 * 'is_normal_group' is set if the highlight group is 'NORMAL'
999 * 'init' is set to TRUE when initializing highlighting.
1000 * Called for the ":highlight" command and the "hlset()" function.
1001 *
1002 * Returns TRUE if the color is set.
1003 */
1004 static int
1005highlight_set_cterm_color(
1006 int idx,
1007 char_u *key,
1008 char_u *key_start,
1009 char_u *arg,
1010 int is_normal_group,
1011 int init)
1012{
1013 int color;
1014 long i;
1015 int off;
1016
1017 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1018 {
1019 if (!init)
1020 HL_TABLE()[idx].sg_set |= SG_CTERM;
1021
1022 // When setting the foreground color, and previously the "bold"
1023 // flag was set for a light color, reset it now
1024 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1025 {
1026 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1027 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1028 }
1029
1030 if (VIM_ISDIGIT(*arg))
1031 color = atoi((char *)arg);
1032 else if (STRICMP(arg, "fg") == 0)
1033 {
1034 if (cterm_normal_fg_color)
1035 color = cterm_normal_fg_color - 1;
1036 else
1037 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001038 emsg(_(e_fg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001039 return FALSE;
1040 }
1041 }
1042 else if (STRICMP(arg, "bg") == 0)
1043 {
1044 if (cterm_normal_bg_color > 0)
1045 color = cterm_normal_bg_color - 1;
1046 else
1047 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001048 emsg(_(e_bg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001049 return FALSE;
1050 }
1051 }
1052 else if (STRICMP(arg, "ul") == 0)
1053 {
1054 if (cterm_normal_ul_color > 0)
1055 color = cterm_normal_ul_color - 1;
1056 else
1057 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00001058 emsg(_(e_ul_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001059 return FALSE;
1060 }
1061 }
1062 else
1063 {
1064 int bold = MAYBE;
1065
1066 // reduce calls to STRICMP a bit, it can be slow
1067 off = TOUPPER_ASC(*arg);
1068 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1069 if (off == color_names[i][0]
1070 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1071 break;
1072 if (i < 0)
1073 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001074 semsg(_(e_color_name_or_number_not_recognized), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001075 return FALSE;
1076 }
1077
1078 color = lookup_color(i, key[5] == 'F', &bold);
1079
1080 // set/reset bold attribute to get light foreground
1081 // colors (on some terminals, e.g. "linux")
1082 if (bold == TRUE)
1083 {
1084 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1085 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1086 }
1087 else if (bold == FALSE)
1088 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1089 }
1090
1091 // Add one to the argument, to avoid zero. Zero is used for
1092 // "NONE", then "color" is -1.
1093 if (key[5] == 'F')
1094 highlight_set_ctermfg(idx, color, is_normal_group);
1095 else if (key[5] == 'B')
1096 highlight_set_ctermbg(idx, color, is_normal_group);
1097 else // ctermul
1098 highlight_set_ctermul(idx, color, is_normal_group);
1099 }
1100
1101 return TRUE;
1102}
1103
1104#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1105/*
1106 * Set the GUI foreground color for the highlight group at 'idx'.
1107 * Returns TRUE if the color is set.
1108 */
1109 static int
1110highlight_set_guifg(
1111 int idx,
1112 char_u *arg,
1113 int is_menu_group UNUSED,
1114 int is_scrollbar_group UNUSED,
1115 int is_tooltip_group UNUSED,
1116 int *do_colors UNUSED,
1117 int init)
1118{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001119# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001120 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001121# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001122 char_u **namep;
1123 int did_change = FALSE;
1124
1125 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1126 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1127 {
1128 if (!init)
1129 HL_TABLE()[idx].sg_set |= SG_GUI;
1130
1131# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1132 // In GUI guifg colors are only used when recognized
1133 i = color_name2handle(arg);
1134 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1135 {
1136 HL_TABLE()[idx].sg_gui_fg = i;
1137# endif
1138 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1139 {
1140 vim_free(*namep);
1141 if (STRCMP(arg, "NONE") != 0)
1142 *namep = vim_strsave(arg);
1143 else
1144 *namep = NULL;
1145 did_change = TRUE;
1146 }
1147# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1148# ifdef FEAT_GUI_X11
1149 if (is_menu_group && gui.menu_fg_pixel != i)
1150 {
1151 gui.menu_fg_pixel = i;
1152 *do_colors = TRUE;
1153 }
1154 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1155 {
1156 gui.scroll_fg_pixel = i;
1157 *do_colors = TRUE;
1158 }
1159# ifdef FEAT_BEVAL_GUI
1160 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1161 {
1162 gui.tooltip_fg_pixel = i;
1163 *do_colors = TRUE;
1164 }
1165# endif
1166# endif
1167 }
1168# endif
1169 }
1170
1171 return did_change;
1172}
1173
1174/*
1175 * Set the GUI background color for the highlight group at 'idx'.
1176 * Returns TRUE if the color is set.
1177 */
1178 static int
1179highlight_set_guibg(
1180 int idx,
1181 char_u *arg,
1182 int is_menu_group UNUSED,
1183 int is_scrollbar_group UNUSED,
1184 int is_tooltip_group UNUSED,
1185 int *do_colors UNUSED,
1186 int init)
1187{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001188# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001189 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001190# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001191 char_u **namep;
1192 int did_change = FALSE;
1193
1194 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1195 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1196 {
1197 if (!init)
1198 HL_TABLE()[idx].sg_set |= SG_GUI;
1199
1200# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Drew Vogele30d1022021-10-24 20:35:07 +01001201 // In GUI guibg colors are only used when recognized
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001202 i = color_name2handle(arg);
1203 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1204 {
1205 HL_TABLE()[idx].sg_gui_bg = i;
1206# endif
1207 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1208 {
1209 vim_free(*namep);
1210 if (STRCMP(arg, "NONE") != 0)
1211 *namep = vim_strsave(arg);
1212 else
1213 *namep = NULL;
1214 did_change = TRUE;
1215 }
1216# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1217# ifdef FEAT_GUI_X11
1218 if (is_menu_group && gui.menu_bg_pixel != i)
1219 {
1220 gui.menu_bg_pixel = i;
1221 *do_colors = TRUE;
1222 }
1223 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1224 {
1225 gui.scroll_bg_pixel = i;
1226 *do_colors = TRUE;
1227 }
1228# ifdef FEAT_BEVAL_GUI
1229 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1230 {
1231 gui.tooltip_bg_pixel = i;
1232 *do_colors = TRUE;
1233 }
1234# endif
1235# endif
1236 }
1237# endif
1238 }
1239
1240 return did_change;
1241}
1242
1243/*
1244 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1245 * Returns TRUE if the color is set.
1246 */
1247 static int
1248highlight_set_guisp(int idx, char_u *arg, int init)
1249{
1250# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1251 int i;
1252# endif
1253 int did_change = FALSE;
1254 char_u **namep;
1255
1256 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1257 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1258 {
1259 if (!init)
1260 HL_TABLE()[idx].sg_set |= SG_GUI;
1261
1262# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1263 // In GUI guisp colors are only used when recognized
1264 i = color_name2handle(arg);
1265 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1266 {
1267 HL_TABLE()[idx].sg_gui_sp = i;
1268# endif
1269 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1270 {
1271 vim_free(*namep);
1272 if (STRCMP(arg, "NONE") != 0)
1273 *namep = vim_strsave(arg);
1274 else
1275 *namep = NULL;
1276 did_change = TRUE;
1277 }
1278# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1279 }
1280# endif
1281 }
1282
1283 return did_change;
1284}
1285#endif
1286
1287/*
1288 * Set the start/stop terminal codes for a highlight group.
1289 * Returns TRUE if the terminal code is set.
1290 */
1291 static int
1292highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1293{
1294 int off;
1295 char_u buf[100];
1296 int len;
1297 char_u *tname;
1298 char_u *p;
1299
1300 if (!init)
1301 HL_TABLE()[idx].sg_set |= SG_TERM;
1302
1303 // The "start" and "stop" arguments can be a literal escape
1304 // sequence, or a comma separated list of terminal codes.
1305 if (STRNCMP(arg, "t_", 2) == 0)
1306 {
1307 off = 0;
1308 buf[0] = 0;
1309 while (arg[off] != NUL)
1310 {
1311 // Isolate one termcap name
1312 for (len = 0; arg[off + len] &&
1313 arg[off + len] != ','; ++len)
1314 ;
1315 tname = vim_strnsave(arg + off, len);
1316 if (tname == NULL) // out of memory
1317 return FALSE;
1318 // lookup the escape sequence for the item
1319 p = get_term_code(tname);
1320 vim_free(tname);
1321 if (p == NULL) // ignore non-existing things
1322 p = (char_u *)"";
1323
1324 // Append it to the already found stuff
1325 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1326 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001327 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001328 return FALSE;
1329 }
1330 STRCAT(buf, p);
1331
1332 // Advance to the next item
1333 off += len;
1334 if (arg[off] == ',') // another one follows
1335 ++off;
1336 }
1337 }
1338 else
1339 {
1340 // Copy characters from arg[] to buf[], translating <> codes.
1341 for (p = arg, off = 0; off < 100 - 6 && *p; )
1342 {
1343 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
1344 if (len > 0) // recognized special char
1345 off += len;
1346 else // copy as normal char
1347 buf[off++] = *p++;
1348 }
1349 buf[off] = NUL;
1350 }
1351
1352 if (STRCMP(buf, "NONE") == 0) // resetting the value
1353 p = NULL;
1354 else
1355 p = vim_strsave(buf);
1356 if (key[2] == 'A')
1357 {
1358 vim_free(HL_TABLE()[idx].sg_start);
1359 HL_TABLE()[idx].sg_start = p;
1360 }
1361 else
1362 {
1363 vim_free(HL_TABLE()[idx].sg_stop);
1364 HL_TABLE()[idx].sg_stop = p;
1365 }
1366 return TRUE;
1367}
1368
1369/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001370 * Handle the ":highlight .." command.
1371 * When using ":hi clear" this is called recursively for each group with
1372 * "forceit" and "init" both TRUE.
1373 */
1374 void
1375do_highlight(
1376 char_u *line,
1377 int forceit,
1378 int init) // TRUE when called for initializing
1379{
1380 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001381 char_u *linep;
1382 char_u *key_start;
1383 char_u *arg_start;
1384 char_u *key = NULL, *arg = NULL;
1385 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001386 int id;
1387 int idx;
1388 hl_group_T item_before;
1389 int did_change = FALSE;
1390 int dodefault = FALSE;
1391 int doclear = FALSE;
1392 int dolink = FALSE;
1393 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001394 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001395#ifdef FEAT_GUI_X11
1396 int is_menu_group = FALSE; // "Menu" group
1397 int is_scrollbar_group = FALSE; // "Scrollbar" group
1398 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001399#else
1400# define is_menu_group 0
1401# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001402# define is_scrollbar_group 0
1403#endif
1404#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1405 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001406#endif
1407#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1408 int did_highlight_changed = FALSE;
1409#endif
1410
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001411 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001412 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001413 {
1414 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1415 // TODO: only call when the group has attributes set
1416 highlight_list_one((int)i);
1417 return;
1418 }
1419
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001420 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001421 name_end = skiptowhite(line);
1422 linep = skipwhite(name_end);
1423
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001424 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001425 if (STRNCMP(line, "default", name_end - line) == 0)
1426 {
1427 dodefault = TRUE;
1428 line = linep;
1429 name_end = skiptowhite(line);
1430 linep = skipwhite(name_end);
1431 }
1432
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001433 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001434 if (STRNCMP(line, "clear", name_end - line) == 0)
1435 doclear = TRUE;
1436 if (STRNCMP(line, "link", name_end - line) == 0)
1437 dolink = TRUE;
1438
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001439 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001440 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001441 {
1442 id = syn_namen2id(line, (int)(name_end - line));
1443 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001444 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001445 else
1446 highlight_list_one(id);
1447 return;
1448 }
1449
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001450 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001451 if (dolink)
1452 {
1453 char_u *from_start = linep;
1454 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001455 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001456 char_u *to_start;
1457 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001458 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001459
1460 from_end = skiptowhite(from_start);
1461 to_start = skipwhite(from_end);
1462 to_end = skiptowhite(to_start);
1463
Bram Moolenaar1966c242020-04-20 22:42:32 +02001464 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001465 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001466 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001467 return;
1468 }
1469
Bram Moolenaar1966c242020-04-20 22:42:32 +02001470 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001471 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001472 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001473 return;
1474 }
1475
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001476 from_len = (int)(from_end - from_start);
1477 to_len = (int)(to_end - to_start);
1478 highlight_group_link(from_start, from_len, to_start, to_len,
1479 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001480 return;
1481 }
1482
1483 if (doclear)
1484 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001485 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001486 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001487 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001488 // ":highlight clear" without group name
1489 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001490 return;
1491 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001492 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001493 name_end = skiptowhite(line);
1494 linep = skipwhite(name_end);
1495 }
1496
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001497 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001498 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001499 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001500 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001501 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001502
1503 // Return if "default" was used and the group already has settings.
1504 if (dodefault && hl_has_settings(idx, TRUE))
1505 return;
1506
1507 // Make a copy so we can check if any attribute actually changed.
1508 item_before = HL_TABLE()[idx];
1509
1510 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1511 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001512#ifdef FEAT_GUI_X11
1513 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1514 is_menu_group = TRUE;
1515 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1516 is_scrollbar_group = TRUE;
1517 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1518 is_tooltip_group = TRUE;
1519#endif
1520
1521 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1522 if (doclear || (forceit && init))
1523 {
1524 highlight_clear(idx);
1525 if (!doclear)
1526 HL_TABLE()[idx].sg_set = 0;
1527 }
1528
1529 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001530 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001531 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001532 key_start = linep;
1533 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001534 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001535 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001536 error = TRUE;
1537 break;
1538 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001539
1540 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1541 // or "guibg").
1542 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1543 ++linep;
1544 vim_free(key);
1545 key = vim_strnsave_up(key_start, linep - key_start);
1546 if (key == NULL)
1547 {
1548 error = TRUE;
1549 break;
1550 }
1551 linep = skipwhite(linep);
1552
1553 if (STRCMP(key, "NONE") == 0)
1554 {
1555 if (!init || HL_TABLE()[idx].sg_set == 0)
1556 {
1557 if (!init)
1558 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1559 highlight_clear(idx);
1560 }
1561 continue;
1562 }
1563
1564 // Check for the equal sign.
1565 if (*linep != '=')
1566 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001567 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001568 error = TRUE;
1569 break;
1570 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001571 ++linep;
1572
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001573 // Isolate the argument.
1574 linep = skipwhite(linep);
1575 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001576 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001577 arg_start = ++linep;
1578 linep = vim_strchr(linep, '\'');
1579 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001580 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001581 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001582 error = TRUE;
1583 break;
1584 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001585 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001586 else
1587 {
1588 arg_start = linep;
1589 linep = skiptowhite(linep);
1590 }
1591 if (linep == arg_start)
1592 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001593 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001594 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001595 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001596 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001597 vim_free(arg);
1598 arg = vim_strnsave(arg_start, linep - arg_start);
1599 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001600 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001601 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001602 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001603 }
1604 if (*linep == '\'')
1605 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001606
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001607 // Store the argument.
1608 if (STRCMP(key, "TERM") == 0
1609 || STRCMP(key, "CTERM") == 0
1610 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001611 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001612 if (!highlight_set_termgui_attr(idx, key, arg, init))
1613 {
1614 error = TRUE;
1615 break;
1616 }
1617 }
1618 else if (STRCMP(key, "FONT") == 0)
1619 {
1620 // in non-GUI fonts are simply ignored
1621#ifdef FEAT_GUI
1622 if (highlight_set_font(idx, arg, is_normal_group,
1623 is_menu_group, is_tooltip_group))
1624 did_change = TRUE;
1625#endif
1626 }
1627 else if (STRCMP(key, "CTERMFG") == 0
1628 || STRCMP(key, "CTERMBG") == 0
1629 || STRCMP(key, "CTERMUL") == 0)
1630 {
1631 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1632 is_normal_group, init))
1633 {
1634 error = TRUE;
1635 break;
1636 }
1637 }
1638 else if (STRCMP(key, "GUIFG") == 0)
1639 {
1640#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1641 if (highlight_set_guifg(idx, arg, is_menu_group,
1642 is_scrollbar_group, is_tooltip_group,
1643 &do_colors, init))
1644 did_change = TRUE;
1645#endif
1646 }
1647 else if (STRCMP(key, "GUIBG") == 0)
1648 {
1649#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1650 if (highlight_set_guibg(idx, arg, is_menu_group,
1651 is_scrollbar_group, is_tooltip_group,
1652 &do_colors, init))
1653 did_change = TRUE;
1654#endif
1655 }
1656 else if (STRCMP(key, "GUISP") == 0)
1657 {
1658#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1659 if (highlight_set_guisp(idx, arg, init))
1660 did_change = TRUE;
1661#endif
1662 }
1663 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1664 {
1665 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1666 {
1667 error = TRUE;
1668 break;
1669 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001670 }
1671 else
1672 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001673 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001674 error = TRUE;
1675 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001676 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001677 HL_TABLE()[idx].sg_cleared = FALSE;
1678
1679 // When highlighting has been given for a group, don't link it.
1680 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1681 HL_TABLE()[idx].sg_link = 0;
1682
1683 // Continue with next argument.
1684 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001685 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001686
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001687 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001688 if (error && idx == highlight_ga.ga_len)
1689 syn_unadd_group();
1690 else
1691 {
1692 if (is_normal_group)
1693 {
1694 HL_TABLE()[idx].sg_term_attr = 0;
1695 HL_TABLE()[idx].sg_cterm_attr = 0;
1696#ifdef FEAT_GUI
1697 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001698 // Need to update all groups, because they might be using "bg"
1699 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001700#endif
1701#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1702 if (USE_24BIT)
1703 {
1704 highlight_gui_started();
1705 did_highlight_changed = TRUE;
1706 redraw_all_later(NOT_VALID);
1707 }
1708#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001709#ifdef FEAT_VTP
1710 control_console_color_rgb();
1711#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001712 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001713#ifdef FEAT_GUI_X11
1714# ifdef FEAT_MENU
1715 else if (is_menu_group)
1716 {
1717 if (gui.in_use && do_colors)
1718 gui_mch_new_menu_colors();
1719 }
1720# endif
1721 else if (is_scrollbar_group)
1722 {
1723 if (gui.in_use && do_colors)
1724 gui_new_scrollbar_colors();
1725 else
1726 set_hl_attr(idx);
1727 }
1728# ifdef FEAT_BEVAL_GUI
1729 else if (is_tooltip_group)
1730 {
1731 if (gui.in_use && do_colors)
1732 gui_mch_new_tooltip_colors();
1733 }
1734# endif
1735#endif
1736 else
1737 set_hl_attr(idx);
1738#ifdef FEAT_EVAL
1739 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001740 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001741#endif
1742 }
1743
1744 vim_free(key);
1745 vim_free(arg);
1746
1747 // Only call highlight_changed() once, after a sequence of highlight
1748 // commands, and only if an attribute actually changed.
1749 if ((did_change
1750 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1751#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1752 && !did_highlight_changed
1753#endif
1754 )
1755 {
1756 // Do not trigger a redraw when highlighting is changed while
1757 // redrawing. This may happen when evaluating 'statusline' changes the
1758 // StatusLine group.
1759 if (!updating_screen)
1760 redraw_all_later(NOT_VALID);
1761 need_highlight_changed = TRUE;
1762 }
1763}
1764
1765#if defined(EXITFREE) || defined(PROTO)
1766 void
1767free_highlight(void)
1768{
1769 int i;
1770
1771 for (i = 0; i < highlight_ga.ga_len; ++i)
1772 {
1773 highlight_clear(i);
1774 vim_free(HL_TABLE()[i].sg_name);
1775 vim_free(HL_TABLE()[i].sg_name_u);
1776 }
1777 ga_clear(&highlight_ga);
1778}
1779#endif
1780
1781/*
1782 * Reset the cterm colors to what they were before Vim was started, if
1783 * possible. Otherwise reset them to zero.
1784 */
1785 void
1786restore_cterm_colors(void)
1787{
1788#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1789 // Since t_me has been set, this probably means that the user
1790 // wants to use this as default colors. Need to reset default
1791 // background/foreground colors.
1792 mch_set_normal_colors();
1793#else
1794# ifdef VIMDLL
1795 if (!gui.in_use)
1796 {
1797 mch_set_normal_colors();
1798 return;
1799 }
1800# endif
1801 cterm_normal_fg_color = 0;
1802 cterm_normal_fg_bold = 0;
1803 cterm_normal_bg_color = 0;
1804# ifdef FEAT_TERMGUICOLORS
1805 cterm_normal_fg_gui_color = INVALCOLOR;
1806 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001807 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001808# endif
1809#endif
1810}
1811
1812/*
1813 * Return TRUE if highlight group "idx" has any settings.
1814 * When "check_link" is TRUE also check for an existing link.
1815 */
1816 static int
1817hl_has_settings(int idx, int check_link)
1818{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001819 return HL_TABLE()[idx].sg_cleared == 0
1820 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001821 || HL_TABLE()[idx].sg_cterm_attr != 0
1822 || HL_TABLE()[idx].sg_cterm_fg != 0
1823 || HL_TABLE()[idx].sg_cterm_bg != 0
1824#ifdef FEAT_GUI
1825 || HL_TABLE()[idx].sg_gui_attr != 0
1826 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1827 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1828 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1829 || HL_TABLE()[idx].sg_font_name != NULL
1830#endif
1831 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1832}
1833
1834/*
1835 * Clear highlighting for one group.
1836 */
1837 static void
1838highlight_clear(int idx)
1839{
1840 HL_TABLE()[idx].sg_cleared = TRUE;
1841
1842 HL_TABLE()[idx].sg_term = 0;
1843 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1844 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1845 HL_TABLE()[idx].sg_term_attr = 0;
1846 HL_TABLE()[idx].sg_cterm = 0;
1847 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1848 HL_TABLE()[idx].sg_cterm_fg = 0;
1849 HL_TABLE()[idx].sg_cterm_bg = 0;
1850 HL_TABLE()[idx].sg_cterm_attr = 0;
1851#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1852 HL_TABLE()[idx].sg_gui = 0;
1853 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1854 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1855 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1856#endif
1857#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1858 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1859 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001860 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001861#endif
1862#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001863 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1864 HL_TABLE()[idx].sg_font = NOFONT;
1865# ifdef FEAT_XFONTSET
1866 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1867 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1868# endif
1869 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1870 HL_TABLE()[idx].sg_gui_attr = 0;
1871#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001872 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001873 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001874#ifdef FEAT_EVAL
1875 // Since we set the default link, set the location to where the default
1876 // link was set.
1877 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001878#endif
1879}
1880
1881#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1882/*
1883 * Set the normal foreground and background colors according to the "Normal"
1884 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1885 * "Tooltip" colors.
1886 */
1887 void
1888set_normal_colors(void)
1889{
1890# ifdef FEAT_GUI
1891# ifdef FEAT_TERMGUICOLORS
1892 if (gui.in_use)
1893# endif
1894 {
1895 if (set_group_colors((char_u *)"Normal",
1896 &gui.norm_pixel, &gui.back_pixel,
1897 FALSE, TRUE, FALSE))
1898 {
1899 gui_mch_new_colors();
1900 must_redraw = CLEAR;
1901 }
1902# ifdef FEAT_GUI_X11
1903 if (set_group_colors((char_u *)"Menu",
1904 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1905 TRUE, FALSE, FALSE))
1906 {
1907# ifdef FEAT_MENU
1908 gui_mch_new_menu_colors();
1909# endif
1910 must_redraw = CLEAR;
1911 }
1912# ifdef FEAT_BEVAL_GUI
1913 if (set_group_colors((char_u *)"Tooltip",
1914 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1915 FALSE, FALSE, TRUE))
1916 {
1917# ifdef FEAT_TOOLBAR
1918 gui_mch_new_tooltip_colors();
1919# endif
1920 must_redraw = CLEAR;
1921 }
1922# endif
1923 if (set_group_colors((char_u *)"Scrollbar",
1924 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1925 FALSE, FALSE, FALSE))
1926 {
1927 gui_new_scrollbar_colors();
1928 must_redraw = CLEAR;
1929 }
1930# endif
1931 }
1932# endif
1933# ifdef FEAT_TERMGUICOLORS
1934# ifdef FEAT_GUI
1935 else
1936# endif
1937 {
1938 int idx;
1939
1940 idx = syn_name2id((char_u *)"Normal") - 1;
1941 if (idx >= 0)
1942 {
1943 gui_do_one_color(idx, FALSE, FALSE);
1944
1945 // If the normal fg or bg color changed a complete redraw is
1946 // required.
1947 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1948 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1949 {
1950 // if the GUI color is INVALCOLOR then we use the default cterm
1951 // color
1952 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1953 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1954 must_redraw = CLEAR;
1955 }
1956 }
1957 }
1958# endif
1959}
1960#endif
1961
1962#if defined(FEAT_GUI) || defined(PROTO)
1963/*
1964 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1965 */
1966 static int
1967set_group_colors(
1968 char_u *name,
1969 guicolor_T *fgp,
1970 guicolor_T *bgp,
1971 int do_menu,
1972 int use_norm,
1973 int do_tooltip)
1974{
1975 int idx;
1976
1977 idx = syn_name2id(name) - 1;
1978 if (idx >= 0)
1979 {
1980 gui_do_one_color(idx, do_menu, do_tooltip);
1981
1982 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1983 *fgp = HL_TABLE()[idx].sg_gui_fg;
1984 else if (use_norm)
1985 *fgp = gui.def_norm_pixel;
1986 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1987 *bgp = HL_TABLE()[idx].sg_gui_bg;
1988 else if (use_norm)
1989 *bgp = gui.def_back_pixel;
1990 return TRUE;
1991 }
1992 return FALSE;
1993}
1994
1995/*
1996 * Get the font of the "Normal" group.
1997 * Returns "" when it's not found or not set.
1998 */
1999 char_u *
2000hl_get_font_name(void)
2001{
2002 int id;
2003 char_u *s;
2004
2005 id = syn_name2id((char_u *)"Normal");
2006 if (id > 0)
2007 {
2008 s = HL_TABLE()[id - 1].sg_font_name;
2009 if (s != NULL)
2010 return s;
2011 }
2012 return (char_u *)"";
2013}
2014
2015/*
2016 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2017 * actually chosen to be used.
2018 */
2019 void
2020hl_set_font_name(char_u *font_name)
2021{
2022 int id;
2023
2024 id = syn_name2id((char_u *)"Normal");
2025 if (id > 0)
2026 {
2027 vim_free(HL_TABLE()[id - 1].sg_font_name);
2028 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2029 }
2030}
2031
2032/*
2033 * Set background color for "Normal" group. Called by gui_set_bg_color()
2034 * when the color is known.
2035 */
2036 void
2037hl_set_bg_color_name(
2038 char_u *name) // must have been allocated
2039{
2040 int id;
2041
2042 if (name != NULL)
2043 {
2044 id = syn_name2id((char_u *)"Normal");
2045 if (id > 0)
2046 {
2047 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2048 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2049 }
2050 }
2051}
2052
2053/*
2054 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2055 * when the color is known.
2056 */
2057 void
2058hl_set_fg_color_name(
2059 char_u *name) // must have been allocated
2060{
2061 int id;
2062
2063 if (name != NULL)
2064 {
2065 id = syn_name2id((char_u *)"Normal");
2066 if (id > 0)
2067 {
2068 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2069 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2070 }
2071 }
2072}
2073
2074/*
2075 * Return the handle for a font name.
2076 * Returns NOFONT when failed.
2077 */
2078 static GuiFont
2079font_name2handle(char_u *name)
2080{
2081 if (STRCMP(name, "NONE") == 0)
2082 return NOFONT;
2083
2084 return gui_mch_get_font(name, TRUE);
2085}
2086
2087# ifdef FEAT_XFONTSET
2088/*
2089 * Return the handle for a fontset name.
2090 * Returns NOFONTSET when failed.
2091 */
2092 static GuiFontset
2093fontset_name2handle(char_u *name, int fixed_width)
2094{
2095 if (STRCMP(name, "NONE") == 0)
2096 return NOFONTSET;
2097
2098 return gui_mch_get_fontset(name, TRUE, fixed_width);
2099}
2100# endif
2101
2102/*
2103 * Get the font or fontset for one highlight group.
2104 */
2105 static void
2106hl_do_font(
2107 int idx,
2108 char_u *arg,
2109 int do_normal, // set normal font
2110 int do_menu UNUSED, // set menu font
2111 int do_tooltip UNUSED, // set tooltip font
2112 int free_font) // free current font/fontset
2113{
2114# ifdef FEAT_XFONTSET
2115 // If 'guifontset' is not empty, first try using the name as a
2116 // fontset. If that doesn't work, use it as a font name.
2117 if (*p_guifontset != NUL
2118# ifdef FONTSET_ALWAYS
2119 || do_menu
2120# endif
2121# ifdef FEAT_BEVAL_TIP
2122 // In Athena & Motif, the Tooltip highlight group is always a fontset
2123 || do_tooltip
2124# endif
2125 )
2126 {
2127 if (free_font)
2128 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2129 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2130# ifdef FONTSET_ALWAYS
2131 || do_menu
2132# endif
2133# ifdef FEAT_BEVAL_TIP
2134 || do_tooltip
2135# endif
2136 );
2137 }
2138 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2139 {
2140 // If it worked and it's the Normal group, use it as the normal
2141 // fontset. Same for the Menu group.
2142 if (do_normal)
2143 gui_init_font(arg, TRUE);
2144# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2145 if (do_menu)
2146 {
2147# ifdef FONTSET_ALWAYS
2148 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2149# else
2150 // YIKES! This is a bug waiting to crash the program
2151 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2152# endif
2153 gui_mch_new_menu_font();
2154 }
2155# ifdef FEAT_BEVAL_GUI
2156 if (do_tooltip)
2157 {
2158 // The Athena widget set cannot currently handle switching between
2159 // displaying a single font and a fontset.
2160 // If the XtNinternational resource is set to True at widget
2161 // creation, then a fontset is always used, otherwise an
2162 // XFontStruct is used.
2163 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2164 gui_mch_new_tooltip_font();
2165 }
2166# endif
2167# endif
2168 }
2169 else
2170# endif
2171 {
2172 if (free_font)
2173 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2174 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2175 // If it worked and it's the Normal group, use it as the
2176 // normal font. Same for the Menu group.
2177 if (HL_TABLE()[idx].sg_font != NOFONT)
2178 {
2179 if (do_normal)
2180 gui_init_font(arg, FALSE);
2181#ifndef FONTSET_ALWAYS
2182# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2183 if (do_menu)
2184 {
2185 gui.menu_font = HL_TABLE()[idx].sg_font;
2186 gui_mch_new_menu_font();
2187 }
2188# endif
2189#endif
2190 }
2191 }
2192}
2193
2194#endif // FEAT_GUI
2195
2196#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2197/*
2198 * Return the handle for a color name.
2199 * Returns INVALCOLOR when failed.
2200 */
2201 guicolor_T
2202color_name2handle(char_u *name)
2203{
2204 if (STRCMP(name, "NONE") == 0)
2205 return INVALCOLOR;
2206
2207 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2208 {
2209#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2210 if (gui.in_use)
2211#endif
2212#ifdef FEAT_GUI
2213 return gui.norm_pixel;
2214#endif
2215#ifdef FEAT_TERMGUICOLORS
2216 if (cterm_normal_fg_gui_color != INVALCOLOR)
2217 return cterm_normal_fg_gui_color;
2218 // Guess that the foreground is black or white.
2219 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2220#endif
2221 }
2222 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2223 {
2224#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2225 if (gui.in_use)
2226#endif
2227#ifdef FEAT_GUI
2228 return gui.back_pixel;
2229#endif
2230#ifdef FEAT_TERMGUICOLORS
2231 if (cterm_normal_bg_gui_color != INVALCOLOR)
2232 return cterm_normal_bg_gui_color;
2233 // Guess that the background is white or black.
2234 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2235#endif
2236 }
2237
2238 return GUI_GET_COLOR(name);
2239}
Drew Vogele30d1022021-10-24 20:35:07 +01002240
2241// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2242// values as used by the MS-Windows GDI api. It should be used only for
2243// MS-Windows GDI builds.
2244# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2245# undef RGB
2246# endif
2247# ifndef RGB
2248# define RGB(r, g, b) ((r<<16) | (g<<8) | (b))
2249# endif
2250
2251# ifdef VIMDLL
2252 static guicolor_T
2253gui_adjust_rgb(guicolor_T c)
2254{
2255 if (gui.in_use)
2256 return c;
2257 else
2258 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2259}
2260# else
2261# define gui_adjust_rgb(c) (c)
2262# endif
2263
2264 static int
2265hex_digit(int c)
2266{
2267 if (isdigit(c))
2268 return c - '0';
2269 c = TOLOWER_ASC(c);
2270 if (c >= 'a' && c <= 'f')
2271 return c - 'a' + 10;
2272 return 0x1ffffff;
2273}
2274
2275 guicolor_T
2276decode_hex_color(char_u *hex)
2277{
2278 guicolor_T color;
2279
2280 if (hex[0] != '#' || STRLEN(hex) != 7)
2281 return INVALCOLOR;
2282
2283 // Name is in "#rrggbb" format
2284 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2285 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2286 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2287 if (color > 0xffffff)
2288 return INVALCOLOR;
2289 return gui_adjust_rgb(color);
2290}
2291
Bram Moolenaar2a521962021-10-25 10:30:14 +01002292#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002293// Returns the color currently mapped to the given name or INVALCOLOR if no
2294// such name exists in the color table. The convention is to use lowercase for
2295// all keys in the v:colornames dictionary. The value can be either a string in
2296// the form #rrggbb or a number, either of which is converted to a guicolor_T.
2297 guicolor_T
2298colorname2rgb(char_u *name)
2299{
2300 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2301 char_u *lc_name;
2302 dictitem_T *colentry;
2303 char_u *colstr;
2304 varnumber_T colnum;
2305
2306 lc_name = strlow_save(name);
2307 if (lc_name == NULL)
2308 return INVALCOLOR;
2309
2310 colentry = dict_find(colornames_table, lc_name, -1);
2311 vim_free(lc_name);
2312 if (colentry == NULL)
2313 return INVALCOLOR;
2314
2315 if (colentry->di_tv.v_type == VAR_STRING)
2316 {
2317 colstr = tv_get_string_strict(&colentry->di_tv);
2318 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2319 {
2320 return decode_hex_color(colstr);
2321 }
2322 else
2323 {
2324 semsg(_(e_bad_color_string_str), colstr);
2325 return INVALCOLOR;
2326 }
2327 }
2328
2329 if (colentry->di_tv.v_type == VAR_NUMBER)
2330 {
2331 colnum = tv_get_number(&colentry->di_tv);
2332 return (guicolor_T)colnum;
2333 }
2334
2335 return INVALCOLOR;
2336}
2337
Drew Vogele30d1022021-10-24 20:35:07 +01002338/*
2339 * Load a default color list. Intended to support legacy color names but allows
2340 * the user to override the color values. Only loaded once.
2341 */
2342 void
2343load_default_colors_lists()
2344{
2345 // Lacking a default color list isn't the end of the world but it is likely
2346 // an inconvenience so users should know when it is missing.
2347 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
2348 msg("failed to load colors/lists/default.vim");
2349}
2350#endif
2351
2352 guicolor_T
2353gui_get_color_cmn(char_u *name)
2354{
2355 int i;
2356 guicolor_T color;
2357
2358 struct rgbcolor_table_S {
2359 char_u *color_name;
2360 guicolor_T color;
2361 };
2362
2363 // Only non X11 colors (not present in rgb.txt) and colors in
2364 // color_names[], useful when $VIMRUNTIME is not found,.
2365 static struct rgbcolor_table_S rgb_table[] = {
2366 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2367 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2368 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2369 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2370 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2371 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2372 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2373 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2374 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2375 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2376 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2377 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2378 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2379 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2380 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2381 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2382 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2383 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2384 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2385 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2386 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2387 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2388 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2389 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2390 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2391 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2392 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2393 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2394 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2395 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2396 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2397 };
2398
2399 color = decode_hex_color(name);
2400 if (color != INVALCOLOR)
2401 return color;
2402
2403 // Check if the name is one of the colors we know
2404 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2405 if (STRICMP(name, rgb_table[i].color_name) == 0)
2406 return gui_adjust_rgb(rgb_table[i].color);
2407
2408#if defined(FEAT_EVAL)
2409 /*
2410 * Not a traditional color. Load additional color aliases and then consult the alias table.
2411 */
2412
2413 color = colorname2rgb(name);
2414 if (color == INVALCOLOR)
2415 {
2416 load_default_colors_lists();
2417 color = colorname2rgb(name);
2418 }
2419
2420 return color;
2421#else
2422 return INVALCOLOR;
2423#endif
2424}
2425
2426 guicolor_T
2427gui_get_rgb_color_cmn(int r, int g, int b)
2428{
2429 guicolor_T color = RGB(r, g, b);
2430
2431 if (color > 0xffffff)
2432 return INVALCOLOR;
2433 return gui_adjust_rgb(color);
2434}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002435#endif
2436
2437/*
2438 * Table with the specifications for an attribute number.
2439 * Note that this table is used by ALL buffers. This is required because the
2440 * GUI can redraw at any time for any buffer.
2441 */
2442static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2443
2444#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2445
2446static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2447
2448#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2449
2450#ifdef FEAT_GUI
2451static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2452
2453#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2454#endif
2455
2456/*
2457 * Return the attr number for a set of colors and font.
2458 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2459 * if the combination is new.
2460 * Return 0 for error (no more room).
2461 */
2462 static int
2463get_attr_entry(garray_T *table, attrentry_T *aep)
2464{
2465 int i;
2466 attrentry_T *taep;
2467 static int recursive = FALSE;
2468
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002469 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002470 table->ga_itemsize = sizeof(attrentry_T);
2471 table->ga_growsize = 7;
2472
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002473 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002474 for (i = 0; i < table->ga_len; ++i)
2475 {
2476 taep = &(((attrentry_T *)table->ga_data)[i]);
2477 if ( aep->ae_attr == taep->ae_attr
2478 && (
2479#ifdef FEAT_GUI
2480 (table == &gui_attr_table
2481 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2482 && aep->ae_u.gui.bg_color
2483 == taep->ae_u.gui.bg_color
2484 && aep->ae_u.gui.sp_color
2485 == taep->ae_u.gui.sp_color
2486 && aep->ae_u.gui.font == taep->ae_u.gui.font
2487# ifdef FEAT_XFONTSET
2488 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2489# endif
2490 ))
2491 ||
2492#endif
2493 (table == &term_attr_table
2494 && (aep->ae_u.term.start == NULL)
2495 == (taep->ae_u.term.start == NULL)
2496 && (aep->ae_u.term.start == NULL
2497 || STRCMP(aep->ae_u.term.start,
2498 taep->ae_u.term.start) == 0)
2499 && (aep->ae_u.term.stop == NULL)
2500 == (taep->ae_u.term.stop == NULL)
2501 && (aep->ae_u.term.stop == NULL
2502 || STRCMP(aep->ae_u.term.stop,
2503 taep->ae_u.term.stop) == 0))
2504 || (table == &cterm_attr_table
2505 && aep->ae_u.cterm.fg_color
2506 == taep->ae_u.cterm.fg_color
2507 && aep->ae_u.cterm.bg_color
2508 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002509 && aep->ae_u.cterm.ul_color
2510 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002511#ifdef FEAT_TERMGUICOLORS
2512 && aep->ae_u.cterm.fg_rgb
2513 == taep->ae_u.cterm.fg_rgb
2514 && aep->ae_u.cterm.bg_rgb
2515 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002516 && aep->ae_u.cterm.ul_rgb
2517 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002518#endif
2519 )))
2520
2521 return i + ATTR_OFF;
2522 }
2523
2524 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2525 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002526 // Running out of attribute entries! remove all attributes, and
2527 // compute new ones for all groups.
2528 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002529 if (recursive)
2530 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002531 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002532 return 0;
2533 }
2534 recursive = TRUE;
2535
2536 clear_hl_tables();
2537
2538 must_redraw = CLEAR;
2539
2540 for (i = 0; i < highlight_ga.ga_len; ++i)
2541 set_hl_attr(i);
2542
2543 recursive = FALSE;
2544 }
2545
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002546 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002547 if (ga_grow(table, 1) == FAIL)
2548 return 0;
2549
2550 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002551 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002552 taep->ae_attr = aep->ae_attr;
2553#ifdef FEAT_GUI
2554 if (table == &gui_attr_table)
2555 {
2556 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2557 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2558 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2559 taep->ae_u.gui.font = aep->ae_u.gui.font;
2560# ifdef FEAT_XFONTSET
2561 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2562# endif
2563 }
2564#endif
2565 if (table == &term_attr_table)
2566 {
2567 if (aep->ae_u.term.start == NULL)
2568 taep->ae_u.term.start = NULL;
2569 else
2570 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2571 if (aep->ae_u.term.stop == NULL)
2572 taep->ae_u.term.stop = NULL;
2573 else
2574 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2575 }
2576 else if (table == &cterm_attr_table)
2577 {
2578 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2579 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002580 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002581#ifdef FEAT_TERMGUICOLORS
2582 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2583 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002584 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002585#endif
2586 }
2587 ++table->ga_len;
2588 return (table->ga_len - 1 + ATTR_OFF);
2589}
2590
2591#if defined(FEAT_TERMINAL) || defined(PROTO)
2592/*
2593 * Get an attribute index for a cterm entry.
2594 * Uses an existing entry when possible or adds one when needed.
2595 */
2596 int
2597get_cterm_attr_idx(int attr, int fg, int bg)
2598{
2599 attrentry_T at_en;
2600
Bram Moolenaara80faa82020-04-12 19:37:17 +02002601 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002602#ifdef FEAT_TERMGUICOLORS
2603 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2604 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002605 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002606#endif
2607 at_en.ae_attr = attr;
2608 at_en.ae_u.cterm.fg_color = fg;
2609 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002610 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002611 return get_attr_entry(&cterm_attr_table, &at_en);
2612}
2613#endif
2614
2615#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2616/*
2617 * Get an attribute index for a 'termguicolors' entry.
2618 * Uses an existing entry when possible or adds one when needed.
2619 */
2620 int
2621get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2622{
2623 attrentry_T at_en;
2624
Bram Moolenaara80faa82020-04-12 19:37:17 +02002625 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002626 at_en.ae_attr = attr;
2627 if (fg == INVALCOLOR && bg == INVALCOLOR)
2628 {
2629 // If both GUI colors are not set fall back to the cterm colors. Helps
2630 // if the GUI only has an attribute, such as undercurl.
2631 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2632 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2633 }
2634 else
2635 {
2636 at_en.ae_u.cterm.fg_rgb = fg;
2637 at_en.ae_u.cterm.bg_rgb = bg;
2638 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002639 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002640 return get_attr_entry(&cterm_attr_table, &at_en);
2641}
2642#endif
2643
2644#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2645/*
2646 * Get an attribute index for a cterm entry.
2647 * Uses an existing entry when possible or adds one when needed.
2648 */
2649 int
2650get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2651{
2652 attrentry_T at_en;
2653
Bram Moolenaara80faa82020-04-12 19:37:17 +02002654 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002655 at_en.ae_attr = attr;
2656 at_en.ae_u.gui.fg_color = fg;
2657 at_en.ae_u.gui.bg_color = bg;
2658 return get_attr_entry(&gui_attr_table, &at_en);
2659}
2660#endif
2661
2662/*
2663 * Clear all highlight tables.
2664 */
2665 void
2666clear_hl_tables(void)
2667{
2668 int i;
2669 attrentry_T *taep;
2670
2671#ifdef FEAT_GUI
2672 ga_clear(&gui_attr_table);
2673#endif
2674 for (i = 0; i < term_attr_table.ga_len; ++i)
2675 {
2676 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2677 vim_free(taep->ae_u.term.start);
2678 vim_free(taep->ae_u.term.stop);
2679 }
2680 ga_clear(&term_attr_table);
2681 ga_clear(&cterm_attr_table);
2682}
2683
2684/*
2685 * Combine special attributes (e.g., for spelling) with other attributes
2686 * (e.g., for syntax highlighting).
2687 * "prim_attr" overrules "char_attr".
2688 * This creates a new group when required.
2689 * Since we expect there to be few spelling mistakes we don't cache the
2690 * result.
2691 * Return the resulting attributes.
2692 */
2693 int
2694hl_combine_attr(int char_attr, int prim_attr)
2695{
2696 attrentry_T *char_aep = NULL;
2697 attrentry_T *spell_aep;
2698 attrentry_T new_en;
2699
2700 if (char_attr == 0)
2701 return prim_attr;
2702 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2703 return ATTR_COMBINE(char_attr, prim_attr);
2704#ifdef FEAT_GUI
2705 if (gui.in_use)
2706 {
2707 if (char_attr > HL_ALL)
2708 char_aep = syn_gui_attr2entry(char_attr);
2709 if (char_aep != NULL)
2710 new_en = *char_aep;
2711 else
2712 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002713 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002714 new_en.ae_u.gui.fg_color = INVALCOLOR;
2715 new_en.ae_u.gui.bg_color = INVALCOLOR;
2716 new_en.ae_u.gui.sp_color = INVALCOLOR;
2717 if (char_attr <= HL_ALL)
2718 new_en.ae_attr = char_attr;
2719 }
2720
2721 if (prim_attr <= HL_ALL)
2722 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2723 else
2724 {
2725 spell_aep = syn_gui_attr2entry(prim_attr);
2726 if (spell_aep != NULL)
2727 {
2728 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2729 spell_aep->ae_attr);
2730 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2731 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2732 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2733 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2734 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2735 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2736 if (spell_aep->ae_u.gui.font != NOFONT)
2737 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2738# ifdef FEAT_XFONTSET
2739 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2740 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2741# endif
2742 }
2743 }
2744 return get_attr_entry(&gui_attr_table, &new_en);
2745 }
2746#endif
2747
2748 if (IS_CTERM)
2749 {
2750 if (char_attr > HL_ALL)
2751 char_aep = syn_cterm_attr2entry(char_attr);
2752 if (char_aep != NULL)
2753 new_en = *char_aep;
2754 else
2755 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002756 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002757#ifdef FEAT_TERMGUICOLORS
2758 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2759 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002760 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002761#endif
2762 if (char_attr <= HL_ALL)
2763 new_en.ae_attr = char_attr;
2764 }
2765
2766 if (prim_attr <= HL_ALL)
2767 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2768 else
2769 {
2770 spell_aep = syn_cterm_attr2entry(prim_attr);
2771 if (spell_aep != NULL)
2772 {
2773 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2774 spell_aep->ae_attr);
2775 if (spell_aep->ae_u.cterm.fg_color > 0)
2776 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2777 if (spell_aep->ae_u.cterm.bg_color > 0)
2778 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002779 if (spell_aep->ae_u.cterm.ul_color > 0)
2780 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002781#ifdef FEAT_TERMGUICOLORS
2782 // If both fg and bg are not set fall back to cterm colors.
2783 // Helps for SpellBad which uses undercurl in the GUI.
2784 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2785 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2786 {
2787 if (spell_aep->ae_u.cterm.fg_color > 0)
2788 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2789 if (spell_aep->ae_u.cterm.bg_color > 0)
2790 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2791 }
2792 else
2793 {
2794 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2795 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2796 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2797 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2798 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002799 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2800 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002801#endif
2802 }
2803 }
2804 return get_attr_entry(&cterm_attr_table, &new_en);
2805 }
2806
2807 if (char_attr > HL_ALL)
2808 char_aep = syn_term_attr2entry(char_attr);
2809 if (char_aep != NULL)
2810 new_en = *char_aep;
2811 else
2812 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002813 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002814 if (char_attr <= HL_ALL)
2815 new_en.ae_attr = char_attr;
2816 }
2817
2818 if (prim_attr <= HL_ALL)
2819 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2820 else
2821 {
2822 spell_aep = syn_term_attr2entry(prim_attr);
2823 if (spell_aep != NULL)
2824 {
2825 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2826 if (spell_aep->ae_u.term.start != NULL)
2827 {
2828 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2829 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2830 }
2831 }
2832 }
2833 return get_attr_entry(&term_attr_table, &new_en);
2834}
2835
2836#ifdef FEAT_GUI
2837 attrentry_T *
2838syn_gui_attr2entry(int attr)
2839{
2840 attr -= ATTR_OFF;
2841 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2842 return NULL;
2843 return &(GUI_ATTR_ENTRY(attr));
2844}
2845#endif
2846
2847/*
2848 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2849 * Only to be used when "attr" > HL_ALL.
2850 */
2851 int
2852syn_attr2attr(int attr)
2853{
2854 attrentry_T *aep;
2855
2856#ifdef FEAT_GUI
2857 if (gui.in_use)
2858 aep = syn_gui_attr2entry(attr);
2859 else
2860#endif
2861 if (IS_CTERM)
2862 aep = syn_cterm_attr2entry(attr);
2863 else
2864 aep = syn_term_attr2entry(attr);
2865
2866 if (aep == NULL) // highlighting not set
2867 return 0;
2868 return aep->ae_attr;
2869}
2870
2871
2872 attrentry_T *
2873syn_term_attr2entry(int attr)
2874{
2875 attr -= ATTR_OFF;
2876 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2877 return NULL;
2878 return &(TERM_ATTR_ENTRY(attr));
2879}
2880
2881 attrentry_T *
2882syn_cterm_attr2entry(int attr)
2883{
2884 attr -= ATTR_OFF;
2885 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2886 return NULL;
2887 return &(CTERM_ATTR_ENTRY(attr));
2888}
2889
2890#define LIST_ATTR 1
2891#define LIST_STRING 2
2892#define LIST_INT 3
2893
2894 static void
2895highlight_list_one(int id)
2896{
2897 hl_group_T *sgp;
2898 int didh = FALSE;
2899
2900 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2901
2902 if (message_filtered(sgp->sg_name))
2903 return;
2904
2905 didh = highlight_list_arg(id, didh, LIST_ATTR,
2906 sgp->sg_term, NULL, "term");
2907 didh = highlight_list_arg(id, didh, LIST_STRING,
2908 0, sgp->sg_start, "start");
2909 didh = highlight_list_arg(id, didh, LIST_STRING,
2910 0, sgp->sg_stop, "stop");
2911
2912 didh = highlight_list_arg(id, didh, LIST_ATTR,
2913 sgp->sg_cterm, NULL, "cterm");
2914 didh = highlight_list_arg(id, didh, LIST_INT,
2915 sgp->sg_cterm_fg, NULL, "ctermfg");
2916 didh = highlight_list_arg(id, didh, LIST_INT,
2917 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002918 didh = highlight_list_arg(id, didh, LIST_INT,
2919 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002920
2921#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2922 didh = highlight_list_arg(id, didh, LIST_ATTR,
2923 sgp->sg_gui, NULL, "gui");
2924 didh = highlight_list_arg(id, didh, LIST_STRING,
2925 0, sgp->sg_gui_fg_name, "guifg");
2926 didh = highlight_list_arg(id, didh, LIST_STRING,
2927 0, sgp->sg_gui_bg_name, "guibg");
2928 didh = highlight_list_arg(id, didh, LIST_STRING,
2929 0, sgp->sg_gui_sp_name, "guisp");
2930#endif
2931#ifdef FEAT_GUI
2932 didh = highlight_list_arg(id, didh, LIST_STRING,
2933 0, sgp->sg_font_name, "font");
2934#endif
2935
2936 if (sgp->sg_link && !got_int)
2937 {
2938 (void)syn_list_header(didh, 9999, id);
2939 didh = TRUE;
2940 msg_puts_attr("links to", HL_ATTR(HLF_D));
2941 msg_putchar(' ');
2942 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2943 }
2944
2945 if (!didh)
2946 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2947#ifdef FEAT_EVAL
2948 if (p_verbose > 0)
2949 last_set_msg(sgp->sg_script_ctx);
2950#endif
2951}
2952
2953 static int
2954highlight_list_arg(
2955 int id,
2956 int didh,
2957 int type,
2958 int iarg,
2959 char_u *sarg,
2960 char *name)
2961{
2962 char_u buf[100];
2963 char_u *ts;
2964 int i;
2965
2966 if (got_int)
2967 return FALSE;
2968 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2969 {
2970 ts = buf;
2971 if (type == LIST_INT)
2972 sprintf((char *)buf, "%d", iarg - 1);
2973 else if (type == LIST_STRING)
2974 ts = sarg;
2975 else // type == LIST_ATTR
2976 {
2977 buf[0] = NUL;
2978 for (i = 0; hl_attr_table[i] != 0; ++i)
2979 {
2980 if (iarg & hl_attr_table[i])
2981 {
2982 if (buf[0] != NUL)
2983 vim_strcat(buf, (char_u *)",", 100);
2984 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2985 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2986 }
2987 }
2988 }
2989
2990 (void)syn_list_header(didh,
2991 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2992 didh = TRUE;
2993 if (!got_int)
2994 {
2995 if (*name != NUL)
2996 {
2997 msg_puts_attr(name, HL_ATTR(HLF_D));
2998 msg_puts_attr("=", HL_ATTR(HLF_D));
2999 }
3000 msg_outtrans(ts);
3001 }
3002 }
3003 return didh;
3004}
3005
3006#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3007/*
3008 * Return "1" if highlight group "id" has attribute "flag".
3009 * Return NULL otherwise.
3010 */
3011 char_u *
3012highlight_has_attr(
3013 int id,
3014 int flag,
3015 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3016{
3017 int attr;
3018
3019 if (id <= 0 || id > highlight_ga.ga_len)
3020 return NULL;
3021
3022#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3023 if (modec == 'g')
3024 attr = HL_TABLE()[id - 1].sg_gui;
3025 else
3026#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003027 {
3028 if (modec == 'c')
3029 attr = HL_TABLE()[id - 1].sg_cterm;
3030 else
3031 attr = HL_TABLE()[id - 1].sg_term;
3032 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003033
3034 if (attr & flag)
3035 return (char_u *)"1";
3036 return NULL;
3037}
3038#endif
3039
3040#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3041/*
3042 * Return color name of highlight group "id".
3043 */
3044 char_u *
3045highlight_color(
3046 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003047 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003048 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3049{
3050 static char_u name[20];
3051 int n;
3052 int fg = FALSE;
3053 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003054 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003055 int font = FALSE;
3056
3057 if (id <= 0 || id > highlight_ga.ga_len)
3058 return NULL;
3059
3060 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3061 fg = TRUE;
3062 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3063 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3064 font = TRUE;
3065 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3066 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003067 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3068 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003069 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3070 return NULL;
3071 if (modec == 'g')
3072 {
3073# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3074# ifdef FEAT_GUI
3075 // return font name
3076 if (font)
3077 return HL_TABLE()[id - 1].sg_font_name;
3078# endif
3079
3080 // return #RRGGBB form (only possible when GUI is running)
3081 if ((USE_24BIT) && what[2] == '#')
3082 {
3083 guicolor_T color;
3084 long_u rgb;
3085 static char_u buf[10];
3086
3087 if (fg)
3088 color = HL_TABLE()[id - 1].sg_gui_fg;
3089 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003090 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003091 else
3092 color = HL_TABLE()[id - 1].sg_gui_bg;
3093 if (color == INVALCOLOR)
3094 return NULL;
3095 rgb = (long_u)GUI_MCH_GET_RGB(color);
3096 sprintf((char *)buf, "#%02x%02x%02x",
3097 (unsigned)(rgb >> 16),
3098 (unsigned)(rgb >> 8) & 255,
3099 (unsigned)rgb & 255);
3100 return buf;
3101 }
3102# endif
3103 if (fg)
3104 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3105 if (sp)
3106 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3107 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3108 }
3109 if (font || sp)
3110 return NULL;
3111 if (modec == 'c')
3112 {
3113 if (fg)
3114 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003115 else if (ul)
3116 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003117 else
3118 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3119 if (n < 0)
3120 return NULL;
3121 sprintf((char *)name, "%d", n);
3122 return name;
3123 }
3124 // term doesn't have color
3125 return NULL;
3126}
3127#endif
3128
3129#if (defined(FEAT_SYN_HL) \
3130 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3131 && defined(FEAT_PRINTER)) || defined(PROTO)
3132/*
3133 * Return color name of highlight group "id" as RGB value.
3134 */
3135 long_u
3136highlight_gui_color_rgb(
3137 int id,
3138 int fg) // TRUE = fg, FALSE = bg
3139{
3140 guicolor_T color;
3141
3142 if (id <= 0 || id > highlight_ga.ga_len)
3143 return 0L;
3144
3145 if (fg)
3146 color = HL_TABLE()[id - 1].sg_gui_fg;
3147 else
3148 color = HL_TABLE()[id - 1].sg_gui_bg;
3149
3150 if (color == INVALCOLOR)
3151 return 0L;
3152
3153 return GUI_MCH_GET_RGB(color);
3154}
3155#endif
3156
3157/*
3158 * Output the syntax list header.
3159 * Return TRUE when started a new line.
3160 */
3161 int
3162syn_list_header(
3163 int did_header, // did header already
3164 int outlen, // length of string that comes
3165 int id) // highlight group id
3166{
3167 int endcol = 19;
3168 int newline = TRUE;
3169 int name_col = 0;
3170
3171 if (!did_header)
3172 {
3173 msg_putchar('\n');
3174 if (got_int)
3175 return TRUE;
3176 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3177 name_col = msg_col;
3178 endcol = 15;
3179 }
3180 else if (msg_col + outlen + 1 >= Columns)
3181 {
3182 msg_putchar('\n');
3183 if (got_int)
3184 return TRUE;
3185 }
3186 else
3187 {
3188 if (msg_col >= endcol) // wrap around is like starting a new line
3189 newline = FALSE;
3190 }
3191
3192 if (msg_col >= endcol) // output at least one space
3193 endcol = msg_col + 1;
3194 if (Columns <= endcol) // avoid hang for tiny window
3195 endcol = Columns - 1;
3196
3197 msg_advance(endcol);
3198
3199 // Show "xxx" with the attributes.
3200 if (!did_header)
3201 {
3202 if (endcol == Columns - 1 && endcol <= name_col)
3203 msg_putchar(' ');
3204 msg_puts_attr("xxx", syn_id2attr(id));
3205 msg_putchar(' ');
3206 }
3207
3208 return newline;
3209}
3210
3211/*
3212 * Set the attribute numbers for a highlight group.
3213 * Called after one of the attributes has changed.
3214 */
3215 static void
3216set_hl_attr(
3217 int idx) // index in array
3218{
3219 attrentry_T at_en;
3220 hl_group_T *sgp = HL_TABLE() + idx;
3221
3222 // The "Normal" group doesn't need an attribute number
3223 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3224 return;
3225
3226#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003227 // For the GUI mode: If there are other than "normal" highlighting
3228 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003229 if (sgp->sg_gui_fg == INVALCOLOR
3230 && sgp->sg_gui_bg == INVALCOLOR
3231 && sgp->sg_gui_sp == INVALCOLOR
3232 && sgp->sg_font == NOFONT
3233# ifdef FEAT_XFONTSET
3234 && sgp->sg_fontset == NOFONTSET
3235# endif
3236 )
3237 {
3238 sgp->sg_gui_attr = sgp->sg_gui;
3239 }
3240 else
3241 {
3242 at_en.ae_attr = sgp->sg_gui;
3243 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3244 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3245 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3246 at_en.ae_u.gui.font = sgp->sg_font;
3247# ifdef FEAT_XFONTSET
3248 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3249# endif
3250 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3251 }
3252#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003253 // For the term mode: If there are other than "normal" highlighting
3254 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003255 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3256 sgp->sg_term_attr = sgp->sg_term;
3257 else
3258 {
3259 at_en.ae_attr = sgp->sg_term;
3260 at_en.ae_u.term.start = sgp->sg_start;
3261 at_en.ae_u.term.stop = sgp->sg_stop;
3262 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3263 }
3264
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003265 // For the color term mode: If there are other than "normal"
3266 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003267 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003268# ifdef FEAT_TERMGUICOLORS
3269 && sgp->sg_gui_fg == INVALCOLOR
3270 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003271 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003272# endif
3273 )
3274 sgp->sg_cterm_attr = sgp->sg_cterm;
3275 else
3276 {
3277 at_en.ae_attr = sgp->sg_cterm;
3278 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3279 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003280 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003281# ifdef FEAT_TERMGUICOLORS
3282# ifdef MSWIN
3283# ifdef VIMDLL
3284 // Only when not using the GUI.
3285 if (!gui.in_use && !gui.starting)
3286# endif
3287 {
3288 int id;
3289 guicolor_T fg, bg;
3290
3291 id = syn_name2id((char_u *)"Normal");
3292 if (id > 0)
3293 {
3294 syn_id2colors(id, &fg, &bg);
3295 if (sgp->sg_gui_fg == INVALCOLOR)
3296 sgp->sg_gui_fg = fg;
3297 if (sgp->sg_gui_bg == INVALCOLOR)
3298 sgp->sg_gui_bg = bg;
3299 }
3300
3301 }
3302# endif
3303 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3304 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003305 // Only use the underline/undercurl color when used, it may clear the
3306 // background color if not supported.
3307 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
3308 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3309 else
3310 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003311 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3312 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3313 {
3314 // If both fg and bg are invalid fall back to the cterm colors.
3315 // Helps when the GUI only uses an attribute, e.g. undercurl.
3316 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3317 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3318 }
3319# endif
3320 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3321 }
3322}
3323
3324/*
3325 * Lookup a highlight group name and return its ID.
3326 * If it is not found, 0 is returned.
3327 */
3328 int
3329syn_name2id(char_u *name)
3330{
3331 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003332 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003333
3334 // Avoid using stricmp() too much, it's slow on some systems
3335 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3336 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003337 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003338 vim_strup(name_u);
3339 for (i = highlight_ga.ga_len; --i >= 0; )
3340 if (HL_TABLE()[i].sg_name_u != NULL
3341 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3342 break;
3343 return i + 1;
3344}
3345
3346/*
3347 * Lookup a highlight group name and return its attributes.
3348 * Return zero if not found.
3349 */
3350 int
3351syn_name2attr(char_u *name)
3352{
3353 int id = syn_name2id(name);
3354
3355 if (id != 0)
3356 return syn_id2attr(id);
3357 return 0;
3358}
3359
3360#if defined(FEAT_EVAL) || defined(PROTO)
3361/*
3362 * Return TRUE if highlight group "name" exists.
3363 */
3364 int
3365highlight_exists(char_u *name)
3366{
3367 return (syn_name2id(name) > 0);
3368}
3369
3370# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3371/*
3372 * Return the name of highlight group "id".
3373 * When not a valid ID return an empty string.
3374 */
3375 char_u *
3376syn_id2name(int id)
3377{
3378 if (id <= 0 || id > highlight_ga.ga_len)
3379 return (char_u *)"";
3380 return HL_TABLE()[id - 1].sg_name;
3381}
3382# endif
3383#endif
3384
3385/*
3386 * Like syn_name2id(), but take a pointer + length argument.
3387 */
3388 int
3389syn_namen2id(char_u *linep, int len)
3390{
3391 char_u *name;
3392 int id = 0;
3393
3394 name = vim_strnsave(linep, len);
3395 if (name != NULL)
3396 {
3397 id = syn_name2id(name);
3398 vim_free(name);
3399 }
3400 return id;
3401}
3402
3403/*
3404 * Find highlight group name in the table and return its ID.
3405 * The argument is a pointer to the name and the length of the name.
3406 * If it doesn't exist yet, a new entry is created.
3407 * Return 0 for failure.
3408 */
3409 int
3410syn_check_group(char_u *pp, int len)
3411{
3412 int id;
3413 char_u *name;
3414
erw7f7f7aaf2021-12-07 21:29:20 +00003415 if (len > MAX_SYN_NAME)
3416 {
3417 emsg(_(e_highlight_group_name_too_long));
3418 return 0;
3419 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003420 name = vim_strnsave(pp, len);
3421 if (name == NULL)
3422 return 0;
3423
3424 id = syn_name2id(name);
3425 if (id == 0) // doesn't exist yet
3426 id = syn_add_group(name);
3427 else
3428 vim_free(name);
3429 return id;
3430}
3431
3432/*
3433 * Add new highlight group and return its ID.
3434 * "name" must be an allocated string, it will be consumed.
3435 * Return 0 for failure.
3436 */
3437 static int
3438syn_add_group(char_u *name)
3439{
3440 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003441 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003442
3443 // Check that the name is ASCII letters, digits and underscore.
3444 for (p = name; *p != NUL; ++p)
3445 {
3446 if (!vim_isprintc(*p))
3447 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003448 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003449 vim_free(name);
3450 return 0;
3451 }
3452 else if (!ASCII_ISALNUM(*p) && *p != '_')
3453 {
3454 // This is an error, but since there previously was no check only
3455 // give a warning.
3456 msg_source(HL_ATTR(HLF_W));
3457 msg(_("W18: Invalid character in group name"));
3458 break;
3459 }
3460 }
3461
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003462 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003463 if (highlight_ga.ga_data == NULL)
3464 {
3465 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3466 highlight_ga.ga_growsize = 10;
3467 }
3468
3469 if (highlight_ga.ga_len >= MAX_HL_ID)
3470 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003471 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003472 vim_free(name);
3473 return 0;
3474 }
3475
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003476 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003477 if (ga_grow(&highlight_ga, 1) == FAIL)
3478 {
3479 vim_free(name);
3480 return 0;
3481 }
3482
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003483 name_up = vim_strsave_up(name);
3484 if (name_up == NULL)
3485 {
3486 vim_free(name);
3487 return 0;
3488 }
3489
Bram Moolenaara80faa82020-04-12 19:37:17 +02003490 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003491 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003492 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003493#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3494 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3495 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003496 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003497#endif
3498 ++highlight_ga.ga_len;
3499
3500 return highlight_ga.ga_len; // ID is index plus one
3501}
3502
3503/*
3504 * When, just after calling syn_add_group(), an error is discovered, this
3505 * function deletes the new name.
3506 */
3507 static void
3508syn_unadd_group(void)
3509{
3510 --highlight_ga.ga_len;
3511 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3512 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3513}
3514
3515/*
3516 * Translate a group ID to highlight attributes.
3517 */
3518 int
3519syn_id2attr(int hl_id)
3520{
3521 int attr;
3522 hl_group_T *sgp;
3523
3524 hl_id = syn_get_final_id(hl_id);
3525 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3526
3527#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003528 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003529 if (gui.in_use)
3530 attr = sgp->sg_gui_attr;
3531 else
3532#endif
3533 if (IS_CTERM)
3534 attr = sgp->sg_cterm_attr;
3535 else
3536 attr = sgp->sg_term_attr;
3537
3538 return attr;
3539}
3540
3541#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3542/*
3543 * Get the GUI colors and attributes for a group ID.
3544 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3545 */
3546 int
3547syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3548{
3549 hl_group_T *sgp;
3550
3551 hl_id = syn_get_final_id(hl_id);
3552 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3553
3554 *fgp = sgp->sg_gui_fg;
3555 *bgp = sgp->sg_gui_bg;
3556 return sgp->sg_gui;
3557}
3558#endif
3559
3560#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003561 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3562 && defined(FEAT_TERMGUICOLORS)) \
3563 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003564 void
3565syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3566{
3567 hl_group_T *sgp;
3568
3569 hl_id = syn_get_final_id(hl_id);
3570 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3571 *fgp = sgp->sg_cterm_fg - 1;
3572 *bgp = sgp->sg_cterm_bg - 1;
3573}
3574#endif
3575
3576/*
3577 * Translate a group ID to the final group ID (following links).
3578 */
3579 int
3580syn_get_final_id(int hl_id)
3581{
3582 int count;
3583 hl_group_T *sgp;
3584
3585 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3586 return 0; // Can be called from eval!!
3587
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003588 // Follow links until there is no more.
3589 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003590 for (count = 100; --count >= 0; )
3591 {
3592 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3593 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3594 break;
3595 hl_id = sgp->sg_link;
3596 }
3597
3598 return hl_id;
3599}
3600
3601#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3602/*
3603 * Call this function just after the GUI has started.
3604 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3605 * It finds the font and color handles for the highlighting groups.
3606 */
3607 void
3608highlight_gui_started(void)
3609{
3610 int idx;
3611
3612 // First get the colors from the "Normal" and "Menu" group, if set
3613 if (USE_24BIT)
3614 set_normal_colors();
3615
3616 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3617 gui_do_one_color(idx, FALSE, FALSE);
3618
3619 highlight_changed();
3620}
3621
3622 static void
3623gui_do_one_color(
3624 int idx,
3625 int do_menu UNUSED, // TRUE: might set the menu font
3626 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3627{
3628 int didit = FALSE;
3629
3630# ifdef FEAT_GUI
3631# ifdef FEAT_TERMGUICOLORS
3632 if (gui.in_use)
3633# endif
3634 if (HL_TABLE()[idx].sg_font_name != NULL)
3635 {
3636 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3637 do_tooltip, TRUE);
3638 didit = TRUE;
3639 }
3640# endif
3641 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3642 {
3643 HL_TABLE()[idx].sg_gui_fg =
3644 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3645 didit = TRUE;
3646 }
3647 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3648 {
3649 HL_TABLE()[idx].sg_gui_bg =
3650 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3651 didit = TRUE;
3652 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003653 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3654 {
3655 HL_TABLE()[idx].sg_gui_sp =
3656 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3657 didit = TRUE;
3658 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003659 if (didit) // need to get a new attr number
3660 set_hl_attr(idx);
3661}
3662#endif
3663
3664#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3665/*
3666 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3667 */
3668 static void
3669combine_stl_hlt(
3670 int id,
3671 int id_S,
3672 int id_alt,
3673 int hlcnt,
3674 int i,
3675 int hlf,
3676 int *table)
3677{
3678 hl_group_T *hlt = HL_TABLE();
3679
3680 if (id_alt == 0)
3681 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003682 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003683 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3684 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3685# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3686 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3687# endif
3688 }
3689 else
3690 mch_memmove(&hlt[hlcnt + i],
3691 &hlt[id_alt - 1],
3692 sizeof(hl_group_T));
3693 hlt[hlcnt + i].sg_link = 0;
3694
3695 hlt[hlcnt + i].sg_term ^=
3696 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3697 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3698 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3699 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3700 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3701 hlt[hlcnt + i].sg_cterm ^=
3702 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3703 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3704 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3705 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3706 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3707# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3708 hlt[hlcnt + i].sg_gui ^=
3709 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3710# endif
3711# ifdef FEAT_GUI
3712 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3713 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3714 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3715 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3716 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3717 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3718 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3719 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3720# ifdef FEAT_XFONTSET
3721 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3722 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3723# endif
3724# endif
3725 highlight_ga.ga_len = hlcnt + i + 1;
3726 set_hl_attr(hlcnt + i); // At long last we can apply
3727 table[i] = syn_id2attr(hlcnt + i + 1);
3728}
3729#endif
3730
3731/*
3732 * Translate the 'highlight' option into attributes in highlight_attr[] and
3733 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3734 * corresponding highlights to use on top of HLF_SNC is computed.
3735 * Called only when the 'highlight' option has been changed and upon first
3736 * screen redraw after any :highlight command.
3737 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3738 */
3739 int
3740highlight_changed(void)
3741{
3742 int hlf;
3743 int i;
3744 char_u *p;
3745 int attr;
3746 char_u *end;
3747 int id;
3748#ifdef USER_HIGHLIGHT
3749 char_u userhl[30]; // use 30 to avoid compiler warning
3750# ifdef FEAT_STL_OPT
3751 int id_S = -1;
3752 int id_SNC = 0;
3753# ifdef FEAT_TERMINAL
3754 int id_ST = 0;
3755 int id_STNC = 0;
3756# endif
3757 int hlcnt;
3758# endif
3759#endif
3760 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3761
3762 need_highlight_changed = FALSE;
3763
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003764#ifdef FEAT_TERMINAL
3765 term_update_colors_all();
3766 term_update_wincolor_all();
3767#endif
3768
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003769 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003770 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3771 highlight_attr[hlf] = 0;
3772
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003773 // First set all attributes to their default value.
3774 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003775 for (i = 0; i < 2; ++i)
3776 {
3777 if (i)
3778 p = p_hl;
3779 else
3780 p = get_highlight_default();
3781 if (p == NULL) // just in case
3782 continue;
3783
3784 while (*p)
3785 {
3786 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3787 if (hl_flags[hlf] == *p)
3788 break;
3789 ++p;
3790 if (hlf == (int)HLF_COUNT || *p == NUL)
3791 return FAIL;
3792
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003793 // Allow several hl_flags to be combined, like "bu" for
3794 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003795 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003796 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003797 {
3798 if (VIM_ISWHITE(*p)) // ignore white space
3799 continue;
3800
3801 if (attr > HL_ALL) // Combination with ':' is not allowed.
3802 return FAIL;
3803
3804 switch (*p)
3805 {
3806 case 'b': attr |= HL_BOLD;
3807 break;
3808 case 'i': attr |= HL_ITALIC;
3809 break;
3810 case '-':
3811 case 'n': // no highlighting
3812 break;
3813 case 'r': attr |= HL_INVERSE;
3814 break;
3815 case 's': attr |= HL_STANDOUT;
3816 break;
3817 case 'u': attr |= HL_UNDERLINE;
3818 break;
3819 case 'c': attr |= HL_UNDERCURL;
3820 break;
3821 case 't': attr |= HL_STRIKETHROUGH;
3822 break;
3823 case ':': ++p; // highlight group name
3824 if (attr || *p == NUL) // no combinations
3825 return FAIL;
3826 end = vim_strchr(p, ',');
3827 if (end == NULL)
3828 end = p + STRLEN(p);
3829 id = syn_check_group(p, (int)(end - p));
3830 if (id == 0)
3831 return FAIL;
3832 attr = syn_id2attr(id);
3833 p = end - 1;
3834#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3835 if (hlf == (int)HLF_SNC)
3836 id_SNC = syn_get_final_id(id);
3837# ifdef FEAT_TERMINAL
3838 else if (hlf == (int)HLF_ST)
3839 id_ST = syn_get_final_id(id);
3840 else if (hlf == (int)HLF_STNC)
3841 id_STNC = syn_get_final_id(id);
3842# endif
3843 else if (hlf == (int)HLF_S)
3844 id_S = syn_get_final_id(id);
3845#endif
3846 break;
3847 default: return FAIL;
3848 }
3849 }
3850 highlight_attr[hlf] = attr;
3851
3852 p = skip_to_option_part(p); // skip comma and spaces
3853 }
3854 }
3855
3856#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003857 // Setup the user highlights
3858 //
3859 // Temporarily utilize 28 more hl entries:
3860 // 9 for User1-User9 combined with StatusLineNC
3861 // 9 for User1-User9 combined with StatusLineTerm
3862 // 9 for User1-User9 combined with StatusLineTermNC
3863 // 1 for StatusLine default
3864 // Have to be in there simultaneously in case of table overflows in
3865 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003866# ifdef FEAT_STL_OPT
3867 if (ga_grow(&highlight_ga, 28) == FAIL)
3868 return FAIL;
3869 hlcnt = highlight_ga.ga_len;
3870 if (id_S == -1)
3871 {
3872 // Make sure id_S is always valid to simplify code below. Use the last
3873 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003874 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003875 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3876 id_S = hlcnt + 19;
3877 }
3878# endif
3879 for (i = 0; i < 9; i++)
3880 {
3881 sprintf((char *)userhl, "User%d", i + 1);
3882 id = syn_name2id(userhl);
3883 if (id == 0)
3884 {
3885 highlight_user[i] = 0;
3886# ifdef FEAT_STL_OPT
3887 highlight_stlnc[i] = 0;
3888# ifdef FEAT_TERMINAL
3889 highlight_stlterm[i] = 0;
3890 highlight_stltermnc[i] = 0;
3891# endif
3892# endif
3893 }
3894 else
3895 {
3896 highlight_user[i] = syn_id2attr(id);
3897# ifdef FEAT_STL_OPT
3898 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3899 HLF_SNC, highlight_stlnc);
3900# ifdef FEAT_TERMINAL
3901 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3902 HLF_ST, highlight_stlterm);
3903 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3904 HLF_STNC, highlight_stltermnc);
3905# endif
3906# endif
3907 }
3908 }
3909# ifdef FEAT_STL_OPT
3910 highlight_ga.ga_len = hlcnt;
3911# endif
3912
3913#endif // USER_HIGHLIGHT
3914
3915 return OK;
3916}
3917
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003918static void highlight_list(void);
3919static void highlight_list_two(int cnt, int attr);
3920
3921/*
3922 * Handle command line completion for :highlight command.
3923 */
3924 void
3925set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3926{
3927 char_u *p;
3928
3929 // Default: expand group names
3930 xp->xp_context = EXPAND_HIGHLIGHT;
3931 xp->xp_pattern = arg;
3932 include_link = 2;
3933 include_default = 1;
3934
3935 // (part of) subcommand already typed
3936 if (*arg != NUL)
3937 {
3938 p = skiptowhite(arg);
3939 if (*p != NUL) // past "default" or group name
3940 {
3941 include_default = 0;
3942 if (STRNCMP("default", arg, p - arg) == 0)
3943 {
3944 arg = skipwhite(p);
3945 xp->xp_pattern = arg;
3946 p = skiptowhite(arg);
3947 }
3948 if (*p != NUL) // past group name
3949 {
3950 include_link = 0;
3951 if (arg[1] == 'i' && arg[0] == 'N')
3952 highlight_list();
3953 if (STRNCMP("link", arg, p - arg) == 0
3954 || STRNCMP("clear", arg, p - arg) == 0)
3955 {
3956 xp->xp_pattern = skipwhite(p);
3957 p = skiptowhite(xp->xp_pattern);
3958 if (*p != NUL) // past first group name
3959 {
3960 xp->xp_pattern = skipwhite(p);
3961 p = skiptowhite(xp->xp_pattern);
3962 }
3963 }
3964 if (*p != NUL) // past group name(s)
3965 xp->xp_context = EXPAND_NOTHING;
3966 }
3967 }
3968 }
3969}
3970
3971/*
3972 * List highlighting matches in a nice way.
3973 */
3974 static void
3975highlight_list(void)
3976{
3977 int i;
3978
3979 for (i = 10; --i >= 0; )
3980 highlight_list_two(i, HL_ATTR(HLF_D));
3981 for (i = 40; --i >= 0; )
3982 highlight_list_two(99, 0);
3983}
3984
3985 static void
3986highlight_list_two(int cnt, int attr)
3987{
3988 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3989 msg_clr_eos();
3990 out_flush();
3991 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3992}
3993
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003994/*
3995 * Function given to ExpandGeneric() to obtain the list of group names.
3996 */
3997 char_u *
3998get_highlight_name(expand_T *xp UNUSED, int idx)
3999{
4000 return get_highlight_name_ext(xp, idx, TRUE);
4001}
4002
4003/*
4004 * Obtain a highlight group name.
4005 * When "skip_cleared" is TRUE don't return a cleared entry.
4006 */
4007 char_u *
4008get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4009{
4010 if (idx < 0)
4011 return NULL;
4012
4013 // Items are never removed from the table, skip the ones that were
4014 // cleared.
4015 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4016 return (char_u *)"";
4017
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004018 if (idx == highlight_ga.ga_len && include_none != 0)
4019 return (char_u *)"none";
4020 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4021 return (char_u *)"default";
4022 if (idx == highlight_ga.ga_len + include_none + include_default
4023 && include_link != 0)
4024 return (char_u *)"link";
4025 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4026 && include_link != 0)
4027 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004028 if (idx >= highlight_ga.ga_len)
4029 return NULL;
4030 return HL_TABLE()[idx].sg_name;
4031}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004032
4033#if defined(FEAT_GUI) || defined(PROTO)
4034/*
4035 * Free all the highlight group fonts.
4036 * Used when quitting for systems which need it.
4037 */
4038 void
4039free_highlight_fonts(void)
4040{
4041 int idx;
4042
4043 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4044 {
4045 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4046 HL_TABLE()[idx].sg_font = NOFONT;
4047# ifdef FEAT_XFONTSET
4048 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4049 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4050# endif
4051 }
4052
4053 gui_mch_free_font(gui.norm_font);
4054# ifdef FEAT_XFONTSET
4055 gui_mch_free_fontset(gui.fontset);
4056# endif
4057# ifndef FEAT_GUI_GTK
4058 gui_mch_free_font(gui.bold_font);
4059 gui_mch_free_font(gui.ital_font);
4060 gui_mch_free_font(gui.boldital_font);
4061# endif
4062}
4063#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004064
4065#if defined(FEAT_EVAL) || defined(PROTO)
4066/*
4067 * Convert each of the highlight attribute bits (bold, standout, underline,
4068 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4069 * the attribute name as the key.
4070 */
4071 static dict_T *
4072highlight_get_attr_dict(int hlattr)
4073{
4074 dict_T *dict;
4075 int i;
4076
4077 dict = dict_alloc();
4078 if (dict == NULL)
4079 return NULL;
4080
4081 for (i = 0; hl_attr_table[i] != 0; ++i)
4082 {
4083 if (hlattr & hl_attr_table[i])
4084 {
4085 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4086 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4087 }
4088 }
4089
4090 return dict;
4091}
4092
4093/*
4094 * Return the attributes of the highlight group at index 'hl_idx' as a
4095 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4096 * links recursively.
4097 */
4098 static dict_T *
4099highlight_get_info(int hl_idx, int resolve_link)
4100{
4101 dict_T *dict;
4102 hl_group_T *sgp;
4103 dict_T *attr_dict;
4104 int hlgid;
4105
4106 dict = dict_alloc();
4107 if (dict == NULL)
4108 return dict;
4109
4110 sgp = &HL_TABLE()[hl_idx];
4111 // highlight group id is 1-based
4112 hlgid = hl_idx + 1;
4113
4114 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4115 goto error;
4116 if (dict_add_number(dict, "id", hlgid) == FAIL)
4117 goto error;
4118
4119 if (sgp->sg_link && resolve_link)
4120 {
4121 // resolve the highlight group link recursively
4122 while (sgp->sg_link)
4123 {
4124 hlgid = sgp->sg_link;
4125 sgp = &HL_TABLE()[sgp->sg_link - 1];
4126 }
4127 }
4128
4129 if (sgp->sg_term != 0)
4130 {
4131 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4132 if (attr_dict != NULL)
4133 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4134 goto error;
4135 }
4136 if (sgp->sg_start != NULL)
4137 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4138 goto error;
4139 if (sgp->sg_stop != NULL)
4140 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4141 goto error;
4142 if (sgp->sg_cterm != 0)
4143 {
4144 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4145 if (attr_dict != NULL)
4146 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4147 goto error;
4148 }
4149 if (sgp->sg_cterm_fg != 0)
4150 if (dict_add_string(dict, "ctermfg",
4151 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4152 goto error;
4153 if (sgp->sg_cterm_bg != 0)
4154 if (dict_add_string(dict, "ctermbg",
4155 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4156 goto error;
4157 if (sgp->sg_cterm_ul != 0)
4158 if (dict_add_string(dict, "ctermul",
4159 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4160 goto error;
4161 if (sgp->sg_gui != 0)
4162 {
4163 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4164 if (attr_dict != NULL)
4165 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4166 goto error;
4167 }
4168 if (sgp->sg_gui_fg_name != NULL)
4169 if (dict_add_string(dict, "guifg",
4170 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4171 goto error;
4172 if (sgp->sg_gui_bg_name != NULL)
4173 if (dict_add_string(dict, "guibg",
4174 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4175 goto error;
4176 if (sgp->sg_gui_sp_name != NULL)
4177 if (dict_add_string(dict, "guisp",
4178 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4179 goto error;
4180# ifdef FEAT_GUI
4181 if (sgp->sg_font_name != NULL)
4182 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4183 goto error;
4184# endif
4185 if (sgp->sg_link)
4186 {
4187 char_u *link;
4188
4189 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4190 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4191 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004192
4193 if (sgp->sg_deflink)
4194 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004195 }
4196 if (dict_len(dict) == 2)
4197 // If only 'name' is present, then the highlight group is cleared.
4198 dict_add_bool(dict, "cleared", VVAL_TRUE);
4199
4200 return dict;
4201
4202error:
4203 vim_free(dict);
4204 return NULL;
4205}
4206
4207/*
4208 * "hlget([name])" function
4209 * Return the attributes of a specific highlight group (if specified) or all
4210 * the highlight groups.
4211 */
4212 void
4213f_hlget(typval_T *argvars, typval_T *rettv)
4214{
4215 list_T *list;
4216 dict_T *dict;
4217 int i;
4218 char_u *hlarg = NULL;
4219 int resolve_link = FALSE;
4220
4221 if (rettv_list_alloc(rettv) == FAIL)
4222 return;
4223
4224 if (check_for_opt_string_arg(argvars, 0) == FAIL
4225 || (argvars[0].v_type != VAR_UNKNOWN
4226 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4227 return;
4228
4229 if (argvars[0].v_type != VAR_UNKNOWN)
4230 {
4231 // highlight group name supplied
4232 hlarg = tv_get_string_chk(&argvars[0]);
4233 if (hlarg == NULL)
4234 return;
4235
4236 if (argvars[1].v_type != VAR_UNKNOWN)
4237 {
4238 int error = FALSE;
4239
4240 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4241 if (error)
4242 return;
4243 }
4244 }
4245
4246 list = rettv->vval.v_list;
4247 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4248 {
4249 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4250 {
4251 dict = highlight_get_info(i, resolve_link);
4252 if (dict != NULL)
4253 list_append_dict(list, dict);
4254 }
4255 }
4256}
4257
4258/*
4259 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4260 * 'dict' or the value is not a string type. If the value is not a string type
4261 * or is NULL, then 'error' is set to TRUE.
4262 */
4263 static char_u *
4264hldict_get_string(dict_T *dict, char_u *key, int *error)
4265{
4266 dictitem_T *di;
4267
4268 *error = FALSE;
4269 di = dict_find(dict, key, -1);
4270 if (di == NULL)
4271 return NULL;
4272
4273 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4274 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004275 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004276 *error = TRUE;
4277 return NULL;
4278 }
4279
4280 return di->di_tv.vval.v_string;
4281}
4282
4283/*
4284 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4285 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4286 * Dictionary or is NULL.
4287 */
4288 static int
4289hldict_attr_to_str(
4290 dict_T *dict,
4291 char_u *key,
4292 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004293 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004294{
4295 dictitem_T *di;
4296 dict_T *attrdict;
4297 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004298 char_u *p;
4299 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004300
4301 attr_str[0] = NUL;
4302 di = dict_find(dict, key, -1);
4303 if (di == NULL)
4304 return TRUE;
4305
4306 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4307 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004308 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004309 return FALSE;
4310 }
4311
4312 attrdict = di->di_tv.vval.v_dict;
4313
4314 // If the attribute dict is empty, then return NONE to clear the attributes
4315 if (dict_len(attrdict) == 0)
4316 {
4317 vim_strcat(attr_str, (char_u *)"NONE", len);
4318 return TRUE;
4319 }
4320
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004321 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004322 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4323 {
4324 if (dict_get_bool(attrdict, (char_u *)hl_name_table[i],
4325 VVAL_FALSE) == VVAL_TRUE)
4326 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004327 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4328 STRCPY(p, (char_u *)",");
4329 sz = STRLEN(hl_name_table[i]);
4330 if (p - attr_str + sz + 1 < len)
4331 {
4332 STRCPY(p, (char_u *)hl_name_table[i]);
4333 p += sz;
4334 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004335 }
4336 }
4337
4338 return TRUE;
4339}
4340
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004341// Temporary buffer used to store the command string produced by hlset().
4342// IObuff cannot be used for this as the error messages produced by hlset()
4343// internally use IObuff.
4344#define HLSETBUFSZ 512
4345static char_u hlsetBuf[HLSETBUFSZ + 1];
4346
4347/*
4348 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4349 * "dptr", which points into "hlsetBuf".
4350 * Returns the updated pointer.
4351 */
4352 static char_u *
4353add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4354{
4355 size_t vallen;
4356
4357 // Do nothing if the value is not specified or is empty
4358 if (value == NULL || *value == NUL)
4359 return dptr;
4360
4361 vallen = STRLEN(value);
4362 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4363 {
4364 STRCPY(dptr, attr);
4365 dptr += attrlen;
4366 STRCPY(dptr, value);
4367 dptr += vallen;
4368 }
4369
4370 return dptr;
4371}
4372
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004373/*
4374 * Add or update a highlight group using 'dict' items. Returns TRUE if
4375 * successfully updated the highlight group.
4376 */
4377 static int
4378hlg_add_or_update(dict_T *dict)
4379{
4380 char_u *name;
4381 int error;
4382 char_u term_attr[80];
4383 char_u cterm_attr[80];
4384 char_u gui_attr[80];
4385 char_u *start;
4386 char_u *stop;
4387 char_u *ctermfg;
4388 char_u *ctermbg;
4389 char_u *ctermul;
4390 char_u *guifg;
4391 char_u *guibg;
4392 char_u *guisp;
4393# ifdef FEAT_GUI
4394 char_u *font;
4395# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004396 int forceit = FALSE;
4397 int dodefault = FALSE;
4398 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004399 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004400
4401 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004402 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004403 return FALSE;
4404
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004405 if (dict_get_bool(dict, (char_u *)"force", VVAL_FALSE) == VVAL_TRUE)
4406 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004407
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004408 if (dict_get_bool(dict, (char_u *)"default", VVAL_FALSE) == VVAL_TRUE)
4409 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004410
4411 if (dict_find(dict, (char_u *)"cleared", -1) != NULL)
4412 {
4413 varnumber_T cleared;
4414
4415 // clear a highlight group
4416 cleared = dict_get_bool(dict, (char_u *)"cleared", FALSE);
4417 if (cleared == TRUE)
4418 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004419 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4420 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004421 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004422 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004423 }
4424
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004425 if (dict_find(dict, (char_u *)"linksto", -1) != NULL)
4426 {
4427 char_u *linksto;
4428
4429 // link highlight groups
4430 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004431 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004432 return FALSE;
4433
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004434 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004435 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004436 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004437
4438 done = TRUE;
4439 }
4440
4441 // If 'cleared' or 'linksto' are specified, then don't process the other
4442 // attributes.
4443 if (done)
4444 return TRUE;
4445
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004446 start = hldict_get_string(dict, (char_u *)"start", &error);
4447 if (error)
4448 return FALSE;
4449
4450 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4451 if (error)
4452 return FALSE;
4453
4454 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004455 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004456 return FALSE;
4457
4458 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004459 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004460 return FALSE;
4461
4462 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4463 if (error)
4464 return FALSE;
4465
4466 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4467 if (error)
4468 return FALSE;
4469
4470 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4471 if (error)
4472 return FALSE;
4473
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004474 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004475 return FALSE;
4476
4477 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4478 if (error)
4479 return FALSE;
4480
4481 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4482 if (error)
4483 return FALSE;
4484
4485 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4486 if (error)
4487 return FALSE;
4488
4489# ifdef FEAT_GUI
4490 font = hldict_get_string(dict, (char_u *)"font", &error);
4491 if (error)
4492 return FALSE;
4493# endif
4494
4495 // If none of the attributes are specified, then do nothing.
4496 if (term_attr[0] == NUL && start == NULL && stop == NULL
4497 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4498 && ctermul == NULL && gui_attr[0] == NUL
4499# ifdef FEAT_GUI
4500 && font == NULL
4501# endif
4502 && guifg == NULL && guibg == NULL && guisp == NULL
4503 )
4504 return TRUE;
4505
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004506 hlsetBuf[0] = NUL;
4507 p = hlsetBuf;
4508 if (dodefault)
4509 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4510 p = add_attr_and_value(p, (char_u *)"", 0, name);
4511 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4512 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4513 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4514 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4515 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4516 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4517 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4518 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004519# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004520 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004521# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004522 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4523 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
4524 p = add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004525
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004526 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004527
4528 return TRUE;
4529}
4530
4531/*
4532 * "hlset([{highlight_attr}])" function
4533 * Add or modify highlight groups
4534 */
4535 void
4536f_hlset(typval_T *argvars, typval_T *rettv)
4537{
4538 listitem_T *li;
4539 dict_T *dict;
4540
4541 rettv->vval.v_number = -1;
4542
4543 if (check_for_list_arg(argvars, 0) == FAIL)
4544 return;
4545
4546 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4547 {
4548 if (li->li_tv.v_type != VAR_DICT)
4549 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004550 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004551 return;
4552 }
4553
4554 dict = li->li_tv.vval.v_dict;
4555 if (!hlg_add_or_update(dict))
4556 return;
4557 }
4558
4559 rettv->vval.v_number = 0;
4560}
4561#endif