blob: 3a12312fc52fa4faf3e1d22634e8c0db9fc6e0e7 [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);
510 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
511 }
512 recursive = FALSE;
513
514 return retval;
515}
516
517static char *(color_names[28]) = {
518 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
519 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
520 "Gray", "Grey", "LightGray", "LightGrey",
521 "DarkGray", "DarkGrey",
522 "Blue", "LightBlue", "Green", "LightGreen",
523 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
524 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
525 // indices:
526 // 0, 1, 2, 3,
527 // 4, 5, 6, 7,
528 // 8, 9, 10, 11,
529 // 12, 13,
530 // 14, 15, 16, 17,
531 // 18, 19, 20, 21, 22,
532 // 23, 24, 25, 26, 27
533static int color_numbers_16[28] = {0, 1, 2, 3,
534 4, 5, 6, 6,
535 7, 7, 7, 7,
536 8, 8,
537 9, 9, 10, 10,
538 11, 11, 12, 12, 13,
539 13, 14, 14, 15, -1};
540// for xterm with 88 colors...
541static int color_numbers_88[28] = {0, 4, 2, 6,
542 1, 5, 32, 72,
543 84, 84, 7, 7,
544 82, 82,
545 12, 43, 10, 61,
546 14, 63, 9, 74, 13,
547 75, 11, 78, 15, -1};
548// for xterm with 256 colors...
549static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200550 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200551 248, 248, 7, 7,
552 242, 242,
553 12, 81, 10, 121,
554 14, 159, 9, 224, 13,
555 225, 11, 229, 15, -1};
556// for terminals with less than 16 colors...
557static int color_numbers_8[28] = {0, 4, 2, 6,
558 1, 5, 3, 3,
559 7, 7, 7, 7,
560 0+8, 0+8,
561 4+8, 4+8, 2+8, 2+8,
562 6+8, 6+8, 1+8, 1+8, 5+8,
563 5+8, 3+8, 3+8, 7+8, -1};
564
565/*
566 * Lookup the "cterm" value to be used for color with index "idx" in
567 * color_names[].
568 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
569 * colors, otherwise it will be unchanged.
570 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100571 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200572lookup_color(int idx, int foreground, int *boldp)
573{
574 int color = color_numbers_16[idx];
575 char_u *p;
576
577 // Use the _16 table to check if it's a valid color name.
578 if (color < 0)
579 return -1;
580
581 if (t_colors == 8)
582 {
583 // t_Co is 8: use the 8 colors table
584#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100585 // On qnx, the 8 & 16 color arrays are the same
586 if (STRNCMP(T_NAME, "qansi", 5) == 0)
587 color = color_numbers_16[idx];
588 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200589#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100590 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200591 if (foreground)
592 {
593 // set/reset bold attribute to get light foreground
594 // colors (on some terminals, e.g. "linux")
595 if (color & 8)
596 *boldp = TRUE;
597 else
598 *boldp = FALSE;
599 }
600 color &= 7; // truncate to 8 colors
601 }
602 else if (t_colors == 16 || t_colors == 88
603 || t_colors >= 256)
604 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100605 // Guess: if the termcap entry ends in 'm', it is
606 // probably an xterm-like terminal. Use the changed
607 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200608 if (*T_CAF != NUL)
609 p = T_CAF;
610 else
611 p = T_CSF;
612 if (*p != NUL && (t_colors > 256
613 || *(p + STRLEN(p) - 1) == 'm'))
614 {
615 if (t_colors == 88)
616 color = color_numbers_88[idx];
617 else if (t_colors >= 256)
618 color = color_numbers_256[idx];
619 else
620 color = color_numbers_8[idx];
621 }
622#ifdef FEAT_TERMRESPONSE
623 if (t_colors >= 256 && color == 15 && is_mac_terminal)
624 // Terminal.app has a bug: 15 is light grey. Use white
625 // from the color cube instead.
626 color = 231;
627#endif
628 }
629 return color;
630}
631
632/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100633 * Link highlight group 'from_hg' to 'to_hg'.
634 * 'dodefault' is set to TRUE for ":highlight default link".
635 * 'forceit' is set to TRUE for ":higlight! link"
636 * 'init' is set to TRUE when initializing all the highlight groups.
637 */
638 static void
639highlight_group_link(
640 char_u *from_hg,
641 int from_len,
642 char_u *to_hg,
643 int to_len,
644 int dodefault,
645 int forceit,
646 int init)
647{
648 int from_id;
649 int to_id;
650 hl_group_T *hlgroup = NULL;
651
652 from_id = syn_check_group(from_hg, from_len);
653 if (STRNCMP(to_hg, "NONE", 4) == 0)
654 to_id = 0;
655 else
656 to_id = syn_check_group(to_hg, to_len);
657
658 if (from_id > 0)
659 {
660 hlgroup = &HL_TABLE()[from_id - 1];
661 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
662 {
663 hlgroup->sg_deflink = to_id;
664#ifdef FEAT_EVAL
665 hlgroup->sg_deflink_sctx = current_sctx;
666 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
667#endif
668 }
669 }
670
671 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
672 {
673 // Don't allow a link when there already is some highlighting
674 // for the group, unless '!' is used
675 if (to_id > 0 && !forceit && !init
676 && hl_has_settings(from_id - 1, dodefault))
677 {
678 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000679 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100680 }
681 else if (hlgroup->sg_link != to_id
682#ifdef FEAT_EVAL
683 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
684#endif
685 || hlgroup->sg_cleared)
686 {
687 if (!init)
688 hlgroup->sg_set |= SG_LINK;
689 hlgroup->sg_link = to_id;
690#ifdef FEAT_EVAL
691 hlgroup->sg_script_ctx = current_sctx;
692 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
693#endif
694 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100695 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100696
697 // Only call highlight_changed() once after multiple changes.
698 need_highlight_changed = TRUE;
699 }
700 }
701
702}
703
704/*
705 * Reset all highlighting to the defaults. Removes all highlighting for the
706 * groups added by the user.
707 */
708 static void
709highlight_reset_all(void)
710{
711 int idx;
712
713#ifdef FEAT_GUI
714 // First, we do not destroy the old values, but allocate the new
715 // ones and update the display. THEN we destroy the old values.
716 // If we destroy the old values first, then the old values
717 // (such as GuiFont's or GuiFontset's) will still be displayed but
718 // invalid because they were free'd.
719 if (gui.in_use)
720 {
721# ifdef FEAT_BEVAL_TIP
722 gui_init_tooltip_font();
723# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100724# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100725 gui_init_menu_font();
726# endif
727 }
728# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
729 gui_mch_def_colors();
730# endif
731# ifdef FEAT_GUI_X11
732# ifdef FEAT_MENU
733
734 // This only needs to be done when there is no Menu highlight
735 // group defined by default, which IS currently the case.
736 gui_mch_new_menu_colors();
737# endif
738 if (gui.in_use)
739 {
740 gui_new_scrollbar_colors();
741# ifdef FEAT_BEVAL_GUI
742 gui_mch_new_tooltip_colors();
743# endif
744# ifdef FEAT_MENU
745 gui_mch_new_menu_font();
746# endif
747 }
748# endif
749
750 // Ok, we're done allocating the new default graphics items.
751 // The screen should already be refreshed at this point.
752 // It is now Ok to clear out the old data.
753#endif
754#ifdef FEAT_EVAL
755 do_unlet((char_u *)"g:colors_name", TRUE);
756#endif
757 restore_cterm_colors();
758
759 // Clear all default highlight groups and load the defaults.
760 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
761 highlight_clear(idx);
762 init_highlight(TRUE, TRUE);
763#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
764 if (USE_24BIT)
765 highlight_gui_started();
766 else
767#endif
768 highlight_changed();
769 redraw_later_clear();
770}
771
772/*
773 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
774 * index 'idx'.
775 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
776 * 'arg' is the list of attribute names separated by comma.
777 * 'init' is set to TRUE when initializing all the highlight groups.
778 * Returns TRUE if the attributes are set.
779 */
780 static int
781highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
782{
783 int attr;
784 int off;
785 long i;
786 int len;
787
788 attr = 0;
789 off = 0;
790 while (arg[off] != NUL)
791 {
792 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
793 {
794 len = (int)STRLEN(hl_name_table[i]);
795 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
796 {
797 attr |= hl_attr_table[i];
798 off += len;
799 break;
800 }
801 }
802 if (i < 0)
803 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000804 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100805 return FALSE;
806 }
807 if (arg[off] == ',') // another one follows
808 ++off;
809 }
810 if (*key == 'T')
811 {
812 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
813 {
814 if (!init)
815 HL_TABLE()[idx].sg_set |= SG_TERM;
816 HL_TABLE()[idx].sg_term = attr;
817 }
818 }
819 else if (*key == 'C')
820 {
821 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
822 {
823 if (!init)
824 HL_TABLE()[idx].sg_set |= SG_CTERM;
825 HL_TABLE()[idx].sg_cterm = attr;
826 HL_TABLE()[idx].sg_cterm_bold = FALSE;
827 }
828 }
829#if defined(FEAT_GUI) || defined(FEAT_EVAL)
830 else
831 {
832 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
833 {
834 if (!init)
835 HL_TABLE()[idx].sg_set |= SG_GUI;
836 HL_TABLE()[idx].sg_gui = attr;
837 }
838 }
839#endif
840
841 return TRUE;
842}
843
844#ifdef FEAT_GUI
845/*
846 * Set the font for the highlight group at 'idx'.
847 * 'arg' is the font name.
848 * Returns TRUE if the font is changed.
849 */
850 static int
851highlight_set_font(
852 int idx,
853 char_u *arg,
854 int is_normal_group,
855 int is_menu_group,
856 int is_tooltip_group)
857{
858 int did_change = FALSE;
859
860 // in non-GUI fonts are simply ignored
861 if (HL_TABLE()[idx].sg_font_name != NULL
862 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
863 {
864 // Font name didn't change, ignore.
865 }
866 else if (!gui.shell_created)
867 {
868 // GUI not started yet, always accept the name.
869 vim_free(HL_TABLE()[idx].sg_font_name);
870 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
871 did_change = TRUE;
872 }
873 else
874 {
875 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
876# ifdef FEAT_XFONTSET
877 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
878# endif
879 // First, save the current font/fontset.
880 // Then try to allocate the font/fontset.
881 // If the allocation fails, HL_TABLE()[idx].sg_font OR
882 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
883
884 HL_TABLE()[idx].sg_font = NOFONT;
885# ifdef FEAT_XFONTSET
886 HL_TABLE()[idx].sg_fontset = NOFONTSET;
887# endif
888 hl_do_font(idx, arg, is_normal_group, is_menu_group,
889 is_tooltip_group, FALSE);
890
891# ifdef FEAT_XFONTSET
892 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
893 {
894 // New fontset was accepted. Free the old one, if there
895 // was one.
896 gui_mch_free_fontset(temp_sg_fontset);
897 vim_free(HL_TABLE()[idx].sg_font_name);
898 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
899 did_change = TRUE;
900 }
901 else
902 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
903# endif
904 if (HL_TABLE()[idx].sg_font != NOFONT)
905 {
906 // New font was accepted. Free the old one, if there was
907 // one.
908 gui_mch_free_font(temp_sg_font);
909 vim_free(HL_TABLE()[idx].sg_font_name);
910 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
911 did_change = TRUE;
912 }
913 else
914 HL_TABLE()[idx].sg_font = temp_sg_font;
915 }
916
917 return did_change;
918}
919#endif
920
921/*
922 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
923 * Returns TRUE if the foreground color is set.
924 */
925 static void
926highlight_set_ctermfg(int idx, int color, int is_normal_group)
927{
928 HL_TABLE()[idx].sg_cterm_fg = color + 1;
929 if (is_normal_group)
930 {
931 cterm_normal_fg_color = color + 1;
932 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
933#ifdef FEAT_GUI
934 // Don't do this if the GUI is used.
935 if (!gui.in_use && !gui.starting)
936#endif
937 {
Bram Moolenaar471c0fa2022-08-22 15:19:16 +0100938 set_must_redraw(UPD_CLEAR);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100939 if (termcap_active && color >= 0)
940 term_fg_color(color);
941 }
942 }
943}
944
945/*
946 * Set the cterm background color for the highlight group at 'idx' to 'color'.
947 * Returns TRUE if the background color is set.
948 */
949 static void
950highlight_set_ctermbg(int idx, int color, int is_normal_group)
951{
952 HL_TABLE()[idx].sg_cterm_bg = color + 1;
953 if (is_normal_group)
954 {
955 cterm_normal_bg_color = color + 1;
956#ifdef FEAT_GUI
957 // Don't mess with 'background' if the GUI is used.
958 if (!gui.in_use && !gui.starting)
959#endif
960 {
Bram Moolenaar471c0fa2022-08-22 15:19:16 +0100961 set_must_redraw(UPD_CLEAR);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100962 if (color >= 0)
963 {
964 int dark = -1;
965
966 if (termcap_active)
967 term_bg_color(color);
968 if (t_colors < 16)
969 dark = (color == 0 || color == 4);
970 // Limit the heuristic to the standard 16 colors
971 else if (color < 16)
972 dark = (color < 7 || color == 8);
973 // Set the 'background' option if the value is
974 // wrong.
975 if (dark != -1
976 && dark != (*p_bg == 'd')
977 && !option_was_set((char_u *)"bg"))
978 {
Bram Moolenaar31e5c602022-04-15 13:53:33 +0100979 set_option_value_give_err((char_u *)"bg",
980 0L, (char_u *)(dark ? "dark" : "light"), 0);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100981 reset_option_was_set((char_u *)"bg");
982 }
983 }
984 }
985 }
986}
987
988/*
989 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
990 * Returns TRUE if the underline color is set.
991 */
992 static void
993highlight_set_ctermul(int idx, int color, int is_normal_group)
994{
995 HL_TABLE()[idx].sg_cterm_ul = color + 1;
996 if (is_normal_group)
997 {
998 cterm_normal_ul_color = color + 1;
999#ifdef FEAT_GUI
1000 // Don't do this if the GUI is used.
1001 if (!gui.in_use && !gui.starting)
1002#endif
1003 {
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001004 set_must_redraw(UPD_CLEAR);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001005 if (termcap_active && color >= 0)
1006 term_ul_color(color);
1007 }
1008 }
1009}
1010
1011/*
1012 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1013 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1014 * 'keystart' is the color name/value.
1015 * 'arg' is the color name or the numeric value as a string.
1016 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1017 * 'init' is set to TRUE when initializing highlighting.
1018 * Called for the ":highlight" command and the "hlset()" function.
1019 *
1020 * Returns TRUE if the color is set.
1021 */
1022 static int
1023highlight_set_cterm_color(
1024 int idx,
1025 char_u *key,
1026 char_u *key_start,
1027 char_u *arg,
1028 int is_normal_group,
1029 int init)
1030{
1031 int color;
1032 long i;
1033 int off;
1034
1035 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1036 {
1037 if (!init)
1038 HL_TABLE()[idx].sg_set |= SG_CTERM;
1039
1040 // When setting the foreground color, and previously the "bold"
1041 // flag was set for a light color, reset it now
1042 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1043 {
1044 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1045 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1046 }
1047
1048 if (VIM_ISDIGIT(*arg))
1049 color = atoi((char *)arg);
1050 else if (STRICMP(arg, "fg") == 0)
1051 {
1052 if (cterm_normal_fg_color)
1053 color = cterm_normal_fg_color - 1;
1054 else
1055 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001056 emsg(_(e_fg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001057 return FALSE;
1058 }
1059 }
1060 else if (STRICMP(arg, "bg") == 0)
1061 {
1062 if (cterm_normal_bg_color > 0)
1063 color = cterm_normal_bg_color - 1;
1064 else
1065 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001066 emsg(_(e_bg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001067 return FALSE;
1068 }
1069 }
1070 else if (STRICMP(arg, "ul") == 0)
1071 {
1072 if (cterm_normal_ul_color > 0)
1073 color = cterm_normal_ul_color - 1;
1074 else
1075 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00001076 emsg(_(e_ul_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001077 return FALSE;
1078 }
1079 }
1080 else
1081 {
1082 int bold = MAYBE;
1083
1084 // reduce calls to STRICMP a bit, it can be slow
1085 off = TOUPPER_ASC(*arg);
1086 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1087 if (off == color_names[i][0]
1088 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1089 break;
1090 if (i < 0)
1091 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001092 semsg(_(e_color_name_or_number_not_recognized), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001093 return FALSE;
1094 }
1095
1096 color = lookup_color(i, key[5] == 'F', &bold);
1097
1098 // set/reset bold attribute to get light foreground
1099 // colors (on some terminals, e.g. "linux")
1100 if (bold == TRUE)
1101 {
1102 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1103 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1104 }
1105 else if (bold == FALSE)
1106 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1107 }
1108
1109 // Add one to the argument, to avoid zero. Zero is used for
1110 // "NONE", then "color" is -1.
1111 if (key[5] == 'F')
1112 highlight_set_ctermfg(idx, color, is_normal_group);
1113 else if (key[5] == 'B')
1114 highlight_set_ctermbg(idx, color, is_normal_group);
1115 else // ctermul
1116 highlight_set_ctermul(idx, color, is_normal_group);
1117 }
1118
1119 return TRUE;
1120}
1121
1122#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1123/*
1124 * Set the GUI foreground color for the highlight group at 'idx'.
1125 * Returns TRUE if the color is set.
1126 */
1127 static int
1128highlight_set_guifg(
1129 int idx,
1130 char_u *arg,
1131 int is_menu_group UNUSED,
1132 int is_scrollbar_group UNUSED,
1133 int is_tooltip_group UNUSED,
1134 int *do_colors UNUSED,
1135 int init)
1136{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001137# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001138 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001139# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001140 char_u **namep;
1141 int did_change = FALSE;
1142
1143 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1144 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1145 {
1146 if (!init)
1147 HL_TABLE()[idx].sg_set |= SG_GUI;
1148
1149# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1150 // In GUI guifg colors are only used when recognized
1151 i = color_name2handle(arg);
1152 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1153 {
1154 HL_TABLE()[idx].sg_gui_fg = i;
1155# endif
1156 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1157 {
1158 vim_free(*namep);
1159 if (STRCMP(arg, "NONE") != 0)
1160 *namep = vim_strsave(arg);
1161 else
1162 *namep = NULL;
1163 did_change = TRUE;
1164 }
1165# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1166# ifdef FEAT_GUI_X11
1167 if (is_menu_group && gui.menu_fg_pixel != i)
1168 {
1169 gui.menu_fg_pixel = i;
1170 *do_colors = TRUE;
1171 }
1172 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1173 {
1174 gui.scroll_fg_pixel = i;
1175 *do_colors = TRUE;
1176 }
1177# ifdef FEAT_BEVAL_GUI
1178 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1179 {
1180 gui.tooltip_fg_pixel = i;
1181 *do_colors = TRUE;
1182 }
1183# endif
1184# endif
1185 }
1186# endif
1187 }
1188
1189 return did_change;
1190}
1191
1192/*
1193 * Set the GUI background color for the highlight group at 'idx'.
1194 * Returns TRUE if the color is set.
1195 */
1196 static int
1197highlight_set_guibg(
1198 int idx,
1199 char_u *arg,
1200 int is_menu_group UNUSED,
1201 int is_scrollbar_group UNUSED,
1202 int is_tooltip_group UNUSED,
1203 int *do_colors UNUSED,
1204 int init)
1205{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001206# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001207 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001208# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001209 char_u **namep;
1210 int did_change = FALSE;
1211
1212 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1213 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1214 {
1215 if (!init)
1216 HL_TABLE()[idx].sg_set |= SG_GUI;
1217
1218# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Drew Vogele30d1022021-10-24 20:35:07 +01001219 // In GUI guibg colors are only used when recognized
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001220 i = color_name2handle(arg);
1221 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1222 {
1223 HL_TABLE()[idx].sg_gui_bg = i;
1224# endif
1225 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1226 {
1227 vim_free(*namep);
1228 if (STRCMP(arg, "NONE") != 0)
1229 *namep = vim_strsave(arg);
1230 else
1231 *namep = NULL;
1232 did_change = TRUE;
1233 }
1234# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1235# ifdef FEAT_GUI_X11
1236 if (is_menu_group && gui.menu_bg_pixel != i)
1237 {
1238 gui.menu_bg_pixel = i;
1239 *do_colors = TRUE;
1240 }
1241 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1242 {
1243 gui.scroll_bg_pixel = i;
1244 *do_colors = TRUE;
1245 }
1246# ifdef FEAT_BEVAL_GUI
1247 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1248 {
1249 gui.tooltip_bg_pixel = i;
1250 *do_colors = TRUE;
1251 }
1252# endif
1253# endif
1254 }
1255# endif
1256 }
1257
1258 return did_change;
1259}
1260
1261/*
1262 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1263 * Returns TRUE if the color is set.
1264 */
1265 static int
1266highlight_set_guisp(int idx, char_u *arg, int init)
1267{
1268# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1269 int i;
1270# endif
1271 int did_change = FALSE;
1272 char_u **namep;
1273
1274 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1275 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1276 {
1277 if (!init)
1278 HL_TABLE()[idx].sg_set |= SG_GUI;
1279
1280# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1281 // In GUI guisp colors are only used when recognized
1282 i = color_name2handle(arg);
1283 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1284 {
1285 HL_TABLE()[idx].sg_gui_sp = i;
1286# endif
1287 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1288 {
1289 vim_free(*namep);
1290 if (STRCMP(arg, "NONE") != 0)
1291 *namep = vim_strsave(arg);
1292 else
1293 *namep = NULL;
1294 did_change = TRUE;
1295 }
1296# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1297 }
1298# endif
1299 }
1300
1301 return did_change;
1302}
1303#endif
1304
1305/*
1306 * Set the start/stop terminal codes for a highlight group.
1307 * Returns TRUE if the terminal code is set.
1308 */
1309 static int
1310highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1311{
1312 int off;
1313 char_u buf[100];
1314 int len;
1315 char_u *tname;
1316 char_u *p;
1317
1318 if (!init)
1319 HL_TABLE()[idx].sg_set |= SG_TERM;
1320
1321 // The "start" and "stop" arguments can be a literal escape
1322 // sequence, or a comma separated list of terminal codes.
1323 if (STRNCMP(arg, "t_", 2) == 0)
1324 {
1325 off = 0;
1326 buf[0] = 0;
1327 while (arg[off] != NUL)
1328 {
1329 // Isolate one termcap name
1330 for (len = 0; arg[off + len] &&
1331 arg[off + len] != ','; ++len)
1332 ;
1333 tname = vim_strnsave(arg + off, len);
1334 if (tname == NULL) // out of memory
1335 return FALSE;
1336 // lookup the escape sequence for the item
1337 p = get_term_code(tname);
1338 vim_free(tname);
1339 if (p == NULL) // ignore non-existing things
1340 p = (char_u *)"";
1341
1342 // Append it to the already found stuff
1343 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1344 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001345 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001346 return FALSE;
1347 }
1348 STRCAT(buf, p);
1349
1350 // Advance to the next item
1351 off += len;
1352 if (arg[off] == ',') // another one follows
1353 ++off;
1354 }
1355 }
1356 else
1357 {
1358 // Copy characters from arg[] to buf[], translating <> codes.
1359 for (p = arg, off = 0; off < 100 - 6 && *p; )
1360 {
zeertzjqdb088872022-05-02 22:53:45 +01001361 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001362 if (len > 0) // recognized special char
1363 off += len;
1364 else // copy as normal char
1365 buf[off++] = *p++;
1366 }
1367 buf[off] = NUL;
1368 }
1369
1370 if (STRCMP(buf, "NONE") == 0) // resetting the value
1371 p = NULL;
1372 else
1373 p = vim_strsave(buf);
1374 if (key[2] == 'A')
1375 {
1376 vim_free(HL_TABLE()[idx].sg_start);
1377 HL_TABLE()[idx].sg_start = p;
1378 }
1379 else
1380 {
1381 vim_free(HL_TABLE()[idx].sg_stop);
1382 HL_TABLE()[idx].sg_stop = p;
1383 }
1384 return TRUE;
1385}
1386
1387/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001388 * Handle the ":highlight .." command.
1389 * When using ":hi clear" this is called recursively for each group with
1390 * "forceit" and "init" both TRUE.
1391 */
1392 void
1393do_highlight(
1394 char_u *line,
1395 int forceit,
1396 int init) // TRUE when called for initializing
1397{
1398 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001399 char_u *linep;
1400 char_u *key_start;
1401 char_u *arg_start;
1402 char_u *key = NULL, *arg = NULL;
1403 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001404 int id;
1405 int idx;
1406 hl_group_T item_before;
1407 int did_change = FALSE;
1408 int dodefault = FALSE;
1409 int doclear = FALSE;
1410 int dolink = FALSE;
1411 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001412 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001413#ifdef FEAT_GUI_X11
1414 int is_menu_group = FALSE; // "Menu" group
1415 int is_scrollbar_group = FALSE; // "Scrollbar" group
1416 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001417#else
1418# define is_menu_group 0
1419# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001420# define is_scrollbar_group 0
1421#endif
1422#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1423 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001424#endif
1425#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1426 int did_highlight_changed = FALSE;
1427#endif
1428
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001429 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001430 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001431 {
Bram Moolenaar24735f22022-08-30 15:44:22 +01001432 dont_use_message_window();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001433 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1434 // TODO: only call when the group has attributes set
1435 highlight_list_one((int)i);
1436 return;
1437 }
1438
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001439 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001440 name_end = skiptowhite(line);
1441 linep = skipwhite(name_end);
1442
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001443 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001444 if (STRNCMP(line, "default", name_end - line) == 0)
1445 {
1446 dodefault = TRUE;
1447 line = linep;
1448 name_end = skiptowhite(line);
1449 linep = skipwhite(name_end);
1450 }
1451
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001452 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001453 if (STRNCMP(line, "clear", name_end - line) == 0)
1454 doclear = TRUE;
1455 if (STRNCMP(line, "link", name_end - line) == 0)
1456 dolink = TRUE;
1457
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001458 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001459 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001460 {
1461 id = syn_namen2id(line, (int)(name_end - line));
1462 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001463 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001464 else
1465 highlight_list_one(id);
1466 return;
1467 }
1468
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001469 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001470 if (dolink)
1471 {
1472 char_u *from_start = linep;
1473 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001474 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001475 char_u *to_start;
1476 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001477 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001478
1479 from_end = skiptowhite(from_start);
1480 to_start = skipwhite(from_end);
1481 to_end = skiptowhite(to_start);
1482
Bram Moolenaar1966c242020-04-20 22:42:32 +02001483 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001484 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001485 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001486 return;
1487 }
1488
Bram Moolenaar1966c242020-04-20 22:42:32 +02001489 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001490 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001491 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001492 return;
1493 }
1494
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001495 from_len = (int)(from_end - from_start);
1496 to_len = (int)(to_end - to_start);
1497 highlight_group_link(from_start, from_len, to_start, to_len,
1498 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001499 return;
1500 }
1501
1502 if (doclear)
1503 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001504 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001505 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001506 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001507 // ":highlight clear" without group name
1508 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001509 return;
1510 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001511 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001512 name_end = skiptowhite(line);
1513 linep = skipwhite(name_end);
1514 }
1515
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001516 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001517 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001518 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001519 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001520 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001521
1522 // Return if "default" was used and the group already has settings.
1523 if (dodefault && hl_has_settings(idx, TRUE))
1524 return;
1525
1526 // Make a copy so we can check if any attribute actually changed.
1527 item_before = HL_TABLE()[idx];
1528
1529 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1530 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001531#ifdef FEAT_GUI_X11
1532 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1533 is_menu_group = TRUE;
1534 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1535 is_scrollbar_group = TRUE;
1536 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1537 is_tooltip_group = TRUE;
1538#endif
1539
1540 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1541 if (doclear || (forceit && init))
1542 {
1543 highlight_clear(idx);
1544 if (!doclear)
1545 HL_TABLE()[idx].sg_set = 0;
1546 }
1547
1548 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001549 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001550 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001551 key_start = linep;
1552 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001553 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001554 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001555 error = TRUE;
1556 break;
1557 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001558
1559 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1560 // or "guibg").
1561 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1562 ++linep;
1563 vim_free(key);
1564 key = vim_strnsave_up(key_start, linep - key_start);
1565 if (key == NULL)
1566 {
1567 error = TRUE;
1568 break;
1569 }
1570 linep = skipwhite(linep);
1571
1572 if (STRCMP(key, "NONE") == 0)
1573 {
1574 if (!init || HL_TABLE()[idx].sg_set == 0)
1575 {
1576 if (!init)
1577 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1578 highlight_clear(idx);
1579 }
1580 continue;
1581 }
1582
1583 // Check for the equal sign.
1584 if (*linep != '=')
1585 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001586 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001587 error = TRUE;
1588 break;
1589 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001590 ++linep;
1591
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001592 // Isolate the argument.
1593 linep = skipwhite(linep);
1594 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001595 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001596 arg_start = ++linep;
1597 linep = vim_strchr(linep, '\'');
1598 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001599 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001600 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001601 error = TRUE;
1602 break;
1603 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001604 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001605 else
1606 {
1607 arg_start = linep;
1608 linep = skiptowhite(linep);
1609 }
1610 if (linep == arg_start)
1611 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001612 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001613 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001614 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001615 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001616 vim_free(arg);
1617 arg = vim_strnsave(arg_start, linep - arg_start);
1618 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001619 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001620 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001621 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001622 }
1623 if (*linep == '\'')
1624 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001625
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001626 // Store the argument.
1627 if (STRCMP(key, "TERM") == 0
1628 || STRCMP(key, "CTERM") == 0
1629 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001630 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001631 if (!highlight_set_termgui_attr(idx, key, arg, init))
1632 {
1633 error = TRUE;
1634 break;
1635 }
1636 }
1637 else if (STRCMP(key, "FONT") == 0)
1638 {
1639 // in non-GUI fonts are simply ignored
1640#ifdef FEAT_GUI
1641 if (highlight_set_font(idx, arg, is_normal_group,
1642 is_menu_group, is_tooltip_group))
1643 did_change = TRUE;
1644#endif
1645 }
1646 else if (STRCMP(key, "CTERMFG") == 0
1647 || STRCMP(key, "CTERMBG") == 0
1648 || STRCMP(key, "CTERMUL") == 0)
1649 {
1650 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1651 is_normal_group, init))
1652 {
1653 error = TRUE;
1654 break;
1655 }
1656 }
1657 else if (STRCMP(key, "GUIFG") == 0)
1658 {
1659#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1660 if (highlight_set_guifg(idx, arg, is_menu_group,
1661 is_scrollbar_group, is_tooltip_group,
1662 &do_colors, init))
1663 did_change = TRUE;
1664#endif
1665 }
1666 else if (STRCMP(key, "GUIBG") == 0)
1667 {
1668#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1669 if (highlight_set_guibg(idx, arg, is_menu_group,
1670 is_scrollbar_group, is_tooltip_group,
1671 &do_colors, init))
1672 did_change = TRUE;
1673#endif
1674 }
1675 else if (STRCMP(key, "GUISP") == 0)
1676 {
1677#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1678 if (highlight_set_guisp(idx, arg, init))
1679 did_change = TRUE;
1680#endif
1681 }
1682 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1683 {
1684 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1685 {
1686 error = TRUE;
1687 break;
1688 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001689 }
1690 else
1691 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001692 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001693 error = TRUE;
1694 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001695 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001696 HL_TABLE()[idx].sg_cleared = FALSE;
1697
1698 // When highlighting has been given for a group, don't link it.
1699 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1700 HL_TABLE()[idx].sg_link = 0;
1701
1702 // Continue with next argument.
1703 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001704 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001705
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001706 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001707 if (error && idx == highlight_ga.ga_len)
1708 syn_unadd_group();
1709 else
1710 {
1711 if (is_normal_group)
1712 {
1713 HL_TABLE()[idx].sg_term_attr = 0;
1714 HL_TABLE()[idx].sg_cterm_attr = 0;
1715#ifdef FEAT_GUI
1716 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001717 // Need to update all groups, because they might be using "bg"
1718 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001719#endif
1720#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1721 if (USE_24BIT)
1722 {
1723 highlight_gui_started();
1724 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001725 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001726 }
1727#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001728#ifdef FEAT_VTP
1729 control_console_color_rgb();
1730#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001731 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001732#ifdef FEAT_GUI_X11
1733# ifdef FEAT_MENU
1734 else if (is_menu_group)
1735 {
1736 if (gui.in_use && do_colors)
1737 gui_mch_new_menu_colors();
1738 }
1739# endif
1740 else if (is_scrollbar_group)
1741 {
1742 if (gui.in_use && do_colors)
1743 gui_new_scrollbar_colors();
1744 else
1745 set_hl_attr(idx);
1746 }
1747# ifdef FEAT_BEVAL_GUI
1748 else if (is_tooltip_group)
1749 {
1750 if (gui.in_use && do_colors)
1751 gui_mch_new_tooltip_colors();
1752 }
1753# endif
1754#endif
1755 else
1756 set_hl_attr(idx);
1757#ifdef FEAT_EVAL
1758 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001759 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001760#endif
1761 }
1762
1763 vim_free(key);
1764 vim_free(arg);
1765
1766 // Only call highlight_changed() once, after a sequence of highlight
1767 // commands, and only if an attribute actually changed.
1768 if ((did_change
1769 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1770#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1771 && !did_highlight_changed
1772#endif
1773 )
1774 {
1775 // Do not trigger a redraw when highlighting is changed while
1776 // redrawing. This may happen when evaluating 'statusline' changes the
1777 // StatusLine group.
1778 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001779 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001780 need_highlight_changed = TRUE;
1781 }
1782}
1783
1784#if defined(EXITFREE) || defined(PROTO)
1785 void
1786free_highlight(void)
1787{
1788 int i;
1789
1790 for (i = 0; i < highlight_ga.ga_len; ++i)
1791 {
1792 highlight_clear(i);
1793 vim_free(HL_TABLE()[i].sg_name);
1794 vim_free(HL_TABLE()[i].sg_name_u);
1795 }
1796 ga_clear(&highlight_ga);
1797}
1798#endif
1799
1800/*
1801 * Reset the cterm colors to what they were before Vim was started, if
1802 * possible. Otherwise reset them to zero.
1803 */
1804 void
1805restore_cterm_colors(void)
1806{
1807#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1808 // Since t_me has been set, this probably means that the user
1809 // wants to use this as default colors. Need to reset default
1810 // background/foreground colors.
1811 mch_set_normal_colors();
1812#else
1813# ifdef VIMDLL
1814 if (!gui.in_use)
1815 {
1816 mch_set_normal_colors();
1817 return;
1818 }
1819# endif
1820 cterm_normal_fg_color = 0;
1821 cterm_normal_fg_bold = 0;
1822 cterm_normal_bg_color = 0;
1823# ifdef FEAT_TERMGUICOLORS
1824 cterm_normal_fg_gui_color = INVALCOLOR;
1825 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001826 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001827# endif
1828#endif
1829}
1830
1831/*
1832 * Return TRUE if highlight group "idx" has any settings.
1833 * When "check_link" is TRUE also check for an existing link.
1834 */
1835 static int
1836hl_has_settings(int idx, int check_link)
1837{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001838 return HL_TABLE()[idx].sg_cleared == 0
1839 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001840 || HL_TABLE()[idx].sg_cterm_attr != 0
1841 || HL_TABLE()[idx].sg_cterm_fg != 0
1842 || HL_TABLE()[idx].sg_cterm_bg != 0
1843#ifdef FEAT_GUI
1844 || HL_TABLE()[idx].sg_gui_attr != 0
1845 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1846 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1847 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1848 || HL_TABLE()[idx].sg_font_name != NULL
1849#endif
1850 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1851}
1852
1853/*
1854 * Clear highlighting for one group.
1855 */
1856 static void
1857highlight_clear(int idx)
1858{
1859 HL_TABLE()[idx].sg_cleared = TRUE;
1860
1861 HL_TABLE()[idx].sg_term = 0;
1862 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1863 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1864 HL_TABLE()[idx].sg_term_attr = 0;
1865 HL_TABLE()[idx].sg_cterm = 0;
1866 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1867 HL_TABLE()[idx].sg_cterm_fg = 0;
1868 HL_TABLE()[idx].sg_cterm_bg = 0;
1869 HL_TABLE()[idx].sg_cterm_attr = 0;
1870#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1871 HL_TABLE()[idx].sg_gui = 0;
1872 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1873 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1874 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1875#endif
1876#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1877 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1878 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001879 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001880#endif
1881#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001882 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1883 HL_TABLE()[idx].sg_font = NOFONT;
1884# ifdef FEAT_XFONTSET
1885 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1886 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1887# endif
1888 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1889 HL_TABLE()[idx].sg_gui_attr = 0;
1890#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001891 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001892 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001893#ifdef FEAT_EVAL
1894 // Since we set the default link, set the location to where the default
1895 // link was set.
1896 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001897#endif
1898}
1899
1900#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1901/*
1902 * Set the normal foreground and background colors according to the "Normal"
1903 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1904 * "Tooltip" colors.
1905 */
1906 void
1907set_normal_colors(void)
1908{
1909# ifdef FEAT_GUI
1910# ifdef FEAT_TERMGUICOLORS
1911 if (gui.in_use)
1912# endif
1913 {
1914 if (set_group_colors((char_u *)"Normal",
1915 &gui.norm_pixel, &gui.back_pixel,
1916 FALSE, TRUE, FALSE))
1917 {
1918 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001919 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001920 }
1921# ifdef FEAT_GUI_X11
1922 if (set_group_colors((char_u *)"Menu",
1923 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1924 TRUE, FALSE, FALSE))
1925 {
1926# ifdef FEAT_MENU
1927 gui_mch_new_menu_colors();
1928# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001929 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001930 }
1931# ifdef FEAT_BEVAL_GUI
1932 if (set_group_colors((char_u *)"Tooltip",
1933 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1934 FALSE, FALSE, TRUE))
1935 {
1936# ifdef FEAT_TOOLBAR
1937 gui_mch_new_tooltip_colors();
1938# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001939 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001940 }
1941# endif
1942 if (set_group_colors((char_u *)"Scrollbar",
1943 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1944 FALSE, FALSE, FALSE))
1945 {
1946 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001947 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001948 }
1949# endif
1950 }
1951# endif
1952# ifdef FEAT_TERMGUICOLORS
1953# ifdef FEAT_GUI
1954 else
1955# endif
1956 {
1957 int idx;
1958
1959 idx = syn_name2id((char_u *)"Normal") - 1;
1960 if (idx >= 0)
1961 {
1962 gui_do_one_color(idx, FALSE, FALSE);
1963
1964 // If the normal fg or bg color changed a complete redraw is
1965 // required.
1966 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1967 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1968 {
1969 // if the GUI color is INVALCOLOR then we use the default cterm
1970 // color
1971 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1972 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001973 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001974 }
1975 }
1976 }
1977# endif
1978}
1979#endif
1980
1981#if defined(FEAT_GUI) || defined(PROTO)
1982/*
1983 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1984 */
1985 static int
1986set_group_colors(
1987 char_u *name,
1988 guicolor_T *fgp,
1989 guicolor_T *bgp,
1990 int do_menu,
1991 int use_norm,
1992 int do_tooltip)
1993{
1994 int idx;
1995
1996 idx = syn_name2id(name) - 1;
1997 if (idx >= 0)
1998 {
1999 gui_do_one_color(idx, do_menu, do_tooltip);
2000
2001 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2002 *fgp = HL_TABLE()[idx].sg_gui_fg;
2003 else if (use_norm)
2004 *fgp = gui.def_norm_pixel;
2005 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2006 *bgp = HL_TABLE()[idx].sg_gui_bg;
2007 else if (use_norm)
2008 *bgp = gui.def_back_pixel;
2009 return TRUE;
2010 }
2011 return FALSE;
2012}
2013
2014/*
2015 * Get the font of the "Normal" group.
2016 * Returns "" when it's not found or not set.
2017 */
2018 char_u *
2019hl_get_font_name(void)
2020{
2021 int id;
2022 char_u *s;
2023
2024 id = syn_name2id((char_u *)"Normal");
2025 if (id > 0)
2026 {
2027 s = HL_TABLE()[id - 1].sg_font_name;
2028 if (s != NULL)
2029 return s;
2030 }
2031 return (char_u *)"";
2032}
2033
2034/*
2035 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2036 * actually chosen to be used.
2037 */
2038 void
2039hl_set_font_name(char_u *font_name)
2040{
2041 int id;
2042
2043 id = syn_name2id((char_u *)"Normal");
2044 if (id > 0)
2045 {
2046 vim_free(HL_TABLE()[id - 1].sg_font_name);
2047 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2048 }
2049}
2050
2051/*
2052 * Set background color for "Normal" group. Called by gui_set_bg_color()
2053 * when the color is known.
2054 */
2055 void
2056hl_set_bg_color_name(
2057 char_u *name) // must have been allocated
2058{
2059 int id;
2060
2061 if (name != NULL)
2062 {
2063 id = syn_name2id((char_u *)"Normal");
2064 if (id > 0)
2065 {
2066 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2067 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2068 }
2069 }
2070}
2071
2072/*
2073 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2074 * when the color is known.
2075 */
2076 void
2077hl_set_fg_color_name(
2078 char_u *name) // must have been allocated
2079{
2080 int id;
2081
2082 if (name != NULL)
2083 {
2084 id = syn_name2id((char_u *)"Normal");
2085 if (id > 0)
2086 {
2087 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2088 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2089 }
2090 }
2091}
2092
2093/*
2094 * Return the handle for a font name.
2095 * Returns NOFONT when failed.
2096 */
2097 static GuiFont
2098font_name2handle(char_u *name)
2099{
2100 if (STRCMP(name, "NONE") == 0)
2101 return NOFONT;
2102
2103 return gui_mch_get_font(name, TRUE);
2104}
2105
2106# ifdef FEAT_XFONTSET
2107/*
2108 * Return the handle for a fontset name.
2109 * Returns NOFONTSET when failed.
2110 */
2111 static GuiFontset
2112fontset_name2handle(char_u *name, int fixed_width)
2113{
2114 if (STRCMP(name, "NONE") == 0)
2115 return NOFONTSET;
2116
2117 return gui_mch_get_fontset(name, TRUE, fixed_width);
2118}
2119# endif
2120
2121/*
2122 * Get the font or fontset for one highlight group.
2123 */
2124 static void
2125hl_do_font(
2126 int idx,
2127 char_u *arg,
2128 int do_normal, // set normal font
2129 int do_menu UNUSED, // set menu font
2130 int do_tooltip UNUSED, // set tooltip font
2131 int free_font) // free current font/fontset
2132{
2133# ifdef FEAT_XFONTSET
2134 // If 'guifontset' is not empty, first try using the name as a
2135 // fontset. If that doesn't work, use it as a font name.
2136 if (*p_guifontset != NUL
2137# ifdef FONTSET_ALWAYS
2138 || do_menu
2139# endif
2140# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002141 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002142 || do_tooltip
2143# endif
2144 )
2145 {
2146 if (free_font)
2147 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2148 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2149# ifdef FONTSET_ALWAYS
2150 || do_menu
2151# endif
2152# ifdef FEAT_BEVAL_TIP
2153 || do_tooltip
2154# endif
2155 );
2156 }
2157 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2158 {
2159 // If it worked and it's the Normal group, use it as the normal
2160 // fontset. Same for the Menu group.
2161 if (do_normal)
2162 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002163# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002164 if (do_menu)
2165 {
2166# ifdef FONTSET_ALWAYS
2167 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2168# else
2169 // YIKES! This is a bug waiting to crash the program
2170 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2171# endif
2172 gui_mch_new_menu_font();
2173 }
2174# ifdef FEAT_BEVAL_GUI
2175 if (do_tooltip)
2176 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002177 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002178 // displaying a single font and a fontset.
2179 // If the XtNinternational resource is set to True at widget
2180 // creation, then a fontset is always used, otherwise an
2181 // XFontStruct is used.
2182 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2183 gui_mch_new_tooltip_font();
2184 }
2185# endif
2186# endif
2187 }
2188 else
2189# endif
2190 {
2191 if (free_font)
2192 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2193 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2194 // If it worked and it's the Normal group, use it as the
2195 // normal font. Same for the Menu group.
2196 if (HL_TABLE()[idx].sg_font != NOFONT)
2197 {
2198 if (do_normal)
2199 gui_init_font(arg, FALSE);
2200#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002201# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002202 if (do_menu)
2203 {
2204 gui.menu_font = HL_TABLE()[idx].sg_font;
2205 gui_mch_new_menu_font();
2206 }
2207# endif
2208#endif
2209 }
2210 }
2211}
2212
2213#endif // FEAT_GUI
2214
2215#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2216/*
2217 * Return the handle for a color name.
2218 * Returns INVALCOLOR when failed.
2219 */
2220 guicolor_T
2221color_name2handle(char_u *name)
2222{
2223 if (STRCMP(name, "NONE") == 0)
2224 return INVALCOLOR;
2225
2226 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2227 {
2228#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2229 if (gui.in_use)
2230#endif
2231#ifdef FEAT_GUI
2232 return gui.norm_pixel;
2233#endif
2234#ifdef FEAT_TERMGUICOLORS
2235 if (cterm_normal_fg_gui_color != INVALCOLOR)
2236 return cterm_normal_fg_gui_color;
2237 // Guess that the foreground is black or white.
2238 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2239#endif
2240 }
2241 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2242 {
2243#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2244 if (gui.in_use)
2245#endif
2246#ifdef FEAT_GUI
2247 return gui.back_pixel;
2248#endif
2249#ifdef FEAT_TERMGUICOLORS
2250 if (cterm_normal_bg_gui_color != INVALCOLOR)
2251 return cterm_normal_bg_gui_color;
2252 // Guess that the background is white or black.
2253 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2254#endif
2255 }
2256
2257 return GUI_GET_COLOR(name);
2258}
Drew Vogele30d1022021-10-24 20:35:07 +01002259
2260// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2261// values as used by the MS-Windows GDI api. It should be used only for
2262// MS-Windows GDI builds.
2263# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2264# undef RGB
2265# endif
2266# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002267# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002268# endif
2269
2270# ifdef VIMDLL
2271 static guicolor_T
2272gui_adjust_rgb(guicolor_T c)
2273{
2274 if (gui.in_use)
2275 return c;
2276 else
2277 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2278}
2279# else
2280# define gui_adjust_rgb(c) (c)
2281# endif
2282
2283 static int
2284hex_digit(int c)
2285{
2286 if (isdigit(c))
2287 return c - '0';
2288 c = TOLOWER_ASC(c);
2289 if (c >= 'a' && c <= 'f')
2290 return c - 'a' + 10;
2291 return 0x1ffffff;
2292}
2293
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002294 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002295decode_hex_color(char_u *hex)
2296{
2297 guicolor_T color;
2298
2299 if (hex[0] != '#' || STRLEN(hex) != 7)
2300 return INVALCOLOR;
2301
2302 // Name is in "#rrggbb" format
2303 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2304 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2305 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2306 if (color > 0xffffff)
2307 return INVALCOLOR;
2308 return gui_adjust_rgb(color);
2309}
2310
Bram Moolenaar2a521962021-10-25 10:30:14 +01002311#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002312// Returns the color currently mapped to the given name or INVALCOLOR if no
2313// such name exists in the color table. The convention is to use lowercase for
2314// all keys in the v:colornames dictionary. The value can be either a string in
2315// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002316 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002317colorname2rgb(char_u *name)
2318{
2319 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2320 char_u *lc_name;
2321 dictitem_T *colentry;
2322 char_u *colstr;
2323 varnumber_T colnum;
2324
2325 lc_name = strlow_save(name);
2326 if (lc_name == NULL)
2327 return INVALCOLOR;
2328
2329 colentry = dict_find(colornames_table, lc_name, -1);
2330 vim_free(lc_name);
2331 if (colentry == NULL)
2332 return INVALCOLOR;
2333
2334 if (colentry->di_tv.v_type == VAR_STRING)
2335 {
2336 colstr = tv_get_string_strict(&colentry->di_tv);
2337 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2338 {
2339 return decode_hex_color(colstr);
2340 }
2341 else
2342 {
2343 semsg(_(e_bad_color_string_str), colstr);
2344 return INVALCOLOR;
2345 }
2346 }
2347
2348 if (colentry->di_tv.v_type == VAR_NUMBER)
2349 {
2350 colnum = tv_get_number(&colentry->di_tv);
2351 return (guicolor_T)colnum;
2352 }
2353
2354 return INVALCOLOR;
2355}
2356
Drew Vogele30d1022021-10-24 20:35:07 +01002357#endif
2358
2359 guicolor_T
2360gui_get_color_cmn(char_u *name)
2361{
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002362 int i;
Drew Vogele30d1022021-10-24 20:35:07 +01002363 guicolor_T color;
2364
2365 struct rgbcolor_table_S {
2366 char_u *color_name;
2367 guicolor_T color;
2368 };
2369
2370 // Only non X11 colors (not present in rgb.txt) and colors in
2371 // color_names[], useful when $VIMRUNTIME is not found,.
2372 static struct rgbcolor_table_S rgb_table[] = {
2373 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2374 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2375 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2376 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2377 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2378 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2379 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2380 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2381 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2382 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2383 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2384 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2385 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2386 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2387 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2388 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2389 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2390 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2391 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2392 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2393 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2394 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2395 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2396 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2397 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2398 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2399 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2400 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2401 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2402 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2403 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2404 };
2405
2406 color = decode_hex_color(name);
2407 if (color != INVALCOLOR)
2408 return color;
2409
2410 // Check if the name is one of the colors we know
2411 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2412 if (STRICMP(name, rgb_table[i].color_name) == 0)
2413 return gui_adjust_rgb(rgb_table[i].color);
2414
2415#if defined(FEAT_EVAL)
2416 /*
2417 * Not a traditional color. Load additional color aliases and then consult the alias table.
2418 */
2419
2420 color = colorname2rgb(name);
2421 if (color == INVALCOLOR)
2422 {
2423 load_default_colors_lists();
2424 color = colorname2rgb(name);
2425 }
2426
2427 return color;
2428#else
2429 return INVALCOLOR;
2430#endif
2431}
2432
2433 guicolor_T
2434gui_get_rgb_color_cmn(int r, int g, int b)
2435{
2436 guicolor_T color = RGB(r, g, b);
2437
2438 if (color > 0xffffff)
2439 return INVALCOLOR;
2440 return gui_adjust_rgb(color);
2441}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002442#endif
2443
2444/*
2445 * Table with the specifications for an attribute number.
2446 * Note that this table is used by ALL buffers. This is required because the
2447 * GUI can redraw at any time for any buffer.
2448 */
2449static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2450
2451#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2452
2453static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2454
2455#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2456
2457#ifdef FEAT_GUI
2458static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2459
2460#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2461#endif
2462
2463/*
2464 * Return the attr number for a set of colors and font.
2465 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2466 * if the combination is new.
2467 * Return 0 for error (no more room).
2468 */
2469 static int
2470get_attr_entry(garray_T *table, attrentry_T *aep)
2471{
2472 int i;
2473 attrentry_T *taep;
2474 static int recursive = FALSE;
2475
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002476 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002477 table->ga_itemsize = sizeof(attrentry_T);
2478 table->ga_growsize = 7;
2479
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002480 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002481 for (i = 0; i < table->ga_len; ++i)
2482 {
2483 taep = &(((attrentry_T *)table->ga_data)[i]);
2484 if ( aep->ae_attr == taep->ae_attr
2485 && (
2486#ifdef FEAT_GUI
2487 (table == &gui_attr_table
2488 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2489 && aep->ae_u.gui.bg_color
2490 == taep->ae_u.gui.bg_color
2491 && aep->ae_u.gui.sp_color
2492 == taep->ae_u.gui.sp_color
2493 && aep->ae_u.gui.font == taep->ae_u.gui.font
2494# ifdef FEAT_XFONTSET
2495 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2496# endif
2497 ))
2498 ||
2499#endif
2500 (table == &term_attr_table
2501 && (aep->ae_u.term.start == NULL)
2502 == (taep->ae_u.term.start == NULL)
2503 && (aep->ae_u.term.start == NULL
2504 || STRCMP(aep->ae_u.term.start,
2505 taep->ae_u.term.start) == 0)
2506 && (aep->ae_u.term.stop == NULL)
2507 == (taep->ae_u.term.stop == NULL)
2508 && (aep->ae_u.term.stop == NULL
2509 || STRCMP(aep->ae_u.term.stop,
2510 taep->ae_u.term.stop) == 0))
2511 || (table == &cterm_attr_table
2512 && aep->ae_u.cterm.fg_color
2513 == taep->ae_u.cterm.fg_color
2514 && aep->ae_u.cterm.bg_color
2515 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002516 && aep->ae_u.cterm.ul_color
2517 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002518#ifdef FEAT_TERMGUICOLORS
2519 && aep->ae_u.cterm.fg_rgb
2520 == taep->ae_u.cterm.fg_rgb
2521 && aep->ae_u.cterm.bg_rgb
2522 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002523 && aep->ae_u.cterm.ul_rgb
2524 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002525#endif
2526 )))
2527
2528 return i + ATTR_OFF;
2529 }
2530
2531 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2532 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002533 // Running out of attribute entries! remove all attributes, and
2534 // compute new ones for all groups.
2535 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002536 if (recursive)
2537 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002538 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002539 return 0;
2540 }
2541 recursive = TRUE;
2542
2543 clear_hl_tables();
2544
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002545 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002546
2547 for (i = 0; i < highlight_ga.ga_len; ++i)
2548 set_hl_attr(i);
2549
2550 recursive = FALSE;
2551 }
2552
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002553 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002554 if (ga_grow(table, 1) == FAIL)
2555 return 0;
2556
2557 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002558 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002559 taep->ae_attr = aep->ae_attr;
2560#ifdef FEAT_GUI
2561 if (table == &gui_attr_table)
2562 {
2563 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2564 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2565 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2566 taep->ae_u.gui.font = aep->ae_u.gui.font;
2567# ifdef FEAT_XFONTSET
2568 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2569# endif
2570 }
2571#endif
2572 if (table == &term_attr_table)
2573 {
2574 if (aep->ae_u.term.start == NULL)
2575 taep->ae_u.term.start = NULL;
2576 else
2577 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2578 if (aep->ae_u.term.stop == NULL)
2579 taep->ae_u.term.stop = NULL;
2580 else
2581 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2582 }
2583 else if (table == &cterm_attr_table)
2584 {
2585 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2586 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002587 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002588#ifdef FEAT_TERMGUICOLORS
2589 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2590 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002591 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002592#endif
2593 }
2594 ++table->ga_len;
2595 return (table->ga_len - 1 + ATTR_OFF);
2596}
2597
2598#if defined(FEAT_TERMINAL) || defined(PROTO)
2599/*
2600 * Get an attribute index for a cterm entry.
2601 * Uses an existing entry when possible or adds one when needed.
2602 */
2603 int
2604get_cterm_attr_idx(int attr, int fg, int bg)
2605{
2606 attrentry_T at_en;
2607
Bram Moolenaara80faa82020-04-12 19:37:17 +02002608 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002609#ifdef FEAT_TERMGUICOLORS
2610 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2611 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002612 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002613#endif
2614 at_en.ae_attr = attr;
2615 at_en.ae_u.cterm.fg_color = fg;
2616 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002617 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002618 return get_attr_entry(&cterm_attr_table, &at_en);
2619}
2620#endif
2621
2622#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2623/*
2624 * Get an attribute index for a 'termguicolors' entry.
2625 * Uses an existing entry when possible or adds one when needed.
2626 */
2627 int
2628get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2629{
2630 attrentry_T at_en;
2631
Bram Moolenaara80faa82020-04-12 19:37:17 +02002632 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002633 at_en.ae_attr = attr;
2634 if (fg == INVALCOLOR && bg == INVALCOLOR)
2635 {
2636 // If both GUI colors are not set fall back to the cterm colors. Helps
2637 // if the GUI only has an attribute, such as undercurl.
2638 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2639 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2640 }
2641 else
2642 {
2643 at_en.ae_u.cterm.fg_rgb = fg;
2644 at_en.ae_u.cterm.bg_rgb = bg;
2645 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002646 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002647 return get_attr_entry(&cterm_attr_table, &at_en);
2648}
2649#endif
2650
2651#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2652/*
2653 * Get an attribute index for a cterm entry.
2654 * Uses an existing entry when possible or adds one when needed.
2655 */
2656 int
2657get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2658{
2659 attrentry_T at_en;
2660
Bram Moolenaara80faa82020-04-12 19:37:17 +02002661 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002662 at_en.ae_attr = attr;
2663 at_en.ae_u.gui.fg_color = fg;
2664 at_en.ae_u.gui.bg_color = bg;
2665 return get_attr_entry(&gui_attr_table, &at_en);
2666}
2667#endif
2668
2669/*
2670 * Clear all highlight tables.
2671 */
2672 void
2673clear_hl_tables(void)
2674{
2675 int i;
2676 attrentry_T *taep;
2677
2678#ifdef FEAT_GUI
2679 ga_clear(&gui_attr_table);
2680#endif
2681 for (i = 0; i < term_attr_table.ga_len; ++i)
2682 {
2683 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2684 vim_free(taep->ae_u.term.start);
2685 vim_free(taep->ae_u.term.stop);
2686 }
2687 ga_clear(&term_attr_table);
2688 ga_clear(&cterm_attr_table);
2689}
2690
2691/*
2692 * Combine special attributes (e.g., for spelling) with other attributes
2693 * (e.g., for syntax highlighting).
2694 * "prim_attr" overrules "char_attr".
2695 * This creates a new group when required.
2696 * Since we expect there to be few spelling mistakes we don't cache the
2697 * result.
2698 * Return the resulting attributes.
2699 */
2700 int
2701hl_combine_attr(int char_attr, int prim_attr)
2702{
2703 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002704 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002705 attrentry_T new_en;
2706
2707 if (char_attr == 0)
2708 return prim_attr;
2709 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2710 return ATTR_COMBINE(char_attr, prim_attr);
2711#ifdef FEAT_GUI
2712 if (gui.in_use)
2713 {
2714 if (char_attr > HL_ALL)
2715 char_aep = syn_gui_attr2entry(char_attr);
2716 if (char_aep != NULL)
2717 new_en = *char_aep;
2718 else
2719 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002720 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002721 new_en.ae_u.gui.fg_color = INVALCOLOR;
2722 new_en.ae_u.gui.bg_color = INVALCOLOR;
2723 new_en.ae_u.gui.sp_color = INVALCOLOR;
2724 if (char_attr <= HL_ALL)
2725 new_en.ae_attr = char_attr;
2726 }
2727
2728 if (prim_attr <= HL_ALL)
2729 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2730 else
2731 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002732 prim_aep = syn_gui_attr2entry(prim_attr);
2733 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002734 {
2735 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002736 prim_aep->ae_attr);
2737 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2738 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2739 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2740 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2741 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2742 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2743 if (prim_aep->ae_u.gui.font != NOFONT)
2744 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002745# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002746 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2747 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002748# endif
2749 }
2750 }
2751 return get_attr_entry(&gui_attr_table, &new_en);
2752 }
2753#endif
2754
2755 if (IS_CTERM)
2756 {
2757 if (char_attr > HL_ALL)
2758 char_aep = syn_cterm_attr2entry(char_attr);
2759 if (char_aep != NULL)
2760 new_en = *char_aep;
2761 else
2762 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002763 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002764#ifdef FEAT_TERMGUICOLORS
2765 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2766 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002767 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002768#endif
2769 if (char_attr <= HL_ALL)
2770 new_en.ae_attr = char_attr;
2771 }
2772
2773 if (prim_attr <= HL_ALL)
2774 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2775 else
2776 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002777 prim_aep = syn_cterm_attr2entry(prim_attr);
2778 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002779 {
2780 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002781 prim_aep->ae_attr);
2782 if (prim_aep->ae_u.cterm.fg_color > 0)
2783 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2784 if (prim_aep->ae_u.cterm.bg_color > 0)
2785 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2786 if (prim_aep->ae_u.cterm.ul_color > 0)
2787 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002788#ifdef FEAT_TERMGUICOLORS
2789 // If both fg and bg are not set fall back to cterm colors.
2790 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002791 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2792 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002793 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002794 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002795 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002796 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002797 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2798 }
2799 else
2800 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002801 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2802 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2803 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2804 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002805 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002806 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2807 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002808#endif
2809 }
2810 }
2811 return get_attr_entry(&cterm_attr_table, &new_en);
2812 }
2813
2814 if (char_attr > HL_ALL)
2815 char_aep = syn_term_attr2entry(char_attr);
2816 if (char_aep != NULL)
2817 new_en = *char_aep;
2818 else
2819 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002820 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002821 if (char_attr <= HL_ALL)
2822 new_en.ae_attr = char_attr;
2823 }
2824
2825 if (prim_attr <= HL_ALL)
2826 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2827 else
2828 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002829 prim_aep = syn_term_attr2entry(prim_attr);
2830 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002831 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002832 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2833 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002834 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002835 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2836 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002837 }
2838 }
2839 }
2840 return get_attr_entry(&term_attr_table, &new_en);
2841}
2842
2843#ifdef FEAT_GUI
2844 attrentry_T *
2845syn_gui_attr2entry(int attr)
2846{
2847 attr -= ATTR_OFF;
2848 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2849 return NULL;
2850 return &(GUI_ATTR_ENTRY(attr));
2851}
2852#endif
2853
2854/*
2855 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2856 * Only to be used when "attr" > HL_ALL.
2857 */
2858 int
2859syn_attr2attr(int attr)
2860{
2861 attrentry_T *aep;
2862
2863#ifdef FEAT_GUI
2864 if (gui.in_use)
2865 aep = syn_gui_attr2entry(attr);
2866 else
2867#endif
2868 if (IS_CTERM)
2869 aep = syn_cterm_attr2entry(attr);
2870 else
2871 aep = syn_term_attr2entry(attr);
2872
2873 if (aep == NULL) // highlighting not set
2874 return 0;
2875 return aep->ae_attr;
2876}
2877
2878
2879 attrentry_T *
2880syn_term_attr2entry(int attr)
2881{
2882 attr -= ATTR_OFF;
2883 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2884 return NULL;
2885 return &(TERM_ATTR_ENTRY(attr));
2886}
2887
2888 attrentry_T *
2889syn_cterm_attr2entry(int attr)
2890{
2891 attr -= ATTR_OFF;
2892 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2893 return NULL;
2894 return &(CTERM_ATTR_ENTRY(attr));
2895}
2896
2897#define LIST_ATTR 1
2898#define LIST_STRING 2
2899#define LIST_INT 3
2900
2901 static void
2902highlight_list_one(int id)
2903{
2904 hl_group_T *sgp;
2905 int didh = FALSE;
2906
2907 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2908
2909 if (message_filtered(sgp->sg_name))
2910 return;
2911
2912 didh = highlight_list_arg(id, didh, LIST_ATTR,
2913 sgp->sg_term, NULL, "term");
2914 didh = highlight_list_arg(id, didh, LIST_STRING,
2915 0, sgp->sg_start, "start");
2916 didh = highlight_list_arg(id, didh, LIST_STRING,
2917 0, sgp->sg_stop, "stop");
2918
2919 didh = highlight_list_arg(id, didh, LIST_ATTR,
2920 sgp->sg_cterm, NULL, "cterm");
2921 didh = highlight_list_arg(id, didh, LIST_INT,
2922 sgp->sg_cterm_fg, NULL, "ctermfg");
2923 didh = highlight_list_arg(id, didh, LIST_INT,
2924 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002925 didh = highlight_list_arg(id, didh, LIST_INT,
2926 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002927
2928#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2929 didh = highlight_list_arg(id, didh, LIST_ATTR,
2930 sgp->sg_gui, NULL, "gui");
2931 didh = highlight_list_arg(id, didh, LIST_STRING,
2932 0, sgp->sg_gui_fg_name, "guifg");
2933 didh = highlight_list_arg(id, didh, LIST_STRING,
2934 0, sgp->sg_gui_bg_name, "guibg");
2935 didh = highlight_list_arg(id, didh, LIST_STRING,
2936 0, sgp->sg_gui_sp_name, "guisp");
2937#endif
2938#ifdef FEAT_GUI
2939 didh = highlight_list_arg(id, didh, LIST_STRING,
2940 0, sgp->sg_font_name, "font");
2941#endif
2942
2943 if (sgp->sg_link && !got_int)
2944 {
2945 (void)syn_list_header(didh, 9999, id);
2946 didh = TRUE;
2947 msg_puts_attr("links to", HL_ATTR(HLF_D));
2948 msg_putchar(' ');
2949 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2950 }
2951
2952 if (!didh)
2953 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2954#ifdef FEAT_EVAL
2955 if (p_verbose > 0)
2956 last_set_msg(sgp->sg_script_ctx);
2957#endif
2958}
2959
2960 static int
2961highlight_list_arg(
2962 int id,
2963 int didh,
2964 int type,
2965 int iarg,
2966 char_u *sarg,
2967 char *name)
2968{
Bram Moolenaar84f54632022-06-29 18:39:11 +01002969 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002970 char_u *ts;
2971 int i;
2972
2973 if (got_int)
2974 return FALSE;
2975 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2976 {
2977 ts = buf;
2978 if (type == LIST_INT)
2979 sprintf((char *)buf, "%d", iarg - 1);
2980 else if (type == LIST_STRING)
2981 ts = sarg;
2982 else // type == LIST_ATTR
2983 {
2984 buf[0] = NUL;
2985 for (i = 0; hl_attr_table[i] != 0; ++i)
2986 {
2987 if (iarg & hl_attr_table[i])
2988 {
2989 if (buf[0] != NUL)
Bram Moolenaar84f54632022-06-29 18:39:11 +01002990 vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
2991 vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002992 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2993 }
2994 }
2995 }
2996
2997 (void)syn_list_header(didh,
2998 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2999 didh = TRUE;
3000 if (!got_int)
3001 {
3002 if (*name != NUL)
3003 {
3004 msg_puts_attr(name, HL_ATTR(HLF_D));
3005 msg_puts_attr("=", HL_ATTR(HLF_D));
3006 }
3007 msg_outtrans(ts);
3008 }
3009 }
3010 return didh;
3011}
3012
3013#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3014/*
3015 * Return "1" if highlight group "id" has attribute "flag".
3016 * Return NULL otherwise.
3017 */
3018 char_u *
3019highlight_has_attr(
3020 int id,
3021 int flag,
3022 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3023{
3024 int attr;
3025
3026 if (id <= 0 || id > highlight_ga.ga_len)
3027 return NULL;
3028
3029#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3030 if (modec == 'g')
3031 attr = HL_TABLE()[id - 1].sg_gui;
3032 else
3033#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003034 {
3035 if (modec == 'c')
3036 attr = HL_TABLE()[id - 1].sg_cterm;
3037 else
3038 attr = HL_TABLE()[id - 1].sg_term;
3039 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003040
3041 if (attr & flag)
3042 return (char_u *)"1";
3043 return NULL;
3044}
3045#endif
3046
3047#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3048/*
3049 * Return color name of highlight group "id".
3050 */
3051 char_u *
3052highlight_color(
3053 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003054 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003055 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3056{
3057 static char_u name[20];
3058 int n;
3059 int fg = FALSE;
3060 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003061 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003062 int font = FALSE;
3063
3064 if (id <= 0 || id > highlight_ga.ga_len)
3065 return NULL;
3066
3067 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3068 fg = TRUE;
3069 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3070 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3071 font = TRUE;
3072 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3073 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003074 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3075 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003076 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3077 return NULL;
3078 if (modec == 'g')
3079 {
3080# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3081# ifdef FEAT_GUI
3082 // return font name
3083 if (font)
3084 return HL_TABLE()[id - 1].sg_font_name;
3085# endif
3086
3087 // return #RRGGBB form (only possible when GUI is running)
3088 if ((USE_24BIT) && what[2] == '#')
3089 {
3090 guicolor_T color;
3091 long_u rgb;
3092 static char_u buf[10];
3093
3094 if (fg)
3095 color = HL_TABLE()[id - 1].sg_gui_fg;
3096 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003097 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003098 else
3099 color = HL_TABLE()[id - 1].sg_gui_bg;
3100 if (color == INVALCOLOR)
3101 return NULL;
3102 rgb = (long_u)GUI_MCH_GET_RGB(color);
3103 sprintf((char *)buf, "#%02x%02x%02x",
3104 (unsigned)(rgb >> 16),
3105 (unsigned)(rgb >> 8) & 255,
3106 (unsigned)rgb & 255);
3107 return buf;
3108 }
3109# endif
3110 if (fg)
3111 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3112 if (sp)
3113 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3114 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3115 }
3116 if (font || sp)
3117 return NULL;
3118 if (modec == 'c')
3119 {
3120 if (fg)
3121 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003122 else if (ul)
3123 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003124 else
3125 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3126 if (n < 0)
3127 return NULL;
3128 sprintf((char *)name, "%d", n);
3129 return name;
3130 }
3131 // term doesn't have color
3132 return NULL;
3133}
3134#endif
3135
3136#if (defined(FEAT_SYN_HL) \
3137 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3138 && defined(FEAT_PRINTER)) || defined(PROTO)
3139/*
3140 * Return color name of highlight group "id" as RGB value.
3141 */
3142 long_u
3143highlight_gui_color_rgb(
3144 int id,
3145 int fg) // TRUE = fg, FALSE = bg
3146{
3147 guicolor_T color;
3148
3149 if (id <= 0 || id > highlight_ga.ga_len)
3150 return 0L;
3151
3152 if (fg)
3153 color = HL_TABLE()[id - 1].sg_gui_fg;
3154 else
3155 color = HL_TABLE()[id - 1].sg_gui_bg;
3156
3157 if (color == INVALCOLOR)
3158 return 0L;
3159
3160 return GUI_MCH_GET_RGB(color);
3161}
3162#endif
3163
3164/*
3165 * Output the syntax list header.
3166 * Return TRUE when started a new line.
3167 */
3168 int
3169syn_list_header(
3170 int did_header, // did header already
3171 int outlen, // length of string that comes
3172 int id) // highlight group id
3173{
3174 int endcol = 19;
3175 int newline = TRUE;
3176 int name_col = 0;
3177
3178 if (!did_header)
3179 {
3180 msg_putchar('\n');
3181 if (got_int)
3182 return TRUE;
3183 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3184 name_col = msg_col;
3185 endcol = 15;
3186 }
3187 else if (msg_col + outlen + 1 >= Columns)
3188 {
3189 msg_putchar('\n');
3190 if (got_int)
3191 return TRUE;
3192 }
3193 else
3194 {
3195 if (msg_col >= endcol) // wrap around is like starting a new line
3196 newline = FALSE;
3197 }
3198
3199 if (msg_col >= endcol) // output at least one space
3200 endcol = msg_col + 1;
3201 if (Columns <= endcol) // avoid hang for tiny window
3202 endcol = Columns - 1;
3203
3204 msg_advance(endcol);
3205
3206 // Show "xxx" with the attributes.
3207 if (!did_header)
3208 {
3209 if (endcol == Columns - 1 && endcol <= name_col)
3210 msg_putchar(' ');
3211 msg_puts_attr("xxx", syn_id2attr(id));
3212 msg_putchar(' ');
3213 }
3214
3215 return newline;
3216}
3217
3218/*
3219 * Set the attribute numbers for a highlight group.
3220 * Called after one of the attributes has changed.
3221 */
3222 static void
3223set_hl_attr(
3224 int idx) // index in array
3225{
3226 attrentry_T at_en;
3227 hl_group_T *sgp = HL_TABLE() + idx;
3228
3229 // The "Normal" group doesn't need an attribute number
3230 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3231 return;
3232
3233#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003234 // For the GUI mode: If there are other than "normal" highlighting
3235 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003236 if (sgp->sg_gui_fg == INVALCOLOR
3237 && sgp->sg_gui_bg == INVALCOLOR
3238 && sgp->sg_gui_sp == INVALCOLOR
3239 && sgp->sg_font == NOFONT
3240# ifdef FEAT_XFONTSET
3241 && sgp->sg_fontset == NOFONTSET
3242# endif
3243 )
3244 {
3245 sgp->sg_gui_attr = sgp->sg_gui;
3246 }
3247 else
3248 {
3249 at_en.ae_attr = sgp->sg_gui;
3250 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3251 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3252 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3253 at_en.ae_u.gui.font = sgp->sg_font;
3254# ifdef FEAT_XFONTSET
3255 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3256# endif
3257 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3258 }
3259#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003260 // For the term mode: If there are other than "normal" highlighting
3261 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003262 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3263 sgp->sg_term_attr = sgp->sg_term;
3264 else
3265 {
3266 at_en.ae_attr = sgp->sg_term;
3267 at_en.ae_u.term.start = sgp->sg_start;
3268 at_en.ae_u.term.stop = sgp->sg_stop;
3269 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3270 }
3271
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003272 // For the color term mode: If there are other than "normal"
3273 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003274 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003275# ifdef FEAT_TERMGUICOLORS
3276 && sgp->sg_gui_fg == INVALCOLOR
3277 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003278 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003279# endif
3280 )
3281 sgp->sg_cterm_attr = sgp->sg_cterm;
3282 else
3283 {
3284 at_en.ae_attr = sgp->sg_cterm;
3285 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3286 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003287 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003288# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003289 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3290 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003291 // Only use the underline/undercurl color when used, it may clear the
3292 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003293 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3294 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003295 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3296 else
3297 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003298 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3299 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3300 {
3301 // If both fg and bg are invalid fall back to the cterm colors.
3302 // Helps when the GUI only uses an attribute, e.g. undercurl.
3303 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3304 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3305 }
3306# endif
3307 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3308 }
3309}
3310
3311/*
3312 * Lookup a highlight group name and return its ID.
3313 * If it is not found, 0 is returned.
3314 */
3315 int
3316syn_name2id(char_u *name)
3317{
3318 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003319 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003320
3321 // Avoid using stricmp() too much, it's slow on some systems
3322 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3323 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003324 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003325 vim_strup(name_u);
3326 for (i = highlight_ga.ga_len; --i >= 0; )
3327 if (HL_TABLE()[i].sg_name_u != NULL
3328 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3329 break;
3330 return i + 1;
3331}
3332
3333/*
3334 * Lookup a highlight group name and return its attributes.
3335 * Return zero if not found.
3336 */
3337 int
3338syn_name2attr(char_u *name)
3339{
3340 int id = syn_name2id(name);
3341
3342 if (id != 0)
3343 return syn_id2attr(id);
3344 return 0;
3345}
3346
3347#if defined(FEAT_EVAL) || defined(PROTO)
3348/*
3349 * Return TRUE if highlight group "name" exists.
3350 */
3351 int
3352highlight_exists(char_u *name)
3353{
3354 return (syn_name2id(name) > 0);
3355}
3356
3357# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3358/*
3359 * Return the name of highlight group "id".
3360 * When not a valid ID return an empty string.
3361 */
3362 char_u *
3363syn_id2name(int id)
3364{
3365 if (id <= 0 || id > highlight_ga.ga_len)
3366 return (char_u *)"";
3367 return HL_TABLE()[id - 1].sg_name;
3368}
3369# endif
3370#endif
3371
3372/*
3373 * Like syn_name2id(), but take a pointer + length argument.
3374 */
3375 int
3376syn_namen2id(char_u *linep, int len)
3377{
3378 char_u *name;
3379 int id = 0;
3380
3381 name = vim_strnsave(linep, len);
3382 if (name != NULL)
3383 {
3384 id = syn_name2id(name);
3385 vim_free(name);
3386 }
3387 return id;
3388}
3389
3390/*
3391 * Find highlight group name in the table and return its ID.
3392 * The argument is a pointer to the name and the length of the name.
3393 * If it doesn't exist yet, a new entry is created.
3394 * Return 0 for failure.
3395 */
3396 int
3397syn_check_group(char_u *pp, int len)
3398{
3399 int id;
3400 char_u *name;
3401
erw7f7f7aaf2021-12-07 21:29:20 +00003402 if (len > MAX_SYN_NAME)
3403 {
3404 emsg(_(e_highlight_group_name_too_long));
3405 return 0;
3406 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003407 name = vim_strnsave(pp, len);
3408 if (name == NULL)
3409 return 0;
3410
3411 id = syn_name2id(name);
3412 if (id == 0) // doesn't exist yet
3413 id = syn_add_group(name);
3414 else
3415 vim_free(name);
3416 return id;
3417}
3418
3419/*
3420 * Add new highlight group and return its ID.
3421 * "name" must be an allocated string, it will be consumed.
3422 * Return 0 for failure.
3423 */
3424 static int
3425syn_add_group(char_u *name)
3426{
3427 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003428 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003429
3430 // Check that the name is ASCII letters, digits and underscore.
3431 for (p = name; *p != NUL; ++p)
3432 {
3433 if (!vim_isprintc(*p))
3434 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003435 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003436 vim_free(name);
3437 return 0;
3438 }
3439 else if (!ASCII_ISALNUM(*p) && *p != '_')
3440 {
3441 // This is an error, but since there previously was no check only
3442 // give a warning.
3443 msg_source(HL_ATTR(HLF_W));
3444 msg(_("W18: Invalid character in group name"));
3445 break;
3446 }
3447 }
3448
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003449 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003450 if (highlight_ga.ga_data == NULL)
3451 {
3452 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3453 highlight_ga.ga_growsize = 10;
3454 }
3455
3456 if (highlight_ga.ga_len >= MAX_HL_ID)
3457 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003458 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003459 vim_free(name);
3460 return 0;
3461 }
3462
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003463 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003464 if (ga_grow(&highlight_ga, 1) == FAIL)
3465 {
3466 vim_free(name);
3467 return 0;
3468 }
3469
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003470 name_up = vim_strsave_up(name);
3471 if (name_up == NULL)
3472 {
3473 vim_free(name);
3474 return 0;
3475 }
3476
Bram Moolenaara80faa82020-04-12 19:37:17 +02003477 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003478 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003479 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003480#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3481 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3482 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003483 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003484#endif
3485 ++highlight_ga.ga_len;
3486
3487 return highlight_ga.ga_len; // ID is index plus one
3488}
3489
3490/*
3491 * When, just after calling syn_add_group(), an error is discovered, this
3492 * function deletes the new name.
3493 */
3494 static void
3495syn_unadd_group(void)
3496{
3497 --highlight_ga.ga_len;
3498 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3499 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3500}
3501
3502/*
3503 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003504 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003505 */
3506 int
3507syn_id2attr(int hl_id)
3508{
3509 int attr;
3510 hl_group_T *sgp;
3511
3512 hl_id = syn_get_final_id(hl_id);
3513 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3514
3515#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003516 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003517 if (gui.in_use)
3518 attr = sgp->sg_gui_attr;
3519 else
3520#endif
3521 if (IS_CTERM)
3522 attr = sgp->sg_cterm_attr;
3523 else
3524 attr = sgp->sg_term_attr;
3525
3526 return attr;
3527}
3528
3529#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3530/*
3531 * Get the GUI colors and attributes for a group ID.
3532 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3533 */
3534 int
3535syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3536{
3537 hl_group_T *sgp;
3538
3539 hl_id = syn_get_final_id(hl_id);
3540 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3541
3542 *fgp = sgp->sg_gui_fg;
3543 *bgp = sgp->sg_gui_bg;
3544 return sgp->sg_gui;
3545}
3546#endif
3547
3548#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003549 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3550 && defined(FEAT_TERMGUICOLORS)) \
3551 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003552 void
3553syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3554{
3555 hl_group_T *sgp;
3556
3557 hl_id = syn_get_final_id(hl_id);
3558 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3559 *fgp = sgp->sg_cterm_fg - 1;
3560 *bgp = sgp->sg_cterm_bg - 1;
3561}
3562#endif
3563
3564/*
3565 * Translate a group ID to the final group ID (following links).
3566 */
3567 int
3568syn_get_final_id(int hl_id)
3569{
3570 int count;
3571 hl_group_T *sgp;
3572
3573 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3574 return 0; // Can be called from eval!!
3575
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003576 // Follow links until there is no more.
3577 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003578 for (count = 100; --count >= 0; )
3579 {
3580 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3581 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3582 break;
3583 hl_id = sgp->sg_link;
3584 }
3585
3586 return hl_id;
3587}
3588
3589#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3590/*
3591 * Call this function just after the GUI has started.
3592 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3593 * It finds the font and color handles for the highlighting groups.
3594 */
3595 void
3596highlight_gui_started(void)
3597{
3598 int idx;
3599
3600 // First get the colors from the "Normal" and "Menu" group, if set
3601 if (USE_24BIT)
3602 set_normal_colors();
3603
3604 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3605 gui_do_one_color(idx, FALSE, FALSE);
3606
3607 highlight_changed();
3608}
3609
3610 static void
3611gui_do_one_color(
3612 int idx,
3613 int do_menu UNUSED, // TRUE: might set the menu font
3614 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3615{
3616 int didit = FALSE;
3617
3618# ifdef FEAT_GUI
3619# ifdef FEAT_TERMGUICOLORS
3620 if (gui.in_use)
3621# endif
3622 if (HL_TABLE()[idx].sg_font_name != NULL)
3623 {
3624 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3625 do_tooltip, TRUE);
3626 didit = TRUE;
3627 }
3628# endif
3629 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3630 {
3631 HL_TABLE()[idx].sg_gui_fg =
3632 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3633 didit = TRUE;
3634 }
3635 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3636 {
3637 HL_TABLE()[idx].sg_gui_bg =
3638 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3639 didit = TRUE;
3640 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003641 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3642 {
3643 HL_TABLE()[idx].sg_gui_sp =
3644 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3645 didit = TRUE;
3646 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003647 if (didit) // need to get a new attr number
3648 set_hl_attr(idx);
3649}
3650#endif
3651
3652#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3653/*
3654 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3655 */
3656 static void
3657combine_stl_hlt(
3658 int id,
3659 int id_S,
3660 int id_alt,
3661 int hlcnt,
3662 int i,
3663 int hlf,
3664 int *table)
3665{
3666 hl_group_T *hlt = HL_TABLE();
3667
3668 if (id_alt == 0)
3669 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003670 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003671 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3672 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3673# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3674 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3675# endif
3676 }
3677 else
3678 mch_memmove(&hlt[hlcnt + i],
3679 &hlt[id_alt - 1],
3680 sizeof(hl_group_T));
3681 hlt[hlcnt + i].sg_link = 0;
3682
3683 hlt[hlcnt + i].sg_term ^=
3684 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3685 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3686 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3687 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3688 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3689 hlt[hlcnt + i].sg_cterm ^=
3690 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3691 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3692 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3693 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3694 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3695# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3696 hlt[hlcnt + i].sg_gui ^=
3697 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3698# endif
3699# ifdef FEAT_GUI
3700 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3701 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3702 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3703 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3704 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3705 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3706 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3707 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3708# ifdef FEAT_XFONTSET
3709 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3710 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3711# endif
3712# endif
3713 highlight_ga.ga_len = hlcnt + i + 1;
3714 set_hl_attr(hlcnt + i); // At long last we can apply
3715 table[i] = syn_id2attr(hlcnt + i + 1);
3716}
3717#endif
3718
3719/*
3720 * Translate the 'highlight' option into attributes in highlight_attr[] and
3721 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3722 * corresponding highlights to use on top of HLF_SNC is computed.
3723 * Called only when the 'highlight' option has been changed and upon first
3724 * screen redraw after any :highlight command.
3725 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3726 */
3727 int
3728highlight_changed(void)
3729{
3730 int hlf;
3731 int i;
3732 char_u *p;
3733 int attr;
3734 char_u *end;
3735 int id;
3736#ifdef USER_HIGHLIGHT
3737 char_u userhl[30]; // use 30 to avoid compiler warning
3738# ifdef FEAT_STL_OPT
3739 int id_S = -1;
3740 int id_SNC = 0;
3741# ifdef FEAT_TERMINAL
3742 int id_ST = 0;
3743 int id_STNC = 0;
3744# endif
3745 int hlcnt;
3746# endif
3747#endif
3748 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3749
3750 need_highlight_changed = FALSE;
3751
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003752#ifdef FEAT_TERMINAL
3753 term_update_colors_all();
3754 term_update_wincolor_all();
3755#endif
3756
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003757 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003758 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3759 highlight_attr[hlf] = 0;
3760
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003761 // First set all attributes to their default value.
3762 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003763 for (i = 0; i < 2; ++i)
3764 {
3765 if (i)
3766 p = p_hl;
3767 else
3768 p = get_highlight_default();
3769 if (p == NULL) // just in case
3770 continue;
3771
3772 while (*p)
3773 {
3774 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3775 if (hl_flags[hlf] == *p)
3776 break;
3777 ++p;
3778 if (hlf == (int)HLF_COUNT || *p == NUL)
3779 return FAIL;
3780
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003781 // Allow several hl_flags to be combined, like "bu" for
3782 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003783 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003784 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003785 {
3786 if (VIM_ISWHITE(*p)) // ignore white space
3787 continue;
3788
3789 if (attr > HL_ALL) // Combination with ':' is not allowed.
3790 return FAIL;
3791
3792 switch (*p)
3793 {
3794 case 'b': attr |= HL_BOLD;
3795 break;
3796 case 'i': attr |= HL_ITALIC;
3797 break;
3798 case '-':
3799 case 'n': // no highlighting
3800 break;
3801 case 'r': attr |= HL_INVERSE;
3802 break;
3803 case 's': attr |= HL_STANDOUT;
3804 break;
3805 case 'u': attr |= HL_UNDERLINE;
3806 break;
3807 case 'c': attr |= HL_UNDERCURL;
3808 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003809 case '2': attr |= HL_UNDERDOUBLE;
3810 break;
3811 case 'd': attr |= HL_UNDERDOTTED;
3812 break;
3813 case '=': attr |= HL_UNDERDASHED;
3814 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003815 case 't': attr |= HL_STRIKETHROUGH;
3816 break;
3817 case ':': ++p; // highlight group name
3818 if (attr || *p == NUL) // no combinations
3819 return FAIL;
3820 end = vim_strchr(p, ',');
3821 if (end == NULL)
3822 end = p + STRLEN(p);
3823 id = syn_check_group(p, (int)(end - p));
3824 if (id == 0)
3825 return FAIL;
3826 attr = syn_id2attr(id);
3827 p = end - 1;
3828#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3829 if (hlf == (int)HLF_SNC)
3830 id_SNC = syn_get_final_id(id);
3831# ifdef FEAT_TERMINAL
3832 else if (hlf == (int)HLF_ST)
3833 id_ST = syn_get_final_id(id);
3834 else if (hlf == (int)HLF_STNC)
3835 id_STNC = syn_get_final_id(id);
3836# endif
3837 else if (hlf == (int)HLF_S)
3838 id_S = syn_get_final_id(id);
3839#endif
3840 break;
3841 default: return FAIL;
3842 }
3843 }
3844 highlight_attr[hlf] = attr;
3845
3846 p = skip_to_option_part(p); // skip comma and spaces
3847 }
3848 }
3849
3850#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003851 // Setup the user highlights
3852 //
3853 // Temporarily utilize 28 more hl entries:
3854 // 9 for User1-User9 combined with StatusLineNC
3855 // 9 for User1-User9 combined with StatusLineTerm
3856 // 9 for User1-User9 combined with StatusLineTermNC
3857 // 1 for StatusLine default
3858 // Have to be in there simultaneously in case of table overflows in
3859 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003860# ifdef FEAT_STL_OPT
3861 if (ga_grow(&highlight_ga, 28) == FAIL)
3862 return FAIL;
3863 hlcnt = highlight_ga.ga_len;
3864 if (id_S == -1)
3865 {
3866 // Make sure id_S is always valid to simplify code below. Use the last
3867 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003868 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003869 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3870 id_S = hlcnt + 19;
3871 }
3872# endif
3873 for (i = 0; i < 9; i++)
3874 {
3875 sprintf((char *)userhl, "User%d", i + 1);
3876 id = syn_name2id(userhl);
3877 if (id == 0)
3878 {
3879 highlight_user[i] = 0;
3880# ifdef FEAT_STL_OPT
3881 highlight_stlnc[i] = 0;
3882# ifdef FEAT_TERMINAL
3883 highlight_stlterm[i] = 0;
3884 highlight_stltermnc[i] = 0;
3885# endif
3886# endif
3887 }
3888 else
3889 {
3890 highlight_user[i] = syn_id2attr(id);
3891# ifdef FEAT_STL_OPT
3892 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3893 HLF_SNC, highlight_stlnc);
3894# ifdef FEAT_TERMINAL
3895 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3896 HLF_ST, highlight_stlterm);
3897 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3898 HLF_STNC, highlight_stltermnc);
3899# endif
3900# endif
3901 }
3902 }
3903# ifdef FEAT_STL_OPT
3904 highlight_ga.ga_len = hlcnt;
3905# endif
3906
3907#endif // USER_HIGHLIGHT
3908
3909 return OK;
3910}
3911
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003912static void highlight_list(void);
3913static void highlight_list_two(int cnt, int attr);
3914
3915/*
3916 * Handle command line completion for :highlight command.
3917 */
3918 void
3919set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3920{
3921 char_u *p;
3922
3923 // Default: expand group names
3924 xp->xp_context = EXPAND_HIGHLIGHT;
3925 xp->xp_pattern = arg;
3926 include_link = 2;
3927 include_default = 1;
3928
3929 // (part of) subcommand already typed
3930 if (*arg != NUL)
3931 {
3932 p = skiptowhite(arg);
3933 if (*p != NUL) // past "default" or group name
3934 {
3935 include_default = 0;
3936 if (STRNCMP("default", arg, p - arg) == 0)
3937 {
3938 arg = skipwhite(p);
3939 xp->xp_pattern = arg;
3940 p = skiptowhite(arg);
3941 }
3942 if (*p != NUL) // past group name
3943 {
3944 include_link = 0;
3945 if (arg[1] == 'i' && arg[0] == 'N')
3946 highlight_list();
3947 if (STRNCMP("link", arg, p - arg) == 0
3948 || STRNCMP("clear", arg, p - arg) == 0)
3949 {
3950 xp->xp_pattern = skipwhite(p);
3951 p = skiptowhite(xp->xp_pattern);
3952 if (*p != NUL) // past first group name
3953 {
3954 xp->xp_pattern = skipwhite(p);
3955 p = skiptowhite(xp->xp_pattern);
3956 }
3957 }
3958 if (*p != NUL) // past group name(s)
3959 xp->xp_context = EXPAND_NOTHING;
3960 }
3961 }
3962 }
3963}
3964
3965/*
3966 * List highlighting matches in a nice way.
3967 */
3968 static void
3969highlight_list(void)
3970{
3971 int i;
3972
3973 for (i = 10; --i >= 0; )
3974 highlight_list_two(i, HL_ATTR(HLF_D));
3975 for (i = 40; --i >= 0; )
3976 highlight_list_two(99, 0);
3977}
3978
3979 static void
3980highlight_list_two(int cnt, int attr)
3981{
3982 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3983 msg_clr_eos();
3984 out_flush();
3985 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3986}
3987
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003988/*
3989 * Function given to ExpandGeneric() to obtain the list of group names.
3990 */
3991 char_u *
3992get_highlight_name(expand_T *xp UNUSED, int idx)
3993{
3994 return get_highlight_name_ext(xp, idx, TRUE);
3995}
3996
3997/*
3998 * Obtain a highlight group name.
3999 * When "skip_cleared" is TRUE don't return a cleared entry.
4000 */
4001 char_u *
4002get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4003{
4004 if (idx < 0)
4005 return NULL;
4006
4007 // Items are never removed from the table, skip the ones that were
4008 // cleared.
4009 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4010 return (char_u *)"";
4011
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004012 if (idx == highlight_ga.ga_len && include_none != 0)
4013 return (char_u *)"none";
4014 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4015 return (char_u *)"default";
4016 if (idx == highlight_ga.ga_len + include_none + include_default
4017 && include_link != 0)
4018 return (char_u *)"link";
4019 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4020 && include_link != 0)
4021 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004022 if (idx >= highlight_ga.ga_len)
4023 return NULL;
4024 return HL_TABLE()[idx].sg_name;
4025}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004026
4027#if defined(FEAT_GUI) || defined(PROTO)
4028/*
4029 * Free all the highlight group fonts.
4030 * Used when quitting for systems which need it.
4031 */
4032 void
4033free_highlight_fonts(void)
4034{
4035 int idx;
4036
4037 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4038 {
4039 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4040 HL_TABLE()[idx].sg_font = NOFONT;
4041# ifdef FEAT_XFONTSET
4042 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4043 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4044# endif
4045 }
4046
4047 gui_mch_free_font(gui.norm_font);
4048# ifdef FEAT_XFONTSET
4049 gui_mch_free_fontset(gui.fontset);
4050# endif
4051# ifndef FEAT_GUI_GTK
4052 gui_mch_free_font(gui.bold_font);
4053 gui_mch_free_font(gui.ital_font);
4054 gui_mch_free_font(gui.boldital_font);
4055# endif
4056}
4057#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004058
4059#if defined(FEAT_EVAL) || defined(PROTO)
4060/*
4061 * Convert each of the highlight attribute bits (bold, standout, underline,
4062 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4063 * the attribute name as the key.
4064 */
4065 static dict_T *
4066highlight_get_attr_dict(int hlattr)
4067{
4068 dict_T *dict;
4069 int i;
4070
4071 dict = dict_alloc();
4072 if (dict == NULL)
4073 return NULL;
4074
4075 for (i = 0; hl_attr_table[i] != 0; ++i)
4076 {
4077 if (hlattr & hl_attr_table[i])
4078 {
4079 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4080 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4081 }
4082 }
4083
4084 return dict;
4085}
4086
4087/*
4088 * Return the attributes of the highlight group at index 'hl_idx' as a
4089 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4090 * links recursively.
4091 */
4092 static dict_T *
4093highlight_get_info(int hl_idx, int resolve_link)
4094{
4095 dict_T *dict;
4096 hl_group_T *sgp;
4097 dict_T *attr_dict;
4098 int hlgid;
4099
4100 dict = dict_alloc();
4101 if (dict == NULL)
4102 return dict;
4103
4104 sgp = &HL_TABLE()[hl_idx];
4105 // highlight group id is 1-based
4106 hlgid = hl_idx + 1;
4107
4108 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4109 goto error;
4110 if (dict_add_number(dict, "id", hlgid) == FAIL)
4111 goto error;
4112
4113 if (sgp->sg_link && resolve_link)
4114 {
4115 // resolve the highlight group link recursively
4116 while (sgp->sg_link)
4117 {
4118 hlgid = sgp->sg_link;
4119 sgp = &HL_TABLE()[sgp->sg_link - 1];
4120 }
4121 }
4122
4123 if (sgp->sg_term != 0)
4124 {
4125 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4126 if (attr_dict != NULL)
4127 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4128 goto error;
4129 }
4130 if (sgp->sg_start != NULL)
4131 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4132 goto error;
4133 if (sgp->sg_stop != NULL)
4134 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4135 goto error;
4136 if (sgp->sg_cterm != 0)
4137 {
4138 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4139 if (attr_dict != NULL)
4140 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4141 goto error;
4142 }
4143 if (sgp->sg_cterm_fg != 0)
4144 if (dict_add_string(dict, "ctermfg",
4145 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4146 goto error;
4147 if (sgp->sg_cterm_bg != 0)
4148 if (dict_add_string(dict, "ctermbg",
4149 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4150 goto error;
4151 if (sgp->sg_cterm_ul != 0)
4152 if (dict_add_string(dict, "ctermul",
4153 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4154 goto error;
4155 if (sgp->sg_gui != 0)
4156 {
4157 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4158 if (attr_dict != NULL)
4159 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4160 goto error;
4161 }
4162 if (sgp->sg_gui_fg_name != NULL)
4163 if (dict_add_string(dict, "guifg",
4164 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4165 goto error;
4166 if (sgp->sg_gui_bg_name != NULL)
4167 if (dict_add_string(dict, "guibg",
4168 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4169 goto error;
4170 if (sgp->sg_gui_sp_name != NULL)
4171 if (dict_add_string(dict, "guisp",
4172 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4173 goto error;
4174# ifdef FEAT_GUI
4175 if (sgp->sg_font_name != NULL)
4176 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4177 goto error;
4178# endif
4179 if (sgp->sg_link)
4180 {
4181 char_u *link;
4182
4183 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4184 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4185 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004186
4187 if (sgp->sg_deflink)
4188 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004189 }
4190 if (dict_len(dict) == 2)
4191 // If only 'name' is present, then the highlight group is cleared.
4192 dict_add_bool(dict, "cleared", VVAL_TRUE);
4193
4194 return dict;
4195
4196error:
4197 vim_free(dict);
4198 return NULL;
4199}
4200
4201/*
4202 * "hlget([name])" function
4203 * Return the attributes of a specific highlight group (if specified) or all
4204 * the highlight groups.
4205 */
4206 void
4207f_hlget(typval_T *argvars, typval_T *rettv)
4208{
4209 list_T *list;
4210 dict_T *dict;
4211 int i;
4212 char_u *hlarg = NULL;
4213 int resolve_link = FALSE;
4214
4215 if (rettv_list_alloc(rettv) == FAIL)
4216 return;
4217
4218 if (check_for_opt_string_arg(argvars, 0) == FAIL
4219 || (argvars[0].v_type != VAR_UNKNOWN
4220 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4221 return;
4222
4223 if (argvars[0].v_type != VAR_UNKNOWN)
4224 {
4225 // highlight group name supplied
4226 hlarg = tv_get_string_chk(&argvars[0]);
4227 if (hlarg == NULL)
4228 return;
4229
4230 if (argvars[1].v_type != VAR_UNKNOWN)
4231 {
4232 int error = FALSE;
4233
4234 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4235 if (error)
4236 return;
4237 }
4238 }
4239
4240 list = rettv->vval.v_list;
4241 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4242 {
4243 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4244 {
4245 dict = highlight_get_info(i, resolve_link);
4246 if (dict != NULL)
4247 list_append_dict(list, dict);
4248 }
4249 }
4250}
4251
4252/*
4253 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4254 * 'dict' or the value is not a string type. If the value is not a string type
4255 * or is NULL, then 'error' is set to TRUE.
4256 */
4257 static char_u *
4258hldict_get_string(dict_T *dict, char_u *key, int *error)
4259{
4260 dictitem_T *di;
4261
4262 *error = FALSE;
4263 di = dict_find(dict, key, -1);
4264 if (di == NULL)
4265 return NULL;
4266
4267 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4268 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004269 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004270 *error = TRUE;
4271 return NULL;
4272 }
4273
4274 return di->di_tv.vval.v_string;
4275}
4276
4277/*
4278 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4279 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4280 * Dictionary or is NULL.
4281 */
4282 static int
4283hldict_attr_to_str(
4284 dict_T *dict,
4285 char_u *key,
4286 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004287 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004288{
4289 dictitem_T *di;
4290 dict_T *attrdict;
4291 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004292 char_u *p;
4293 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004294
4295 attr_str[0] = NUL;
4296 di = dict_find(dict, key, -1);
4297 if (di == NULL)
4298 return TRUE;
4299
4300 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4301 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004302 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004303 return FALSE;
4304 }
4305
4306 attrdict = di->di_tv.vval.v_dict;
4307
4308 // If the attribute dict is empty, then return NONE to clear the attributes
4309 if (dict_len(attrdict) == 0)
4310 {
4311 vim_strcat(attr_str, (char_u *)"NONE", len);
4312 return TRUE;
4313 }
4314
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004315 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004316 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4317 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01004318 if (dict_get_bool(attrdict, hl_name_table[i], VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004319 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004320 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4321 STRCPY(p, (char_u *)",");
4322 sz = STRLEN(hl_name_table[i]);
4323 if (p - attr_str + sz + 1 < len)
4324 {
4325 STRCPY(p, (char_u *)hl_name_table[i]);
4326 p += sz;
4327 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004328 }
4329 }
4330
4331 return TRUE;
4332}
4333
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004334// Temporary buffer used to store the command string produced by hlset().
4335// IObuff cannot be used for this as the error messages produced by hlset()
4336// internally use IObuff.
4337#define HLSETBUFSZ 512
4338static char_u hlsetBuf[HLSETBUFSZ + 1];
4339
4340/*
4341 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4342 * "dptr", which points into "hlsetBuf".
4343 * Returns the updated pointer.
4344 */
4345 static char_u *
4346add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4347{
4348 size_t vallen;
4349
4350 // Do nothing if the value is not specified or is empty
4351 if (value == NULL || *value == NUL)
4352 return dptr;
4353
4354 vallen = STRLEN(value);
4355 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4356 {
4357 STRCPY(dptr, attr);
4358 dptr += attrlen;
4359 STRCPY(dptr, value);
4360 dptr += vallen;
4361 }
4362
4363 return dptr;
4364}
4365
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004366/*
4367 * Add or update a highlight group using 'dict' items. Returns TRUE if
4368 * successfully updated the highlight group.
4369 */
4370 static int
4371hlg_add_or_update(dict_T *dict)
4372{
4373 char_u *name;
4374 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004375 char_u term_attr[MAX_ATTR_LEN];
4376 char_u cterm_attr[MAX_ATTR_LEN];
4377 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004378 char_u *start;
4379 char_u *stop;
4380 char_u *ctermfg;
4381 char_u *ctermbg;
4382 char_u *ctermul;
4383 char_u *guifg;
4384 char_u *guibg;
4385 char_u *guisp;
4386# ifdef FEAT_GUI
4387 char_u *font;
4388# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004389 int forceit = FALSE;
4390 int dodefault = FALSE;
4391 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004392 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004393
4394 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004395 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004396 return FALSE;
4397
Bram Moolenaard61efa52022-07-23 09:52:04 +01004398 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004399 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004400
Bram Moolenaard61efa52022-07-23 09:52:04 +01004401 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004402 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004403
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004404 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004405 {
4406 varnumber_T cleared;
4407
4408 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004409 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004410 if (cleared == TRUE)
4411 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004412 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4413 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004414 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004415 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004416 }
4417
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004418 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004419 {
4420 char_u *linksto;
4421
4422 // link highlight groups
4423 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004424 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004425 return FALSE;
4426
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004427 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004428 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004429 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004430
4431 done = TRUE;
4432 }
4433
4434 // If 'cleared' or 'linksto' are specified, then don't process the other
4435 // attributes.
4436 if (done)
4437 return TRUE;
4438
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004439 start = hldict_get_string(dict, (char_u *)"start", &error);
4440 if (error)
4441 return FALSE;
4442
4443 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4444 if (error)
4445 return FALSE;
4446
4447 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004448 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004449 return FALSE;
4450
4451 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004452 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004453 return FALSE;
4454
4455 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4456 if (error)
4457 return FALSE;
4458
4459 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4460 if (error)
4461 return FALSE;
4462
4463 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4464 if (error)
4465 return FALSE;
4466
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004467 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004468 return FALSE;
4469
4470 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4471 if (error)
4472 return FALSE;
4473
4474 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4475 if (error)
4476 return FALSE;
4477
4478 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4479 if (error)
4480 return FALSE;
4481
4482# ifdef FEAT_GUI
4483 font = hldict_get_string(dict, (char_u *)"font", &error);
4484 if (error)
4485 return FALSE;
4486# endif
4487
4488 // If none of the attributes are specified, then do nothing.
4489 if (term_attr[0] == NUL && start == NULL && stop == NULL
4490 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4491 && ctermul == NULL && gui_attr[0] == NUL
4492# ifdef FEAT_GUI
4493 && font == NULL
4494# endif
4495 && guifg == NULL && guibg == NULL && guisp == NULL
4496 )
4497 return TRUE;
4498
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004499 hlsetBuf[0] = NUL;
4500 p = hlsetBuf;
4501 if (dodefault)
4502 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4503 p = add_attr_and_value(p, (char_u *)"", 0, name);
4504 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4505 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4506 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4507 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4508 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4509 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4510 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4511 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004512# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004513 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004514# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004515 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4516 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
4517 p = add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004518
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004519 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004520
4521 return TRUE;
4522}
4523
4524/*
4525 * "hlset([{highlight_attr}])" function
4526 * Add or modify highlight groups
4527 */
4528 void
4529f_hlset(typval_T *argvars, typval_T *rettv)
4530{
4531 listitem_T *li;
4532 dict_T *dict;
4533
4534 rettv->vval.v_number = -1;
4535
4536 if (check_for_list_arg(argvars, 0) == FAIL)
4537 return;
4538
4539 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4540 {
4541 if (li->li_tv.v_type != VAR_DICT)
4542 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004543 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004544 return;
4545 }
4546
4547 dict = li->li_tv.vval.v_dict;
4548 if (!hlg_add_or_update(dict))
4549 return;
4550 }
4551
4552 rettv->vval.v_number = 0;
4553}
4554#endif