blob: f937e082fef4426d4a2cf5b6df24ea05623d436d [file] [log] [blame]
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +020011 * Highlighting stuff.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020012 */
13
14#include "vim.h"
15
16#define SG_TERM 1 // term has been set
17#define SG_CTERM 2 // cterm has been set
18#define SG_GUI 4 // gui has been set
19#define SG_LINK 8 // link has been set
20
erw7f7f7aaf2021-12-07 21:29:20 +000021#define MAX_SYN_NAME 200
22
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020023/*
24 * The "term", "cterm" and "gui" arguments can be any combination of the
25 * following names, separated by commas (but no spaces!).
26 */
27static char *(hl_name_table[]) =
Bram Moolenaar84f54632022-06-29 18:39:11 +010028 {"bold", "standout", "underline",
29 "undercurl", "underdouble", "underdotted", "underdashed",
30 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020031static int hl_attr_table[] =
Bram Moolenaar84f54632022-06-29 18:39:11 +010032 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE,
33 HL_UNDERCURL, HL_UNDERDOUBLE, HL_UNDERDOTTED, HL_UNDERDASHED,
34 HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
35// length of all attribute names, plus commas, together (and a bit more)
36#define MAX_ATTR_LEN 120
37
kylo252ae6f1d82022-02-16 19:24:07 +000038#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? (attr_b) : (attr_a)) | (attr_b))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020039
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020040/*
41 * Structure that stores information about a highlight group.
42 * The ID of a highlight group is also called group ID. It is the index in
43 * the highlight_ga array PLUS ONE.
44 */
45typedef struct
46{
47 char_u *sg_name; // highlight group name
48 char_u *sg_name_u; // uppercase of sg_name
49 int sg_cleared; // "hi clear" was used
50// for normal terminals
51 int sg_term; // "term=" highlighting attributes
52 char_u *sg_start; // terminal string for start highl
53 char_u *sg_stop; // terminal string for stop highl
54 int sg_term_attr; // Screen attr for term mode
55// for color terminals
56 int sg_cterm; // "cterm=" highlighting attr
57 int sg_cterm_bold; // bold attr was set for light color
58 int sg_cterm_fg; // terminal fg color number + 1
59 int sg_cterm_bg; // terminal bg color number + 1
Bram Moolenaare023e882020-05-31 16:42:30 +020060 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020061 int sg_cterm_attr; // Screen attr for color term mode
62// for when using the GUI
63#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
64 guicolor_T sg_gui_fg; // GUI foreground color handle
65 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020066 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020067#endif
68#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020069 GuiFont sg_font; // GUI font handle
70#ifdef FEAT_XFONTSET
71 GuiFontset sg_fontset; // GUI fontset handle
72#endif
73 char_u *sg_font_name; // GUI font or fontset name
74 int sg_gui_attr; // Screen attr for GUI mode
75#endif
76#if defined(FEAT_GUI) || defined(FEAT_EVAL)
77// Store the sp color name for the GUI or synIDattr()
78 int sg_gui; // "gui=" highlighting attributes
79 char_u *sg_gui_fg_name;// GUI foreground color name
80 char_u *sg_gui_bg_name;// GUI background color name
81 char_u *sg_gui_sp_name;// GUI special color name
82#endif
83 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +020084 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020085 int sg_set; // combination of SG_* flags
86#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +020087 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020088 sctx_T sg_script_ctx; // script in which the group was last set
89#endif
90} hl_group_T;
91
92// highlight groups for 'highlight' option
93static garray_T highlight_ga;
94#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
95
96/*
97 * An attribute number is the index in attr_table plus ATTR_OFF.
98 */
99#define ATTR_OFF (HL_ALL + 1)
100
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200101static void syn_unadd_group(void);
102static void set_hl_attr(int idx);
103static void highlight_list_one(int id);
104static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
105static int syn_add_group(char_u *name);
106static int hl_has_settings(int idx, int check_link);
107static void highlight_clear(int idx);
108
109#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
110static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
111#endif
112#ifdef FEAT_GUI
113static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
114static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
115#endif
116
117/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200118 * The default highlight groups. These are compiled-in for fast startup and
119 * they still work when the runtime files can't be found.
120 * When making changes here, also change runtime/colors/default.vim!
121 * The #ifdefs are needed to reduce the amount of static data. Helps to make
122 * the 16 bit DOS (museum) version compile.
123 */
124#if defined(FEAT_GUI) || defined(FEAT_EVAL)
125# define CENT(a, b) b
126#else
127# define CENT(a, b) a
128#endif
129static char *(highlight_init_both[]) = {
130 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
131 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
132 CENT("IncSearch term=reverse cterm=reverse",
133 "IncSearch term=reverse cterm=reverse gui=reverse"),
134 CENT("ModeMsg term=bold cterm=bold",
135 "ModeMsg term=bold cterm=bold gui=bold"),
136 CENT("NonText term=bold ctermfg=Blue",
137 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
138 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
139 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
140 CENT("StatusLineNC term=reverse cterm=reverse",
141 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
142 "default link EndOfBuffer NonText",
143 CENT("VertSplit term=reverse cterm=reverse",
144 "VertSplit term=reverse cterm=reverse gui=reverse"),
145#ifdef FEAT_CLIPBOARD
146 CENT("VisualNOS term=underline,bold cterm=underline,bold",
147 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
148#endif
149#ifdef FEAT_DIFF
150 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
151 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
152#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200153 CENT("PmenuSbar ctermbg=Grey",
154 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200155 CENT("TabLineSel term=bold cterm=bold",
156 "TabLineSel term=bold cterm=bold gui=bold"),
157 CENT("TabLineFill term=reverse cterm=reverse",
158 "TabLineFill term=reverse cterm=reverse gui=reverse"),
159#ifdef FEAT_GUI
160 "Cursor guibg=fg guifg=bg",
161 "lCursor guibg=fg guifg=bg", // should be different, but what?
162#endif
163 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000164 "default link CursorLineSign SignColumn",
165 "default link CursorLineFold FoldColumn",
LemonBoya4399382022-04-09 21:04:08 +0100166 "default link CurSearch Search",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200167 CENT("Normal cterm=NONE", "Normal gui=NONE"),
168 NULL
169};
170
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200171// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200172static char *(highlight_init_light[]) = {
173 CENT("Directory term=bold ctermfg=DarkBlue",
174 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
175 CENT("LineNr term=underline ctermfg=Brown",
176 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200177 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
178 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200179 CENT("MoreMsg term=bold ctermfg=DarkGreen",
180 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
181 CENT("Question term=standout ctermfg=DarkGreen",
182 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
183 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
184 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
185#ifdef FEAT_SPELL
186 CENT("SpellBad term=reverse ctermbg=LightRed",
187 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
188 CENT("SpellCap term=reverse ctermbg=LightBlue",
189 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
190 CENT("SpellRare term=reverse ctermbg=LightMagenta",
191 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
192 CENT("SpellLocal term=underline ctermbg=Cyan",
193 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
194#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200195 CENT("PmenuThumb ctermbg=Black",
196 "PmenuThumb ctermbg=Black guibg=Black"),
197 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
198 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
199 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
200 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200201 CENT("SpecialKey term=bold ctermfg=DarkBlue",
202 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
203 CENT("Title term=bold ctermfg=DarkMagenta",
204 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
205 CENT("WarningMsg term=standout ctermfg=DarkRed",
206 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200207 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
208 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200209#ifdef FEAT_FOLDING
210 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
211 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
212 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
213 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
214#endif
215#ifdef FEAT_SIGNS
216 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
217 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
218#endif
219 CENT("Visual term=reverse",
220 "Visual term=reverse guibg=LightGrey"),
221#ifdef FEAT_DIFF
222 CENT("DiffAdd term=bold ctermbg=LightBlue",
223 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
224 CENT("DiffChange term=bold ctermbg=LightMagenta",
225 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
226 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
227 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
228#endif
229 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
230 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
231#ifdef FEAT_SYN_HL
232 CENT("CursorColumn term=reverse ctermbg=LightGrey",
233 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
234 CENT("CursorLine term=underline cterm=underline",
235 "CursorLine term=underline cterm=underline guibg=Grey90"),
236 CENT("ColorColumn term=reverse ctermbg=LightRed",
237 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
238#endif
239#ifdef FEAT_CONCEAL
240 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
241 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
242#endif
243 CENT("MatchParen term=reverse ctermbg=Cyan",
244 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
245#ifdef FEAT_TERMINAL
246 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
247 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
248 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
249 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
250#endif
251#ifdef FEAT_MENU
252 CENT("ToolbarLine term=underline ctermbg=LightGrey",
253 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
254 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
255 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
256#endif
257 NULL
258};
259
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200260// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200261static char *(highlight_init_dark[]) = {
262 CENT("Directory term=bold ctermfg=LightCyan",
263 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
264 CENT("LineNr term=underline ctermfg=Yellow",
265 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200266 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
267 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200268 CENT("MoreMsg term=bold ctermfg=LightGreen",
269 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
270 CENT("Question term=standout ctermfg=LightGreen",
271 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
272 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
273 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
274 CENT("SpecialKey term=bold ctermfg=LightBlue",
275 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
276#ifdef FEAT_SPELL
277 CENT("SpellBad term=reverse ctermbg=Red",
278 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
279 CENT("SpellCap term=reverse ctermbg=Blue",
280 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
281 CENT("SpellRare term=reverse ctermbg=Magenta",
282 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
283 CENT("SpellLocal term=underline ctermbg=Cyan",
284 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
285#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200286 CENT("PmenuThumb ctermbg=White",
287 "PmenuThumb ctermbg=White guibg=White"),
288 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
289 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
290 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
291 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200292 CENT("Title term=bold ctermfg=LightMagenta",
293 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
294 CENT("WarningMsg term=standout ctermfg=LightRed",
295 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200296 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
297 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200298#ifdef FEAT_FOLDING
299 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
300 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
301 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
302 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
303#endif
304#ifdef FEAT_SIGNS
305 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
306 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
307#endif
308 CENT("Visual term=reverse",
309 "Visual term=reverse guibg=DarkGrey"),
310#ifdef FEAT_DIFF
311 CENT("DiffAdd term=bold ctermbg=DarkBlue",
312 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
313 CENT("DiffChange term=bold ctermbg=DarkMagenta",
314 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
315 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
316 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
317#endif
318 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
319 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
320#ifdef FEAT_SYN_HL
321 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
322 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
323 CENT("CursorLine term=underline cterm=underline",
324 "CursorLine term=underline cterm=underline guibg=Grey40"),
325 CENT("ColorColumn term=reverse ctermbg=DarkRed",
326 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
327#endif
328 CENT("MatchParen term=reverse ctermbg=DarkCyan",
329 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
330#ifdef FEAT_CONCEAL
331 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
332 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
333#endif
334#ifdef FEAT_TERMINAL
335 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
336 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
337 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
338 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
339#endif
340#ifdef FEAT_MENU
341 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
342 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
343 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
344 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
345#endif
346 NULL
347};
348
Dominique Pelle748b3082022-01-08 12:41:16 +0000349#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200350/*
351 * Returns the number of highlight groups.
352 */
353 int
354highlight_num_groups(void)
355{
356 return highlight_ga.ga_len;
357}
358
359/*
360 * Returns the name of a highlight group.
361 */
362 char_u *
363highlight_group_name(int id)
364{
365 return HL_TABLE()[id].sg_name;
366}
367
368/*
369 * Returns the ID of the link to a highlight group.
370 */
371 int
372highlight_link_id(int id)
373{
374 return HL_TABLE()[id].sg_link;
375}
Dominique Pelle748b3082022-01-08 12:41:16 +0000376#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200377
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200378 void
379init_highlight(
380 int both, // include groups where 'bg' doesn't matter
381 int reset) // clear group first
382{
383 int i;
384 char **pp;
385 static int had_both = FALSE;
386#ifdef FEAT_EVAL
387 char_u *p;
388
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100389 // Try finding the color scheme file. Used when a color file was loaded
390 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200391 p = get_var_value((char_u *)"g:colors_name");
392 if (p != NULL)
393 {
394 // The value of g:colors_name could be freed when sourcing the script,
395 // making "p" invalid, so copy it.
396 char_u *copy_p = vim_strsave(p);
397 int r;
398
399 if (copy_p != NULL)
400 {
401 r = load_colors(copy_p);
402 vim_free(copy_p);
403 if (r == OK)
404 return;
405 }
406 }
407
408#endif
409
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100410 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200411 if (both)
412 {
413 had_both = TRUE;
414 pp = highlight_init_both;
415 for (i = 0; pp[i] != NULL; ++i)
416 do_highlight((char_u *)pp[i], reset, TRUE);
417 }
418 else if (!had_both)
419 // Don't do anything before the call with both == TRUE from main().
420 // Not everything has been setup then, and that call will overrule
421 // everything anyway.
422 return;
423
424 if (*p_bg == 'l')
425 pp = highlight_init_light;
426 else
427 pp = highlight_init_dark;
428 for (i = 0; pp[i] != NULL; ++i)
429 do_highlight((char_u *)pp[i], reset, TRUE);
430
431 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
432 // depend on the number of colors available.
433 // With 8 colors brown is equal to yellow, need to use black for Search fg
434 // to avoid Statement highlighted text disappears.
435 // Clear the attributes, needed when changing the t_Co value.
436 if (t_colors > 8)
437 do_highlight((char_u *)(*p_bg == 'l'
438 ? "Visual cterm=NONE ctermbg=LightGrey"
439 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
440 else
441 {
442 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
443 FALSE, TRUE);
444 if (*p_bg == 'l')
445 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
446 }
447
448#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100449 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200450 if (get_var_value((char_u *)"g:syntax_on") != NULL)
451 {
452 static int recursive = 0;
453
454 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000455 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200456 else
457 {
458 ++recursive;
459 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
460 --recursive;
461 }
462 }
463#endif
464}
465
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000466#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
467/*
468 * Load a default color list. Intended to support legacy color names but allows
469 * the user to override the color values. Only loaded once.
470 */
471 static void
472load_default_colors_lists()
473{
474 // Lacking a default color list isn't the end of the world but it is likely
475 // an inconvenience so users should know when it is missing.
476 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
477 msg("failed to load colors/lists/default.vim");
478}
479#endif
480
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200481/*
482 * Load color file "name".
483 * Return OK for success, FAIL for failure.
484 */
485 int
486load_colors(char_u *name)
487{
488 char_u *buf;
489 int retval = FAIL;
490 static int recursive = FALSE;
491
492 // When being called recursively, this is probably because setting
493 // 'background' caused the highlighting to be reloaded. This means it is
494 // working, thus we should return OK.
495 if (recursive)
496 return OK;
497
498 recursive = TRUE;
499 buf = alloc(STRLEN(name) + 12);
500 if (buf != NULL)
501 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100502#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100503 load_default_colors_lists();
504#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200505 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
506 curbuf->b_fname, FALSE, curbuf);
507 sprintf((char *)buf, "colors/%s.vim", name);
508 retval = source_runtime(buf, DIP_START + DIP_OPT);
509 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100510 if (retval == OK)
511 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
512 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200513 }
514 recursive = FALSE;
515
516 return retval;
517}
518
519static char *(color_names[28]) = {
520 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
521 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
522 "Gray", "Grey", "LightGray", "LightGrey",
523 "DarkGray", "DarkGrey",
524 "Blue", "LightBlue", "Green", "LightGreen",
525 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
526 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
527 // indices:
528 // 0, 1, 2, 3,
529 // 4, 5, 6, 7,
530 // 8, 9, 10, 11,
531 // 12, 13,
532 // 14, 15, 16, 17,
533 // 18, 19, 20, 21, 22,
534 // 23, 24, 25, 26, 27
535static int color_numbers_16[28] = {0, 1, 2, 3,
536 4, 5, 6, 6,
537 7, 7, 7, 7,
538 8, 8,
539 9, 9, 10, 10,
540 11, 11, 12, 12, 13,
541 13, 14, 14, 15, -1};
542// for xterm with 88 colors...
543static int color_numbers_88[28] = {0, 4, 2, 6,
544 1, 5, 32, 72,
545 84, 84, 7, 7,
546 82, 82,
547 12, 43, 10, 61,
548 14, 63, 9, 74, 13,
549 75, 11, 78, 15, -1};
550// for xterm with 256 colors...
551static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200552 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200553 248, 248, 7, 7,
554 242, 242,
555 12, 81, 10, 121,
556 14, 159, 9, 224, 13,
557 225, 11, 229, 15, -1};
558// for terminals with less than 16 colors...
559static int color_numbers_8[28] = {0, 4, 2, 6,
560 1, 5, 3, 3,
561 7, 7, 7, 7,
562 0+8, 0+8,
563 4+8, 4+8, 2+8, 2+8,
564 6+8, 6+8, 1+8, 1+8, 5+8,
565 5+8, 3+8, 3+8, 7+8, -1};
566
567/*
568 * Lookup the "cterm" value to be used for color with index "idx" in
569 * color_names[].
570 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
571 * colors, otherwise it will be unchanged.
572 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100573 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200574lookup_color(int idx, int foreground, int *boldp)
575{
576 int color = color_numbers_16[idx];
577 char_u *p;
578
579 // Use the _16 table to check if it's a valid color name.
580 if (color < 0)
581 return -1;
582
583 if (t_colors == 8)
584 {
585 // t_Co is 8: use the 8 colors table
586#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100587 // On qnx, the 8 & 16 color arrays are the same
588 if (STRNCMP(T_NAME, "qansi", 5) == 0)
589 color = color_numbers_16[idx];
590 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200591#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100592 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200593 if (foreground)
594 {
595 // set/reset bold attribute to get light foreground
596 // colors (on some terminals, e.g. "linux")
597 if (color & 8)
598 *boldp = TRUE;
599 else
600 *boldp = FALSE;
601 }
602 color &= 7; // truncate to 8 colors
603 }
604 else if (t_colors == 16 || t_colors == 88
605 || t_colors >= 256)
606 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100607 // Guess: if the termcap entry ends in 'm', it is
608 // probably an xterm-like terminal. Use the changed
609 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200610 if (*T_CAF != NUL)
611 p = T_CAF;
612 else
613 p = T_CSF;
614 if (*p != NUL && (t_colors > 256
615 || *(p + STRLEN(p) - 1) == 'm'))
616 {
617 if (t_colors == 88)
618 color = color_numbers_88[idx];
619 else if (t_colors >= 256)
620 color = color_numbers_256[idx];
621 else
622 color = color_numbers_8[idx];
623 }
624#ifdef FEAT_TERMRESPONSE
625 if (t_colors >= 256 && color == 15 && is_mac_terminal)
626 // Terminal.app has a bug: 15 is light grey. Use white
627 // from the color cube instead.
628 color = 231;
629#endif
630 }
631 return color;
632}
633
634/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100635 * Link highlight group 'from_hg' to 'to_hg'.
636 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000637 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100638 * 'init' is set to TRUE when initializing all the highlight groups.
639 */
640 static void
641highlight_group_link(
642 char_u *from_hg,
643 int from_len,
644 char_u *to_hg,
645 int to_len,
646 int dodefault,
647 int forceit,
648 int init)
649{
650 int from_id;
651 int to_id;
652 hl_group_T *hlgroup = NULL;
653
654 from_id = syn_check_group(from_hg, from_len);
655 if (STRNCMP(to_hg, "NONE", 4) == 0)
656 to_id = 0;
657 else
658 to_id = syn_check_group(to_hg, to_len);
659
660 if (from_id > 0)
661 {
662 hlgroup = &HL_TABLE()[from_id - 1];
663 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
664 {
665 hlgroup->sg_deflink = to_id;
666#ifdef FEAT_EVAL
667 hlgroup->sg_deflink_sctx = current_sctx;
668 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
669#endif
670 }
671 }
672
673 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
674 {
675 // Don't allow a link when there already is some highlighting
676 // for the group, unless '!' is used
677 if (to_id > 0 && !forceit && !init
678 && hl_has_settings(from_id - 1, dodefault))
679 {
680 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000681 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100682 }
683 else if (hlgroup->sg_link != to_id
684#ifdef FEAT_EVAL
685 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
686#endif
687 || hlgroup->sg_cleared)
688 {
689 if (!init)
690 hlgroup->sg_set |= SG_LINK;
691 hlgroup->sg_link = to_id;
692#ifdef FEAT_EVAL
693 hlgroup->sg_script_ctx = current_sctx;
694 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
695#endif
696 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100697 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100698
699 // Only call highlight_changed() once after multiple changes.
700 need_highlight_changed = TRUE;
701 }
702 }
703
704}
705
706/*
707 * Reset all highlighting to the defaults. Removes all highlighting for the
708 * groups added by the user.
709 */
710 static void
711highlight_reset_all(void)
712{
713 int idx;
714
715#ifdef FEAT_GUI
716 // First, we do not destroy the old values, but allocate the new
717 // ones and update the display. THEN we destroy the old values.
718 // If we destroy the old values first, then the old values
719 // (such as GuiFont's or GuiFontset's) will still be displayed but
720 // invalid because they were free'd.
721 if (gui.in_use)
722 {
723# ifdef FEAT_BEVAL_TIP
724 gui_init_tooltip_font();
725# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100726# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100727 gui_init_menu_font();
728# endif
729 }
730# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
731 gui_mch_def_colors();
732# endif
733# ifdef FEAT_GUI_X11
734# ifdef FEAT_MENU
735
736 // This only needs to be done when there is no Menu highlight
737 // group defined by default, which IS currently the case.
738 gui_mch_new_menu_colors();
739# endif
740 if (gui.in_use)
741 {
742 gui_new_scrollbar_colors();
743# ifdef FEAT_BEVAL_GUI
744 gui_mch_new_tooltip_colors();
745# endif
746# ifdef FEAT_MENU
747 gui_mch_new_menu_font();
748# endif
749 }
750# endif
751
752 // Ok, we're done allocating the new default graphics items.
753 // The screen should already be refreshed at this point.
754 // It is now Ok to clear out the old data.
755#endif
756#ifdef FEAT_EVAL
757 do_unlet((char_u *)"g:colors_name", TRUE);
758#endif
759 restore_cterm_colors();
760
761 // Clear all default highlight groups and load the defaults.
762 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
763 highlight_clear(idx);
764 init_highlight(TRUE, TRUE);
765#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
766 if (USE_24BIT)
767 highlight_gui_started();
768 else
769#endif
770 highlight_changed();
771 redraw_later_clear();
772}
773
774/*
775 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
776 * index 'idx'.
777 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
778 * 'arg' is the list of attribute names separated by comma.
779 * 'init' is set to TRUE when initializing all the highlight groups.
780 * Returns TRUE if the attributes are set.
781 */
782 static int
783highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
784{
785 int attr;
786 int off;
787 long i;
788 int len;
789
790 attr = 0;
791 off = 0;
792 while (arg[off] != NUL)
793 {
794 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
795 {
796 len = (int)STRLEN(hl_name_table[i]);
797 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
798 {
799 attr |= hl_attr_table[i];
800 off += len;
801 break;
802 }
803 }
804 if (i < 0)
805 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000806 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100807 return FALSE;
808 }
809 if (arg[off] == ',') // another one follows
810 ++off;
811 }
812 if (*key == 'T')
813 {
814 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
815 {
816 if (!init)
817 HL_TABLE()[idx].sg_set |= SG_TERM;
818 HL_TABLE()[idx].sg_term = attr;
819 }
820 }
821 else if (*key == 'C')
822 {
823 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
824 {
825 if (!init)
826 HL_TABLE()[idx].sg_set |= SG_CTERM;
827 HL_TABLE()[idx].sg_cterm = attr;
828 HL_TABLE()[idx].sg_cterm_bold = FALSE;
829 }
830 }
831#if defined(FEAT_GUI) || defined(FEAT_EVAL)
832 else
833 {
834 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
835 {
836 if (!init)
837 HL_TABLE()[idx].sg_set |= SG_GUI;
838 HL_TABLE()[idx].sg_gui = attr;
839 }
840 }
841#endif
842
843 return TRUE;
844}
845
846#ifdef FEAT_GUI
847/*
848 * Set the font for the highlight group at 'idx'.
849 * 'arg' is the font name.
850 * Returns TRUE if the font is changed.
851 */
852 static int
853highlight_set_font(
854 int idx,
855 char_u *arg,
856 int is_normal_group,
857 int is_menu_group,
858 int is_tooltip_group)
859{
860 int did_change = FALSE;
861
862 // in non-GUI fonts are simply ignored
863 if (HL_TABLE()[idx].sg_font_name != NULL
864 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
865 {
866 // Font name didn't change, ignore.
867 }
868 else if (!gui.shell_created)
869 {
870 // GUI not started yet, always accept the name.
871 vim_free(HL_TABLE()[idx].sg_font_name);
872 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
873 did_change = TRUE;
874 }
875 else
876 {
877 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
878# ifdef FEAT_XFONTSET
879 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
880# endif
881 // First, save the current font/fontset.
882 // Then try to allocate the font/fontset.
883 // If the allocation fails, HL_TABLE()[idx].sg_font OR
884 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
885
886 HL_TABLE()[idx].sg_font = NOFONT;
887# ifdef FEAT_XFONTSET
888 HL_TABLE()[idx].sg_fontset = NOFONTSET;
889# endif
890 hl_do_font(idx, arg, is_normal_group, is_menu_group,
891 is_tooltip_group, FALSE);
892
893# ifdef FEAT_XFONTSET
894 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
895 {
896 // New fontset was accepted. Free the old one, if there
897 // was one.
898 gui_mch_free_fontset(temp_sg_fontset);
899 vim_free(HL_TABLE()[idx].sg_font_name);
900 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
901 did_change = TRUE;
902 }
903 else
904 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
905# endif
906 if (HL_TABLE()[idx].sg_font != NOFONT)
907 {
908 // New font was accepted. Free the old one, if there was
909 // one.
910 gui_mch_free_font(temp_sg_font);
911 vim_free(HL_TABLE()[idx].sg_font_name);
912 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
913 did_change = TRUE;
914 }
915 else
916 HL_TABLE()[idx].sg_font = temp_sg_font;
917 }
918
919 return did_change;
920}
921#endif
922
923/*
924 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
925 * Returns TRUE if the foreground color is set.
926 */
927 static void
928highlight_set_ctermfg(int idx, int color, int is_normal_group)
929{
930 HL_TABLE()[idx].sg_cterm_fg = color + 1;
931 if (is_normal_group)
932 {
933 cterm_normal_fg_color = color + 1;
934 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
935#ifdef FEAT_GUI
936 // Don't do this if the GUI is used.
937 if (!gui.in_use && !gui.starting)
938#endif
939 {
Bram Moolenaar471c0fa2022-08-22 15:19:16 +0100940 set_must_redraw(UPD_CLEAR);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100941 if (termcap_active && color >= 0)
942 term_fg_color(color);
943 }
944 }
945}
946
947/*
948 * Set the cterm background color for the highlight group at 'idx' to 'color'.
949 * Returns TRUE if the background color is set.
950 */
951 static void
952highlight_set_ctermbg(int idx, int color, int is_normal_group)
953{
954 HL_TABLE()[idx].sg_cterm_bg = color + 1;
955 if (is_normal_group)
956 {
957 cterm_normal_bg_color = color + 1;
958#ifdef FEAT_GUI
959 // Don't mess with 'background' if the GUI is used.
960 if (!gui.in_use && !gui.starting)
961#endif
962 {
Bram Moolenaar471c0fa2022-08-22 15:19:16 +0100963 set_must_redraw(UPD_CLEAR);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100964 if (color >= 0)
965 {
966 int dark = -1;
967
968 if (termcap_active)
969 term_bg_color(color);
970 if (t_colors < 16)
971 dark = (color == 0 || color == 4);
972 // Limit the heuristic to the standard 16 colors
973 else if (color < 16)
974 dark = (color < 7 || color == 8);
975 // Set the 'background' option if the value is
976 // wrong.
977 if (dark != -1
978 && dark != (*p_bg == 'd')
979 && !option_was_set((char_u *)"bg"))
980 {
Bram Moolenaar31e5c602022-04-15 13:53:33 +0100981 set_option_value_give_err((char_u *)"bg",
982 0L, (char_u *)(dark ? "dark" : "light"), 0);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100983 reset_option_was_set((char_u *)"bg");
984 }
985 }
986 }
987 }
988}
989
990/*
991 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
992 * Returns TRUE if the underline color is set.
993 */
994 static void
995highlight_set_ctermul(int idx, int color, int is_normal_group)
996{
997 HL_TABLE()[idx].sg_cterm_ul = color + 1;
998 if (is_normal_group)
999 {
1000 cterm_normal_ul_color = color + 1;
1001#ifdef FEAT_GUI
1002 // Don't do this if the GUI is used.
1003 if (!gui.in_use && !gui.starting)
1004#endif
1005 {
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001006 set_must_redraw(UPD_CLEAR);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001007 if (termcap_active && color >= 0)
1008 term_ul_color(color);
1009 }
1010 }
1011}
1012
1013/*
1014 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1015 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1016 * 'keystart' is the color name/value.
1017 * 'arg' is the color name or the numeric value as a string.
1018 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1019 * 'init' is set to TRUE when initializing highlighting.
1020 * Called for the ":highlight" command and the "hlset()" function.
1021 *
1022 * Returns TRUE if the color is set.
1023 */
1024 static int
1025highlight_set_cterm_color(
1026 int idx,
1027 char_u *key,
1028 char_u *key_start,
1029 char_u *arg,
1030 int is_normal_group,
1031 int init)
1032{
1033 int color;
1034 long i;
1035 int off;
1036
1037 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1038 {
1039 if (!init)
1040 HL_TABLE()[idx].sg_set |= SG_CTERM;
1041
1042 // When setting the foreground color, and previously the "bold"
1043 // flag was set for a light color, reset it now
1044 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1045 {
1046 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1047 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1048 }
1049
1050 if (VIM_ISDIGIT(*arg))
1051 color = atoi((char *)arg);
1052 else if (STRICMP(arg, "fg") == 0)
1053 {
1054 if (cterm_normal_fg_color)
1055 color = cterm_normal_fg_color - 1;
1056 else
1057 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001058 emsg(_(e_fg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001059 return FALSE;
1060 }
1061 }
1062 else if (STRICMP(arg, "bg") == 0)
1063 {
1064 if (cterm_normal_bg_color > 0)
1065 color = cterm_normal_bg_color - 1;
1066 else
1067 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001068 emsg(_(e_bg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001069 return FALSE;
1070 }
1071 }
1072 else if (STRICMP(arg, "ul") == 0)
1073 {
1074 if (cterm_normal_ul_color > 0)
1075 color = cterm_normal_ul_color - 1;
1076 else
1077 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00001078 emsg(_(e_ul_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001079 return FALSE;
1080 }
1081 }
1082 else
1083 {
1084 int bold = MAYBE;
1085
1086 // reduce calls to STRICMP a bit, it can be slow
1087 off = TOUPPER_ASC(*arg);
1088 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1089 if (off == color_names[i][0]
1090 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1091 break;
1092 if (i < 0)
1093 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001094 semsg(_(e_color_name_or_number_not_recognized), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001095 return FALSE;
1096 }
1097
1098 color = lookup_color(i, key[5] == 'F', &bold);
1099
1100 // set/reset bold attribute to get light foreground
1101 // colors (on some terminals, e.g. "linux")
1102 if (bold == TRUE)
1103 {
1104 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1105 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1106 }
1107 else if (bold == FALSE)
1108 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1109 }
1110
1111 // Add one to the argument, to avoid zero. Zero is used for
1112 // "NONE", then "color" is -1.
1113 if (key[5] == 'F')
1114 highlight_set_ctermfg(idx, color, is_normal_group);
1115 else if (key[5] == 'B')
1116 highlight_set_ctermbg(idx, color, is_normal_group);
1117 else // ctermul
1118 highlight_set_ctermul(idx, color, is_normal_group);
1119 }
1120
1121 return TRUE;
1122}
1123
1124#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1125/*
1126 * Set the GUI foreground color for the highlight group at 'idx'.
1127 * Returns TRUE if the color is set.
1128 */
1129 static int
1130highlight_set_guifg(
1131 int idx,
1132 char_u *arg,
1133 int is_menu_group UNUSED,
1134 int is_scrollbar_group UNUSED,
1135 int is_tooltip_group UNUSED,
1136 int *do_colors UNUSED,
1137 int init)
1138{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001139# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001140 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001141# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001142 char_u **namep;
1143 int did_change = FALSE;
1144
1145 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1146 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1147 {
1148 if (!init)
1149 HL_TABLE()[idx].sg_set |= SG_GUI;
1150
1151# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1152 // In GUI guifg colors are only used when recognized
1153 i = color_name2handle(arg);
1154 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1155 {
1156 HL_TABLE()[idx].sg_gui_fg = i;
1157# endif
1158 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1159 {
1160 vim_free(*namep);
1161 if (STRCMP(arg, "NONE") != 0)
1162 *namep = vim_strsave(arg);
1163 else
1164 *namep = NULL;
1165 did_change = TRUE;
1166 }
1167# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1168# ifdef FEAT_GUI_X11
1169 if (is_menu_group && gui.menu_fg_pixel != i)
1170 {
1171 gui.menu_fg_pixel = i;
1172 *do_colors = TRUE;
1173 }
1174 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1175 {
1176 gui.scroll_fg_pixel = i;
1177 *do_colors = TRUE;
1178 }
1179# ifdef FEAT_BEVAL_GUI
1180 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1181 {
1182 gui.tooltip_fg_pixel = i;
1183 *do_colors = TRUE;
1184 }
1185# endif
1186# endif
1187 }
1188# endif
1189 }
1190
1191 return did_change;
1192}
1193
1194/*
1195 * Set the GUI background color for the highlight group at 'idx'.
1196 * Returns TRUE if the color is set.
1197 */
1198 static int
1199highlight_set_guibg(
1200 int idx,
1201 char_u *arg,
1202 int is_menu_group UNUSED,
1203 int is_scrollbar_group UNUSED,
1204 int is_tooltip_group UNUSED,
1205 int *do_colors UNUSED,
1206 int init)
1207{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001208# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001209 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001210# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001211 char_u **namep;
1212 int did_change = FALSE;
1213
1214 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1215 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1216 {
1217 if (!init)
1218 HL_TABLE()[idx].sg_set |= SG_GUI;
1219
1220# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Drew Vogele30d1022021-10-24 20:35:07 +01001221 // In GUI guibg colors are only used when recognized
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001222 i = color_name2handle(arg);
1223 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1224 {
1225 HL_TABLE()[idx].sg_gui_bg = i;
1226# endif
1227 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1228 {
1229 vim_free(*namep);
1230 if (STRCMP(arg, "NONE") != 0)
1231 *namep = vim_strsave(arg);
1232 else
1233 *namep = NULL;
1234 did_change = TRUE;
1235 }
1236# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1237# ifdef FEAT_GUI_X11
1238 if (is_menu_group && gui.menu_bg_pixel != i)
1239 {
1240 gui.menu_bg_pixel = i;
1241 *do_colors = TRUE;
1242 }
1243 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1244 {
1245 gui.scroll_bg_pixel = i;
1246 *do_colors = TRUE;
1247 }
1248# ifdef FEAT_BEVAL_GUI
1249 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1250 {
1251 gui.tooltip_bg_pixel = i;
1252 *do_colors = TRUE;
1253 }
1254# endif
1255# endif
1256 }
1257# endif
1258 }
1259
1260 return did_change;
1261}
1262
1263/*
1264 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1265 * Returns TRUE if the color is set.
1266 */
1267 static int
1268highlight_set_guisp(int idx, char_u *arg, int init)
1269{
1270# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1271 int i;
1272# endif
1273 int did_change = FALSE;
1274 char_u **namep;
1275
1276 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1277 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1278 {
1279 if (!init)
1280 HL_TABLE()[idx].sg_set |= SG_GUI;
1281
1282# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1283 // In GUI guisp colors are only used when recognized
1284 i = color_name2handle(arg);
1285 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1286 {
1287 HL_TABLE()[idx].sg_gui_sp = i;
1288# endif
1289 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1290 {
1291 vim_free(*namep);
1292 if (STRCMP(arg, "NONE") != 0)
1293 *namep = vim_strsave(arg);
1294 else
1295 *namep = NULL;
1296 did_change = TRUE;
1297 }
1298# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1299 }
1300# endif
1301 }
1302
1303 return did_change;
1304}
1305#endif
1306
1307/*
1308 * Set the start/stop terminal codes for a highlight group.
1309 * Returns TRUE if the terminal code is set.
1310 */
1311 static int
1312highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1313{
1314 int off;
1315 char_u buf[100];
1316 int len;
1317 char_u *tname;
1318 char_u *p;
1319
1320 if (!init)
1321 HL_TABLE()[idx].sg_set |= SG_TERM;
1322
1323 // The "start" and "stop" arguments can be a literal escape
1324 // sequence, or a comma separated list of terminal codes.
1325 if (STRNCMP(arg, "t_", 2) == 0)
1326 {
1327 off = 0;
1328 buf[0] = 0;
1329 while (arg[off] != NUL)
1330 {
1331 // Isolate one termcap name
1332 for (len = 0; arg[off + len] &&
1333 arg[off + len] != ','; ++len)
1334 ;
1335 tname = vim_strnsave(arg + off, len);
1336 if (tname == NULL) // out of memory
1337 return FALSE;
1338 // lookup the escape sequence for the item
1339 p = get_term_code(tname);
1340 vim_free(tname);
1341 if (p == NULL) // ignore non-existing things
1342 p = (char_u *)"";
1343
1344 // Append it to the already found stuff
1345 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1346 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001347 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001348 return FALSE;
1349 }
1350 STRCAT(buf, p);
1351
1352 // Advance to the next item
1353 off += len;
1354 if (arg[off] == ',') // another one follows
1355 ++off;
1356 }
1357 }
1358 else
1359 {
1360 // Copy characters from arg[] to buf[], translating <> codes.
1361 for (p = arg, off = 0; off < 100 - 6 && *p; )
1362 {
zeertzjqdb088872022-05-02 22:53:45 +01001363 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001364 if (len > 0) // recognized special char
1365 off += len;
1366 else // copy as normal char
1367 buf[off++] = *p++;
1368 }
1369 buf[off] = NUL;
1370 }
1371
1372 if (STRCMP(buf, "NONE") == 0) // resetting the value
1373 p = NULL;
1374 else
1375 p = vim_strsave(buf);
1376 if (key[2] == 'A')
1377 {
1378 vim_free(HL_TABLE()[idx].sg_start);
1379 HL_TABLE()[idx].sg_start = p;
1380 }
1381 else
1382 {
1383 vim_free(HL_TABLE()[idx].sg_stop);
1384 HL_TABLE()[idx].sg_stop = p;
1385 }
1386 return TRUE;
1387}
1388
1389/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001390 * Handle the ":highlight .." command.
1391 * When using ":hi clear" this is called recursively for each group with
1392 * "forceit" and "init" both TRUE.
1393 */
1394 void
1395do_highlight(
1396 char_u *line,
1397 int forceit,
1398 int init) // TRUE when called for initializing
1399{
1400 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001401 char_u *linep;
1402 char_u *key_start;
1403 char_u *arg_start;
1404 char_u *key = NULL, *arg = NULL;
1405 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001406 int id;
1407 int idx;
1408 hl_group_T item_before;
1409 int did_change = FALSE;
1410 int dodefault = FALSE;
1411 int doclear = FALSE;
1412 int dolink = FALSE;
1413 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001414 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001415#ifdef FEAT_GUI_X11
1416 int is_menu_group = FALSE; // "Menu" group
1417 int is_scrollbar_group = FALSE; // "Scrollbar" group
1418 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001419#else
1420# define is_menu_group 0
1421# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001422# define is_scrollbar_group 0
1423#endif
1424#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1425 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001426#endif
1427#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1428 int did_highlight_changed = FALSE;
1429#endif
1430
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001431 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001432 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001433 {
1434 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1435 // TODO: only call when the group has attributes set
1436 highlight_list_one((int)i);
1437 return;
1438 }
1439
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001440 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001441 name_end = skiptowhite(line);
1442 linep = skipwhite(name_end);
1443
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001444 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001445 if (STRNCMP(line, "default", name_end - line) == 0)
1446 {
1447 dodefault = TRUE;
1448 line = linep;
1449 name_end = skiptowhite(line);
1450 linep = skipwhite(name_end);
1451 }
1452
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001453 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001454 if (STRNCMP(line, "clear", name_end - line) == 0)
1455 doclear = TRUE;
1456 if (STRNCMP(line, "link", name_end - line) == 0)
1457 dolink = TRUE;
1458
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001459 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001460 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001461 {
1462 id = syn_namen2id(line, (int)(name_end - line));
1463 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001464 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001465 else
1466 highlight_list_one(id);
1467 return;
1468 }
1469
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001470 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001471 if (dolink)
1472 {
1473 char_u *from_start = linep;
1474 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001475 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001476 char_u *to_start;
1477 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001478 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001479
1480 from_end = skiptowhite(from_start);
1481 to_start = skipwhite(from_end);
1482 to_end = skiptowhite(to_start);
1483
Bram Moolenaar1966c242020-04-20 22:42:32 +02001484 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001485 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001486 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001487 return;
1488 }
1489
Bram Moolenaar1966c242020-04-20 22:42:32 +02001490 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001491 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001492 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001493 return;
1494 }
1495
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001496 from_len = (int)(from_end - from_start);
1497 to_len = (int)(to_end - to_start);
1498 highlight_group_link(from_start, from_len, to_start, to_len,
1499 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001500 return;
1501 }
1502
1503 if (doclear)
1504 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001505 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001506 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001507 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001508 // ":highlight clear" without group name
1509 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001510 return;
1511 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001512 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001513 name_end = skiptowhite(line);
1514 linep = skipwhite(name_end);
1515 }
1516
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001517 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001518 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001519 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001520 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001521 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001522
1523 // Return if "default" was used and the group already has settings.
1524 if (dodefault && hl_has_settings(idx, TRUE))
1525 return;
1526
1527 // Make a copy so we can check if any attribute actually changed.
1528 item_before = HL_TABLE()[idx];
1529
1530 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1531 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001532#ifdef FEAT_GUI_X11
1533 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1534 is_menu_group = TRUE;
1535 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1536 is_scrollbar_group = TRUE;
1537 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1538 is_tooltip_group = TRUE;
1539#endif
1540
1541 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1542 if (doclear || (forceit && init))
1543 {
1544 highlight_clear(idx);
1545 if (!doclear)
1546 HL_TABLE()[idx].sg_set = 0;
1547 }
1548
1549 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001550 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001551 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001552 key_start = linep;
1553 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001554 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001555 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001556 error = TRUE;
1557 break;
1558 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001559
1560 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1561 // or "guibg").
1562 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1563 ++linep;
1564 vim_free(key);
1565 key = vim_strnsave_up(key_start, linep - key_start);
1566 if (key == NULL)
1567 {
1568 error = TRUE;
1569 break;
1570 }
1571 linep = skipwhite(linep);
1572
1573 if (STRCMP(key, "NONE") == 0)
1574 {
1575 if (!init || HL_TABLE()[idx].sg_set == 0)
1576 {
1577 if (!init)
1578 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1579 highlight_clear(idx);
1580 }
1581 continue;
1582 }
1583
1584 // Check for the equal sign.
1585 if (*linep != '=')
1586 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001587 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001588 error = TRUE;
1589 break;
1590 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001591 ++linep;
1592
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001593 // Isolate the argument.
1594 linep = skipwhite(linep);
1595 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001596 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001597 arg_start = ++linep;
1598 linep = vim_strchr(linep, '\'');
1599 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001600 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001601 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001602 error = TRUE;
1603 break;
1604 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001605 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001606 else
1607 {
1608 arg_start = linep;
1609 linep = skiptowhite(linep);
1610 }
1611 if (linep == arg_start)
1612 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001613 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001614 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001615 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001616 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001617 vim_free(arg);
1618 arg = vim_strnsave(arg_start, linep - arg_start);
1619 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001620 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001621 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001622 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001623 }
1624 if (*linep == '\'')
1625 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001626
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001627 // Store the argument.
1628 if (STRCMP(key, "TERM") == 0
1629 || STRCMP(key, "CTERM") == 0
1630 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001631 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001632 if (!highlight_set_termgui_attr(idx, key, arg, init))
1633 {
1634 error = TRUE;
1635 break;
1636 }
1637 }
1638 else if (STRCMP(key, "FONT") == 0)
1639 {
1640 // in non-GUI fonts are simply ignored
1641#ifdef FEAT_GUI
1642 if (highlight_set_font(idx, arg, is_normal_group,
1643 is_menu_group, is_tooltip_group))
1644 did_change = TRUE;
1645#endif
1646 }
1647 else if (STRCMP(key, "CTERMFG") == 0
1648 || STRCMP(key, "CTERMBG") == 0
1649 || STRCMP(key, "CTERMUL") == 0)
1650 {
1651 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1652 is_normal_group, init))
1653 {
1654 error = TRUE;
1655 break;
1656 }
1657 }
1658 else if (STRCMP(key, "GUIFG") == 0)
1659 {
1660#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1661 if (highlight_set_guifg(idx, arg, is_menu_group,
1662 is_scrollbar_group, is_tooltip_group,
1663 &do_colors, init))
1664 did_change = TRUE;
1665#endif
1666 }
1667 else if (STRCMP(key, "GUIBG") == 0)
1668 {
1669#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1670 if (highlight_set_guibg(idx, arg, is_menu_group,
1671 is_scrollbar_group, is_tooltip_group,
1672 &do_colors, init))
1673 did_change = TRUE;
1674#endif
1675 }
1676 else if (STRCMP(key, "GUISP") == 0)
1677 {
1678#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1679 if (highlight_set_guisp(idx, arg, init))
1680 did_change = TRUE;
1681#endif
1682 }
1683 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1684 {
1685 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1686 {
1687 error = TRUE;
1688 break;
1689 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001690 }
1691 else
1692 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001693 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001694 error = TRUE;
1695 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001696 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001697 HL_TABLE()[idx].sg_cleared = FALSE;
1698
1699 // When highlighting has been given for a group, don't link it.
1700 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1701 HL_TABLE()[idx].sg_link = 0;
1702
1703 // Continue with next argument.
1704 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001705 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001706
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001707 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001708 if (error && idx == highlight_ga.ga_len)
1709 syn_unadd_group();
1710 else
1711 {
1712 if (is_normal_group)
1713 {
1714 HL_TABLE()[idx].sg_term_attr = 0;
1715 HL_TABLE()[idx].sg_cterm_attr = 0;
1716#ifdef FEAT_GUI
1717 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001718 // Need to update all groups, because they might be using "bg"
1719 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001720#endif
1721#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1722 if (USE_24BIT)
1723 {
1724 highlight_gui_started();
1725 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001726 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001727 }
1728#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001729#ifdef FEAT_VTP
1730 control_console_color_rgb();
1731#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001732 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001733#ifdef FEAT_GUI_X11
1734# ifdef FEAT_MENU
1735 else if (is_menu_group)
1736 {
1737 if (gui.in_use && do_colors)
1738 gui_mch_new_menu_colors();
1739 }
1740# endif
1741 else if (is_scrollbar_group)
1742 {
1743 if (gui.in_use && do_colors)
1744 gui_new_scrollbar_colors();
1745 else
1746 set_hl_attr(idx);
1747 }
1748# ifdef FEAT_BEVAL_GUI
1749 else if (is_tooltip_group)
1750 {
1751 if (gui.in_use && do_colors)
1752 gui_mch_new_tooltip_colors();
1753 }
1754# endif
1755#endif
1756 else
1757 set_hl_attr(idx);
1758#ifdef FEAT_EVAL
1759 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001760 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001761#endif
1762 }
1763
1764 vim_free(key);
1765 vim_free(arg);
1766
1767 // Only call highlight_changed() once, after a sequence of highlight
1768 // commands, and only if an attribute actually changed.
1769 if ((did_change
1770 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1771#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1772 && !did_highlight_changed
1773#endif
1774 )
1775 {
1776 // Do not trigger a redraw when highlighting is changed while
1777 // redrawing. This may happen when evaluating 'statusline' changes the
1778 // StatusLine group.
1779 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001780 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001781 need_highlight_changed = TRUE;
1782 }
1783}
1784
1785#if defined(EXITFREE) || defined(PROTO)
1786 void
1787free_highlight(void)
1788{
1789 int i;
1790
1791 for (i = 0; i < highlight_ga.ga_len; ++i)
1792 {
1793 highlight_clear(i);
1794 vim_free(HL_TABLE()[i].sg_name);
1795 vim_free(HL_TABLE()[i].sg_name_u);
1796 }
1797 ga_clear(&highlight_ga);
1798}
1799#endif
1800
1801/*
1802 * Reset the cterm colors to what they were before Vim was started, if
1803 * possible. Otherwise reset them to zero.
1804 */
1805 void
1806restore_cterm_colors(void)
1807{
1808#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1809 // Since t_me has been set, this probably means that the user
1810 // wants to use this as default colors. Need to reset default
1811 // background/foreground colors.
1812 mch_set_normal_colors();
1813#else
1814# ifdef VIMDLL
1815 if (!gui.in_use)
1816 {
1817 mch_set_normal_colors();
1818 return;
1819 }
1820# endif
1821 cterm_normal_fg_color = 0;
1822 cterm_normal_fg_bold = 0;
1823 cterm_normal_bg_color = 0;
1824# ifdef FEAT_TERMGUICOLORS
1825 cterm_normal_fg_gui_color = INVALCOLOR;
1826 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001827 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001828# endif
1829#endif
1830}
1831
1832/*
1833 * Return TRUE if highlight group "idx" has any settings.
1834 * When "check_link" is TRUE also check for an existing link.
1835 */
1836 static int
1837hl_has_settings(int idx, int check_link)
1838{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001839 return HL_TABLE()[idx].sg_cleared == 0
1840 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001841 || HL_TABLE()[idx].sg_cterm_attr != 0
1842 || HL_TABLE()[idx].sg_cterm_fg != 0
1843 || HL_TABLE()[idx].sg_cterm_bg != 0
1844#ifdef FEAT_GUI
1845 || HL_TABLE()[idx].sg_gui_attr != 0
1846 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1847 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1848 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1849 || HL_TABLE()[idx].sg_font_name != NULL
1850#endif
1851 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1852}
1853
1854/*
1855 * Clear highlighting for one group.
1856 */
1857 static void
1858highlight_clear(int idx)
1859{
1860 HL_TABLE()[idx].sg_cleared = TRUE;
1861
1862 HL_TABLE()[idx].sg_term = 0;
1863 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1864 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1865 HL_TABLE()[idx].sg_term_attr = 0;
1866 HL_TABLE()[idx].sg_cterm = 0;
1867 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1868 HL_TABLE()[idx].sg_cterm_fg = 0;
1869 HL_TABLE()[idx].sg_cterm_bg = 0;
1870 HL_TABLE()[idx].sg_cterm_attr = 0;
1871#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1872 HL_TABLE()[idx].sg_gui = 0;
1873 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1874 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1875 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1876#endif
1877#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1878 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1879 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001880 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001881#endif
1882#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001883 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1884 HL_TABLE()[idx].sg_font = NOFONT;
1885# ifdef FEAT_XFONTSET
1886 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1887 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1888# endif
1889 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1890 HL_TABLE()[idx].sg_gui_attr = 0;
1891#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001892 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001893 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001894#ifdef FEAT_EVAL
1895 // Since we set the default link, set the location to where the default
1896 // link was set.
1897 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001898#endif
1899}
1900
1901#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1902/*
1903 * Set the normal foreground and background colors according to the "Normal"
1904 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1905 * "Tooltip" colors.
1906 */
1907 void
1908set_normal_colors(void)
1909{
1910# ifdef FEAT_GUI
1911# ifdef FEAT_TERMGUICOLORS
1912 if (gui.in_use)
1913# endif
1914 {
1915 if (set_group_colors((char_u *)"Normal",
1916 &gui.norm_pixel, &gui.back_pixel,
1917 FALSE, TRUE, FALSE))
1918 {
1919 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001920 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001921 }
1922# ifdef FEAT_GUI_X11
1923 if (set_group_colors((char_u *)"Menu",
1924 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1925 TRUE, FALSE, FALSE))
1926 {
1927# ifdef FEAT_MENU
1928 gui_mch_new_menu_colors();
1929# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001930 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001931 }
1932# ifdef FEAT_BEVAL_GUI
1933 if (set_group_colors((char_u *)"Tooltip",
1934 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1935 FALSE, FALSE, TRUE))
1936 {
1937# ifdef FEAT_TOOLBAR
1938 gui_mch_new_tooltip_colors();
1939# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001940 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001941 }
1942# endif
1943 if (set_group_colors((char_u *)"Scrollbar",
1944 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1945 FALSE, FALSE, FALSE))
1946 {
1947 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001948 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001949 }
1950# endif
1951 }
1952# endif
1953# ifdef FEAT_TERMGUICOLORS
1954# ifdef FEAT_GUI
1955 else
1956# endif
1957 {
1958 int idx;
1959
1960 idx = syn_name2id((char_u *)"Normal") - 1;
1961 if (idx >= 0)
1962 {
1963 gui_do_one_color(idx, FALSE, FALSE);
1964
1965 // If the normal fg or bg color changed a complete redraw is
1966 // required.
1967 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1968 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1969 {
1970 // if the GUI color is INVALCOLOR then we use the default cterm
1971 // color
1972 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1973 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001974 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001975 }
1976 }
1977 }
1978# endif
1979}
1980#endif
1981
1982#if defined(FEAT_GUI) || defined(PROTO)
1983/*
1984 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1985 */
1986 static int
1987set_group_colors(
1988 char_u *name,
1989 guicolor_T *fgp,
1990 guicolor_T *bgp,
1991 int do_menu,
1992 int use_norm,
1993 int do_tooltip)
1994{
1995 int idx;
1996
1997 idx = syn_name2id(name) - 1;
1998 if (idx >= 0)
1999 {
2000 gui_do_one_color(idx, do_menu, do_tooltip);
2001
2002 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2003 *fgp = HL_TABLE()[idx].sg_gui_fg;
2004 else if (use_norm)
2005 *fgp = gui.def_norm_pixel;
2006 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2007 *bgp = HL_TABLE()[idx].sg_gui_bg;
2008 else if (use_norm)
2009 *bgp = gui.def_back_pixel;
2010 return TRUE;
2011 }
2012 return FALSE;
2013}
2014
2015/*
2016 * Get the font of the "Normal" group.
2017 * Returns "" when it's not found or not set.
2018 */
2019 char_u *
2020hl_get_font_name(void)
2021{
2022 int id;
2023 char_u *s;
2024
2025 id = syn_name2id((char_u *)"Normal");
2026 if (id > 0)
2027 {
2028 s = HL_TABLE()[id - 1].sg_font_name;
2029 if (s != NULL)
2030 return s;
2031 }
2032 return (char_u *)"";
2033}
2034
2035/*
2036 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2037 * actually chosen to be used.
2038 */
2039 void
2040hl_set_font_name(char_u *font_name)
2041{
2042 int id;
2043
2044 id = syn_name2id((char_u *)"Normal");
2045 if (id > 0)
2046 {
2047 vim_free(HL_TABLE()[id - 1].sg_font_name);
2048 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2049 }
2050}
2051
2052/*
2053 * Set background color for "Normal" group. Called by gui_set_bg_color()
2054 * when the color is known.
2055 */
2056 void
2057hl_set_bg_color_name(
2058 char_u *name) // must have been allocated
2059{
2060 int id;
2061
2062 if (name != NULL)
2063 {
2064 id = syn_name2id((char_u *)"Normal");
2065 if (id > 0)
2066 {
2067 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2068 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2069 }
2070 }
2071}
2072
2073/*
2074 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2075 * when the color is known.
2076 */
2077 void
2078hl_set_fg_color_name(
2079 char_u *name) // must have been allocated
2080{
2081 int id;
2082
2083 if (name != NULL)
2084 {
2085 id = syn_name2id((char_u *)"Normal");
2086 if (id > 0)
2087 {
2088 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2089 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2090 }
2091 }
2092}
2093
2094/*
2095 * Return the handle for a font name.
2096 * Returns NOFONT when failed.
2097 */
2098 static GuiFont
2099font_name2handle(char_u *name)
2100{
2101 if (STRCMP(name, "NONE") == 0)
2102 return NOFONT;
2103
2104 return gui_mch_get_font(name, TRUE);
2105}
2106
2107# ifdef FEAT_XFONTSET
2108/*
2109 * Return the handle for a fontset name.
2110 * Returns NOFONTSET when failed.
2111 */
2112 static GuiFontset
2113fontset_name2handle(char_u *name, int fixed_width)
2114{
2115 if (STRCMP(name, "NONE") == 0)
2116 return NOFONTSET;
2117
2118 return gui_mch_get_fontset(name, TRUE, fixed_width);
2119}
2120# endif
2121
2122/*
2123 * Get the font or fontset for one highlight group.
2124 */
2125 static void
2126hl_do_font(
2127 int idx,
2128 char_u *arg,
2129 int do_normal, // set normal font
2130 int do_menu UNUSED, // set menu font
2131 int do_tooltip UNUSED, // set tooltip font
2132 int free_font) // free current font/fontset
2133{
2134# ifdef FEAT_XFONTSET
2135 // If 'guifontset' is not empty, first try using the name as a
2136 // fontset. If that doesn't work, use it as a font name.
2137 if (*p_guifontset != NUL
2138# ifdef FONTSET_ALWAYS
2139 || do_menu
2140# endif
2141# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002142 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002143 || do_tooltip
2144# endif
2145 )
2146 {
2147 if (free_font)
2148 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2149 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2150# ifdef FONTSET_ALWAYS
2151 || do_menu
2152# endif
2153# ifdef FEAT_BEVAL_TIP
2154 || do_tooltip
2155# endif
2156 );
2157 }
2158 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2159 {
2160 // If it worked and it's the Normal group, use it as the normal
2161 // fontset. Same for the Menu group.
2162 if (do_normal)
2163 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002164# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002165 if (do_menu)
2166 {
2167# ifdef FONTSET_ALWAYS
2168 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2169# else
2170 // YIKES! This is a bug waiting to crash the program
2171 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2172# endif
2173 gui_mch_new_menu_font();
2174 }
2175# ifdef FEAT_BEVAL_GUI
2176 if (do_tooltip)
2177 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002178 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002179 // displaying a single font and a fontset.
2180 // If the XtNinternational resource is set to True at widget
2181 // creation, then a fontset is always used, otherwise an
2182 // XFontStruct is used.
2183 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2184 gui_mch_new_tooltip_font();
2185 }
2186# endif
2187# endif
2188 }
2189 else
2190# endif
2191 {
2192 if (free_font)
2193 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2194 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2195 // If it worked and it's the Normal group, use it as the
2196 // normal font. Same for the Menu group.
2197 if (HL_TABLE()[idx].sg_font != NOFONT)
2198 {
2199 if (do_normal)
2200 gui_init_font(arg, FALSE);
2201#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002202# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002203 if (do_menu)
2204 {
2205 gui.menu_font = HL_TABLE()[idx].sg_font;
2206 gui_mch_new_menu_font();
2207 }
2208# endif
2209#endif
2210 }
2211 }
2212}
2213
2214#endif // FEAT_GUI
2215
2216#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2217/*
2218 * Return the handle for a color name.
2219 * Returns INVALCOLOR when failed.
2220 */
2221 guicolor_T
2222color_name2handle(char_u *name)
2223{
2224 if (STRCMP(name, "NONE") == 0)
2225 return INVALCOLOR;
2226
2227 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2228 {
2229#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2230 if (gui.in_use)
2231#endif
2232#ifdef FEAT_GUI
2233 return gui.norm_pixel;
2234#endif
2235#ifdef FEAT_TERMGUICOLORS
2236 if (cterm_normal_fg_gui_color != INVALCOLOR)
2237 return cterm_normal_fg_gui_color;
2238 // Guess that the foreground is black or white.
2239 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2240#endif
2241 }
2242 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2243 {
2244#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2245 if (gui.in_use)
2246#endif
2247#ifdef FEAT_GUI
2248 return gui.back_pixel;
2249#endif
2250#ifdef FEAT_TERMGUICOLORS
2251 if (cterm_normal_bg_gui_color != INVALCOLOR)
2252 return cterm_normal_bg_gui_color;
2253 // Guess that the background is white or black.
2254 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2255#endif
2256 }
2257
2258 return GUI_GET_COLOR(name);
2259}
Drew Vogele30d1022021-10-24 20:35:07 +01002260
2261// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2262// values as used by the MS-Windows GDI api. It should be used only for
2263// MS-Windows GDI builds.
2264# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2265# undef RGB
2266# endif
2267# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002268# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002269# endif
2270
2271# ifdef VIMDLL
2272 static guicolor_T
2273gui_adjust_rgb(guicolor_T c)
2274{
2275 if (gui.in_use)
2276 return c;
2277 else
2278 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2279}
2280# else
2281# define gui_adjust_rgb(c) (c)
2282# endif
2283
2284 static int
2285hex_digit(int c)
2286{
2287 if (isdigit(c))
2288 return c - '0';
2289 c = TOLOWER_ASC(c);
2290 if (c >= 'a' && c <= 'f')
2291 return c - 'a' + 10;
2292 return 0x1ffffff;
2293}
2294
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002295 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002296decode_hex_color(char_u *hex)
2297{
2298 guicolor_T color;
2299
2300 if (hex[0] != '#' || STRLEN(hex) != 7)
2301 return INVALCOLOR;
2302
2303 // Name is in "#rrggbb" format
2304 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2305 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2306 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2307 if (color > 0xffffff)
2308 return INVALCOLOR;
2309 return gui_adjust_rgb(color);
2310}
2311
Bram Moolenaar2a521962021-10-25 10:30:14 +01002312#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002313// Returns the color currently mapped to the given name or INVALCOLOR if no
2314// such name exists in the color table. The convention is to use lowercase for
2315// all keys in the v:colornames dictionary. The value can be either a string in
2316// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002317 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002318colorname2rgb(char_u *name)
2319{
2320 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2321 char_u *lc_name;
2322 dictitem_T *colentry;
2323 char_u *colstr;
2324 varnumber_T colnum;
2325
2326 lc_name = strlow_save(name);
2327 if (lc_name == NULL)
2328 return INVALCOLOR;
2329
2330 colentry = dict_find(colornames_table, lc_name, -1);
2331 vim_free(lc_name);
2332 if (colentry == NULL)
2333 return INVALCOLOR;
2334
2335 if (colentry->di_tv.v_type == VAR_STRING)
2336 {
2337 colstr = tv_get_string_strict(&colentry->di_tv);
2338 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2339 {
2340 return decode_hex_color(colstr);
2341 }
2342 else
2343 {
2344 semsg(_(e_bad_color_string_str), colstr);
2345 return INVALCOLOR;
2346 }
2347 }
2348
2349 if (colentry->di_tv.v_type == VAR_NUMBER)
2350 {
2351 colnum = tv_get_number(&colentry->di_tv);
2352 return (guicolor_T)colnum;
2353 }
2354
2355 return INVALCOLOR;
2356}
2357
Drew Vogele30d1022021-10-24 20:35:07 +01002358#endif
2359
2360 guicolor_T
2361gui_get_color_cmn(char_u *name)
2362{
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002363 int i;
Drew Vogele30d1022021-10-24 20:35:07 +01002364 guicolor_T color;
2365
2366 struct rgbcolor_table_S {
2367 char_u *color_name;
2368 guicolor_T color;
2369 };
2370
2371 // Only non X11 colors (not present in rgb.txt) and colors in
2372 // color_names[], useful when $VIMRUNTIME is not found,.
2373 static struct rgbcolor_table_S rgb_table[] = {
2374 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2375 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2376 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2377 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2378 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2379 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2380 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2381 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2382 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2383 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2384 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2385 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2386 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2387 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2388 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2389 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2390 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2391 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2392 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2393 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2394 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2395 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2396 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2397 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2398 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2399 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2400 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2401 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2402 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2403 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2404 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2405 };
2406
2407 color = decode_hex_color(name);
2408 if (color != INVALCOLOR)
2409 return color;
2410
2411 // Check if the name is one of the colors we know
2412 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2413 if (STRICMP(name, rgb_table[i].color_name) == 0)
2414 return gui_adjust_rgb(rgb_table[i].color);
2415
2416#if defined(FEAT_EVAL)
2417 /*
2418 * Not a traditional color. Load additional color aliases and then consult the alias table.
2419 */
2420
2421 color = colorname2rgb(name);
2422 if (color == INVALCOLOR)
2423 {
2424 load_default_colors_lists();
2425 color = colorname2rgb(name);
2426 }
2427
2428 return color;
2429#else
2430 return INVALCOLOR;
2431#endif
2432}
2433
2434 guicolor_T
2435gui_get_rgb_color_cmn(int r, int g, int b)
2436{
2437 guicolor_T color = RGB(r, g, b);
2438
2439 if (color > 0xffffff)
2440 return INVALCOLOR;
2441 return gui_adjust_rgb(color);
2442}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002443#endif
2444
2445/*
2446 * Table with the specifications for an attribute number.
2447 * Note that this table is used by ALL buffers. This is required because the
2448 * GUI can redraw at any time for any buffer.
2449 */
2450static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2451
2452#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2453
2454static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2455
2456#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2457
2458#ifdef FEAT_GUI
2459static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2460
2461#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2462#endif
2463
2464/*
2465 * Return the attr number for a set of colors and font.
2466 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2467 * if the combination is new.
2468 * Return 0 for error (no more room).
2469 */
2470 static int
2471get_attr_entry(garray_T *table, attrentry_T *aep)
2472{
2473 int i;
2474 attrentry_T *taep;
2475 static int recursive = FALSE;
2476
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002477 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002478 table->ga_itemsize = sizeof(attrentry_T);
2479 table->ga_growsize = 7;
2480
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002481 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002482 for (i = 0; i < table->ga_len; ++i)
2483 {
2484 taep = &(((attrentry_T *)table->ga_data)[i]);
2485 if ( aep->ae_attr == taep->ae_attr
2486 && (
2487#ifdef FEAT_GUI
2488 (table == &gui_attr_table
2489 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2490 && aep->ae_u.gui.bg_color
2491 == taep->ae_u.gui.bg_color
2492 && aep->ae_u.gui.sp_color
2493 == taep->ae_u.gui.sp_color
2494 && aep->ae_u.gui.font == taep->ae_u.gui.font
2495# ifdef FEAT_XFONTSET
2496 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2497# endif
2498 ))
2499 ||
2500#endif
2501 (table == &term_attr_table
2502 && (aep->ae_u.term.start == NULL)
2503 == (taep->ae_u.term.start == NULL)
2504 && (aep->ae_u.term.start == NULL
2505 || STRCMP(aep->ae_u.term.start,
2506 taep->ae_u.term.start) == 0)
2507 && (aep->ae_u.term.stop == NULL)
2508 == (taep->ae_u.term.stop == NULL)
2509 && (aep->ae_u.term.stop == NULL
2510 || STRCMP(aep->ae_u.term.stop,
2511 taep->ae_u.term.stop) == 0))
2512 || (table == &cterm_attr_table
2513 && aep->ae_u.cterm.fg_color
2514 == taep->ae_u.cterm.fg_color
2515 && aep->ae_u.cterm.bg_color
2516 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002517 && aep->ae_u.cterm.ul_color
2518 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002519#ifdef FEAT_TERMGUICOLORS
2520 && aep->ae_u.cterm.fg_rgb
2521 == taep->ae_u.cterm.fg_rgb
2522 && aep->ae_u.cterm.bg_rgb
2523 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002524 && aep->ae_u.cterm.ul_rgb
2525 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002526#endif
2527 )))
2528
2529 return i + ATTR_OFF;
2530 }
2531
2532 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2533 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002534 // Running out of attribute entries! remove all attributes, and
2535 // compute new ones for all groups.
2536 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002537 if (recursive)
2538 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002539 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002540 return 0;
2541 }
2542 recursive = TRUE;
2543
2544 clear_hl_tables();
2545
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002546 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002547
2548 for (i = 0; i < highlight_ga.ga_len; ++i)
2549 set_hl_attr(i);
2550
2551 recursive = FALSE;
2552 }
2553
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002554 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002555 if (ga_grow(table, 1) == FAIL)
2556 return 0;
2557
2558 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002559 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002560 taep->ae_attr = aep->ae_attr;
2561#ifdef FEAT_GUI
2562 if (table == &gui_attr_table)
2563 {
2564 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2565 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2566 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2567 taep->ae_u.gui.font = aep->ae_u.gui.font;
2568# ifdef FEAT_XFONTSET
2569 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2570# endif
2571 }
2572#endif
2573 if (table == &term_attr_table)
2574 {
2575 if (aep->ae_u.term.start == NULL)
2576 taep->ae_u.term.start = NULL;
2577 else
2578 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2579 if (aep->ae_u.term.stop == NULL)
2580 taep->ae_u.term.stop = NULL;
2581 else
2582 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2583 }
2584 else if (table == &cterm_attr_table)
2585 {
2586 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2587 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002588 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002589#ifdef FEAT_TERMGUICOLORS
2590 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2591 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002592 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002593#endif
2594 }
2595 ++table->ga_len;
2596 return (table->ga_len - 1 + ATTR_OFF);
2597}
2598
2599#if defined(FEAT_TERMINAL) || defined(PROTO)
2600/*
2601 * Get an attribute index for a cterm entry.
2602 * Uses an existing entry when possible or adds one when needed.
2603 */
2604 int
2605get_cterm_attr_idx(int attr, int fg, int bg)
2606{
2607 attrentry_T at_en;
2608
Bram Moolenaara80faa82020-04-12 19:37:17 +02002609 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002610#ifdef FEAT_TERMGUICOLORS
2611 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2612 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002613 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002614#endif
2615 at_en.ae_attr = attr;
2616 at_en.ae_u.cterm.fg_color = fg;
2617 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002618 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002619 return get_attr_entry(&cterm_attr_table, &at_en);
2620}
2621#endif
2622
2623#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2624/*
2625 * Get an attribute index for a 'termguicolors' entry.
2626 * Uses an existing entry when possible or adds one when needed.
2627 */
2628 int
2629get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2630{
2631 attrentry_T at_en;
2632
Bram Moolenaara80faa82020-04-12 19:37:17 +02002633 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002634 at_en.ae_attr = attr;
2635 if (fg == INVALCOLOR && bg == INVALCOLOR)
2636 {
2637 // If both GUI colors are not set fall back to the cterm colors. Helps
2638 // if the GUI only has an attribute, such as undercurl.
2639 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2640 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2641 }
2642 else
2643 {
2644 at_en.ae_u.cterm.fg_rgb = fg;
2645 at_en.ae_u.cterm.bg_rgb = bg;
2646 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002647 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002648 return get_attr_entry(&cterm_attr_table, &at_en);
2649}
2650#endif
2651
2652#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2653/*
2654 * Get an attribute index for a cterm entry.
2655 * Uses an existing entry when possible or adds one when needed.
2656 */
2657 int
2658get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2659{
2660 attrentry_T at_en;
2661
Bram Moolenaara80faa82020-04-12 19:37:17 +02002662 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002663 at_en.ae_attr = attr;
2664 at_en.ae_u.gui.fg_color = fg;
2665 at_en.ae_u.gui.bg_color = bg;
2666 return get_attr_entry(&gui_attr_table, &at_en);
2667}
2668#endif
2669
2670/*
2671 * Clear all highlight tables.
2672 */
2673 void
2674clear_hl_tables(void)
2675{
2676 int i;
2677 attrentry_T *taep;
2678
2679#ifdef FEAT_GUI
2680 ga_clear(&gui_attr_table);
2681#endif
2682 for (i = 0; i < term_attr_table.ga_len; ++i)
2683 {
2684 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2685 vim_free(taep->ae_u.term.start);
2686 vim_free(taep->ae_u.term.stop);
2687 }
2688 ga_clear(&term_attr_table);
2689 ga_clear(&cterm_attr_table);
2690}
2691
2692/*
2693 * Combine special attributes (e.g., for spelling) with other attributes
2694 * (e.g., for syntax highlighting).
2695 * "prim_attr" overrules "char_attr".
2696 * This creates a new group when required.
2697 * Since we expect there to be few spelling mistakes we don't cache the
2698 * result.
2699 * Return the resulting attributes.
2700 */
2701 int
2702hl_combine_attr(int char_attr, int prim_attr)
2703{
2704 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002705 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002706 attrentry_T new_en;
2707
2708 if (char_attr == 0)
2709 return prim_attr;
2710 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2711 return ATTR_COMBINE(char_attr, prim_attr);
2712#ifdef FEAT_GUI
2713 if (gui.in_use)
2714 {
2715 if (char_attr > HL_ALL)
2716 char_aep = syn_gui_attr2entry(char_attr);
2717 if (char_aep != NULL)
2718 new_en = *char_aep;
2719 else
2720 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002721 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002722 new_en.ae_u.gui.fg_color = INVALCOLOR;
2723 new_en.ae_u.gui.bg_color = INVALCOLOR;
2724 new_en.ae_u.gui.sp_color = INVALCOLOR;
2725 if (char_attr <= HL_ALL)
2726 new_en.ae_attr = char_attr;
2727 }
2728
2729 if (prim_attr <= HL_ALL)
2730 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2731 else
2732 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002733 prim_aep = syn_gui_attr2entry(prim_attr);
2734 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002735 {
2736 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002737 prim_aep->ae_attr);
2738 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2739 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2740 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2741 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2742 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2743 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2744 if (prim_aep->ae_u.gui.font != NOFONT)
2745 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002746# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002747 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2748 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002749# endif
2750 }
2751 }
2752 return get_attr_entry(&gui_attr_table, &new_en);
2753 }
2754#endif
2755
2756 if (IS_CTERM)
2757 {
2758 if (char_attr > HL_ALL)
2759 char_aep = syn_cterm_attr2entry(char_attr);
2760 if (char_aep != NULL)
2761 new_en = *char_aep;
2762 else
2763 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002764 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002765#ifdef FEAT_TERMGUICOLORS
2766 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2767 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002768 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002769#endif
2770 if (char_attr <= HL_ALL)
2771 new_en.ae_attr = char_attr;
2772 }
2773
2774 if (prim_attr <= HL_ALL)
2775 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2776 else
2777 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002778 prim_aep = syn_cterm_attr2entry(prim_attr);
2779 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002780 {
2781 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002782 prim_aep->ae_attr);
2783 if (prim_aep->ae_u.cterm.fg_color > 0)
2784 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2785 if (prim_aep->ae_u.cterm.bg_color > 0)
2786 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2787 if (prim_aep->ae_u.cterm.ul_color > 0)
2788 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002789#ifdef FEAT_TERMGUICOLORS
2790 // If both fg and bg are not set fall back to cterm colors.
2791 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002792 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2793 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002794 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002795 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002796 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002797 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002798 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2799 }
2800 else
2801 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002802 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2803 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2804 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2805 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002806 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002807 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2808 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002809#endif
2810 }
2811 }
2812 return get_attr_entry(&cterm_attr_table, &new_en);
2813 }
2814
2815 if (char_attr > HL_ALL)
2816 char_aep = syn_term_attr2entry(char_attr);
2817 if (char_aep != NULL)
2818 new_en = *char_aep;
2819 else
2820 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002821 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002822 if (char_attr <= HL_ALL)
2823 new_en.ae_attr = char_attr;
2824 }
2825
2826 if (prim_attr <= HL_ALL)
2827 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2828 else
2829 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002830 prim_aep = syn_term_attr2entry(prim_attr);
2831 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002832 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002833 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2834 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002835 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002836 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2837 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002838 }
2839 }
2840 }
2841 return get_attr_entry(&term_attr_table, &new_en);
2842}
2843
2844#ifdef FEAT_GUI
2845 attrentry_T *
2846syn_gui_attr2entry(int attr)
2847{
2848 attr -= ATTR_OFF;
2849 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2850 return NULL;
2851 return &(GUI_ATTR_ENTRY(attr));
2852}
2853#endif
2854
2855/*
2856 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2857 * Only to be used when "attr" > HL_ALL.
2858 */
2859 int
2860syn_attr2attr(int attr)
2861{
2862 attrentry_T *aep;
2863
2864#ifdef FEAT_GUI
2865 if (gui.in_use)
2866 aep = syn_gui_attr2entry(attr);
2867 else
2868#endif
2869 if (IS_CTERM)
2870 aep = syn_cterm_attr2entry(attr);
2871 else
2872 aep = syn_term_attr2entry(attr);
2873
2874 if (aep == NULL) // highlighting not set
2875 return 0;
2876 return aep->ae_attr;
2877}
2878
2879
2880 attrentry_T *
2881syn_term_attr2entry(int attr)
2882{
2883 attr -= ATTR_OFF;
2884 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2885 return NULL;
2886 return &(TERM_ATTR_ENTRY(attr));
2887}
2888
2889 attrentry_T *
2890syn_cterm_attr2entry(int attr)
2891{
2892 attr -= ATTR_OFF;
2893 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2894 return NULL;
2895 return &(CTERM_ATTR_ENTRY(attr));
2896}
2897
2898#define LIST_ATTR 1
2899#define LIST_STRING 2
2900#define LIST_INT 3
2901
2902 static void
2903highlight_list_one(int id)
2904{
2905 hl_group_T *sgp;
2906 int didh = FALSE;
2907
2908 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2909
2910 if (message_filtered(sgp->sg_name))
2911 return;
2912
2913 didh = highlight_list_arg(id, didh, LIST_ATTR,
2914 sgp->sg_term, NULL, "term");
2915 didh = highlight_list_arg(id, didh, LIST_STRING,
2916 0, sgp->sg_start, "start");
2917 didh = highlight_list_arg(id, didh, LIST_STRING,
2918 0, sgp->sg_stop, "stop");
2919
2920 didh = highlight_list_arg(id, didh, LIST_ATTR,
2921 sgp->sg_cterm, NULL, "cterm");
2922 didh = highlight_list_arg(id, didh, LIST_INT,
2923 sgp->sg_cterm_fg, NULL, "ctermfg");
2924 didh = highlight_list_arg(id, didh, LIST_INT,
2925 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002926 didh = highlight_list_arg(id, didh, LIST_INT,
2927 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002928
2929#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2930 didh = highlight_list_arg(id, didh, LIST_ATTR,
2931 sgp->sg_gui, NULL, "gui");
2932 didh = highlight_list_arg(id, didh, LIST_STRING,
2933 0, sgp->sg_gui_fg_name, "guifg");
2934 didh = highlight_list_arg(id, didh, LIST_STRING,
2935 0, sgp->sg_gui_bg_name, "guibg");
2936 didh = highlight_list_arg(id, didh, LIST_STRING,
2937 0, sgp->sg_gui_sp_name, "guisp");
2938#endif
2939#ifdef FEAT_GUI
2940 didh = highlight_list_arg(id, didh, LIST_STRING,
2941 0, sgp->sg_font_name, "font");
2942#endif
2943
2944 if (sgp->sg_link && !got_int)
2945 {
2946 (void)syn_list_header(didh, 9999, id);
2947 didh = TRUE;
2948 msg_puts_attr("links to", HL_ATTR(HLF_D));
2949 msg_putchar(' ');
2950 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2951 }
2952
2953 if (!didh)
2954 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2955#ifdef FEAT_EVAL
2956 if (p_verbose > 0)
2957 last_set_msg(sgp->sg_script_ctx);
2958#endif
2959}
2960
2961 static int
2962highlight_list_arg(
2963 int id,
2964 int didh,
2965 int type,
2966 int iarg,
2967 char_u *sarg,
2968 char *name)
2969{
Bram Moolenaar84f54632022-06-29 18:39:11 +01002970 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002971 char_u *ts;
2972 int i;
2973
2974 if (got_int)
2975 return FALSE;
2976 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2977 {
2978 ts = buf;
2979 if (type == LIST_INT)
2980 sprintf((char *)buf, "%d", iarg - 1);
2981 else if (type == LIST_STRING)
2982 ts = sarg;
2983 else // type == LIST_ATTR
2984 {
2985 buf[0] = NUL;
2986 for (i = 0; hl_attr_table[i] != 0; ++i)
2987 {
2988 if (iarg & hl_attr_table[i])
2989 {
2990 if (buf[0] != NUL)
Bram Moolenaar84f54632022-06-29 18:39:11 +01002991 vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
2992 vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002993 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2994 }
2995 }
2996 }
2997
2998 (void)syn_list_header(didh,
2999 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3000 didh = TRUE;
3001 if (!got_int)
3002 {
3003 if (*name != NUL)
3004 {
3005 msg_puts_attr(name, HL_ATTR(HLF_D));
3006 msg_puts_attr("=", HL_ATTR(HLF_D));
3007 }
3008 msg_outtrans(ts);
3009 }
3010 }
3011 return didh;
3012}
3013
3014#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3015/*
3016 * Return "1" if highlight group "id" has attribute "flag".
3017 * Return NULL otherwise.
3018 */
3019 char_u *
3020highlight_has_attr(
3021 int id,
3022 int flag,
3023 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3024{
3025 int attr;
3026
3027 if (id <= 0 || id > highlight_ga.ga_len)
3028 return NULL;
3029
3030#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3031 if (modec == 'g')
3032 attr = HL_TABLE()[id - 1].sg_gui;
3033 else
3034#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003035 {
3036 if (modec == 'c')
3037 attr = HL_TABLE()[id - 1].sg_cterm;
3038 else
3039 attr = HL_TABLE()[id - 1].sg_term;
3040 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003041
3042 if (attr & flag)
3043 return (char_u *)"1";
3044 return NULL;
3045}
3046#endif
3047
3048#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3049/*
3050 * Return color name of highlight group "id".
3051 */
3052 char_u *
3053highlight_color(
3054 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003055 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003056 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3057{
3058 static char_u name[20];
3059 int n;
3060 int fg = FALSE;
3061 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003062 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003063 int font = FALSE;
3064
3065 if (id <= 0 || id > highlight_ga.ga_len)
3066 return NULL;
3067
3068 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3069 fg = TRUE;
3070 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3071 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3072 font = TRUE;
3073 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3074 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003075 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3076 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003077 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3078 return NULL;
3079 if (modec == 'g')
3080 {
3081# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3082# ifdef FEAT_GUI
3083 // return font name
3084 if (font)
3085 return HL_TABLE()[id - 1].sg_font_name;
3086# endif
3087
3088 // return #RRGGBB form (only possible when GUI is running)
3089 if ((USE_24BIT) && what[2] == '#')
3090 {
3091 guicolor_T color;
3092 long_u rgb;
3093 static char_u buf[10];
3094
3095 if (fg)
3096 color = HL_TABLE()[id - 1].sg_gui_fg;
3097 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003098 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003099 else
3100 color = HL_TABLE()[id - 1].sg_gui_bg;
3101 if (color == INVALCOLOR)
3102 return NULL;
3103 rgb = (long_u)GUI_MCH_GET_RGB(color);
3104 sprintf((char *)buf, "#%02x%02x%02x",
3105 (unsigned)(rgb >> 16),
3106 (unsigned)(rgb >> 8) & 255,
3107 (unsigned)rgb & 255);
3108 return buf;
3109 }
3110# endif
3111 if (fg)
3112 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3113 if (sp)
3114 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3115 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3116 }
3117 if (font || sp)
3118 return NULL;
3119 if (modec == 'c')
3120 {
3121 if (fg)
3122 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003123 else if (ul)
3124 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003125 else
3126 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3127 if (n < 0)
3128 return NULL;
3129 sprintf((char *)name, "%d", n);
3130 return name;
3131 }
3132 // term doesn't have color
3133 return NULL;
3134}
3135#endif
3136
3137#if (defined(FEAT_SYN_HL) \
3138 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3139 && defined(FEAT_PRINTER)) || defined(PROTO)
3140/*
3141 * Return color name of highlight group "id" as RGB value.
3142 */
3143 long_u
3144highlight_gui_color_rgb(
3145 int id,
3146 int fg) // TRUE = fg, FALSE = bg
3147{
3148 guicolor_T color;
3149
3150 if (id <= 0 || id > highlight_ga.ga_len)
3151 return 0L;
3152
3153 if (fg)
3154 color = HL_TABLE()[id - 1].sg_gui_fg;
3155 else
3156 color = HL_TABLE()[id - 1].sg_gui_bg;
3157
3158 if (color == INVALCOLOR)
3159 return 0L;
3160
3161 return GUI_MCH_GET_RGB(color);
3162}
3163#endif
3164
3165/*
3166 * Output the syntax list header.
3167 * Return TRUE when started a new line.
3168 */
3169 int
3170syn_list_header(
3171 int did_header, // did header already
3172 int outlen, // length of string that comes
3173 int id) // highlight group id
3174{
3175 int endcol = 19;
3176 int newline = TRUE;
3177 int name_col = 0;
3178
3179 if (!did_header)
3180 {
3181 msg_putchar('\n');
3182 if (got_int)
3183 return TRUE;
3184 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3185 name_col = msg_col;
3186 endcol = 15;
3187 }
3188 else if (msg_col + outlen + 1 >= Columns)
3189 {
3190 msg_putchar('\n');
3191 if (got_int)
3192 return TRUE;
3193 }
3194 else
3195 {
3196 if (msg_col >= endcol) // wrap around is like starting a new line
3197 newline = FALSE;
3198 }
3199
3200 if (msg_col >= endcol) // output at least one space
3201 endcol = msg_col + 1;
3202 if (Columns <= endcol) // avoid hang for tiny window
3203 endcol = Columns - 1;
3204
3205 msg_advance(endcol);
3206
3207 // Show "xxx" with the attributes.
3208 if (!did_header)
3209 {
3210 if (endcol == Columns - 1 && endcol <= name_col)
3211 msg_putchar(' ');
3212 msg_puts_attr("xxx", syn_id2attr(id));
3213 msg_putchar(' ');
3214 }
3215
3216 return newline;
3217}
3218
3219/*
3220 * Set the attribute numbers for a highlight group.
3221 * Called after one of the attributes has changed.
3222 */
3223 static void
3224set_hl_attr(
3225 int idx) // index in array
3226{
3227 attrentry_T at_en;
3228 hl_group_T *sgp = HL_TABLE() + idx;
3229
3230 // The "Normal" group doesn't need an attribute number
3231 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3232 return;
3233
3234#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003235 // For the GUI mode: If there are other than "normal" highlighting
3236 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003237 if (sgp->sg_gui_fg == INVALCOLOR
3238 && sgp->sg_gui_bg == INVALCOLOR
3239 && sgp->sg_gui_sp == INVALCOLOR
3240 && sgp->sg_font == NOFONT
3241# ifdef FEAT_XFONTSET
3242 && sgp->sg_fontset == NOFONTSET
3243# endif
3244 )
3245 {
3246 sgp->sg_gui_attr = sgp->sg_gui;
3247 }
3248 else
3249 {
3250 at_en.ae_attr = sgp->sg_gui;
3251 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3252 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3253 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3254 at_en.ae_u.gui.font = sgp->sg_font;
3255# ifdef FEAT_XFONTSET
3256 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3257# endif
3258 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3259 }
3260#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003261 // For the term mode: If there are other than "normal" highlighting
3262 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003263 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3264 sgp->sg_term_attr = sgp->sg_term;
3265 else
3266 {
3267 at_en.ae_attr = sgp->sg_term;
3268 at_en.ae_u.term.start = sgp->sg_start;
3269 at_en.ae_u.term.stop = sgp->sg_stop;
3270 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3271 }
3272
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003273 // For the color term mode: If there are other than "normal"
3274 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003275 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003276# ifdef FEAT_TERMGUICOLORS
3277 && sgp->sg_gui_fg == INVALCOLOR
3278 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003279 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003280# endif
3281 )
3282 sgp->sg_cterm_attr = sgp->sg_cterm;
3283 else
3284 {
3285 at_en.ae_attr = sgp->sg_cterm;
3286 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3287 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003288 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003289# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003290 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3291 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003292 // Only use the underline/undercurl color when used, it may clear the
3293 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003294 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3295 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003296 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3297 else
3298 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003299 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3300 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3301 {
3302 // If both fg and bg are invalid fall back to the cterm colors.
3303 // Helps when the GUI only uses an attribute, e.g. undercurl.
3304 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3305 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3306 }
3307# endif
3308 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3309 }
3310}
3311
3312/*
3313 * Lookup a highlight group name and return its ID.
3314 * If it is not found, 0 is returned.
3315 */
3316 int
3317syn_name2id(char_u *name)
3318{
3319 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003320 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003321
3322 // Avoid using stricmp() too much, it's slow on some systems
3323 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3324 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003325 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003326 vim_strup(name_u);
3327 for (i = highlight_ga.ga_len; --i >= 0; )
3328 if (HL_TABLE()[i].sg_name_u != NULL
3329 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3330 break;
3331 return i + 1;
3332}
3333
3334/*
3335 * Lookup a highlight group name and return its attributes.
3336 * Return zero if not found.
3337 */
3338 int
3339syn_name2attr(char_u *name)
3340{
3341 int id = syn_name2id(name);
3342
3343 if (id != 0)
3344 return syn_id2attr(id);
3345 return 0;
3346}
3347
3348#if defined(FEAT_EVAL) || defined(PROTO)
3349/*
3350 * Return TRUE if highlight group "name" exists.
3351 */
3352 int
3353highlight_exists(char_u *name)
3354{
3355 return (syn_name2id(name) > 0);
3356}
3357
3358# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3359/*
3360 * Return the name of highlight group "id".
3361 * When not a valid ID return an empty string.
3362 */
3363 char_u *
3364syn_id2name(int id)
3365{
3366 if (id <= 0 || id > highlight_ga.ga_len)
3367 return (char_u *)"";
3368 return HL_TABLE()[id - 1].sg_name;
3369}
3370# endif
3371#endif
3372
3373/*
3374 * Like syn_name2id(), but take a pointer + length argument.
3375 */
3376 int
3377syn_namen2id(char_u *linep, int len)
3378{
3379 char_u *name;
3380 int id = 0;
3381
3382 name = vim_strnsave(linep, len);
3383 if (name != NULL)
3384 {
3385 id = syn_name2id(name);
3386 vim_free(name);
3387 }
3388 return id;
3389}
3390
3391/*
3392 * Find highlight group name in the table and return its ID.
3393 * The argument is a pointer to the name and the length of the name.
3394 * If it doesn't exist yet, a new entry is created.
3395 * Return 0 for failure.
3396 */
3397 int
3398syn_check_group(char_u *pp, int len)
3399{
3400 int id;
3401 char_u *name;
3402
erw7f7f7aaf2021-12-07 21:29:20 +00003403 if (len > MAX_SYN_NAME)
3404 {
3405 emsg(_(e_highlight_group_name_too_long));
3406 return 0;
3407 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003408 name = vim_strnsave(pp, len);
3409 if (name == NULL)
3410 return 0;
3411
3412 id = syn_name2id(name);
3413 if (id == 0) // doesn't exist yet
3414 id = syn_add_group(name);
3415 else
3416 vim_free(name);
3417 return id;
3418}
3419
3420/*
3421 * Add new highlight group and return its ID.
3422 * "name" must be an allocated string, it will be consumed.
3423 * Return 0 for failure.
3424 */
3425 static int
3426syn_add_group(char_u *name)
3427{
3428 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003429 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003430
3431 // Check that the name is ASCII letters, digits and underscore.
3432 for (p = name; *p != NUL; ++p)
3433 {
3434 if (!vim_isprintc(*p))
3435 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003436 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003437 vim_free(name);
3438 return 0;
3439 }
3440 else if (!ASCII_ISALNUM(*p) && *p != '_')
3441 {
3442 // This is an error, but since there previously was no check only
3443 // give a warning.
3444 msg_source(HL_ATTR(HLF_W));
3445 msg(_("W18: Invalid character in group name"));
3446 break;
3447 }
3448 }
3449
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003450 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003451 if (highlight_ga.ga_data == NULL)
3452 {
3453 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3454 highlight_ga.ga_growsize = 10;
3455 }
3456
3457 if (highlight_ga.ga_len >= MAX_HL_ID)
3458 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003459 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003460 vim_free(name);
3461 return 0;
3462 }
3463
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003464 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003465 if (ga_grow(&highlight_ga, 1) == FAIL)
3466 {
3467 vim_free(name);
3468 return 0;
3469 }
3470
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003471 name_up = vim_strsave_up(name);
3472 if (name_up == NULL)
3473 {
3474 vim_free(name);
3475 return 0;
3476 }
3477
Bram Moolenaara80faa82020-04-12 19:37:17 +02003478 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003479 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003480 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003481#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3482 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3483 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003484 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003485#endif
3486 ++highlight_ga.ga_len;
3487
3488 return highlight_ga.ga_len; // ID is index plus one
3489}
3490
3491/*
3492 * When, just after calling syn_add_group(), an error is discovered, this
3493 * function deletes the new name.
3494 */
3495 static void
3496syn_unadd_group(void)
3497{
3498 --highlight_ga.ga_len;
3499 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3500 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3501}
3502
3503/*
3504 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003505 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003506 */
3507 int
3508syn_id2attr(int hl_id)
3509{
3510 int attr;
3511 hl_group_T *sgp;
3512
3513 hl_id = syn_get_final_id(hl_id);
3514 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3515
3516#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003517 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003518 if (gui.in_use)
3519 attr = sgp->sg_gui_attr;
3520 else
3521#endif
3522 if (IS_CTERM)
3523 attr = sgp->sg_cterm_attr;
3524 else
3525 attr = sgp->sg_term_attr;
3526
3527 return attr;
3528}
3529
3530#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3531/*
3532 * Get the GUI colors and attributes for a group ID.
3533 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3534 */
3535 int
3536syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3537{
3538 hl_group_T *sgp;
3539
3540 hl_id = syn_get_final_id(hl_id);
3541 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3542
3543 *fgp = sgp->sg_gui_fg;
3544 *bgp = sgp->sg_gui_bg;
3545 return sgp->sg_gui;
3546}
3547#endif
3548
3549#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003550 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3551 && defined(FEAT_TERMGUICOLORS)) \
3552 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003553 void
3554syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3555{
3556 hl_group_T *sgp;
3557
3558 hl_id = syn_get_final_id(hl_id);
3559 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3560 *fgp = sgp->sg_cterm_fg - 1;
3561 *bgp = sgp->sg_cterm_bg - 1;
3562}
3563#endif
3564
3565/*
3566 * Translate a group ID to the final group ID (following links).
3567 */
3568 int
3569syn_get_final_id(int hl_id)
3570{
3571 int count;
3572 hl_group_T *sgp;
3573
3574 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3575 return 0; // Can be called from eval!!
3576
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003577 // Follow links until there is no more.
3578 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003579 for (count = 100; --count >= 0; )
3580 {
3581 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3582 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3583 break;
3584 hl_id = sgp->sg_link;
3585 }
3586
3587 return hl_id;
3588}
3589
3590#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3591/*
3592 * Call this function just after the GUI has started.
3593 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3594 * It finds the font and color handles for the highlighting groups.
3595 */
3596 void
3597highlight_gui_started(void)
3598{
3599 int idx;
3600
3601 // First get the colors from the "Normal" and "Menu" group, if set
3602 if (USE_24BIT)
3603 set_normal_colors();
3604
3605 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3606 gui_do_one_color(idx, FALSE, FALSE);
3607
3608 highlight_changed();
3609}
3610
3611 static void
3612gui_do_one_color(
3613 int idx,
3614 int do_menu UNUSED, // TRUE: might set the menu font
3615 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3616{
3617 int didit = FALSE;
3618
3619# ifdef FEAT_GUI
3620# ifdef FEAT_TERMGUICOLORS
3621 if (gui.in_use)
3622# endif
3623 if (HL_TABLE()[idx].sg_font_name != NULL)
3624 {
3625 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3626 do_tooltip, TRUE);
3627 didit = TRUE;
3628 }
3629# endif
3630 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3631 {
3632 HL_TABLE()[idx].sg_gui_fg =
3633 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3634 didit = TRUE;
3635 }
3636 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3637 {
3638 HL_TABLE()[idx].sg_gui_bg =
3639 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3640 didit = TRUE;
3641 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003642 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3643 {
3644 HL_TABLE()[idx].sg_gui_sp =
3645 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3646 didit = TRUE;
3647 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003648 if (didit) // need to get a new attr number
3649 set_hl_attr(idx);
3650}
3651#endif
3652
3653#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3654/*
3655 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3656 */
3657 static void
3658combine_stl_hlt(
3659 int id,
3660 int id_S,
3661 int id_alt,
3662 int hlcnt,
3663 int i,
3664 int hlf,
3665 int *table)
3666{
3667 hl_group_T *hlt = HL_TABLE();
3668
3669 if (id_alt == 0)
3670 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003671 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003672 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3673 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3674# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3675 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3676# endif
3677 }
3678 else
3679 mch_memmove(&hlt[hlcnt + i],
3680 &hlt[id_alt - 1],
3681 sizeof(hl_group_T));
3682 hlt[hlcnt + i].sg_link = 0;
3683
3684 hlt[hlcnt + i].sg_term ^=
3685 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3686 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3687 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3688 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3689 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3690 hlt[hlcnt + i].sg_cterm ^=
3691 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3692 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3693 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3694 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3695 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3696# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3697 hlt[hlcnt + i].sg_gui ^=
3698 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3699# endif
3700# ifdef FEAT_GUI
3701 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3702 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3703 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3704 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3705 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3706 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3707 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3708 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3709# ifdef FEAT_XFONTSET
3710 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3711 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3712# endif
3713# endif
3714 highlight_ga.ga_len = hlcnt + i + 1;
3715 set_hl_attr(hlcnt + i); // At long last we can apply
3716 table[i] = syn_id2attr(hlcnt + i + 1);
3717}
3718#endif
3719
3720/*
3721 * Translate the 'highlight' option into attributes in highlight_attr[] and
3722 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3723 * corresponding highlights to use on top of HLF_SNC is computed.
3724 * Called only when the 'highlight' option has been changed and upon first
3725 * screen redraw after any :highlight command.
3726 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3727 */
3728 int
3729highlight_changed(void)
3730{
3731 int hlf;
3732 int i;
3733 char_u *p;
3734 int attr;
3735 char_u *end;
3736 int id;
3737#ifdef USER_HIGHLIGHT
3738 char_u userhl[30]; // use 30 to avoid compiler warning
3739# ifdef FEAT_STL_OPT
3740 int id_S = -1;
3741 int id_SNC = 0;
3742# ifdef FEAT_TERMINAL
3743 int id_ST = 0;
3744 int id_STNC = 0;
3745# endif
3746 int hlcnt;
3747# endif
3748#endif
3749 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3750
3751 need_highlight_changed = FALSE;
3752
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003753#ifdef FEAT_TERMINAL
3754 term_update_colors_all();
3755 term_update_wincolor_all();
3756#endif
3757
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003758 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003759 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3760 highlight_attr[hlf] = 0;
3761
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003762 // First set all attributes to their default value.
3763 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003764 for (i = 0; i < 2; ++i)
3765 {
3766 if (i)
3767 p = p_hl;
3768 else
3769 p = get_highlight_default();
3770 if (p == NULL) // just in case
3771 continue;
3772
3773 while (*p)
3774 {
3775 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3776 if (hl_flags[hlf] == *p)
3777 break;
3778 ++p;
3779 if (hlf == (int)HLF_COUNT || *p == NUL)
3780 return FAIL;
3781
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003782 // Allow several hl_flags to be combined, like "bu" for
3783 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003784 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003785 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003786 {
3787 if (VIM_ISWHITE(*p)) // ignore white space
3788 continue;
3789
3790 if (attr > HL_ALL) // Combination with ':' is not allowed.
3791 return FAIL;
3792
3793 switch (*p)
3794 {
3795 case 'b': attr |= HL_BOLD;
3796 break;
3797 case 'i': attr |= HL_ITALIC;
3798 break;
3799 case '-':
3800 case 'n': // no highlighting
3801 break;
3802 case 'r': attr |= HL_INVERSE;
3803 break;
3804 case 's': attr |= HL_STANDOUT;
3805 break;
3806 case 'u': attr |= HL_UNDERLINE;
3807 break;
3808 case 'c': attr |= HL_UNDERCURL;
3809 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003810 case '2': attr |= HL_UNDERDOUBLE;
3811 break;
3812 case 'd': attr |= HL_UNDERDOTTED;
3813 break;
3814 case '=': attr |= HL_UNDERDASHED;
3815 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003816 case 't': attr |= HL_STRIKETHROUGH;
3817 break;
3818 case ':': ++p; // highlight group name
3819 if (attr || *p == NUL) // no combinations
3820 return FAIL;
3821 end = vim_strchr(p, ',');
3822 if (end == NULL)
3823 end = p + STRLEN(p);
3824 id = syn_check_group(p, (int)(end - p));
3825 if (id == 0)
3826 return FAIL;
3827 attr = syn_id2attr(id);
3828 p = end - 1;
3829#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3830 if (hlf == (int)HLF_SNC)
3831 id_SNC = syn_get_final_id(id);
3832# ifdef FEAT_TERMINAL
3833 else if (hlf == (int)HLF_ST)
3834 id_ST = syn_get_final_id(id);
3835 else if (hlf == (int)HLF_STNC)
3836 id_STNC = syn_get_final_id(id);
3837# endif
3838 else if (hlf == (int)HLF_S)
3839 id_S = syn_get_final_id(id);
3840#endif
3841 break;
3842 default: return FAIL;
3843 }
3844 }
3845 highlight_attr[hlf] = attr;
3846
3847 p = skip_to_option_part(p); // skip comma and spaces
3848 }
3849 }
3850
3851#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003852 // Setup the user highlights
3853 //
3854 // Temporarily utilize 28 more hl entries:
3855 // 9 for User1-User9 combined with StatusLineNC
3856 // 9 for User1-User9 combined with StatusLineTerm
3857 // 9 for User1-User9 combined with StatusLineTermNC
3858 // 1 for StatusLine default
3859 // Have to be in there simultaneously in case of table overflows in
3860 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003861# ifdef FEAT_STL_OPT
3862 if (ga_grow(&highlight_ga, 28) == FAIL)
3863 return FAIL;
3864 hlcnt = highlight_ga.ga_len;
3865 if (id_S == -1)
3866 {
3867 // Make sure id_S is always valid to simplify code below. Use the last
3868 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003869 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003870 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3871 id_S = hlcnt + 19;
3872 }
3873# endif
3874 for (i = 0; i < 9; i++)
3875 {
3876 sprintf((char *)userhl, "User%d", i + 1);
3877 id = syn_name2id(userhl);
3878 if (id == 0)
3879 {
3880 highlight_user[i] = 0;
3881# ifdef FEAT_STL_OPT
3882 highlight_stlnc[i] = 0;
3883# ifdef FEAT_TERMINAL
3884 highlight_stlterm[i] = 0;
3885 highlight_stltermnc[i] = 0;
3886# endif
3887# endif
3888 }
3889 else
3890 {
3891 highlight_user[i] = syn_id2attr(id);
3892# ifdef FEAT_STL_OPT
3893 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3894 HLF_SNC, highlight_stlnc);
3895# ifdef FEAT_TERMINAL
3896 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3897 HLF_ST, highlight_stlterm);
3898 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3899 HLF_STNC, highlight_stltermnc);
3900# endif
3901# endif
3902 }
3903 }
3904# ifdef FEAT_STL_OPT
3905 highlight_ga.ga_len = hlcnt;
3906# endif
3907
3908#endif // USER_HIGHLIGHT
3909
3910 return OK;
3911}
3912
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003913static void highlight_list(void);
3914static void highlight_list_two(int cnt, int attr);
3915
3916/*
3917 * Handle command line completion for :highlight command.
3918 */
3919 void
3920set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3921{
3922 char_u *p;
3923
3924 // Default: expand group names
3925 xp->xp_context = EXPAND_HIGHLIGHT;
3926 xp->xp_pattern = arg;
3927 include_link = 2;
3928 include_default = 1;
3929
3930 // (part of) subcommand already typed
3931 if (*arg != NUL)
3932 {
3933 p = skiptowhite(arg);
3934 if (*p != NUL) // past "default" or group name
3935 {
3936 include_default = 0;
3937 if (STRNCMP("default", arg, p - arg) == 0)
3938 {
3939 arg = skipwhite(p);
3940 xp->xp_pattern = arg;
3941 p = skiptowhite(arg);
3942 }
3943 if (*p != NUL) // past group name
3944 {
3945 include_link = 0;
3946 if (arg[1] == 'i' && arg[0] == 'N')
3947 highlight_list();
3948 if (STRNCMP("link", arg, p - arg) == 0
3949 || STRNCMP("clear", arg, p - arg) == 0)
3950 {
3951 xp->xp_pattern = skipwhite(p);
3952 p = skiptowhite(xp->xp_pattern);
3953 if (*p != NUL) // past first group name
3954 {
3955 xp->xp_pattern = skipwhite(p);
3956 p = skiptowhite(xp->xp_pattern);
3957 }
3958 }
3959 if (*p != NUL) // past group name(s)
3960 xp->xp_context = EXPAND_NOTHING;
3961 }
3962 }
3963 }
3964}
3965
3966/*
3967 * List highlighting matches in a nice way.
3968 */
3969 static void
3970highlight_list(void)
3971{
3972 int i;
3973
3974 for (i = 10; --i >= 0; )
3975 highlight_list_two(i, HL_ATTR(HLF_D));
3976 for (i = 40; --i >= 0; )
3977 highlight_list_two(99, 0);
3978}
3979
3980 static void
3981highlight_list_two(int cnt, int attr)
3982{
3983 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3984 msg_clr_eos();
3985 out_flush();
3986 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3987}
3988
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003989/*
3990 * Function given to ExpandGeneric() to obtain the list of group names.
3991 */
3992 char_u *
3993get_highlight_name(expand_T *xp UNUSED, int idx)
3994{
3995 return get_highlight_name_ext(xp, idx, TRUE);
3996}
3997
3998/*
3999 * Obtain a highlight group name.
4000 * When "skip_cleared" is TRUE don't return a cleared entry.
4001 */
4002 char_u *
4003get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4004{
4005 if (idx < 0)
4006 return NULL;
4007
4008 // Items are never removed from the table, skip the ones that were
4009 // cleared.
4010 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4011 return (char_u *)"";
4012
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004013 if (idx == highlight_ga.ga_len && include_none != 0)
4014 return (char_u *)"none";
4015 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4016 return (char_u *)"default";
4017 if (idx == highlight_ga.ga_len + include_none + include_default
4018 && include_link != 0)
4019 return (char_u *)"link";
4020 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4021 && include_link != 0)
4022 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004023 if (idx >= highlight_ga.ga_len)
4024 return NULL;
4025 return HL_TABLE()[idx].sg_name;
4026}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004027
4028#if defined(FEAT_GUI) || defined(PROTO)
4029/*
4030 * Free all the highlight group fonts.
4031 * Used when quitting for systems which need it.
4032 */
4033 void
4034free_highlight_fonts(void)
4035{
4036 int idx;
4037
4038 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4039 {
4040 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4041 HL_TABLE()[idx].sg_font = NOFONT;
4042# ifdef FEAT_XFONTSET
4043 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4044 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4045# endif
4046 }
4047
4048 gui_mch_free_font(gui.norm_font);
4049# ifdef FEAT_XFONTSET
4050 gui_mch_free_fontset(gui.fontset);
4051# endif
4052# ifndef FEAT_GUI_GTK
4053 gui_mch_free_font(gui.bold_font);
4054 gui_mch_free_font(gui.ital_font);
4055 gui_mch_free_font(gui.boldital_font);
4056# endif
4057}
4058#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004059
4060#if defined(FEAT_EVAL) || defined(PROTO)
4061/*
4062 * Convert each of the highlight attribute bits (bold, standout, underline,
4063 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4064 * the attribute name as the key.
4065 */
4066 static dict_T *
4067highlight_get_attr_dict(int hlattr)
4068{
4069 dict_T *dict;
4070 int i;
4071
4072 dict = dict_alloc();
4073 if (dict == NULL)
4074 return NULL;
4075
4076 for (i = 0; hl_attr_table[i] != 0; ++i)
4077 {
4078 if (hlattr & hl_attr_table[i])
4079 {
4080 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4081 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4082 }
4083 }
4084
4085 return dict;
4086}
4087
4088/*
4089 * Return the attributes of the highlight group at index 'hl_idx' as a
4090 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4091 * links recursively.
4092 */
4093 static dict_T *
4094highlight_get_info(int hl_idx, int resolve_link)
4095{
4096 dict_T *dict;
4097 hl_group_T *sgp;
4098 dict_T *attr_dict;
4099 int hlgid;
4100
4101 dict = dict_alloc();
4102 if (dict == NULL)
4103 return dict;
4104
4105 sgp = &HL_TABLE()[hl_idx];
4106 // highlight group id is 1-based
4107 hlgid = hl_idx + 1;
4108
4109 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4110 goto error;
4111 if (dict_add_number(dict, "id", hlgid) == FAIL)
4112 goto error;
4113
4114 if (sgp->sg_link && resolve_link)
4115 {
4116 // resolve the highlight group link recursively
4117 while (sgp->sg_link)
4118 {
4119 hlgid = sgp->sg_link;
4120 sgp = &HL_TABLE()[sgp->sg_link - 1];
4121 }
4122 }
4123
4124 if (sgp->sg_term != 0)
4125 {
4126 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4127 if (attr_dict != NULL)
4128 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4129 goto error;
4130 }
4131 if (sgp->sg_start != NULL)
4132 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4133 goto error;
4134 if (sgp->sg_stop != NULL)
4135 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4136 goto error;
4137 if (sgp->sg_cterm != 0)
4138 {
4139 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4140 if (attr_dict != NULL)
4141 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4142 goto error;
4143 }
4144 if (sgp->sg_cterm_fg != 0)
4145 if (dict_add_string(dict, "ctermfg",
4146 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4147 goto error;
4148 if (sgp->sg_cterm_bg != 0)
4149 if (dict_add_string(dict, "ctermbg",
4150 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4151 goto error;
4152 if (sgp->sg_cterm_ul != 0)
4153 if (dict_add_string(dict, "ctermul",
4154 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4155 goto error;
4156 if (sgp->sg_gui != 0)
4157 {
4158 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4159 if (attr_dict != NULL)
4160 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4161 goto error;
4162 }
4163 if (sgp->sg_gui_fg_name != NULL)
4164 if (dict_add_string(dict, "guifg",
4165 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4166 goto error;
4167 if (sgp->sg_gui_bg_name != NULL)
4168 if (dict_add_string(dict, "guibg",
4169 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4170 goto error;
4171 if (sgp->sg_gui_sp_name != NULL)
4172 if (dict_add_string(dict, "guisp",
4173 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4174 goto error;
4175# ifdef FEAT_GUI
4176 if (sgp->sg_font_name != NULL)
4177 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4178 goto error;
4179# endif
4180 if (sgp->sg_link)
4181 {
4182 char_u *link;
4183
4184 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4185 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4186 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004187
4188 if (sgp->sg_deflink)
4189 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004190 }
4191 if (dict_len(dict) == 2)
4192 // If only 'name' is present, then the highlight group is cleared.
4193 dict_add_bool(dict, "cleared", VVAL_TRUE);
4194
4195 return dict;
4196
4197error:
4198 vim_free(dict);
4199 return NULL;
4200}
4201
4202/*
4203 * "hlget([name])" function
4204 * Return the attributes of a specific highlight group (if specified) or all
4205 * the highlight groups.
4206 */
4207 void
4208f_hlget(typval_T *argvars, typval_T *rettv)
4209{
4210 list_T *list;
4211 dict_T *dict;
4212 int i;
4213 char_u *hlarg = NULL;
4214 int resolve_link = FALSE;
4215
4216 if (rettv_list_alloc(rettv) == FAIL)
4217 return;
4218
4219 if (check_for_opt_string_arg(argvars, 0) == FAIL
4220 || (argvars[0].v_type != VAR_UNKNOWN
4221 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4222 return;
4223
4224 if (argvars[0].v_type != VAR_UNKNOWN)
4225 {
4226 // highlight group name supplied
4227 hlarg = tv_get_string_chk(&argvars[0]);
4228 if (hlarg == NULL)
4229 return;
4230
4231 if (argvars[1].v_type != VAR_UNKNOWN)
4232 {
4233 int error = FALSE;
4234
4235 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4236 if (error)
4237 return;
4238 }
4239 }
4240
4241 list = rettv->vval.v_list;
4242 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4243 {
4244 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4245 {
4246 dict = highlight_get_info(i, resolve_link);
4247 if (dict != NULL)
4248 list_append_dict(list, dict);
4249 }
4250 }
4251}
4252
4253/*
4254 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4255 * 'dict' or the value is not a string type. If the value is not a string type
4256 * or is NULL, then 'error' is set to TRUE.
4257 */
4258 static char_u *
4259hldict_get_string(dict_T *dict, char_u *key, int *error)
4260{
4261 dictitem_T *di;
4262
4263 *error = FALSE;
4264 di = dict_find(dict, key, -1);
4265 if (di == NULL)
4266 return NULL;
4267
4268 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4269 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004270 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004271 *error = TRUE;
4272 return NULL;
4273 }
4274
4275 return di->di_tv.vval.v_string;
4276}
4277
4278/*
4279 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4280 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4281 * Dictionary or is NULL.
4282 */
4283 static int
4284hldict_attr_to_str(
4285 dict_T *dict,
4286 char_u *key,
4287 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004288 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004289{
4290 dictitem_T *di;
4291 dict_T *attrdict;
4292 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004293 char_u *p;
4294 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004295
4296 attr_str[0] = NUL;
4297 di = dict_find(dict, key, -1);
4298 if (di == NULL)
4299 return TRUE;
4300
4301 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4302 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004303 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004304 return FALSE;
4305 }
4306
4307 attrdict = di->di_tv.vval.v_dict;
4308
4309 // If the attribute dict is empty, then return NONE to clear the attributes
4310 if (dict_len(attrdict) == 0)
4311 {
4312 vim_strcat(attr_str, (char_u *)"NONE", len);
4313 return TRUE;
4314 }
4315
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004316 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004317 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4318 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01004319 if (dict_get_bool(attrdict, hl_name_table[i], VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004320 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004321 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4322 STRCPY(p, (char_u *)",");
4323 sz = STRLEN(hl_name_table[i]);
4324 if (p - attr_str + sz + 1 < len)
4325 {
4326 STRCPY(p, (char_u *)hl_name_table[i]);
4327 p += sz;
4328 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004329 }
4330 }
4331
4332 return TRUE;
4333}
4334
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004335// Temporary buffer used to store the command string produced by hlset().
4336// IObuff cannot be used for this as the error messages produced by hlset()
4337// internally use IObuff.
4338#define HLSETBUFSZ 512
4339static char_u hlsetBuf[HLSETBUFSZ + 1];
4340
4341/*
4342 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4343 * "dptr", which points into "hlsetBuf".
4344 * Returns the updated pointer.
4345 */
4346 static char_u *
4347add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4348{
4349 size_t vallen;
4350
4351 // Do nothing if the value is not specified or is empty
4352 if (value == NULL || *value == NUL)
4353 return dptr;
4354
4355 vallen = STRLEN(value);
4356 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4357 {
4358 STRCPY(dptr, attr);
4359 dptr += attrlen;
4360 STRCPY(dptr, value);
4361 dptr += vallen;
4362 }
4363
4364 return dptr;
4365}
4366
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004367/*
4368 * Add or update a highlight group using 'dict' items. Returns TRUE if
4369 * successfully updated the highlight group.
4370 */
4371 static int
4372hlg_add_or_update(dict_T *dict)
4373{
4374 char_u *name;
4375 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004376 char_u term_attr[MAX_ATTR_LEN];
4377 char_u cterm_attr[MAX_ATTR_LEN];
4378 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004379 char_u *start;
4380 char_u *stop;
4381 char_u *ctermfg;
4382 char_u *ctermbg;
4383 char_u *ctermul;
4384 char_u *guifg;
4385 char_u *guibg;
4386 char_u *guisp;
4387# ifdef FEAT_GUI
4388 char_u *font;
4389# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004390 int forceit = FALSE;
4391 int dodefault = FALSE;
4392 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004393 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004394
4395 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004396 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004397 return FALSE;
4398
Bram Moolenaard61efa52022-07-23 09:52:04 +01004399 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004400 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004401
Bram Moolenaard61efa52022-07-23 09:52:04 +01004402 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004403 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004404
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004405 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004406 {
4407 varnumber_T cleared;
4408
4409 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004410 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004411 if (cleared == TRUE)
4412 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004413 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4414 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004415 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004416 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004417 }
4418
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004419 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004420 {
4421 char_u *linksto;
4422
4423 // link highlight groups
4424 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004425 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004426 return FALSE;
4427
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004428 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004429 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004430 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004431
4432 done = TRUE;
4433 }
4434
4435 // If 'cleared' or 'linksto' are specified, then don't process the other
4436 // attributes.
4437 if (done)
4438 return TRUE;
4439
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004440 start = hldict_get_string(dict, (char_u *)"start", &error);
4441 if (error)
4442 return FALSE;
4443
4444 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4445 if (error)
4446 return FALSE;
4447
4448 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004449 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004450 return FALSE;
4451
4452 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004453 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004454 return FALSE;
4455
4456 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4457 if (error)
4458 return FALSE;
4459
4460 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4461 if (error)
4462 return FALSE;
4463
4464 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4465 if (error)
4466 return FALSE;
4467
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004468 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004469 return FALSE;
4470
4471 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4472 if (error)
4473 return FALSE;
4474
4475 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4476 if (error)
4477 return FALSE;
4478
4479 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4480 if (error)
4481 return FALSE;
4482
4483# ifdef FEAT_GUI
4484 font = hldict_get_string(dict, (char_u *)"font", &error);
4485 if (error)
4486 return FALSE;
4487# endif
4488
4489 // If none of the attributes are specified, then do nothing.
4490 if (term_attr[0] == NUL && start == NULL && stop == NULL
4491 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4492 && ctermul == NULL && gui_attr[0] == NUL
4493# ifdef FEAT_GUI
4494 && font == NULL
4495# endif
4496 && guifg == NULL && guibg == NULL && guisp == NULL
4497 )
4498 return TRUE;
4499
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004500 hlsetBuf[0] = NUL;
4501 p = hlsetBuf;
4502 if (dodefault)
4503 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4504 p = add_attr_and_value(p, (char_u *)"", 0, name);
4505 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4506 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4507 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4508 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4509 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4510 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4511 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4512 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004513# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004514 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004515# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004516 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4517 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004518 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004519
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004520 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004521
4522 return TRUE;
4523}
4524
4525/*
4526 * "hlset([{highlight_attr}])" function
4527 * Add or modify highlight groups
4528 */
4529 void
4530f_hlset(typval_T *argvars, typval_T *rettv)
4531{
4532 listitem_T *li;
4533 dict_T *dict;
4534
4535 rettv->vval.v_number = -1;
4536
4537 if (check_for_list_arg(argvars, 0) == FAIL)
4538 return;
4539
4540 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4541 {
4542 if (li->li_tv.v_type != VAR_DICT)
4543 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004544 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004545 return;
4546 }
4547
4548 dict = li->li_tv.vval.v_dict;
4549 if (!hlg_add_or_update(dict))
4550 return;
4551 }
4552
4553 rettv->vval.v_number = 0;
4554}
4555#endif