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