blob: 95cdb46b5ab6fa8cc54731e3028ae59b68aa4a6c [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 {
1432 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1433 // TODO: only call when the group has attributes set
1434 highlight_list_one((int)i);
1435 return;
1436 }
1437
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001438 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001439 name_end = skiptowhite(line);
1440 linep = skipwhite(name_end);
1441
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001442 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001443 if (STRNCMP(line, "default", name_end - line) == 0)
1444 {
1445 dodefault = TRUE;
1446 line = linep;
1447 name_end = skiptowhite(line);
1448 linep = skipwhite(name_end);
1449 }
1450
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001451 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001452 if (STRNCMP(line, "clear", name_end - line) == 0)
1453 doclear = TRUE;
1454 if (STRNCMP(line, "link", name_end - line) == 0)
1455 dolink = TRUE;
1456
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001457 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001458 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001459 {
1460 id = syn_namen2id(line, (int)(name_end - line));
1461 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001462 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001463 else
1464 highlight_list_one(id);
1465 return;
1466 }
1467
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001468 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001469 if (dolink)
1470 {
1471 char_u *from_start = linep;
1472 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001473 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001474 char_u *to_start;
1475 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001476 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001477
1478 from_end = skiptowhite(from_start);
1479 to_start = skipwhite(from_end);
1480 to_end = skiptowhite(to_start);
1481
Bram Moolenaar1966c242020-04-20 22:42:32 +02001482 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001483 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001484 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001485 return;
1486 }
1487
Bram Moolenaar1966c242020-04-20 22:42:32 +02001488 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001489 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001490 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001491 return;
1492 }
1493
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001494 from_len = (int)(from_end - from_start);
1495 to_len = (int)(to_end - to_start);
1496 highlight_group_link(from_start, from_len, to_start, to_len,
1497 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001498 return;
1499 }
1500
1501 if (doclear)
1502 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001503 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001504 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001505 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001506 // ":highlight clear" without group name
1507 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001508 return;
1509 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001510 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001511 name_end = skiptowhite(line);
1512 linep = skipwhite(name_end);
1513 }
1514
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001515 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001516 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001517 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001518 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001519 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001520
1521 // Return if "default" was used and the group already has settings.
1522 if (dodefault && hl_has_settings(idx, TRUE))
1523 return;
1524
1525 // Make a copy so we can check if any attribute actually changed.
1526 item_before = HL_TABLE()[idx];
1527
1528 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1529 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001530#ifdef FEAT_GUI_X11
1531 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1532 is_menu_group = TRUE;
1533 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1534 is_scrollbar_group = TRUE;
1535 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1536 is_tooltip_group = TRUE;
1537#endif
1538
1539 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1540 if (doclear || (forceit && init))
1541 {
1542 highlight_clear(idx);
1543 if (!doclear)
1544 HL_TABLE()[idx].sg_set = 0;
1545 }
1546
1547 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001548 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001549 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001550 key_start = linep;
1551 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001552 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001553 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001554 error = TRUE;
1555 break;
1556 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001557
1558 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1559 // or "guibg").
1560 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1561 ++linep;
1562 vim_free(key);
1563 key = vim_strnsave_up(key_start, linep - key_start);
1564 if (key == NULL)
1565 {
1566 error = TRUE;
1567 break;
1568 }
1569 linep = skipwhite(linep);
1570
1571 if (STRCMP(key, "NONE") == 0)
1572 {
1573 if (!init || HL_TABLE()[idx].sg_set == 0)
1574 {
1575 if (!init)
1576 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1577 highlight_clear(idx);
1578 }
1579 continue;
1580 }
1581
1582 // Check for the equal sign.
1583 if (*linep != '=')
1584 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001585 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001586 error = TRUE;
1587 break;
1588 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001589 ++linep;
1590
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001591 // Isolate the argument.
1592 linep = skipwhite(linep);
1593 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001594 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001595 arg_start = ++linep;
1596 linep = vim_strchr(linep, '\'');
1597 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001598 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001599 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001600 error = TRUE;
1601 break;
1602 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001603 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001604 else
1605 {
1606 arg_start = linep;
1607 linep = skiptowhite(linep);
1608 }
1609 if (linep == arg_start)
1610 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001611 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001612 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001613 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001614 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001615 vim_free(arg);
1616 arg = vim_strnsave(arg_start, linep - arg_start);
1617 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001618 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001619 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001620 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001621 }
1622 if (*linep == '\'')
1623 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001624
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001625 // Store the argument.
1626 if (STRCMP(key, "TERM") == 0
1627 || STRCMP(key, "CTERM") == 0
1628 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001629 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001630 if (!highlight_set_termgui_attr(idx, key, arg, init))
1631 {
1632 error = TRUE;
1633 break;
1634 }
1635 }
1636 else if (STRCMP(key, "FONT") == 0)
1637 {
1638 // in non-GUI fonts are simply ignored
1639#ifdef FEAT_GUI
1640 if (highlight_set_font(idx, arg, is_normal_group,
1641 is_menu_group, is_tooltip_group))
1642 did_change = TRUE;
1643#endif
1644 }
1645 else if (STRCMP(key, "CTERMFG") == 0
1646 || STRCMP(key, "CTERMBG") == 0
1647 || STRCMP(key, "CTERMUL") == 0)
1648 {
1649 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1650 is_normal_group, init))
1651 {
1652 error = TRUE;
1653 break;
1654 }
1655 }
1656 else if (STRCMP(key, "GUIFG") == 0)
1657 {
1658#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1659 if (highlight_set_guifg(idx, arg, is_menu_group,
1660 is_scrollbar_group, is_tooltip_group,
1661 &do_colors, init))
1662 did_change = TRUE;
1663#endif
1664 }
1665 else if (STRCMP(key, "GUIBG") == 0)
1666 {
1667#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1668 if (highlight_set_guibg(idx, arg, is_menu_group,
1669 is_scrollbar_group, is_tooltip_group,
1670 &do_colors, init))
1671 did_change = TRUE;
1672#endif
1673 }
1674 else if (STRCMP(key, "GUISP") == 0)
1675 {
1676#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1677 if (highlight_set_guisp(idx, arg, init))
1678 did_change = TRUE;
1679#endif
1680 }
1681 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1682 {
1683 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1684 {
1685 error = TRUE;
1686 break;
1687 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001688 }
1689 else
1690 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001691 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001692 error = TRUE;
1693 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001694 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001695 HL_TABLE()[idx].sg_cleared = FALSE;
1696
1697 // When highlighting has been given for a group, don't link it.
1698 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1699 HL_TABLE()[idx].sg_link = 0;
1700
1701 // Continue with next argument.
1702 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001703 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001704
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001705 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001706 if (error && idx == highlight_ga.ga_len)
1707 syn_unadd_group();
1708 else
1709 {
1710 if (is_normal_group)
1711 {
1712 HL_TABLE()[idx].sg_term_attr = 0;
1713 HL_TABLE()[idx].sg_cterm_attr = 0;
1714#ifdef FEAT_GUI
1715 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001716 // Need to update all groups, because they might be using "bg"
1717 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001718#endif
1719#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1720 if (USE_24BIT)
1721 {
1722 highlight_gui_started();
1723 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001724 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001725 }
1726#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001727#ifdef FEAT_VTP
1728 control_console_color_rgb();
1729#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001730 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001731#ifdef FEAT_GUI_X11
1732# ifdef FEAT_MENU
1733 else if (is_menu_group)
1734 {
1735 if (gui.in_use && do_colors)
1736 gui_mch_new_menu_colors();
1737 }
1738# endif
1739 else if (is_scrollbar_group)
1740 {
1741 if (gui.in_use && do_colors)
1742 gui_new_scrollbar_colors();
1743 else
1744 set_hl_attr(idx);
1745 }
1746# ifdef FEAT_BEVAL_GUI
1747 else if (is_tooltip_group)
1748 {
1749 if (gui.in_use && do_colors)
1750 gui_mch_new_tooltip_colors();
1751 }
1752# endif
1753#endif
1754 else
1755 set_hl_attr(idx);
1756#ifdef FEAT_EVAL
1757 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001758 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001759#endif
1760 }
1761
1762 vim_free(key);
1763 vim_free(arg);
1764
1765 // Only call highlight_changed() once, after a sequence of highlight
1766 // commands, and only if an attribute actually changed.
1767 if ((did_change
1768 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1769#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1770 && !did_highlight_changed
1771#endif
1772 )
1773 {
1774 // Do not trigger a redraw when highlighting is changed while
1775 // redrawing. This may happen when evaluating 'statusline' changes the
1776 // StatusLine group.
1777 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001778 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001779 need_highlight_changed = TRUE;
1780 }
1781}
1782
1783#if defined(EXITFREE) || defined(PROTO)
1784 void
1785free_highlight(void)
1786{
1787 int i;
1788
1789 for (i = 0; i < highlight_ga.ga_len; ++i)
1790 {
1791 highlight_clear(i);
1792 vim_free(HL_TABLE()[i].sg_name);
1793 vim_free(HL_TABLE()[i].sg_name_u);
1794 }
1795 ga_clear(&highlight_ga);
1796}
1797#endif
1798
1799/*
1800 * Reset the cterm colors to what they were before Vim was started, if
1801 * possible. Otherwise reset them to zero.
1802 */
1803 void
1804restore_cterm_colors(void)
1805{
1806#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1807 // Since t_me has been set, this probably means that the user
1808 // wants to use this as default colors. Need to reset default
1809 // background/foreground colors.
1810 mch_set_normal_colors();
1811#else
1812# ifdef VIMDLL
1813 if (!gui.in_use)
1814 {
1815 mch_set_normal_colors();
1816 return;
1817 }
1818# endif
1819 cterm_normal_fg_color = 0;
1820 cterm_normal_fg_bold = 0;
1821 cterm_normal_bg_color = 0;
1822# ifdef FEAT_TERMGUICOLORS
1823 cterm_normal_fg_gui_color = INVALCOLOR;
1824 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001825 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001826# endif
1827#endif
1828}
1829
1830/*
1831 * Return TRUE if highlight group "idx" has any settings.
1832 * When "check_link" is TRUE also check for an existing link.
1833 */
1834 static int
1835hl_has_settings(int idx, int check_link)
1836{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001837 return HL_TABLE()[idx].sg_cleared == 0
1838 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001839 || HL_TABLE()[idx].sg_cterm_attr != 0
1840 || HL_TABLE()[idx].sg_cterm_fg != 0
1841 || HL_TABLE()[idx].sg_cterm_bg != 0
1842#ifdef FEAT_GUI
1843 || HL_TABLE()[idx].sg_gui_attr != 0
1844 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1845 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1846 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1847 || HL_TABLE()[idx].sg_font_name != NULL
1848#endif
1849 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1850}
1851
1852/*
1853 * Clear highlighting for one group.
1854 */
1855 static void
1856highlight_clear(int idx)
1857{
1858 HL_TABLE()[idx].sg_cleared = TRUE;
1859
1860 HL_TABLE()[idx].sg_term = 0;
1861 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1862 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1863 HL_TABLE()[idx].sg_term_attr = 0;
1864 HL_TABLE()[idx].sg_cterm = 0;
1865 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1866 HL_TABLE()[idx].sg_cterm_fg = 0;
1867 HL_TABLE()[idx].sg_cterm_bg = 0;
1868 HL_TABLE()[idx].sg_cterm_attr = 0;
1869#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1870 HL_TABLE()[idx].sg_gui = 0;
1871 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1872 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1873 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1874#endif
1875#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1876 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1877 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001878 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001879#endif
1880#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001881 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1882 HL_TABLE()[idx].sg_font = NOFONT;
1883# ifdef FEAT_XFONTSET
1884 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1885 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1886# endif
1887 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1888 HL_TABLE()[idx].sg_gui_attr = 0;
1889#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001890 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001891 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001892#ifdef FEAT_EVAL
1893 // Since we set the default link, set the location to where the default
1894 // link was set.
1895 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001896#endif
1897}
1898
1899#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1900/*
1901 * Set the normal foreground and background colors according to the "Normal"
1902 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1903 * "Tooltip" colors.
1904 */
1905 void
1906set_normal_colors(void)
1907{
1908# ifdef FEAT_GUI
1909# ifdef FEAT_TERMGUICOLORS
1910 if (gui.in_use)
1911# endif
1912 {
1913 if (set_group_colors((char_u *)"Normal",
1914 &gui.norm_pixel, &gui.back_pixel,
1915 FALSE, TRUE, FALSE))
1916 {
1917 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001918 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001919 }
1920# ifdef FEAT_GUI_X11
1921 if (set_group_colors((char_u *)"Menu",
1922 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1923 TRUE, FALSE, FALSE))
1924 {
1925# ifdef FEAT_MENU
1926 gui_mch_new_menu_colors();
1927# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001928 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001929 }
1930# ifdef FEAT_BEVAL_GUI
1931 if (set_group_colors((char_u *)"Tooltip",
1932 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1933 FALSE, FALSE, TRUE))
1934 {
1935# ifdef FEAT_TOOLBAR
1936 gui_mch_new_tooltip_colors();
1937# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001938 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001939 }
1940# endif
1941 if (set_group_colors((char_u *)"Scrollbar",
1942 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1943 FALSE, FALSE, FALSE))
1944 {
1945 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001946 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001947 }
1948# endif
1949 }
1950# endif
1951# ifdef FEAT_TERMGUICOLORS
1952# ifdef FEAT_GUI
1953 else
1954# endif
1955 {
1956 int idx;
1957
1958 idx = syn_name2id((char_u *)"Normal") - 1;
1959 if (idx >= 0)
1960 {
1961 gui_do_one_color(idx, FALSE, FALSE);
1962
1963 // If the normal fg or bg color changed a complete redraw is
1964 // required.
1965 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1966 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1967 {
1968 // if the GUI color is INVALCOLOR then we use the default cterm
1969 // color
1970 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1971 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001972 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001973 }
1974 }
1975 }
1976# endif
1977}
1978#endif
1979
1980#if defined(FEAT_GUI) || defined(PROTO)
1981/*
1982 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1983 */
1984 static int
1985set_group_colors(
1986 char_u *name,
1987 guicolor_T *fgp,
1988 guicolor_T *bgp,
1989 int do_menu,
1990 int use_norm,
1991 int do_tooltip)
1992{
1993 int idx;
1994
1995 idx = syn_name2id(name) - 1;
1996 if (idx >= 0)
1997 {
1998 gui_do_one_color(idx, do_menu, do_tooltip);
1999
2000 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2001 *fgp = HL_TABLE()[idx].sg_gui_fg;
2002 else if (use_norm)
2003 *fgp = gui.def_norm_pixel;
2004 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2005 *bgp = HL_TABLE()[idx].sg_gui_bg;
2006 else if (use_norm)
2007 *bgp = gui.def_back_pixel;
2008 return TRUE;
2009 }
2010 return FALSE;
2011}
2012
2013/*
2014 * Get the font of the "Normal" group.
2015 * Returns "" when it's not found or not set.
2016 */
2017 char_u *
2018hl_get_font_name(void)
2019{
2020 int id;
2021 char_u *s;
2022
2023 id = syn_name2id((char_u *)"Normal");
2024 if (id > 0)
2025 {
2026 s = HL_TABLE()[id - 1].sg_font_name;
2027 if (s != NULL)
2028 return s;
2029 }
2030 return (char_u *)"";
2031}
2032
2033/*
2034 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2035 * actually chosen to be used.
2036 */
2037 void
2038hl_set_font_name(char_u *font_name)
2039{
2040 int id;
2041
2042 id = syn_name2id((char_u *)"Normal");
2043 if (id > 0)
2044 {
2045 vim_free(HL_TABLE()[id - 1].sg_font_name);
2046 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2047 }
2048}
2049
2050/*
2051 * Set background color for "Normal" group. Called by gui_set_bg_color()
2052 * when the color is known.
2053 */
2054 void
2055hl_set_bg_color_name(
2056 char_u *name) // must have been allocated
2057{
2058 int id;
2059
2060 if (name != NULL)
2061 {
2062 id = syn_name2id((char_u *)"Normal");
2063 if (id > 0)
2064 {
2065 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2066 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2067 }
2068 }
2069}
2070
2071/*
2072 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2073 * when the color is known.
2074 */
2075 void
2076hl_set_fg_color_name(
2077 char_u *name) // must have been allocated
2078{
2079 int id;
2080
2081 if (name != NULL)
2082 {
2083 id = syn_name2id((char_u *)"Normal");
2084 if (id > 0)
2085 {
2086 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2087 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2088 }
2089 }
2090}
2091
2092/*
2093 * Return the handle for a font name.
2094 * Returns NOFONT when failed.
2095 */
2096 static GuiFont
2097font_name2handle(char_u *name)
2098{
2099 if (STRCMP(name, "NONE") == 0)
2100 return NOFONT;
2101
2102 return gui_mch_get_font(name, TRUE);
2103}
2104
2105# ifdef FEAT_XFONTSET
2106/*
2107 * Return the handle for a fontset name.
2108 * Returns NOFONTSET when failed.
2109 */
2110 static GuiFontset
2111fontset_name2handle(char_u *name, int fixed_width)
2112{
2113 if (STRCMP(name, "NONE") == 0)
2114 return NOFONTSET;
2115
2116 return gui_mch_get_fontset(name, TRUE, fixed_width);
2117}
2118# endif
2119
2120/*
2121 * Get the font or fontset for one highlight group.
2122 */
2123 static void
2124hl_do_font(
2125 int idx,
2126 char_u *arg,
2127 int do_normal, // set normal font
2128 int do_menu UNUSED, // set menu font
2129 int do_tooltip UNUSED, // set tooltip font
2130 int free_font) // free current font/fontset
2131{
2132# ifdef FEAT_XFONTSET
2133 // If 'guifontset' is not empty, first try using the name as a
2134 // fontset. If that doesn't work, use it as a font name.
2135 if (*p_guifontset != NUL
2136# ifdef FONTSET_ALWAYS
2137 || do_menu
2138# endif
2139# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002140 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002141 || do_tooltip
2142# endif
2143 )
2144 {
2145 if (free_font)
2146 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2147 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2148# ifdef FONTSET_ALWAYS
2149 || do_menu
2150# endif
2151# ifdef FEAT_BEVAL_TIP
2152 || do_tooltip
2153# endif
2154 );
2155 }
2156 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2157 {
2158 // If it worked and it's the Normal group, use it as the normal
2159 // fontset. Same for the Menu group.
2160 if (do_normal)
2161 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002162# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002163 if (do_menu)
2164 {
2165# ifdef FONTSET_ALWAYS
2166 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2167# else
2168 // YIKES! This is a bug waiting to crash the program
2169 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2170# endif
2171 gui_mch_new_menu_font();
2172 }
2173# ifdef FEAT_BEVAL_GUI
2174 if (do_tooltip)
2175 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002176 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002177 // displaying a single font and a fontset.
2178 // If the XtNinternational resource is set to True at widget
2179 // creation, then a fontset is always used, otherwise an
2180 // XFontStruct is used.
2181 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2182 gui_mch_new_tooltip_font();
2183 }
2184# endif
2185# endif
2186 }
2187 else
2188# endif
2189 {
2190 if (free_font)
2191 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2192 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2193 // If it worked and it's the Normal group, use it as the
2194 // normal font. Same for the Menu group.
2195 if (HL_TABLE()[idx].sg_font != NOFONT)
2196 {
2197 if (do_normal)
2198 gui_init_font(arg, FALSE);
2199#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002200# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002201 if (do_menu)
2202 {
2203 gui.menu_font = HL_TABLE()[idx].sg_font;
2204 gui_mch_new_menu_font();
2205 }
2206# endif
2207#endif
2208 }
2209 }
2210}
2211
2212#endif // FEAT_GUI
2213
2214#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2215/*
2216 * Return the handle for a color name.
2217 * Returns INVALCOLOR when failed.
2218 */
2219 guicolor_T
2220color_name2handle(char_u *name)
2221{
2222 if (STRCMP(name, "NONE") == 0)
2223 return INVALCOLOR;
2224
2225 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2226 {
2227#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2228 if (gui.in_use)
2229#endif
2230#ifdef FEAT_GUI
2231 return gui.norm_pixel;
2232#endif
2233#ifdef FEAT_TERMGUICOLORS
2234 if (cterm_normal_fg_gui_color != INVALCOLOR)
2235 return cterm_normal_fg_gui_color;
2236 // Guess that the foreground is black or white.
2237 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2238#endif
2239 }
2240 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2241 {
2242#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2243 if (gui.in_use)
2244#endif
2245#ifdef FEAT_GUI
2246 return gui.back_pixel;
2247#endif
2248#ifdef FEAT_TERMGUICOLORS
2249 if (cterm_normal_bg_gui_color != INVALCOLOR)
2250 return cterm_normal_bg_gui_color;
2251 // Guess that the background is white or black.
2252 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2253#endif
2254 }
2255
2256 return GUI_GET_COLOR(name);
2257}
Drew Vogele30d1022021-10-24 20:35:07 +01002258
2259// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2260// values as used by the MS-Windows GDI api. It should be used only for
2261// MS-Windows GDI builds.
2262# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2263# undef RGB
2264# endif
2265# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002266# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002267# endif
2268
2269# ifdef VIMDLL
2270 static guicolor_T
2271gui_adjust_rgb(guicolor_T c)
2272{
2273 if (gui.in_use)
2274 return c;
2275 else
2276 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2277}
2278# else
2279# define gui_adjust_rgb(c) (c)
2280# endif
2281
2282 static int
2283hex_digit(int c)
2284{
2285 if (isdigit(c))
2286 return c - '0';
2287 c = TOLOWER_ASC(c);
2288 if (c >= 'a' && c <= 'f')
2289 return c - 'a' + 10;
2290 return 0x1ffffff;
2291}
2292
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002293 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002294decode_hex_color(char_u *hex)
2295{
2296 guicolor_T color;
2297
2298 if (hex[0] != '#' || STRLEN(hex) != 7)
2299 return INVALCOLOR;
2300
2301 // Name is in "#rrggbb" format
2302 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2303 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2304 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2305 if (color > 0xffffff)
2306 return INVALCOLOR;
2307 return gui_adjust_rgb(color);
2308}
2309
Bram Moolenaar2a521962021-10-25 10:30:14 +01002310#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002311// Returns the color currently mapped to the given name or INVALCOLOR if no
2312// such name exists in the color table. The convention is to use lowercase for
2313// all keys in the v:colornames dictionary. The value can be either a string in
2314// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002315 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002316colorname2rgb(char_u *name)
2317{
2318 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2319 char_u *lc_name;
2320 dictitem_T *colentry;
2321 char_u *colstr;
2322 varnumber_T colnum;
2323
2324 lc_name = strlow_save(name);
2325 if (lc_name == NULL)
2326 return INVALCOLOR;
2327
2328 colentry = dict_find(colornames_table, lc_name, -1);
2329 vim_free(lc_name);
2330 if (colentry == NULL)
2331 return INVALCOLOR;
2332
2333 if (colentry->di_tv.v_type == VAR_STRING)
2334 {
2335 colstr = tv_get_string_strict(&colentry->di_tv);
2336 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2337 {
2338 return decode_hex_color(colstr);
2339 }
2340 else
2341 {
2342 semsg(_(e_bad_color_string_str), colstr);
2343 return INVALCOLOR;
2344 }
2345 }
2346
2347 if (colentry->di_tv.v_type == VAR_NUMBER)
2348 {
2349 colnum = tv_get_number(&colentry->di_tv);
2350 return (guicolor_T)colnum;
2351 }
2352
2353 return INVALCOLOR;
2354}
2355
Drew Vogele30d1022021-10-24 20:35:07 +01002356#endif
2357
2358 guicolor_T
2359gui_get_color_cmn(char_u *name)
2360{
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002361 int i;
Drew Vogele30d1022021-10-24 20:35:07 +01002362 guicolor_T color;
2363
2364 struct rgbcolor_table_S {
2365 char_u *color_name;
2366 guicolor_T color;
2367 };
2368
2369 // Only non X11 colors (not present in rgb.txt) and colors in
2370 // color_names[], useful when $VIMRUNTIME is not found,.
2371 static struct rgbcolor_table_S rgb_table[] = {
2372 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2373 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2374 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2375 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2376 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2377 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2378 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2379 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2380 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2381 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2382 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2383 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2384 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2385 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2386 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2387 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2388 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2389 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2390 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2391 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2392 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2393 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2394 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2395 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2396 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2397 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2398 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2399 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2400 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2401 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2402 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2403 };
2404
2405 color = decode_hex_color(name);
2406 if (color != INVALCOLOR)
2407 return color;
2408
2409 // Check if the name is one of the colors we know
2410 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2411 if (STRICMP(name, rgb_table[i].color_name) == 0)
2412 return gui_adjust_rgb(rgb_table[i].color);
2413
2414#if defined(FEAT_EVAL)
2415 /*
2416 * Not a traditional color. Load additional color aliases and then consult the alias table.
2417 */
2418
2419 color = colorname2rgb(name);
2420 if (color == INVALCOLOR)
2421 {
2422 load_default_colors_lists();
2423 color = colorname2rgb(name);
2424 }
2425
2426 return color;
2427#else
2428 return INVALCOLOR;
2429#endif
2430}
2431
2432 guicolor_T
2433gui_get_rgb_color_cmn(int r, int g, int b)
2434{
2435 guicolor_T color = RGB(r, g, b);
2436
2437 if (color > 0xffffff)
2438 return INVALCOLOR;
2439 return gui_adjust_rgb(color);
2440}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002441#endif
2442
2443/*
2444 * Table with the specifications for an attribute number.
2445 * Note that this table is used by ALL buffers. This is required because the
2446 * GUI can redraw at any time for any buffer.
2447 */
2448static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2449
2450#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2451
2452static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2453
2454#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2455
2456#ifdef FEAT_GUI
2457static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2458
2459#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2460#endif
2461
2462/*
2463 * Return the attr number for a set of colors and font.
2464 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2465 * if the combination is new.
2466 * Return 0 for error (no more room).
2467 */
2468 static int
2469get_attr_entry(garray_T *table, attrentry_T *aep)
2470{
2471 int i;
2472 attrentry_T *taep;
2473 static int recursive = FALSE;
2474
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002475 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002476 table->ga_itemsize = sizeof(attrentry_T);
2477 table->ga_growsize = 7;
2478
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002479 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002480 for (i = 0; i < table->ga_len; ++i)
2481 {
2482 taep = &(((attrentry_T *)table->ga_data)[i]);
2483 if ( aep->ae_attr == taep->ae_attr
2484 && (
2485#ifdef FEAT_GUI
2486 (table == &gui_attr_table
2487 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2488 && aep->ae_u.gui.bg_color
2489 == taep->ae_u.gui.bg_color
2490 && aep->ae_u.gui.sp_color
2491 == taep->ae_u.gui.sp_color
2492 && aep->ae_u.gui.font == taep->ae_u.gui.font
2493# ifdef FEAT_XFONTSET
2494 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2495# endif
2496 ))
2497 ||
2498#endif
2499 (table == &term_attr_table
2500 && (aep->ae_u.term.start == NULL)
2501 == (taep->ae_u.term.start == NULL)
2502 && (aep->ae_u.term.start == NULL
2503 || STRCMP(aep->ae_u.term.start,
2504 taep->ae_u.term.start) == 0)
2505 && (aep->ae_u.term.stop == NULL)
2506 == (taep->ae_u.term.stop == NULL)
2507 && (aep->ae_u.term.stop == NULL
2508 || STRCMP(aep->ae_u.term.stop,
2509 taep->ae_u.term.stop) == 0))
2510 || (table == &cterm_attr_table
2511 && aep->ae_u.cterm.fg_color
2512 == taep->ae_u.cterm.fg_color
2513 && aep->ae_u.cterm.bg_color
2514 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002515 && aep->ae_u.cterm.ul_color
2516 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002517#ifdef FEAT_TERMGUICOLORS
2518 && aep->ae_u.cterm.fg_rgb
2519 == taep->ae_u.cterm.fg_rgb
2520 && aep->ae_u.cterm.bg_rgb
2521 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002522 && aep->ae_u.cterm.ul_rgb
2523 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002524#endif
2525 )))
2526
2527 return i + ATTR_OFF;
2528 }
2529
2530 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2531 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002532 // Running out of attribute entries! remove all attributes, and
2533 // compute new ones for all groups.
2534 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002535 if (recursive)
2536 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002537 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002538 return 0;
2539 }
2540 recursive = TRUE;
2541
2542 clear_hl_tables();
2543
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002544 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002545
2546 for (i = 0; i < highlight_ga.ga_len; ++i)
2547 set_hl_attr(i);
2548
2549 recursive = FALSE;
2550 }
2551
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002552 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002553 if (ga_grow(table, 1) == FAIL)
2554 return 0;
2555
2556 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002557 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002558 taep->ae_attr = aep->ae_attr;
2559#ifdef FEAT_GUI
2560 if (table == &gui_attr_table)
2561 {
2562 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2563 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2564 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2565 taep->ae_u.gui.font = aep->ae_u.gui.font;
2566# ifdef FEAT_XFONTSET
2567 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2568# endif
2569 }
2570#endif
2571 if (table == &term_attr_table)
2572 {
2573 if (aep->ae_u.term.start == NULL)
2574 taep->ae_u.term.start = NULL;
2575 else
2576 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2577 if (aep->ae_u.term.stop == NULL)
2578 taep->ae_u.term.stop = NULL;
2579 else
2580 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2581 }
2582 else if (table == &cterm_attr_table)
2583 {
2584 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2585 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002586 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002587#ifdef FEAT_TERMGUICOLORS
2588 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2589 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002590 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002591#endif
2592 }
2593 ++table->ga_len;
2594 return (table->ga_len - 1 + ATTR_OFF);
2595}
2596
2597#if defined(FEAT_TERMINAL) || defined(PROTO)
2598/*
2599 * Get an attribute index for a cterm entry.
2600 * Uses an existing entry when possible or adds one when needed.
2601 */
2602 int
2603get_cterm_attr_idx(int attr, int fg, int bg)
2604{
2605 attrentry_T at_en;
2606
Bram Moolenaara80faa82020-04-12 19:37:17 +02002607 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002608#ifdef FEAT_TERMGUICOLORS
2609 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2610 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002611 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002612#endif
2613 at_en.ae_attr = attr;
2614 at_en.ae_u.cterm.fg_color = fg;
2615 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002616 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002617 return get_attr_entry(&cterm_attr_table, &at_en);
2618}
2619#endif
2620
2621#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2622/*
2623 * Get an attribute index for a 'termguicolors' entry.
2624 * Uses an existing entry when possible or adds one when needed.
2625 */
2626 int
2627get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2628{
2629 attrentry_T at_en;
2630
Bram Moolenaara80faa82020-04-12 19:37:17 +02002631 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002632 at_en.ae_attr = attr;
2633 if (fg == INVALCOLOR && bg == INVALCOLOR)
2634 {
2635 // If both GUI colors are not set fall back to the cterm colors. Helps
2636 // if the GUI only has an attribute, such as undercurl.
2637 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2638 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2639 }
2640 else
2641 {
2642 at_en.ae_u.cterm.fg_rgb = fg;
2643 at_en.ae_u.cterm.bg_rgb = bg;
2644 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002645 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002646 return get_attr_entry(&cterm_attr_table, &at_en);
2647}
2648#endif
2649
2650#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2651/*
2652 * Get an attribute index for a cterm entry.
2653 * Uses an existing entry when possible or adds one when needed.
2654 */
2655 int
2656get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2657{
2658 attrentry_T at_en;
2659
Bram Moolenaara80faa82020-04-12 19:37:17 +02002660 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002661 at_en.ae_attr = attr;
2662 at_en.ae_u.gui.fg_color = fg;
2663 at_en.ae_u.gui.bg_color = bg;
2664 return get_attr_entry(&gui_attr_table, &at_en);
2665}
2666#endif
2667
2668/*
2669 * Clear all highlight tables.
2670 */
2671 void
2672clear_hl_tables(void)
2673{
2674 int i;
2675 attrentry_T *taep;
2676
2677#ifdef FEAT_GUI
2678 ga_clear(&gui_attr_table);
2679#endif
2680 for (i = 0; i < term_attr_table.ga_len; ++i)
2681 {
2682 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2683 vim_free(taep->ae_u.term.start);
2684 vim_free(taep->ae_u.term.stop);
2685 }
2686 ga_clear(&term_attr_table);
2687 ga_clear(&cterm_attr_table);
2688}
2689
2690/*
2691 * Combine special attributes (e.g., for spelling) with other attributes
2692 * (e.g., for syntax highlighting).
2693 * "prim_attr" overrules "char_attr".
2694 * This creates a new group when required.
2695 * Since we expect there to be few spelling mistakes we don't cache the
2696 * result.
2697 * Return the resulting attributes.
2698 */
2699 int
2700hl_combine_attr(int char_attr, int prim_attr)
2701{
2702 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002703 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002704 attrentry_T new_en;
2705
2706 if (char_attr == 0)
2707 return prim_attr;
2708 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2709 return ATTR_COMBINE(char_attr, prim_attr);
2710#ifdef FEAT_GUI
2711 if (gui.in_use)
2712 {
2713 if (char_attr > HL_ALL)
2714 char_aep = syn_gui_attr2entry(char_attr);
2715 if (char_aep != NULL)
2716 new_en = *char_aep;
2717 else
2718 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002719 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002720 new_en.ae_u.gui.fg_color = INVALCOLOR;
2721 new_en.ae_u.gui.bg_color = INVALCOLOR;
2722 new_en.ae_u.gui.sp_color = INVALCOLOR;
2723 if (char_attr <= HL_ALL)
2724 new_en.ae_attr = char_attr;
2725 }
2726
2727 if (prim_attr <= HL_ALL)
2728 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2729 else
2730 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002731 prim_aep = syn_gui_attr2entry(prim_attr);
2732 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002733 {
2734 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002735 prim_aep->ae_attr);
2736 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2737 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2738 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2739 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2740 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2741 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2742 if (prim_aep->ae_u.gui.font != NOFONT)
2743 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002744# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002745 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2746 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002747# endif
2748 }
2749 }
2750 return get_attr_entry(&gui_attr_table, &new_en);
2751 }
2752#endif
2753
2754 if (IS_CTERM)
2755 {
2756 if (char_attr > HL_ALL)
2757 char_aep = syn_cterm_attr2entry(char_attr);
2758 if (char_aep != NULL)
2759 new_en = *char_aep;
2760 else
2761 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002762 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002763#ifdef FEAT_TERMGUICOLORS
2764 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2765 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002766 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002767#endif
2768 if (char_attr <= HL_ALL)
2769 new_en.ae_attr = char_attr;
2770 }
2771
2772 if (prim_attr <= HL_ALL)
2773 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2774 else
2775 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002776 prim_aep = syn_cterm_attr2entry(prim_attr);
2777 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002778 {
2779 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002780 prim_aep->ae_attr);
2781 if (prim_aep->ae_u.cterm.fg_color > 0)
2782 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2783 if (prim_aep->ae_u.cterm.bg_color > 0)
2784 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2785 if (prim_aep->ae_u.cterm.ul_color > 0)
2786 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002787#ifdef FEAT_TERMGUICOLORS
2788 // If both fg and bg are not set fall back to cterm colors.
2789 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002790 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2791 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002792 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002793 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002794 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002795 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002796 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2797 }
2798 else
2799 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002800 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2801 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2802 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2803 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002804 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002805 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2806 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002807#endif
2808 }
2809 }
2810 return get_attr_entry(&cterm_attr_table, &new_en);
2811 }
2812
2813 if (char_attr > HL_ALL)
2814 char_aep = syn_term_attr2entry(char_attr);
2815 if (char_aep != NULL)
2816 new_en = *char_aep;
2817 else
2818 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002819 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002820 if (char_attr <= HL_ALL)
2821 new_en.ae_attr = char_attr;
2822 }
2823
2824 if (prim_attr <= HL_ALL)
2825 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2826 else
2827 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002828 prim_aep = syn_term_attr2entry(prim_attr);
2829 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002830 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002831 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2832 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002833 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002834 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2835 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002836 }
2837 }
2838 }
2839 return get_attr_entry(&term_attr_table, &new_en);
2840}
2841
2842#ifdef FEAT_GUI
2843 attrentry_T *
2844syn_gui_attr2entry(int attr)
2845{
2846 attr -= ATTR_OFF;
2847 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2848 return NULL;
2849 return &(GUI_ATTR_ENTRY(attr));
2850}
2851#endif
2852
2853/*
2854 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2855 * Only to be used when "attr" > HL_ALL.
2856 */
2857 int
2858syn_attr2attr(int attr)
2859{
2860 attrentry_T *aep;
2861
2862#ifdef FEAT_GUI
2863 if (gui.in_use)
2864 aep = syn_gui_attr2entry(attr);
2865 else
2866#endif
2867 if (IS_CTERM)
2868 aep = syn_cterm_attr2entry(attr);
2869 else
2870 aep = syn_term_attr2entry(attr);
2871
2872 if (aep == NULL) // highlighting not set
2873 return 0;
2874 return aep->ae_attr;
2875}
2876
2877
2878 attrentry_T *
2879syn_term_attr2entry(int attr)
2880{
2881 attr -= ATTR_OFF;
2882 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2883 return NULL;
2884 return &(TERM_ATTR_ENTRY(attr));
2885}
2886
2887 attrentry_T *
2888syn_cterm_attr2entry(int attr)
2889{
2890 attr -= ATTR_OFF;
2891 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2892 return NULL;
2893 return &(CTERM_ATTR_ENTRY(attr));
2894}
2895
2896#define LIST_ATTR 1
2897#define LIST_STRING 2
2898#define LIST_INT 3
2899
2900 static void
2901highlight_list_one(int id)
2902{
2903 hl_group_T *sgp;
2904 int didh = FALSE;
2905
2906 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2907
2908 if (message_filtered(sgp->sg_name))
2909 return;
2910
2911 didh = highlight_list_arg(id, didh, LIST_ATTR,
2912 sgp->sg_term, NULL, "term");
2913 didh = highlight_list_arg(id, didh, LIST_STRING,
2914 0, sgp->sg_start, "start");
2915 didh = highlight_list_arg(id, didh, LIST_STRING,
2916 0, sgp->sg_stop, "stop");
2917
2918 didh = highlight_list_arg(id, didh, LIST_ATTR,
2919 sgp->sg_cterm, NULL, "cterm");
2920 didh = highlight_list_arg(id, didh, LIST_INT,
2921 sgp->sg_cterm_fg, NULL, "ctermfg");
2922 didh = highlight_list_arg(id, didh, LIST_INT,
2923 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002924 didh = highlight_list_arg(id, didh, LIST_INT,
2925 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002926
2927#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2928 didh = highlight_list_arg(id, didh, LIST_ATTR,
2929 sgp->sg_gui, NULL, "gui");
2930 didh = highlight_list_arg(id, didh, LIST_STRING,
2931 0, sgp->sg_gui_fg_name, "guifg");
2932 didh = highlight_list_arg(id, didh, LIST_STRING,
2933 0, sgp->sg_gui_bg_name, "guibg");
2934 didh = highlight_list_arg(id, didh, LIST_STRING,
2935 0, sgp->sg_gui_sp_name, "guisp");
2936#endif
2937#ifdef FEAT_GUI
2938 didh = highlight_list_arg(id, didh, LIST_STRING,
2939 0, sgp->sg_font_name, "font");
2940#endif
2941
2942 if (sgp->sg_link && !got_int)
2943 {
2944 (void)syn_list_header(didh, 9999, id);
2945 didh = TRUE;
2946 msg_puts_attr("links to", HL_ATTR(HLF_D));
2947 msg_putchar(' ');
2948 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2949 }
2950
2951 if (!didh)
2952 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2953#ifdef FEAT_EVAL
2954 if (p_verbose > 0)
2955 last_set_msg(sgp->sg_script_ctx);
2956#endif
2957}
2958
2959 static int
2960highlight_list_arg(
2961 int id,
2962 int didh,
2963 int type,
2964 int iarg,
2965 char_u *sarg,
2966 char *name)
2967{
Bram Moolenaar84f54632022-06-29 18:39:11 +01002968 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002969 char_u *ts;
2970 int i;
2971
2972 if (got_int)
2973 return FALSE;
2974 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2975 {
2976 ts = buf;
2977 if (type == LIST_INT)
2978 sprintf((char *)buf, "%d", iarg - 1);
2979 else if (type == LIST_STRING)
2980 ts = sarg;
2981 else // type == LIST_ATTR
2982 {
2983 buf[0] = NUL;
2984 for (i = 0; hl_attr_table[i] != 0; ++i)
2985 {
2986 if (iarg & hl_attr_table[i])
2987 {
2988 if (buf[0] != NUL)
Bram Moolenaar84f54632022-06-29 18:39:11 +01002989 vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
2990 vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002991 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2992 }
2993 }
2994 }
2995
2996 (void)syn_list_header(didh,
2997 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2998 didh = TRUE;
2999 if (!got_int)
3000 {
3001 if (*name != NUL)
3002 {
3003 msg_puts_attr(name, HL_ATTR(HLF_D));
3004 msg_puts_attr("=", HL_ATTR(HLF_D));
3005 }
3006 msg_outtrans(ts);
3007 }
3008 }
3009 return didh;
3010}
3011
3012#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3013/*
3014 * Return "1" if highlight group "id" has attribute "flag".
3015 * Return NULL otherwise.
3016 */
3017 char_u *
3018highlight_has_attr(
3019 int id,
3020 int flag,
3021 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3022{
3023 int attr;
3024
3025 if (id <= 0 || id > highlight_ga.ga_len)
3026 return NULL;
3027
3028#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3029 if (modec == 'g')
3030 attr = HL_TABLE()[id - 1].sg_gui;
3031 else
3032#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003033 {
3034 if (modec == 'c')
3035 attr = HL_TABLE()[id - 1].sg_cterm;
3036 else
3037 attr = HL_TABLE()[id - 1].sg_term;
3038 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003039
3040 if (attr & flag)
3041 return (char_u *)"1";
3042 return NULL;
3043}
3044#endif
3045
3046#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3047/*
3048 * Return color name of highlight group "id".
3049 */
3050 char_u *
3051highlight_color(
3052 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003053 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003054 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3055{
3056 static char_u name[20];
3057 int n;
3058 int fg = FALSE;
3059 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003060 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003061 int font = FALSE;
3062
3063 if (id <= 0 || id > highlight_ga.ga_len)
3064 return NULL;
3065
3066 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3067 fg = TRUE;
3068 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3069 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3070 font = TRUE;
3071 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3072 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003073 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3074 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003075 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3076 return NULL;
3077 if (modec == 'g')
3078 {
3079# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3080# ifdef FEAT_GUI
3081 // return font name
3082 if (font)
3083 return HL_TABLE()[id - 1].sg_font_name;
3084# endif
3085
3086 // return #RRGGBB form (only possible when GUI is running)
3087 if ((USE_24BIT) && what[2] == '#')
3088 {
3089 guicolor_T color;
3090 long_u rgb;
3091 static char_u buf[10];
3092
3093 if (fg)
3094 color = HL_TABLE()[id - 1].sg_gui_fg;
3095 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003096 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003097 else
3098 color = HL_TABLE()[id - 1].sg_gui_bg;
3099 if (color == INVALCOLOR)
3100 return NULL;
3101 rgb = (long_u)GUI_MCH_GET_RGB(color);
3102 sprintf((char *)buf, "#%02x%02x%02x",
3103 (unsigned)(rgb >> 16),
3104 (unsigned)(rgb >> 8) & 255,
3105 (unsigned)rgb & 255);
3106 return buf;
3107 }
3108# endif
3109 if (fg)
3110 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3111 if (sp)
3112 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3113 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3114 }
3115 if (font || sp)
3116 return NULL;
3117 if (modec == 'c')
3118 {
3119 if (fg)
3120 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003121 else if (ul)
3122 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003123 else
3124 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3125 if (n < 0)
3126 return NULL;
3127 sprintf((char *)name, "%d", n);
3128 return name;
3129 }
3130 // term doesn't have color
3131 return NULL;
3132}
3133#endif
3134
3135#if (defined(FEAT_SYN_HL) \
3136 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3137 && defined(FEAT_PRINTER)) || defined(PROTO)
3138/*
3139 * Return color name of highlight group "id" as RGB value.
3140 */
3141 long_u
3142highlight_gui_color_rgb(
3143 int id,
3144 int fg) // TRUE = fg, FALSE = bg
3145{
3146 guicolor_T color;
3147
3148 if (id <= 0 || id > highlight_ga.ga_len)
3149 return 0L;
3150
3151 if (fg)
3152 color = HL_TABLE()[id - 1].sg_gui_fg;
3153 else
3154 color = HL_TABLE()[id - 1].sg_gui_bg;
3155
3156 if (color == INVALCOLOR)
3157 return 0L;
3158
3159 return GUI_MCH_GET_RGB(color);
3160}
3161#endif
3162
3163/*
3164 * Output the syntax list header.
3165 * Return TRUE when started a new line.
3166 */
3167 int
3168syn_list_header(
3169 int did_header, // did header already
3170 int outlen, // length of string that comes
3171 int id) // highlight group id
3172{
3173 int endcol = 19;
3174 int newline = TRUE;
3175 int name_col = 0;
3176
3177 if (!did_header)
3178 {
3179 msg_putchar('\n');
3180 if (got_int)
3181 return TRUE;
3182 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3183 name_col = msg_col;
3184 endcol = 15;
3185 }
3186 else if (msg_col + outlen + 1 >= Columns)
3187 {
3188 msg_putchar('\n');
3189 if (got_int)
3190 return TRUE;
3191 }
3192 else
3193 {
3194 if (msg_col >= endcol) // wrap around is like starting a new line
3195 newline = FALSE;
3196 }
3197
3198 if (msg_col >= endcol) // output at least one space
3199 endcol = msg_col + 1;
3200 if (Columns <= endcol) // avoid hang for tiny window
3201 endcol = Columns - 1;
3202
3203 msg_advance(endcol);
3204
3205 // Show "xxx" with the attributes.
3206 if (!did_header)
3207 {
3208 if (endcol == Columns - 1 && endcol <= name_col)
3209 msg_putchar(' ');
3210 msg_puts_attr("xxx", syn_id2attr(id));
3211 msg_putchar(' ');
3212 }
3213
3214 return newline;
3215}
3216
3217/*
3218 * Set the attribute numbers for a highlight group.
3219 * Called after one of the attributes has changed.
3220 */
3221 static void
3222set_hl_attr(
3223 int idx) // index in array
3224{
3225 attrentry_T at_en;
3226 hl_group_T *sgp = HL_TABLE() + idx;
3227
3228 // The "Normal" group doesn't need an attribute number
3229 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3230 return;
3231
3232#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003233 // For the GUI mode: If there are other than "normal" highlighting
3234 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003235 if (sgp->sg_gui_fg == INVALCOLOR
3236 && sgp->sg_gui_bg == INVALCOLOR
3237 && sgp->sg_gui_sp == INVALCOLOR
3238 && sgp->sg_font == NOFONT
3239# ifdef FEAT_XFONTSET
3240 && sgp->sg_fontset == NOFONTSET
3241# endif
3242 )
3243 {
3244 sgp->sg_gui_attr = sgp->sg_gui;
3245 }
3246 else
3247 {
3248 at_en.ae_attr = sgp->sg_gui;
3249 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3250 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3251 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3252 at_en.ae_u.gui.font = sgp->sg_font;
3253# ifdef FEAT_XFONTSET
3254 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3255# endif
3256 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3257 }
3258#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003259 // For the term mode: If there are other than "normal" highlighting
3260 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003261 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3262 sgp->sg_term_attr = sgp->sg_term;
3263 else
3264 {
3265 at_en.ae_attr = sgp->sg_term;
3266 at_en.ae_u.term.start = sgp->sg_start;
3267 at_en.ae_u.term.stop = sgp->sg_stop;
3268 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3269 }
3270
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003271 // For the color term mode: If there are other than "normal"
3272 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003273 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003274# ifdef FEAT_TERMGUICOLORS
3275 && sgp->sg_gui_fg == INVALCOLOR
3276 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003277 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003278# endif
3279 )
3280 sgp->sg_cterm_attr = sgp->sg_cterm;
3281 else
3282 {
3283 at_en.ae_attr = sgp->sg_cterm;
3284 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3285 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003286 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003287# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003288 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3289 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003290 // Only use the underline/undercurl color when used, it may clear the
3291 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003292 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3293 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003294 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3295 else
3296 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003297 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3298 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3299 {
3300 // If both fg and bg are invalid fall back to the cterm colors.
3301 // Helps when the GUI only uses an attribute, e.g. undercurl.
3302 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3303 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3304 }
3305# endif
3306 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3307 }
3308}
3309
3310/*
3311 * Lookup a highlight group name and return its ID.
3312 * If it is not found, 0 is returned.
3313 */
3314 int
3315syn_name2id(char_u *name)
3316{
3317 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003318 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003319
3320 // Avoid using stricmp() too much, it's slow on some systems
3321 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3322 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003323 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003324 vim_strup(name_u);
3325 for (i = highlight_ga.ga_len; --i >= 0; )
3326 if (HL_TABLE()[i].sg_name_u != NULL
3327 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3328 break;
3329 return i + 1;
3330}
3331
3332/*
3333 * Lookup a highlight group name and return its attributes.
3334 * Return zero if not found.
3335 */
3336 int
3337syn_name2attr(char_u *name)
3338{
3339 int id = syn_name2id(name);
3340
3341 if (id != 0)
3342 return syn_id2attr(id);
3343 return 0;
3344}
3345
3346#if defined(FEAT_EVAL) || defined(PROTO)
3347/*
3348 * Return TRUE if highlight group "name" exists.
3349 */
3350 int
3351highlight_exists(char_u *name)
3352{
3353 return (syn_name2id(name) > 0);
3354}
3355
3356# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3357/*
3358 * Return the name of highlight group "id".
3359 * When not a valid ID return an empty string.
3360 */
3361 char_u *
3362syn_id2name(int id)
3363{
3364 if (id <= 0 || id > highlight_ga.ga_len)
3365 return (char_u *)"";
3366 return HL_TABLE()[id - 1].sg_name;
3367}
3368# endif
3369#endif
3370
3371/*
3372 * Like syn_name2id(), but take a pointer + length argument.
3373 */
3374 int
3375syn_namen2id(char_u *linep, int len)
3376{
3377 char_u *name;
3378 int id = 0;
3379
3380 name = vim_strnsave(linep, len);
3381 if (name != NULL)
3382 {
3383 id = syn_name2id(name);
3384 vim_free(name);
3385 }
3386 return id;
3387}
3388
3389/*
3390 * Find highlight group name in the table and return its ID.
3391 * The argument is a pointer to the name and the length of the name.
3392 * If it doesn't exist yet, a new entry is created.
3393 * Return 0 for failure.
3394 */
3395 int
3396syn_check_group(char_u *pp, int len)
3397{
3398 int id;
3399 char_u *name;
3400
erw7f7f7aaf2021-12-07 21:29:20 +00003401 if (len > MAX_SYN_NAME)
3402 {
3403 emsg(_(e_highlight_group_name_too_long));
3404 return 0;
3405 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003406 name = vim_strnsave(pp, len);
3407 if (name == NULL)
3408 return 0;
3409
3410 id = syn_name2id(name);
3411 if (id == 0) // doesn't exist yet
3412 id = syn_add_group(name);
3413 else
3414 vim_free(name);
3415 return id;
3416}
3417
3418/*
3419 * Add new highlight group and return its ID.
3420 * "name" must be an allocated string, it will be consumed.
3421 * Return 0 for failure.
3422 */
3423 static int
3424syn_add_group(char_u *name)
3425{
3426 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003427 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003428
3429 // Check that the name is ASCII letters, digits and underscore.
3430 for (p = name; *p != NUL; ++p)
3431 {
3432 if (!vim_isprintc(*p))
3433 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003434 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003435 vim_free(name);
3436 return 0;
3437 }
3438 else if (!ASCII_ISALNUM(*p) && *p != '_')
3439 {
3440 // This is an error, but since there previously was no check only
3441 // give a warning.
3442 msg_source(HL_ATTR(HLF_W));
3443 msg(_("W18: Invalid character in group name"));
3444 break;
3445 }
3446 }
3447
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003448 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003449 if (highlight_ga.ga_data == NULL)
3450 {
3451 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3452 highlight_ga.ga_growsize = 10;
3453 }
3454
3455 if (highlight_ga.ga_len >= MAX_HL_ID)
3456 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003457 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003458 vim_free(name);
3459 return 0;
3460 }
3461
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003462 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003463 if (ga_grow(&highlight_ga, 1) == FAIL)
3464 {
3465 vim_free(name);
3466 return 0;
3467 }
3468
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003469 name_up = vim_strsave_up(name);
3470 if (name_up == NULL)
3471 {
3472 vim_free(name);
3473 return 0;
3474 }
3475
Bram Moolenaara80faa82020-04-12 19:37:17 +02003476 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003477 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003478 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003479#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3480 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3481 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003482 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003483#endif
3484 ++highlight_ga.ga_len;
3485
3486 return highlight_ga.ga_len; // ID is index plus one
3487}
3488
3489/*
3490 * When, just after calling syn_add_group(), an error is discovered, this
3491 * function deletes the new name.
3492 */
3493 static void
3494syn_unadd_group(void)
3495{
3496 --highlight_ga.ga_len;
3497 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3498 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3499}
3500
3501/*
3502 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003503 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003504 */
3505 int
3506syn_id2attr(int hl_id)
3507{
3508 int attr;
3509 hl_group_T *sgp;
3510
3511 hl_id = syn_get_final_id(hl_id);
3512 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3513
3514#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003515 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003516 if (gui.in_use)
3517 attr = sgp->sg_gui_attr;
3518 else
3519#endif
3520 if (IS_CTERM)
3521 attr = sgp->sg_cterm_attr;
3522 else
3523 attr = sgp->sg_term_attr;
3524
3525 return attr;
3526}
3527
3528#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3529/*
3530 * Get the GUI colors and attributes for a group ID.
3531 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3532 */
3533 int
3534syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3535{
3536 hl_group_T *sgp;
3537
3538 hl_id = syn_get_final_id(hl_id);
3539 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3540
3541 *fgp = sgp->sg_gui_fg;
3542 *bgp = sgp->sg_gui_bg;
3543 return sgp->sg_gui;
3544}
3545#endif
3546
3547#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003548 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3549 && defined(FEAT_TERMGUICOLORS)) \
3550 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003551 void
3552syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3553{
3554 hl_group_T *sgp;
3555
3556 hl_id = syn_get_final_id(hl_id);
3557 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3558 *fgp = sgp->sg_cterm_fg - 1;
3559 *bgp = sgp->sg_cterm_bg - 1;
3560}
3561#endif
3562
3563/*
3564 * Translate a group ID to the final group ID (following links).
3565 */
3566 int
3567syn_get_final_id(int hl_id)
3568{
3569 int count;
3570 hl_group_T *sgp;
3571
3572 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3573 return 0; // Can be called from eval!!
3574
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003575 // Follow links until there is no more.
3576 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003577 for (count = 100; --count >= 0; )
3578 {
3579 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3580 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3581 break;
3582 hl_id = sgp->sg_link;
3583 }
3584
3585 return hl_id;
3586}
3587
3588#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3589/*
3590 * Call this function just after the GUI has started.
3591 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3592 * It finds the font and color handles for the highlighting groups.
3593 */
3594 void
3595highlight_gui_started(void)
3596{
3597 int idx;
3598
3599 // First get the colors from the "Normal" and "Menu" group, if set
3600 if (USE_24BIT)
3601 set_normal_colors();
3602
3603 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3604 gui_do_one_color(idx, FALSE, FALSE);
3605
3606 highlight_changed();
3607}
3608
3609 static void
3610gui_do_one_color(
3611 int idx,
3612 int do_menu UNUSED, // TRUE: might set the menu font
3613 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3614{
3615 int didit = FALSE;
3616
3617# ifdef FEAT_GUI
3618# ifdef FEAT_TERMGUICOLORS
3619 if (gui.in_use)
3620# endif
3621 if (HL_TABLE()[idx].sg_font_name != NULL)
3622 {
3623 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3624 do_tooltip, TRUE);
3625 didit = TRUE;
3626 }
3627# endif
3628 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3629 {
3630 HL_TABLE()[idx].sg_gui_fg =
3631 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3632 didit = TRUE;
3633 }
3634 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3635 {
3636 HL_TABLE()[idx].sg_gui_bg =
3637 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3638 didit = TRUE;
3639 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003640 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3641 {
3642 HL_TABLE()[idx].sg_gui_sp =
3643 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3644 didit = TRUE;
3645 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003646 if (didit) // need to get a new attr number
3647 set_hl_attr(idx);
3648}
3649#endif
3650
3651#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3652/*
3653 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3654 */
3655 static void
3656combine_stl_hlt(
3657 int id,
3658 int id_S,
3659 int id_alt,
3660 int hlcnt,
3661 int i,
3662 int hlf,
3663 int *table)
3664{
3665 hl_group_T *hlt = HL_TABLE();
3666
3667 if (id_alt == 0)
3668 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003669 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003670 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3671 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3672# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3673 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3674# endif
3675 }
3676 else
3677 mch_memmove(&hlt[hlcnt + i],
3678 &hlt[id_alt - 1],
3679 sizeof(hl_group_T));
3680 hlt[hlcnt + i].sg_link = 0;
3681
3682 hlt[hlcnt + i].sg_term ^=
3683 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3684 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3685 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3686 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3687 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3688 hlt[hlcnt + i].sg_cterm ^=
3689 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3690 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3691 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3692 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3693 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3694# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3695 hlt[hlcnt + i].sg_gui ^=
3696 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3697# endif
3698# ifdef FEAT_GUI
3699 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3700 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3701 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3702 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3703 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3704 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3705 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3706 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3707# ifdef FEAT_XFONTSET
3708 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3709 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3710# endif
3711# endif
3712 highlight_ga.ga_len = hlcnt + i + 1;
3713 set_hl_attr(hlcnt + i); // At long last we can apply
3714 table[i] = syn_id2attr(hlcnt + i + 1);
3715}
3716#endif
3717
3718/*
3719 * Translate the 'highlight' option into attributes in highlight_attr[] and
3720 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3721 * corresponding highlights to use on top of HLF_SNC is computed.
3722 * Called only when the 'highlight' option has been changed and upon first
3723 * screen redraw after any :highlight command.
3724 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3725 */
3726 int
3727highlight_changed(void)
3728{
3729 int hlf;
3730 int i;
3731 char_u *p;
3732 int attr;
3733 char_u *end;
3734 int id;
3735#ifdef USER_HIGHLIGHT
3736 char_u userhl[30]; // use 30 to avoid compiler warning
3737# ifdef FEAT_STL_OPT
3738 int id_S = -1;
3739 int id_SNC = 0;
3740# ifdef FEAT_TERMINAL
3741 int id_ST = 0;
3742 int id_STNC = 0;
3743# endif
3744 int hlcnt;
3745# endif
3746#endif
3747 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3748
3749 need_highlight_changed = FALSE;
3750
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003751#ifdef FEAT_TERMINAL
3752 term_update_colors_all();
3753 term_update_wincolor_all();
3754#endif
3755
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003756 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003757 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3758 highlight_attr[hlf] = 0;
3759
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003760 // First set all attributes to their default value.
3761 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003762 for (i = 0; i < 2; ++i)
3763 {
3764 if (i)
3765 p = p_hl;
3766 else
3767 p = get_highlight_default();
3768 if (p == NULL) // just in case
3769 continue;
3770
3771 while (*p)
3772 {
3773 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3774 if (hl_flags[hlf] == *p)
3775 break;
3776 ++p;
3777 if (hlf == (int)HLF_COUNT || *p == NUL)
3778 return FAIL;
3779
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003780 // Allow several hl_flags to be combined, like "bu" for
3781 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003782 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003783 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003784 {
3785 if (VIM_ISWHITE(*p)) // ignore white space
3786 continue;
3787
3788 if (attr > HL_ALL) // Combination with ':' is not allowed.
3789 return FAIL;
3790
3791 switch (*p)
3792 {
3793 case 'b': attr |= HL_BOLD;
3794 break;
3795 case 'i': attr |= HL_ITALIC;
3796 break;
3797 case '-':
3798 case 'n': // no highlighting
3799 break;
3800 case 'r': attr |= HL_INVERSE;
3801 break;
3802 case 's': attr |= HL_STANDOUT;
3803 break;
3804 case 'u': attr |= HL_UNDERLINE;
3805 break;
3806 case 'c': attr |= HL_UNDERCURL;
3807 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003808 case '2': attr |= HL_UNDERDOUBLE;
3809 break;
3810 case 'd': attr |= HL_UNDERDOTTED;
3811 break;
3812 case '=': attr |= HL_UNDERDASHED;
3813 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003814 case 't': attr |= HL_STRIKETHROUGH;
3815 break;
3816 case ':': ++p; // highlight group name
3817 if (attr || *p == NUL) // no combinations
3818 return FAIL;
3819 end = vim_strchr(p, ',');
3820 if (end == NULL)
3821 end = p + STRLEN(p);
3822 id = syn_check_group(p, (int)(end - p));
3823 if (id == 0)
3824 return FAIL;
3825 attr = syn_id2attr(id);
3826 p = end - 1;
3827#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3828 if (hlf == (int)HLF_SNC)
3829 id_SNC = syn_get_final_id(id);
3830# ifdef FEAT_TERMINAL
3831 else if (hlf == (int)HLF_ST)
3832 id_ST = syn_get_final_id(id);
3833 else if (hlf == (int)HLF_STNC)
3834 id_STNC = syn_get_final_id(id);
3835# endif
3836 else if (hlf == (int)HLF_S)
3837 id_S = syn_get_final_id(id);
3838#endif
3839 break;
3840 default: return FAIL;
3841 }
3842 }
3843 highlight_attr[hlf] = attr;
3844
3845 p = skip_to_option_part(p); // skip comma and spaces
3846 }
3847 }
3848
3849#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003850 // Setup the user highlights
3851 //
3852 // Temporarily utilize 28 more hl entries:
3853 // 9 for User1-User9 combined with StatusLineNC
3854 // 9 for User1-User9 combined with StatusLineTerm
3855 // 9 for User1-User9 combined with StatusLineTermNC
3856 // 1 for StatusLine default
3857 // Have to be in there simultaneously in case of table overflows in
3858 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003859# ifdef FEAT_STL_OPT
3860 if (ga_grow(&highlight_ga, 28) == FAIL)
3861 return FAIL;
3862 hlcnt = highlight_ga.ga_len;
3863 if (id_S == -1)
3864 {
3865 // Make sure id_S is always valid to simplify code below. Use the last
3866 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003867 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003868 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3869 id_S = hlcnt + 19;
3870 }
3871# endif
3872 for (i = 0; i < 9; i++)
3873 {
3874 sprintf((char *)userhl, "User%d", i + 1);
3875 id = syn_name2id(userhl);
3876 if (id == 0)
3877 {
3878 highlight_user[i] = 0;
3879# ifdef FEAT_STL_OPT
3880 highlight_stlnc[i] = 0;
3881# ifdef FEAT_TERMINAL
3882 highlight_stlterm[i] = 0;
3883 highlight_stltermnc[i] = 0;
3884# endif
3885# endif
3886 }
3887 else
3888 {
3889 highlight_user[i] = syn_id2attr(id);
3890# ifdef FEAT_STL_OPT
3891 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3892 HLF_SNC, highlight_stlnc);
3893# ifdef FEAT_TERMINAL
3894 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3895 HLF_ST, highlight_stlterm);
3896 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3897 HLF_STNC, highlight_stltermnc);
3898# endif
3899# endif
3900 }
3901 }
3902# ifdef FEAT_STL_OPT
3903 highlight_ga.ga_len = hlcnt;
3904# endif
3905
3906#endif // USER_HIGHLIGHT
3907
3908 return OK;
3909}
3910
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003911static void highlight_list(void);
3912static void highlight_list_two(int cnt, int attr);
3913
3914/*
3915 * Handle command line completion for :highlight command.
3916 */
3917 void
3918set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3919{
3920 char_u *p;
3921
3922 // Default: expand group names
3923 xp->xp_context = EXPAND_HIGHLIGHT;
3924 xp->xp_pattern = arg;
3925 include_link = 2;
3926 include_default = 1;
3927
3928 // (part of) subcommand already typed
3929 if (*arg != NUL)
3930 {
3931 p = skiptowhite(arg);
3932 if (*p != NUL) // past "default" or group name
3933 {
3934 include_default = 0;
3935 if (STRNCMP("default", arg, p - arg) == 0)
3936 {
3937 arg = skipwhite(p);
3938 xp->xp_pattern = arg;
3939 p = skiptowhite(arg);
3940 }
3941 if (*p != NUL) // past group name
3942 {
3943 include_link = 0;
3944 if (arg[1] == 'i' && arg[0] == 'N')
3945 highlight_list();
3946 if (STRNCMP("link", arg, p - arg) == 0
3947 || STRNCMP("clear", arg, p - arg) == 0)
3948 {
3949 xp->xp_pattern = skipwhite(p);
3950 p = skiptowhite(xp->xp_pattern);
3951 if (*p != NUL) // past first group name
3952 {
3953 xp->xp_pattern = skipwhite(p);
3954 p = skiptowhite(xp->xp_pattern);
3955 }
3956 }
3957 if (*p != NUL) // past group name(s)
3958 xp->xp_context = EXPAND_NOTHING;
3959 }
3960 }
3961 }
3962}
3963
3964/*
3965 * List highlighting matches in a nice way.
3966 */
3967 static void
3968highlight_list(void)
3969{
3970 int i;
3971
3972 for (i = 10; --i >= 0; )
3973 highlight_list_two(i, HL_ATTR(HLF_D));
3974 for (i = 40; --i >= 0; )
3975 highlight_list_two(99, 0);
3976}
3977
3978 static void
3979highlight_list_two(int cnt, int attr)
3980{
3981 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3982 msg_clr_eos();
3983 out_flush();
3984 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3985}
3986
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003987/*
3988 * Function given to ExpandGeneric() to obtain the list of group names.
3989 */
3990 char_u *
3991get_highlight_name(expand_T *xp UNUSED, int idx)
3992{
3993 return get_highlight_name_ext(xp, idx, TRUE);
3994}
3995
3996/*
3997 * Obtain a highlight group name.
3998 * When "skip_cleared" is TRUE don't return a cleared entry.
3999 */
4000 char_u *
4001get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4002{
4003 if (idx < 0)
4004 return NULL;
4005
4006 // Items are never removed from the table, skip the ones that were
4007 // cleared.
4008 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4009 return (char_u *)"";
4010
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004011 if (idx == highlight_ga.ga_len && include_none != 0)
4012 return (char_u *)"none";
4013 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4014 return (char_u *)"default";
4015 if (idx == highlight_ga.ga_len + include_none + include_default
4016 && include_link != 0)
4017 return (char_u *)"link";
4018 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4019 && include_link != 0)
4020 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004021 if (idx >= highlight_ga.ga_len)
4022 return NULL;
4023 return HL_TABLE()[idx].sg_name;
4024}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004025
4026#if defined(FEAT_GUI) || defined(PROTO)
4027/*
4028 * Free all the highlight group fonts.
4029 * Used when quitting for systems which need it.
4030 */
4031 void
4032free_highlight_fonts(void)
4033{
4034 int idx;
4035
4036 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4037 {
4038 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4039 HL_TABLE()[idx].sg_font = NOFONT;
4040# ifdef FEAT_XFONTSET
4041 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4042 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4043# endif
4044 }
4045
4046 gui_mch_free_font(gui.norm_font);
4047# ifdef FEAT_XFONTSET
4048 gui_mch_free_fontset(gui.fontset);
4049# endif
4050# ifndef FEAT_GUI_GTK
4051 gui_mch_free_font(gui.bold_font);
4052 gui_mch_free_font(gui.ital_font);
4053 gui_mch_free_font(gui.boldital_font);
4054# endif
4055}
4056#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004057
4058#if defined(FEAT_EVAL) || defined(PROTO)
4059/*
4060 * Convert each of the highlight attribute bits (bold, standout, underline,
4061 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4062 * the attribute name as the key.
4063 */
4064 static dict_T *
4065highlight_get_attr_dict(int hlattr)
4066{
4067 dict_T *dict;
4068 int i;
4069
4070 dict = dict_alloc();
4071 if (dict == NULL)
4072 return NULL;
4073
4074 for (i = 0; hl_attr_table[i] != 0; ++i)
4075 {
4076 if (hlattr & hl_attr_table[i])
4077 {
4078 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4079 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4080 }
4081 }
4082
4083 return dict;
4084}
4085
4086/*
4087 * Return the attributes of the highlight group at index 'hl_idx' as a
4088 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4089 * links recursively.
4090 */
4091 static dict_T *
4092highlight_get_info(int hl_idx, int resolve_link)
4093{
4094 dict_T *dict;
4095 hl_group_T *sgp;
4096 dict_T *attr_dict;
4097 int hlgid;
4098
4099 dict = dict_alloc();
4100 if (dict == NULL)
4101 return dict;
4102
4103 sgp = &HL_TABLE()[hl_idx];
4104 // highlight group id is 1-based
4105 hlgid = hl_idx + 1;
4106
4107 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4108 goto error;
4109 if (dict_add_number(dict, "id", hlgid) == FAIL)
4110 goto error;
4111
4112 if (sgp->sg_link && resolve_link)
4113 {
4114 // resolve the highlight group link recursively
4115 while (sgp->sg_link)
4116 {
4117 hlgid = sgp->sg_link;
4118 sgp = &HL_TABLE()[sgp->sg_link - 1];
4119 }
4120 }
4121
4122 if (sgp->sg_term != 0)
4123 {
4124 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4125 if (attr_dict != NULL)
4126 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4127 goto error;
4128 }
4129 if (sgp->sg_start != NULL)
4130 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4131 goto error;
4132 if (sgp->sg_stop != NULL)
4133 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4134 goto error;
4135 if (sgp->sg_cterm != 0)
4136 {
4137 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4138 if (attr_dict != NULL)
4139 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4140 goto error;
4141 }
4142 if (sgp->sg_cterm_fg != 0)
4143 if (dict_add_string(dict, "ctermfg",
4144 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4145 goto error;
4146 if (sgp->sg_cterm_bg != 0)
4147 if (dict_add_string(dict, "ctermbg",
4148 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4149 goto error;
4150 if (sgp->sg_cterm_ul != 0)
4151 if (dict_add_string(dict, "ctermul",
4152 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4153 goto error;
4154 if (sgp->sg_gui != 0)
4155 {
4156 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4157 if (attr_dict != NULL)
4158 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4159 goto error;
4160 }
4161 if (sgp->sg_gui_fg_name != NULL)
4162 if (dict_add_string(dict, "guifg",
4163 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4164 goto error;
4165 if (sgp->sg_gui_bg_name != NULL)
4166 if (dict_add_string(dict, "guibg",
4167 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4168 goto error;
4169 if (sgp->sg_gui_sp_name != NULL)
4170 if (dict_add_string(dict, "guisp",
4171 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4172 goto error;
4173# ifdef FEAT_GUI
4174 if (sgp->sg_font_name != NULL)
4175 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4176 goto error;
4177# endif
4178 if (sgp->sg_link)
4179 {
4180 char_u *link;
4181
4182 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4183 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4184 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004185
4186 if (sgp->sg_deflink)
4187 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004188 }
4189 if (dict_len(dict) == 2)
4190 // If only 'name' is present, then the highlight group is cleared.
4191 dict_add_bool(dict, "cleared", VVAL_TRUE);
4192
4193 return dict;
4194
4195error:
4196 vim_free(dict);
4197 return NULL;
4198}
4199
4200/*
4201 * "hlget([name])" function
4202 * Return the attributes of a specific highlight group (if specified) or all
4203 * the highlight groups.
4204 */
4205 void
4206f_hlget(typval_T *argvars, typval_T *rettv)
4207{
4208 list_T *list;
4209 dict_T *dict;
4210 int i;
4211 char_u *hlarg = NULL;
4212 int resolve_link = FALSE;
4213
4214 if (rettv_list_alloc(rettv) == FAIL)
4215 return;
4216
4217 if (check_for_opt_string_arg(argvars, 0) == FAIL
4218 || (argvars[0].v_type != VAR_UNKNOWN
4219 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4220 return;
4221
4222 if (argvars[0].v_type != VAR_UNKNOWN)
4223 {
4224 // highlight group name supplied
4225 hlarg = tv_get_string_chk(&argvars[0]);
4226 if (hlarg == NULL)
4227 return;
4228
4229 if (argvars[1].v_type != VAR_UNKNOWN)
4230 {
4231 int error = FALSE;
4232
4233 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4234 if (error)
4235 return;
4236 }
4237 }
4238
4239 list = rettv->vval.v_list;
4240 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4241 {
4242 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4243 {
4244 dict = highlight_get_info(i, resolve_link);
4245 if (dict != NULL)
4246 list_append_dict(list, dict);
4247 }
4248 }
4249}
4250
4251/*
4252 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4253 * 'dict' or the value is not a string type. If the value is not a string type
4254 * or is NULL, then 'error' is set to TRUE.
4255 */
4256 static char_u *
4257hldict_get_string(dict_T *dict, char_u *key, int *error)
4258{
4259 dictitem_T *di;
4260
4261 *error = FALSE;
4262 di = dict_find(dict, key, -1);
4263 if (di == NULL)
4264 return NULL;
4265
4266 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4267 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004268 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004269 *error = TRUE;
4270 return NULL;
4271 }
4272
4273 return di->di_tv.vval.v_string;
4274}
4275
4276/*
4277 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4278 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4279 * Dictionary or is NULL.
4280 */
4281 static int
4282hldict_attr_to_str(
4283 dict_T *dict,
4284 char_u *key,
4285 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004286 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004287{
4288 dictitem_T *di;
4289 dict_T *attrdict;
4290 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004291 char_u *p;
4292 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004293
4294 attr_str[0] = NUL;
4295 di = dict_find(dict, key, -1);
4296 if (di == NULL)
4297 return TRUE;
4298
4299 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4300 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004301 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004302 return FALSE;
4303 }
4304
4305 attrdict = di->di_tv.vval.v_dict;
4306
4307 // If the attribute dict is empty, then return NONE to clear the attributes
4308 if (dict_len(attrdict) == 0)
4309 {
4310 vim_strcat(attr_str, (char_u *)"NONE", len);
4311 return TRUE;
4312 }
4313
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004314 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004315 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4316 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01004317 if (dict_get_bool(attrdict, hl_name_table[i], VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004318 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004319 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4320 STRCPY(p, (char_u *)",");
4321 sz = STRLEN(hl_name_table[i]);
4322 if (p - attr_str + sz + 1 < len)
4323 {
4324 STRCPY(p, (char_u *)hl_name_table[i]);
4325 p += sz;
4326 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004327 }
4328 }
4329
4330 return TRUE;
4331}
4332
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004333// Temporary buffer used to store the command string produced by hlset().
4334// IObuff cannot be used for this as the error messages produced by hlset()
4335// internally use IObuff.
4336#define HLSETBUFSZ 512
4337static char_u hlsetBuf[HLSETBUFSZ + 1];
4338
4339/*
4340 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4341 * "dptr", which points into "hlsetBuf".
4342 * Returns the updated pointer.
4343 */
4344 static char_u *
4345add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4346{
4347 size_t vallen;
4348
4349 // Do nothing if the value is not specified or is empty
4350 if (value == NULL || *value == NUL)
4351 return dptr;
4352
4353 vallen = STRLEN(value);
4354 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4355 {
4356 STRCPY(dptr, attr);
4357 dptr += attrlen;
4358 STRCPY(dptr, value);
4359 dptr += vallen;
4360 }
4361
4362 return dptr;
4363}
4364
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004365/*
4366 * Add or update a highlight group using 'dict' items. Returns TRUE if
4367 * successfully updated the highlight group.
4368 */
4369 static int
4370hlg_add_or_update(dict_T *dict)
4371{
4372 char_u *name;
4373 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004374 char_u term_attr[MAX_ATTR_LEN];
4375 char_u cterm_attr[MAX_ATTR_LEN];
4376 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004377 char_u *start;
4378 char_u *stop;
4379 char_u *ctermfg;
4380 char_u *ctermbg;
4381 char_u *ctermul;
4382 char_u *guifg;
4383 char_u *guibg;
4384 char_u *guisp;
4385# ifdef FEAT_GUI
4386 char_u *font;
4387# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004388 int forceit = FALSE;
4389 int dodefault = FALSE;
4390 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004391 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004392
4393 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004394 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004395 return FALSE;
4396
Bram Moolenaard61efa52022-07-23 09:52:04 +01004397 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004398 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004399
Bram Moolenaard61efa52022-07-23 09:52:04 +01004400 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004401 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004402
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004403 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004404 {
4405 varnumber_T cleared;
4406
4407 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004408 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004409 if (cleared == TRUE)
4410 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004411 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4412 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004413 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004414 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004415 }
4416
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004417 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004418 {
4419 char_u *linksto;
4420
4421 // link highlight groups
4422 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004423 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004424 return FALSE;
4425
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004426 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004427 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004428 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004429
4430 done = TRUE;
4431 }
4432
4433 // If 'cleared' or 'linksto' are specified, then don't process the other
4434 // attributes.
4435 if (done)
4436 return TRUE;
4437
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004438 start = hldict_get_string(dict, (char_u *)"start", &error);
4439 if (error)
4440 return FALSE;
4441
4442 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4443 if (error)
4444 return FALSE;
4445
4446 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004447 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004448 return FALSE;
4449
4450 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004451 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004452 return FALSE;
4453
4454 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4455 if (error)
4456 return FALSE;
4457
4458 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4459 if (error)
4460 return FALSE;
4461
4462 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4463 if (error)
4464 return FALSE;
4465
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004466 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004467 return FALSE;
4468
4469 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4470 if (error)
4471 return FALSE;
4472
4473 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4474 if (error)
4475 return FALSE;
4476
4477 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4478 if (error)
4479 return FALSE;
4480
4481# ifdef FEAT_GUI
4482 font = hldict_get_string(dict, (char_u *)"font", &error);
4483 if (error)
4484 return FALSE;
4485# endif
4486
4487 // If none of the attributes are specified, then do nothing.
4488 if (term_attr[0] == NUL && start == NULL && stop == NULL
4489 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4490 && ctermul == NULL && gui_attr[0] == NUL
4491# ifdef FEAT_GUI
4492 && font == NULL
4493# endif
4494 && guifg == NULL && guibg == NULL && guisp == NULL
4495 )
4496 return TRUE;
4497
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004498 hlsetBuf[0] = NUL;
4499 p = hlsetBuf;
4500 if (dodefault)
4501 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4502 p = add_attr_and_value(p, (char_u *)"", 0, name);
4503 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4504 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4505 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4506 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4507 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4508 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4509 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4510 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004511# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004512 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004513# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004514 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4515 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
4516 p = add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004517
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004518 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004519
4520 return TRUE;
4521}
4522
4523/*
4524 * "hlset([{highlight_attr}])" function
4525 * Add or modify highlight groups
4526 */
4527 void
4528f_hlset(typval_T *argvars, typval_T *rettv)
4529{
4530 listitem_T *li;
4531 dict_T *dict;
4532
4533 rettv->vval.v_number = -1;
4534
4535 if (check_for_list_arg(argvars, 0) == FAIL)
4536 return;
4537
4538 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4539 {
4540 if (li->li_tv.v_type != VAR_DICT)
4541 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004542 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004543 return;
4544 }
4545
4546 dict = li->li_tv.vval.v_dict;
4547 if (!hlg_add_or_update(dict))
4548 return;
4549 }
4550
4551 rettv->vval.v_number = 0;
4552}
4553#endif