blob: 3773ddf70b398f9bf96af83f5c34a650cb5db000 [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",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000167 "default link PmenuKind Pmenu",
168 "default link PmenuKindSel PmenuSel",
169 "default link PmenuExtra Pmenu",
170 "default link PmenuExtraSel PmenuSel",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200171 CENT("Normal cterm=NONE", "Normal gui=NONE"),
172 NULL
173};
174
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200175// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200176static char *(highlight_init_light[]) = {
177 CENT("Directory term=bold ctermfg=DarkBlue",
178 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
179 CENT("LineNr term=underline ctermfg=Brown",
180 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200181 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
182 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200183 CENT("MoreMsg term=bold ctermfg=DarkGreen",
184 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
185 CENT("Question term=standout ctermfg=DarkGreen",
186 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
187 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
188 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
189#ifdef FEAT_SPELL
190 CENT("SpellBad term=reverse ctermbg=LightRed",
191 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
192 CENT("SpellCap term=reverse ctermbg=LightBlue",
193 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
194 CENT("SpellRare term=reverse ctermbg=LightMagenta",
195 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
196 CENT("SpellLocal term=underline ctermbg=Cyan",
197 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
198#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200199 CENT("PmenuThumb ctermbg=Black",
200 "PmenuThumb ctermbg=Black guibg=Black"),
201 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
202 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
203 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
204 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200205 CENT("SpecialKey term=bold ctermfg=DarkBlue",
206 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
207 CENT("Title term=bold ctermfg=DarkMagenta",
208 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
209 CENT("WarningMsg term=standout ctermfg=DarkRed",
210 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200211 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
212 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200213#ifdef FEAT_FOLDING
214 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
215 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
216 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
217 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
218#endif
219#ifdef FEAT_SIGNS
220 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
221 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
222#endif
223 CENT("Visual term=reverse",
224 "Visual term=reverse guibg=LightGrey"),
225#ifdef FEAT_DIFF
226 CENT("DiffAdd term=bold ctermbg=LightBlue",
227 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
228 CENT("DiffChange term=bold ctermbg=LightMagenta",
229 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
230 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
231 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
232#endif
233 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
234 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
235#ifdef FEAT_SYN_HL
236 CENT("CursorColumn term=reverse ctermbg=LightGrey",
237 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
238 CENT("CursorLine term=underline cterm=underline",
239 "CursorLine term=underline cterm=underline guibg=Grey90"),
240 CENT("ColorColumn term=reverse ctermbg=LightRed",
241 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
242#endif
243#ifdef FEAT_CONCEAL
244 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
245 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
246#endif
247 CENT("MatchParen term=reverse ctermbg=Cyan",
248 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
249#ifdef FEAT_TERMINAL
250 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
251 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
252 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
253 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
254#endif
255#ifdef FEAT_MENU
256 CENT("ToolbarLine term=underline ctermbg=LightGrey",
257 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
258 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
259 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
260#endif
261 NULL
262};
263
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200264// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200265static char *(highlight_init_dark[]) = {
266 CENT("Directory term=bold ctermfg=LightCyan",
267 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
268 CENT("LineNr term=underline ctermfg=Yellow",
269 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200270 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
271 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200272 CENT("MoreMsg term=bold ctermfg=LightGreen",
273 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
274 CENT("Question term=standout ctermfg=LightGreen",
275 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
276 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
277 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
278 CENT("SpecialKey term=bold ctermfg=LightBlue",
279 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
280#ifdef FEAT_SPELL
281 CENT("SpellBad term=reverse ctermbg=Red",
282 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
283 CENT("SpellCap term=reverse ctermbg=Blue",
284 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
285 CENT("SpellRare term=reverse ctermbg=Magenta",
286 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
287 CENT("SpellLocal term=underline ctermbg=Cyan",
288 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
289#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200290 CENT("PmenuThumb ctermbg=White",
291 "PmenuThumb ctermbg=White guibg=White"),
292 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
293 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
294 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
295 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200296 CENT("Title term=bold ctermfg=LightMagenta",
297 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
298 CENT("WarningMsg term=standout ctermfg=LightRed",
299 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200300 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
301 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200302#ifdef FEAT_FOLDING
303 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
304 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
305 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
306 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
307#endif
308#ifdef FEAT_SIGNS
309 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
310 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
311#endif
312 CENT("Visual term=reverse",
313 "Visual term=reverse guibg=DarkGrey"),
314#ifdef FEAT_DIFF
315 CENT("DiffAdd term=bold ctermbg=DarkBlue",
316 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
317 CENT("DiffChange term=bold ctermbg=DarkMagenta",
318 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
319 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
320 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
321#endif
322 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
323 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
324#ifdef FEAT_SYN_HL
325 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
326 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
327 CENT("CursorLine term=underline cterm=underline",
328 "CursorLine term=underline cterm=underline guibg=Grey40"),
329 CENT("ColorColumn term=reverse ctermbg=DarkRed",
330 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
331#endif
332 CENT("MatchParen term=reverse ctermbg=DarkCyan",
333 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
334#ifdef FEAT_CONCEAL
335 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
336 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
337#endif
338#ifdef FEAT_TERMINAL
339 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
340 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
341 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
342 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
343#endif
344#ifdef FEAT_MENU
345 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
346 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
347 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
348 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
349#endif
350 NULL
351};
352
Dominique Pelle748b3082022-01-08 12:41:16 +0000353#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200354/*
355 * Returns the number of highlight groups.
356 */
357 int
358highlight_num_groups(void)
359{
360 return highlight_ga.ga_len;
361}
362
363/*
364 * Returns the name of a highlight group.
365 */
366 char_u *
367highlight_group_name(int id)
368{
369 return HL_TABLE()[id].sg_name;
370}
371
372/*
373 * Returns the ID of the link to a highlight group.
374 */
375 int
376highlight_link_id(int id)
377{
378 return HL_TABLE()[id].sg_link;
379}
Dominique Pelle748b3082022-01-08 12:41:16 +0000380#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200381
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200382 void
383init_highlight(
384 int both, // include groups where 'bg' doesn't matter
385 int reset) // clear group first
386{
387 int i;
388 char **pp;
389 static int had_both = FALSE;
390#ifdef FEAT_EVAL
391 char_u *p;
392
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100393 // Try finding the color scheme file. Used when a color file was loaded
394 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200395 p = get_var_value((char_u *)"g:colors_name");
396 if (p != NULL)
397 {
398 // The value of g:colors_name could be freed when sourcing the script,
399 // making "p" invalid, so copy it.
400 char_u *copy_p = vim_strsave(p);
401 int r;
402
403 if (copy_p != NULL)
404 {
405 r = load_colors(copy_p);
406 vim_free(copy_p);
407 if (r == OK)
408 return;
409 }
410 }
411
412#endif
413
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100414 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200415 if (both)
416 {
417 had_both = TRUE;
418 pp = highlight_init_both;
419 for (i = 0; pp[i] != NULL; ++i)
420 do_highlight((char_u *)pp[i], reset, TRUE);
421 }
422 else if (!had_both)
423 // Don't do anything before the call with both == TRUE from main().
424 // Not everything has been setup then, and that call will overrule
425 // everything anyway.
426 return;
427
428 if (*p_bg == 'l')
429 pp = highlight_init_light;
430 else
431 pp = highlight_init_dark;
432 for (i = 0; pp[i] != NULL; ++i)
433 do_highlight((char_u *)pp[i], reset, TRUE);
434
435 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
436 // depend on the number of colors available.
437 // With 8 colors brown is equal to yellow, need to use black for Search fg
438 // to avoid Statement highlighted text disappears.
439 // Clear the attributes, needed when changing the t_Co value.
440 if (t_colors > 8)
441 do_highlight((char_u *)(*p_bg == 'l'
442 ? "Visual cterm=NONE ctermbg=LightGrey"
443 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
444 else
445 {
446 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
447 FALSE, TRUE);
448 if (*p_bg == 'l')
449 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
450 }
451
452#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100453 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200454 if (get_var_value((char_u *)"g:syntax_on") != NULL)
455 {
456 static int recursive = 0;
457
458 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000459 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200460 else
461 {
462 ++recursive;
463 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
464 --recursive;
465 }
466 }
467#endif
468}
469
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000470#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
471/*
472 * Load a default color list. Intended to support legacy color names but allows
473 * the user to override the color values. Only loaded once.
474 */
475 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000476load_default_colors_lists(void)
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000477{
478 // Lacking a default color list isn't the end of the world but it is likely
479 // an inconvenience so users should know when it is missing.
480 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
481 msg("failed to load colors/lists/default.vim");
482}
483#endif
484
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200485/*
486 * Load color file "name".
487 * Return OK for success, FAIL for failure.
488 */
489 int
490load_colors(char_u *name)
491{
492 char_u *buf;
493 int retval = FAIL;
494 static int recursive = FALSE;
495
496 // When being called recursively, this is probably because setting
497 // 'background' caused the highlighting to be reloaded. This means it is
498 // working, thus we should return OK.
499 if (recursive)
500 return OK;
501
502 recursive = TRUE;
503 buf = alloc(STRLEN(name) + 12);
504 if (buf != NULL)
505 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100506#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100507 load_default_colors_lists();
508#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200509 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
510 curbuf->b_fname, FALSE, curbuf);
511 sprintf((char *)buf, "colors/%s.vim", name);
512 retval = source_runtime(buf, DIP_START + DIP_OPT);
513 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100514 if (retval == OK)
515 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
516 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200517 }
518 recursive = FALSE;
519
520 return retval;
521}
522
523static char *(color_names[28]) = {
524 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
525 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
526 "Gray", "Grey", "LightGray", "LightGrey",
527 "DarkGray", "DarkGrey",
528 "Blue", "LightBlue", "Green", "LightGreen",
529 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
530 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
531 // indices:
532 // 0, 1, 2, 3,
533 // 4, 5, 6, 7,
534 // 8, 9, 10, 11,
535 // 12, 13,
536 // 14, 15, 16, 17,
537 // 18, 19, 20, 21, 22,
538 // 23, 24, 25, 26, 27
539static int color_numbers_16[28] = {0, 1, 2, 3,
540 4, 5, 6, 6,
541 7, 7, 7, 7,
542 8, 8,
543 9, 9, 10, 10,
544 11, 11, 12, 12, 13,
545 13, 14, 14, 15, -1};
546// for xterm with 88 colors...
547static int color_numbers_88[28] = {0, 4, 2, 6,
548 1, 5, 32, 72,
549 84, 84, 7, 7,
550 82, 82,
551 12, 43, 10, 61,
552 14, 63, 9, 74, 13,
553 75, 11, 78, 15, -1};
554// for xterm with 256 colors...
555static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200556 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200557 248, 248, 7, 7,
558 242, 242,
559 12, 81, 10, 121,
560 14, 159, 9, 224, 13,
561 225, 11, 229, 15, -1};
562// for terminals with less than 16 colors...
563static int color_numbers_8[28] = {0, 4, 2, 6,
564 1, 5, 3, 3,
565 7, 7, 7, 7,
566 0+8, 0+8,
567 4+8, 4+8, 2+8, 2+8,
568 6+8, 6+8, 1+8, 1+8, 5+8,
569 5+8, 3+8, 3+8, 7+8, -1};
570
571/*
572 * Lookup the "cterm" value to be used for color with index "idx" in
573 * color_names[].
574 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
575 * colors, otherwise it will be unchanged.
576 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100577 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200578lookup_color(int idx, int foreground, int *boldp)
579{
580 int color = color_numbers_16[idx];
581 char_u *p;
582
583 // Use the _16 table to check if it's a valid color name.
584 if (color < 0)
585 return -1;
586
587 if (t_colors == 8)
588 {
589 // t_Co is 8: use the 8 colors table
590#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100591 // On qnx, the 8 & 16 color arrays are the same
592 if (STRNCMP(T_NAME, "qansi", 5) == 0)
593 color = color_numbers_16[idx];
594 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200595#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100596 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200597 if (foreground)
598 {
599 // set/reset bold attribute to get light foreground
600 // colors (on some terminals, e.g. "linux")
601 if (color & 8)
602 *boldp = TRUE;
603 else
604 *boldp = FALSE;
605 }
606 color &= 7; // truncate to 8 colors
607 }
608 else if (t_colors == 16 || t_colors == 88
609 || t_colors >= 256)
610 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100611 // Guess: if the termcap entry ends in 'm', it is
612 // probably an xterm-like terminal. Use the changed
613 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200614 if (*T_CAF != NUL)
615 p = T_CAF;
616 else
617 p = T_CSF;
618 if (*p != NUL && (t_colors > 256
619 || *(p + STRLEN(p) - 1) == 'm'))
620 {
621 if (t_colors == 88)
622 color = color_numbers_88[idx];
623 else if (t_colors >= 256)
624 color = color_numbers_256[idx];
625 else
626 color = color_numbers_8[idx];
627 }
628#ifdef FEAT_TERMRESPONSE
629 if (t_colors >= 256 && color == 15 && is_mac_terminal)
630 // Terminal.app has a bug: 15 is light grey. Use white
631 // from the color cube instead.
632 color = 231;
633#endif
634 }
635 return color;
636}
637
638/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100639 * Link highlight group 'from_hg' to 'to_hg'.
640 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000641 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100642 * 'init' is set to TRUE when initializing all the highlight groups.
643 */
644 static void
645highlight_group_link(
646 char_u *from_hg,
647 int from_len,
648 char_u *to_hg,
649 int to_len,
650 int dodefault,
651 int forceit,
652 int init)
653{
654 int from_id;
655 int to_id;
656 hl_group_T *hlgroup = NULL;
657
658 from_id = syn_check_group(from_hg, from_len);
659 if (STRNCMP(to_hg, "NONE", 4) == 0)
660 to_id = 0;
661 else
662 to_id = syn_check_group(to_hg, to_len);
663
664 if (from_id > 0)
665 {
666 hlgroup = &HL_TABLE()[from_id - 1];
667 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
668 {
669 hlgroup->sg_deflink = to_id;
670#ifdef FEAT_EVAL
671 hlgroup->sg_deflink_sctx = current_sctx;
672 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
673#endif
674 }
675 }
676
677 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
678 {
679 // Don't allow a link when there already is some highlighting
680 // for the group, unless '!' is used
681 if (to_id > 0 && !forceit && !init
682 && hl_has_settings(from_id - 1, dodefault))
683 {
684 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000685 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100686 }
687 else if (hlgroup->sg_link != to_id
688#ifdef FEAT_EVAL
689 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
690#endif
691 || hlgroup->sg_cleared)
692 {
693 if (!init)
694 hlgroup->sg_set |= SG_LINK;
695 hlgroup->sg_link = to_id;
696#ifdef FEAT_EVAL
697 hlgroup->sg_script_ctx = current_sctx;
698 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
699#endif
700 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100701 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100702
703 // Only call highlight_changed() once after multiple changes.
704 need_highlight_changed = TRUE;
705 }
706 }
707
708}
709
710/*
711 * Reset all highlighting to the defaults. Removes all highlighting for the
712 * groups added by the user.
713 */
714 static void
715highlight_reset_all(void)
716{
717 int idx;
718
719#ifdef FEAT_GUI
720 // First, we do not destroy the old values, but allocate the new
721 // ones and update the display. THEN we destroy the old values.
722 // If we destroy the old values first, then the old values
723 // (such as GuiFont's or GuiFontset's) will still be displayed but
724 // invalid because they were free'd.
725 if (gui.in_use)
726 {
727# ifdef FEAT_BEVAL_TIP
728 gui_init_tooltip_font();
729# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100730# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100731 gui_init_menu_font();
732# endif
733 }
734# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
735 gui_mch_def_colors();
736# endif
737# ifdef FEAT_GUI_X11
738# ifdef FEAT_MENU
739
740 // This only needs to be done when there is no Menu highlight
741 // group defined by default, which IS currently the case.
742 gui_mch_new_menu_colors();
743# endif
744 if (gui.in_use)
745 {
746 gui_new_scrollbar_colors();
747# ifdef FEAT_BEVAL_GUI
748 gui_mch_new_tooltip_colors();
749# endif
750# ifdef FEAT_MENU
751 gui_mch_new_menu_font();
752# endif
753 }
754# endif
755
756 // Ok, we're done allocating the new default graphics items.
757 // The screen should already be refreshed at this point.
758 // It is now Ok to clear out the old data.
759#endif
760#ifdef FEAT_EVAL
761 do_unlet((char_u *)"g:colors_name", TRUE);
762#endif
763 restore_cterm_colors();
764
765 // Clear all default highlight groups and load the defaults.
766 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
767 highlight_clear(idx);
768 init_highlight(TRUE, TRUE);
769#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
770 if (USE_24BIT)
771 highlight_gui_started();
772 else
773#endif
774 highlight_changed();
775 redraw_later_clear();
776}
777
778/*
779 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
780 * index 'idx'.
781 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
782 * 'arg' is the list of attribute names separated by comma.
783 * 'init' is set to TRUE when initializing all the highlight groups.
784 * Returns TRUE if the attributes are set.
785 */
786 static int
787highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
788{
789 int attr;
790 int off;
791 long i;
792 int len;
793
794 attr = 0;
795 off = 0;
796 while (arg[off] != NUL)
797 {
798 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
799 {
800 len = (int)STRLEN(hl_name_table[i]);
801 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
802 {
803 attr |= hl_attr_table[i];
804 off += len;
805 break;
806 }
807 }
808 if (i < 0)
809 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000810 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100811 return FALSE;
812 }
813 if (arg[off] == ',') // another one follows
814 ++off;
815 }
816 if (*key == 'T')
817 {
818 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
819 {
820 if (!init)
821 HL_TABLE()[idx].sg_set |= SG_TERM;
822 HL_TABLE()[idx].sg_term = attr;
823 }
824 }
825 else if (*key == 'C')
826 {
827 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
828 {
829 if (!init)
830 HL_TABLE()[idx].sg_set |= SG_CTERM;
831 HL_TABLE()[idx].sg_cterm = attr;
832 HL_TABLE()[idx].sg_cterm_bold = FALSE;
833 }
834 }
835#if defined(FEAT_GUI) || defined(FEAT_EVAL)
836 else
837 {
838 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
839 {
840 if (!init)
841 HL_TABLE()[idx].sg_set |= SG_GUI;
842 HL_TABLE()[idx].sg_gui = attr;
843 }
844 }
845#endif
846
847 return TRUE;
848}
849
850#ifdef FEAT_GUI
851/*
852 * Set the font for the highlight group at 'idx'.
853 * 'arg' is the font name.
854 * Returns TRUE if the font is changed.
855 */
856 static int
857highlight_set_font(
858 int idx,
859 char_u *arg,
860 int is_normal_group,
861 int is_menu_group,
862 int is_tooltip_group)
863{
864 int did_change = FALSE;
865
866 // in non-GUI fonts are simply ignored
867 if (HL_TABLE()[idx].sg_font_name != NULL
868 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
869 {
870 // Font name didn't change, ignore.
871 }
872 else if (!gui.shell_created)
873 {
874 // GUI not started yet, always accept the name.
875 vim_free(HL_TABLE()[idx].sg_font_name);
876 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
877 did_change = TRUE;
878 }
879 else
880 {
881 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
882# ifdef FEAT_XFONTSET
883 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
884# endif
885 // First, save the current font/fontset.
886 // Then try to allocate the font/fontset.
887 // If the allocation fails, HL_TABLE()[idx].sg_font OR
888 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
889
890 HL_TABLE()[idx].sg_font = NOFONT;
891# ifdef FEAT_XFONTSET
892 HL_TABLE()[idx].sg_fontset = NOFONTSET;
893# endif
894 hl_do_font(idx, arg, is_normal_group, is_menu_group,
895 is_tooltip_group, FALSE);
896
897# ifdef FEAT_XFONTSET
898 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
899 {
900 // New fontset was accepted. Free the old one, if there
901 // was one.
902 gui_mch_free_fontset(temp_sg_fontset);
903 vim_free(HL_TABLE()[idx].sg_font_name);
904 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
905 did_change = TRUE;
906 }
907 else
908 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
909# endif
910 if (HL_TABLE()[idx].sg_font != NOFONT)
911 {
912 // New font was accepted. Free the old one, if there was
913 // one.
914 gui_mch_free_font(temp_sg_font);
915 vim_free(HL_TABLE()[idx].sg_font_name);
916 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
917 did_change = TRUE;
918 }
919 else
920 HL_TABLE()[idx].sg_font = temp_sg_font;
921 }
922
923 return did_change;
924}
925#endif
926
927/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000928 * Set the cterm foreground color for the Normal highlight group to "color" and
929 * the bold attribute to "bold".
930 */
931 static void
932hl_set_ctermfg_normal_group(int color, int bold)
933{
934 cterm_normal_fg_color = color + 1;
935 cterm_normal_fg_bold = bold;
936#ifdef FEAT_GUI
937 // Don't do this if the GUI is used.
938 if (!gui.in_use && !gui.starting)
939#endif
940 {
941 set_must_redraw(UPD_CLEAR);
942 if (termcap_active && color >= 0)
943 term_fg_color(color);
944 }
945}
946
947/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100948 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100949 */
950 static void
951highlight_set_ctermfg(int idx, int color, int is_normal_group)
952{
953 HL_TABLE()[idx].sg_cterm_fg = color + 1;
954 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000955 hl_set_ctermfg_normal_group(color,
956 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100957}
958
959/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000960 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100961 */
962 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000963hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100964{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000965 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100966#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000967 // Don't mess with 'background' if the GUI is used.
968 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100969#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000970 {
971 set_must_redraw(UPD_CLEAR);
972 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100973 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000974 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100975
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000976 if (termcap_active)
977 term_bg_color(color);
978 if (t_colors < 16)
979 dark = (color == 0 || color == 4);
980 // Limit the heuristic to the standard 16 colors
981 else if (color < 16)
982 dark = (color < 7 || color == 8);
983 // Set the 'background' option if the value is
984 // wrong.
985 if (dark != -1
986 && dark != (*p_bg == 'd')
987 && !option_was_set((char_u *)"bg"))
988 {
989 set_option_value_give_err((char_u *)"bg",
990 0L, (char_u *)(dark ? "dark" : "light"), 0);
991 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100992 }
993 }
994 }
995}
996
997/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000998 * Set the cterm background color for the highlight group at 'idx' to 'color'.
999 */
1000 static void
1001highlight_set_ctermbg(int idx, int color, int is_normal_group)
1002{
1003 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1004 if (is_normal_group)
1005 hl_set_ctermbg_normal_group(color);
1006}
1007
1008/*
1009 * Set the cterm underline color for the Normal highlight group to "color".
1010 */
1011 static void
1012hl_set_ctermul_normal_group(int color)
1013{
1014 cterm_normal_ul_color = color + 1;
1015#ifdef FEAT_GUI
1016 // Don't do this if the GUI is used.
1017 if (!gui.in_use && !gui.starting)
1018#endif
1019 {
1020 set_must_redraw(UPD_CLEAR);
1021 if (termcap_active && color >= 0)
1022 term_ul_color(color);
1023 }
1024}
1025
1026/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001027 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001028 */
1029 static void
1030highlight_set_ctermul(int idx, int color, int is_normal_group)
1031{
1032 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1033 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001034 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001035}
1036
1037/*
1038 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1039 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1040 * 'keystart' is the color name/value.
1041 * 'arg' is the color name or the numeric value as a string.
1042 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1043 * 'init' is set to TRUE when initializing highlighting.
1044 * Called for the ":highlight" command and the "hlset()" function.
1045 *
1046 * Returns TRUE if the color is set.
1047 */
1048 static int
1049highlight_set_cterm_color(
1050 int idx,
1051 char_u *key,
1052 char_u *key_start,
1053 char_u *arg,
1054 int is_normal_group,
1055 int init)
1056{
1057 int color;
1058 long i;
1059 int off;
1060
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001061 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1062 return FALSE;
1063
1064 if (!init)
1065 HL_TABLE()[idx].sg_set |= SG_CTERM;
1066
1067 // When setting the foreground color, and previously the "bold"
1068 // flag was set for a light color, reset it now
1069 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001070 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001071 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1072 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1073 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001074
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001075 if (VIM_ISDIGIT(*arg))
1076 color = atoi((char *)arg);
1077 else if (STRICMP(arg, "fg") == 0)
1078 {
1079 if (cterm_normal_fg_color)
1080 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001081 else
1082 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001083 emsg(_(e_fg_color_unknown));
1084 return FALSE;
1085 }
1086 }
1087 else if (STRICMP(arg, "bg") == 0)
1088 {
1089 if (cterm_normal_bg_color > 0)
1090 color = cterm_normal_bg_color - 1;
1091 else
1092 {
1093 emsg(_(e_bg_color_unknown));
1094 return FALSE;
1095 }
1096 }
1097 else if (STRICMP(arg, "ul") == 0)
1098 {
1099 if (cterm_normal_ul_color > 0)
1100 color = cterm_normal_ul_color - 1;
1101 else
1102 {
1103 emsg(_(e_ul_color_unknown));
1104 return FALSE;
1105 }
1106 }
1107 else
1108 {
1109 int bold = MAYBE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001110
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001111 // reduce calls to STRICMP a bit, it can be slow
1112 off = TOUPPER_ASC(*arg);
1113 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1114 if (off == color_names[i][0]
1115 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1116 break;
1117 if (i < 0)
1118 {
1119 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1120 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001121 }
1122
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001123 color = lookup_color(i, key[5] == 'F', &bold);
1124
1125 // set/reset bold attribute to get light foreground
1126 // colors (on some terminals, e.g. "linux")
1127 if (bold == TRUE)
1128 {
1129 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1130 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1131 }
1132 else if (bold == FALSE)
1133 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001134 }
1135
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001136 // Add one to the argument, to avoid zero. Zero is used for
1137 // "NONE", then "color" is -1.
1138 if (key[5] == 'F')
1139 highlight_set_ctermfg(idx, color, is_normal_group);
1140 else if (key[5] == 'B')
1141 highlight_set_ctermbg(idx, color, is_normal_group);
1142 else // ctermul
1143 highlight_set_ctermul(idx, color, is_normal_group);
1144
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001145 return TRUE;
1146}
1147
1148#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1149/*
1150 * Set the GUI foreground color for the highlight group at 'idx'.
1151 * Returns TRUE if the color is set.
1152 */
1153 static int
1154highlight_set_guifg(
1155 int idx,
1156 char_u *arg,
1157 int is_menu_group UNUSED,
1158 int is_scrollbar_group UNUSED,
1159 int is_tooltip_group UNUSED,
1160 int *do_colors UNUSED,
1161 int init)
1162{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001163# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001164 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001165# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001166 char_u **namep;
1167 int did_change = FALSE;
1168
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001169 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1170 return FALSE;
1171
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001172 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001173 if (!init)
1174 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001175
1176# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001177 // In GUI guifg colors are only used when recognized
1178 i = color_name2handle(arg);
1179 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1180 {
1181 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001182# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001183 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1184 {
1185 vim_free(*namep);
1186 if (STRCMP(arg, "NONE") != 0)
1187 *namep = vim_strsave(arg);
1188 else
1189 *namep = NULL;
1190 did_change = TRUE;
1191 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001192# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1193# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001194 if (is_menu_group && gui.menu_fg_pixel != i)
1195 {
1196 gui.menu_fg_pixel = i;
1197 *do_colors = TRUE;
1198 }
1199 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1200 {
1201 gui.scroll_fg_pixel = i;
1202 *do_colors = TRUE;
1203 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001204# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001205 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1206 {
1207 gui.tooltip_fg_pixel = i;
1208 *do_colors = TRUE;
1209 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001210# endif
1211# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001212 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001213# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001214
1215 return did_change;
1216}
1217
1218/*
1219 * Set the GUI background color for the highlight group at 'idx'.
1220 * Returns TRUE if the color is set.
1221 */
1222 static int
1223highlight_set_guibg(
1224 int idx,
1225 char_u *arg,
1226 int is_menu_group UNUSED,
1227 int is_scrollbar_group UNUSED,
1228 int is_tooltip_group UNUSED,
1229 int *do_colors UNUSED,
1230 int init)
1231{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001232# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001233 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001234# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001235 char_u **namep;
1236 int did_change = FALSE;
1237
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001238 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1239 return FALSE;
1240
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001241 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001242 if (!init)
1243 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001244
1245# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001246 // In GUI guibg colors are only used when recognized
1247 i = color_name2handle(arg);
1248 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1249 {
1250 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001251# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001252 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1253 {
1254 vim_free(*namep);
1255 if (STRCMP(arg, "NONE") != 0)
1256 *namep = vim_strsave(arg);
1257 else
1258 *namep = NULL;
1259 did_change = TRUE;
1260 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001261# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1262# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001263 if (is_menu_group && gui.menu_bg_pixel != i)
1264 {
1265 gui.menu_bg_pixel = i;
1266 *do_colors = TRUE;
1267 }
1268 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1269 {
1270 gui.scroll_bg_pixel = i;
1271 *do_colors = TRUE;
1272 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001273# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001274 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1275 {
1276 gui.tooltip_bg_pixel = i;
1277 *do_colors = TRUE;
1278 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001279# endif
1280# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001281 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001282# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001283
1284 return did_change;
1285}
1286
1287/*
1288 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1289 * Returns TRUE if the color is set.
1290 */
1291 static int
1292highlight_set_guisp(int idx, char_u *arg, int init)
1293{
1294# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1295 int i;
1296# endif
1297 int did_change = FALSE;
1298 char_u **namep;
1299
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001300 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1301 return FALSE;
1302
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001303 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001304 if (!init)
1305 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001306
1307# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001308 // In GUI guisp colors are only used when recognized
1309 i = color_name2handle(arg);
1310 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1311 {
1312 HL_TABLE()[idx].sg_gui_sp = i;
1313# endif
1314 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001315 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001316 vim_free(*namep);
1317 if (STRCMP(arg, "NONE") != 0)
1318 *namep = vim_strsave(arg);
1319 else
1320 *namep = NULL;
1321 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001322 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001323# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001324 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001325# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001326
1327 return did_change;
1328}
1329#endif
1330
1331/*
1332 * Set the start/stop terminal codes for a highlight group.
1333 * Returns TRUE if the terminal code is set.
1334 */
1335 static int
1336highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1337{
1338 int off;
1339 char_u buf[100];
1340 int len;
1341 char_u *tname;
1342 char_u *p;
1343
1344 if (!init)
1345 HL_TABLE()[idx].sg_set |= SG_TERM;
1346
1347 // The "start" and "stop" arguments can be a literal escape
1348 // sequence, or a comma separated list of terminal codes.
1349 if (STRNCMP(arg, "t_", 2) == 0)
1350 {
1351 off = 0;
1352 buf[0] = 0;
1353 while (arg[off] != NUL)
1354 {
1355 // Isolate one termcap name
1356 for (len = 0; arg[off + len] &&
1357 arg[off + len] != ','; ++len)
1358 ;
1359 tname = vim_strnsave(arg + off, len);
1360 if (tname == NULL) // out of memory
1361 return FALSE;
1362 // lookup the escape sequence for the item
1363 p = get_term_code(tname);
1364 vim_free(tname);
1365 if (p == NULL) // ignore non-existing things
1366 p = (char_u *)"";
1367
1368 // Append it to the already found stuff
1369 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1370 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001371 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001372 return FALSE;
1373 }
1374 STRCAT(buf, p);
1375
1376 // Advance to the next item
1377 off += len;
1378 if (arg[off] == ',') // another one follows
1379 ++off;
1380 }
1381 }
1382 else
1383 {
1384 // Copy characters from arg[] to buf[], translating <> codes.
1385 for (p = arg, off = 0; off < 100 - 6 && *p; )
1386 {
zeertzjqdb088872022-05-02 22:53:45 +01001387 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001388 if (len > 0) // recognized special char
1389 off += len;
1390 else // copy as normal char
1391 buf[off++] = *p++;
1392 }
1393 buf[off] = NUL;
1394 }
1395
1396 if (STRCMP(buf, "NONE") == 0) // resetting the value
1397 p = NULL;
1398 else
1399 p = vim_strsave(buf);
1400 if (key[2] == 'A')
1401 {
1402 vim_free(HL_TABLE()[idx].sg_start);
1403 HL_TABLE()[idx].sg_start = p;
1404 }
1405 else
1406 {
1407 vim_free(HL_TABLE()[idx].sg_stop);
1408 HL_TABLE()[idx].sg_stop = p;
1409 }
1410 return TRUE;
1411}
1412
1413/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001414 * Handle the ":highlight .." command.
1415 * When using ":hi clear" this is called recursively for each group with
1416 * "forceit" and "init" both TRUE.
1417 */
1418 void
1419do_highlight(
1420 char_u *line,
1421 int forceit,
1422 int init) // TRUE when called for initializing
1423{
1424 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001425 char_u *linep;
1426 char_u *key_start;
1427 char_u *arg_start;
1428 char_u *key = NULL, *arg = NULL;
1429 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001430 int id;
1431 int idx;
1432 hl_group_T item_before;
1433 int did_change = FALSE;
1434 int dodefault = FALSE;
1435 int doclear = FALSE;
1436 int dolink = FALSE;
1437 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001438 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001439#ifdef FEAT_GUI_X11
1440 int is_menu_group = FALSE; // "Menu" group
1441 int is_scrollbar_group = FALSE; // "Scrollbar" group
1442 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001443#else
1444# define is_menu_group 0
1445# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001446# define is_scrollbar_group 0
1447#endif
1448#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1449 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001450#endif
1451#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1452 int did_highlight_changed = FALSE;
1453#endif
1454
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001455 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001456 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001457 {
1458 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1459 // TODO: only call when the group has attributes set
1460 highlight_list_one((int)i);
1461 return;
1462 }
1463
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001464 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001465 name_end = skiptowhite(line);
1466 linep = skipwhite(name_end);
1467
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001468 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001469 if (STRNCMP(line, "default", name_end - line) == 0)
1470 {
1471 dodefault = TRUE;
1472 line = linep;
1473 name_end = skiptowhite(line);
1474 linep = skipwhite(name_end);
1475 }
1476
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001477 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001478 if (STRNCMP(line, "clear", name_end - line) == 0)
1479 doclear = TRUE;
1480 if (STRNCMP(line, "link", name_end - line) == 0)
1481 dolink = TRUE;
1482
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001483 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001484 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001485 {
1486 id = syn_namen2id(line, (int)(name_end - line));
1487 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001488 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001489 else
1490 highlight_list_one(id);
1491 return;
1492 }
1493
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001494 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001495 if (dolink)
1496 {
1497 char_u *from_start = linep;
1498 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001499 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001500 char_u *to_start;
1501 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001502 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001503
1504 from_end = skiptowhite(from_start);
1505 to_start = skipwhite(from_end);
1506 to_end = skiptowhite(to_start);
1507
Bram Moolenaar1966c242020-04-20 22:42:32 +02001508 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001509 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001510 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001511 return;
1512 }
1513
Bram Moolenaar1966c242020-04-20 22:42:32 +02001514 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001515 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001516 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001517 return;
1518 }
1519
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001520 from_len = (int)(from_end - from_start);
1521 to_len = (int)(to_end - to_start);
1522 highlight_group_link(from_start, from_len, to_start, to_len,
1523 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001524 return;
1525 }
1526
1527 if (doclear)
1528 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001529 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001530 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001531 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001532 // ":highlight clear" without group name
1533 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001534 return;
1535 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001536 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001537 name_end = skiptowhite(line);
1538 linep = skipwhite(name_end);
1539 }
1540
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001541 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001542 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001543 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001544 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001545 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001546
1547 // Return if "default" was used and the group already has settings.
1548 if (dodefault && hl_has_settings(idx, TRUE))
1549 return;
1550
1551 // Make a copy so we can check if any attribute actually changed.
1552 item_before = HL_TABLE()[idx];
1553
1554 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1555 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001556#ifdef FEAT_GUI_X11
1557 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1558 is_menu_group = TRUE;
1559 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1560 is_scrollbar_group = TRUE;
1561 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1562 is_tooltip_group = TRUE;
1563#endif
1564
1565 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1566 if (doclear || (forceit && init))
1567 {
1568 highlight_clear(idx);
1569 if (!doclear)
1570 HL_TABLE()[idx].sg_set = 0;
1571 }
1572
1573 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001574 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001575 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001576 key_start = linep;
1577 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001578 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001579 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001580 error = TRUE;
1581 break;
1582 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001583
1584 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1585 // or "guibg").
1586 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1587 ++linep;
1588 vim_free(key);
1589 key = vim_strnsave_up(key_start, linep - key_start);
1590 if (key == NULL)
1591 {
1592 error = TRUE;
1593 break;
1594 }
1595 linep = skipwhite(linep);
1596
1597 if (STRCMP(key, "NONE") == 0)
1598 {
1599 if (!init || HL_TABLE()[idx].sg_set == 0)
1600 {
1601 if (!init)
1602 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1603 highlight_clear(idx);
1604 }
1605 continue;
1606 }
1607
1608 // Check for the equal sign.
1609 if (*linep != '=')
1610 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001611 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001612 error = TRUE;
1613 break;
1614 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001615 ++linep;
1616
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001617 // Isolate the argument.
1618 linep = skipwhite(linep);
1619 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001620 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001621 arg_start = ++linep;
1622 linep = vim_strchr(linep, '\'');
1623 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001624 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001625 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001626 error = TRUE;
1627 break;
1628 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001629 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001630 else
1631 {
1632 arg_start = linep;
1633 linep = skiptowhite(linep);
1634 }
1635 if (linep == arg_start)
1636 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001637 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001638 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001639 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001640 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001641 vim_free(arg);
1642 arg = vim_strnsave(arg_start, linep - arg_start);
1643 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001644 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001645 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001646 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001647 }
1648 if (*linep == '\'')
1649 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001650
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001651 // Store the argument.
1652 if (STRCMP(key, "TERM") == 0
1653 || STRCMP(key, "CTERM") == 0
1654 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001655 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001656 if (!highlight_set_termgui_attr(idx, key, arg, init))
1657 {
1658 error = TRUE;
1659 break;
1660 }
1661 }
1662 else if (STRCMP(key, "FONT") == 0)
1663 {
1664 // in non-GUI fonts are simply ignored
1665#ifdef FEAT_GUI
1666 if (highlight_set_font(idx, arg, is_normal_group,
1667 is_menu_group, is_tooltip_group))
1668 did_change = TRUE;
1669#endif
1670 }
1671 else if (STRCMP(key, "CTERMFG") == 0
1672 || STRCMP(key, "CTERMBG") == 0
1673 || STRCMP(key, "CTERMUL") == 0)
1674 {
1675 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1676 is_normal_group, init))
1677 {
1678 error = TRUE;
1679 break;
1680 }
1681 }
1682 else if (STRCMP(key, "GUIFG") == 0)
1683 {
1684#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1685 if (highlight_set_guifg(idx, arg, is_menu_group,
1686 is_scrollbar_group, is_tooltip_group,
1687 &do_colors, init))
1688 did_change = TRUE;
1689#endif
1690 }
1691 else if (STRCMP(key, "GUIBG") == 0)
1692 {
1693#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1694 if (highlight_set_guibg(idx, arg, is_menu_group,
1695 is_scrollbar_group, is_tooltip_group,
1696 &do_colors, init))
1697 did_change = TRUE;
1698#endif
1699 }
1700 else if (STRCMP(key, "GUISP") == 0)
1701 {
1702#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1703 if (highlight_set_guisp(idx, arg, init))
1704 did_change = TRUE;
1705#endif
1706 }
1707 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1708 {
1709 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1710 {
1711 error = TRUE;
1712 break;
1713 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001714 }
1715 else
1716 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001717 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001718 error = TRUE;
1719 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001720 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001721 HL_TABLE()[idx].sg_cleared = FALSE;
1722
1723 // When highlighting has been given for a group, don't link it.
1724 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1725 HL_TABLE()[idx].sg_link = 0;
1726
1727 // Continue with next argument.
1728 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001729 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001730
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001731 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001732 if (error && idx == highlight_ga.ga_len)
1733 syn_unadd_group();
1734 else
1735 {
1736 if (is_normal_group)
1737 {
1738 HL_TABLE()[idx].sg_term_attr = 0;
1739 HL_TABLE()[idx].sg_cterm_attr = 0;
1740#ifdef FEAT_GUI
1741 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001742 // Need to update all groups, because they might be using "bg"
1743 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001744#endif
1745#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1746 if (USE_24BIT)
1747 {
1748 highlight_gui_started();
1749 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001750 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001751 }
1752#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001753#ifdef FEAT_VTP
1754 control_console_color_rgb();
1755#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001756 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001757#ifdef FEAT_GUI_X11
1758# ifdef FEAT_MENU
1759 else if (is_menu_group)
1760 {
1761 if (gui.in_use && do_colors)
1762 gui_mch_new_menu_colors();
1763 }
1764# endif
1765 else if (is_scrollbar_group)
1766 {
1767 if (gui.in_use && do_colors)
1768 gui_new_scrollbar_colors();
1769 else
1770 set_hl_attr(idx);
1771 }
1772# ifdef FEAT_BEVAL_GUI
1773 else if (is_tooltip_group)
1774 {
1775 if (gui.in_use && do_colors)
1776 gui_mch_new_tooltip_colors();
1777 }
1778# endif
1779#endif
1780 else
1781 set_hl_attr(idx);
1782#ifdef FEAT_EVAL
1783 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001784 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001785#endif
1786 }
1787
1788 vim_free(key);
1789 vim_free(arg);
1790
1791 // Only call highlight_changed() once, after a sequence of highlight
1792 // commands, and only if an attribute actually changed.
1793 if ((did_change
1794 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1795#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1796 && !did_highlight_changed
1797#endif
1798 )
1799 {
1800 // Do not trigger a redraw when highlighting is changed while
1801 // redrawing. This may happen when evaluating 'statusline' changes the
1802 // StatusLine group.
1803 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001804 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001805 need_highlight_changed = TRUE;
1806 }
1807}
1808
1809#if defined(EXITFREE) || defined(PROTO)
1810 void
1811free_highlight(void)
1812{
1813 int i;
1814
1815 for (i = 0; i < highlight_ga.ga_len; ++i)
1816 {
1817 highlight_clear(i);
1818 vim_free(HL_TABLE()[i].sg_name);
1819 vim_free(HL_TABLE()[i].sg_name_u);
1820 }
1821 ga_clear(&highlight_ga);
1822}
1823#endif
1824
1825/*
1826 * Reset the cterm colors to what they were before Vim was started, if
1827 * possible. Otherwise reset them to zero.
1828 */
1829 void
1830restore_cterm_colors(void)
1831{
1832#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1833 // Since t_me has been set, this probably means that the user
1834 // wants to use this as default colors. Need to reset default
1835 // background/foreground colors.
1836 mch_set_normal_colors();
1837#else
1838# ifdef VIMDLL
1839 if (!gui.in_use)
1840 {
1841 mch_set_normal_colors();
1842 return;
1843 }
1844# endif
1845 cterm_normal_fg_color = 0;
1846 cterm_normal_fg_bold = 0;
1847 cterm_normal_bg_color = 0;
1848# ifdef FEAT_TERMGUICOLORS
1849 cterm_normal_fg_gui_color = INVALCOLOR;
1850 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001851 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001852# endif
1853#endif
1854}
1855
1856/*
1857 * Return TRUE if highlight group "idx" has any settings.
1858 * When "check_link" is TRUE also check for an existing link.
1859 */
1860 static int
1861hl_has_settings(int idx, int check_link)
1862{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001863 return HL_TABLE()[idx].sg_cleared == 0
1864 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001865 || HL_TABLE()[idx].sg_cterm_attr != 0
1866 || HL_TABLE()[idx].sg_cterm_fg != 0
1867 || HL_TABLE()[idx].sg_cterm_bg != 0
1868#ifdef FEAT_GUI
1869 || HL_TABLE()[idx].sg_gui_attr != 0
1870 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1871 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1872 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1873 || HL_TABLE()[idx].sg_font_name != NULL
1874#endif
1875 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1876}
1877
1878/*
1879 * Clear highlighting for one group.
1880 */
1881 static void
1882highlight_clear(int idx)
1883{
1884 HL_TABLE()[idx].sg_cleared = TRUE;
1885
1886 HL_TABLE()[idx].sg_term = 0;
1887 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1888 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1889 HL_TABLE()[idx].sg_term_attr = 0;
1890 HL_TABLE()[idx].sg_cterm = 0;
1891 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1892 HL_TABLE()[idx].sg_cterm_fg = 0;
1893 HL_TABLE()[idx].sg_cterm_bg = 0;
1894 HL_TABLE()[idx].sg_cterm_attr = 0;
1895#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1896 HL_TABLE()[idx].sg_gui = 0;
1897 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1898 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1899 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1900#endif
1901#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1902 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1903 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001904 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001905#endif
1906#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001907 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1908 HL_TABLE()[idx].sg_font = NOFONT;
1909# ifdef FEAT_XFONTSET
1910 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1911 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1912# endif
1913 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1914 HL_TABLE()[idx].sg_gui_attr = 0;
1915#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001916 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001917 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001918#ifdef FEAT_EVAL
1919 // Since we set the default link, set the location to where the default
1920 // link was set.
1921 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001922#endif
1923}
1924
1925#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1926/*
1927 * Set the normal foreground and background colors according to the "Normal"
1928 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1929 * "Tooltip" colors.
1930 */
1931 void
1932set_normal_colors(void)
1933{
1934# ifdef FEAT_GUI
1935# ifdef FEAT_TERMGUICOLORS
1936 if (gui.in_use)
1937# endif
1938 {
1939 if (set_group_colors((char_u *)"Normal",
1940 &gui.norm_pixel, &gui.back_pixel,
1941 FALSE, TRUE, FALSE))
1942 {
1943 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001944 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001945 }
1946# ifdef FEAT_GUI_X11
1947 if (set_group_colors((char_u *)"Menu",
1948 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1949 TRUE, FALSE, FALSE))
1950 {
1951# ifdef FEAT_MENU
1952 gui_mch_new_menu_colors();
1953# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001954 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001955 }
1956# ifdef FEAT_BEVAL_GUI
1957 if (set_group_colors((char_u *)"Tooltip",
1958 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1959 FALSE, FALSE, TRUE))
1960 {
1961# ifdef FEAT_TOOLBAR
1962 gui_mch_new_tooltip_colors();
1963# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001964 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001965 }
1966# endif
1967 if (set_group_colors((char_u *)"Scrollbar",
1968 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1969 FALSE, FALSE, FALSE))
1970 {
1971 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001972 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001973 }
1974# endif
1975 }
1976# endif
1977# ifdef FEAT_TERMGUICOLORS
1978# ifdef FEAT_GUI
1979 else
1980# endif
1981 {
1982 int idx;
1983
1984 idx = syn_name2id((char_u *)"Normal") - 1;
1985 if (idx >= 0)
1986 {
1987 gui_do_one_color(idx, FALSE, FALSE);
1988
1989 // If the normal fg or bg color changed a complete redraw is
1990 // required.
1991 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1992 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1993 {
1994 // if the GUI color is INVALCOLOR then we use the default cterm
1995 // color
1996 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1997 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001998 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001999 }
2000 }
2001 }
2002# endif
2003}
2004#endif
2005
2006#if defined(FEAT_GUI) || defined(PROTO)
2007/*
2008 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2009 */
2010 static int
2011set_group_colors(
2012 char_u *name,
2013 guicolor_T *fgp,
2014 guicolor_T *bgp,
2015 int do_menu,
2016 int use_norm,
2017 int do_tooltip)
2018{
2019 int idx;
2020
2021 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002022 if (idx < 0)
2023 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002024
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002025 gui_do_one_color(idx, do_menu, do_tooltip);
2026
2027 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2028 *fgp = HL_TABLE()[idx].sg_gui_fg;
2029 else if (use_norm)
2030 *fgp = gui.def_norm_pixel;
2031 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2032 *bgp = HL_TABLE()[idx].sg_gui_bg;
2033 else if (use_norm)
2034 *bgp = gui.def_back_pixel;
2035 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002036}
2037
2038/*
2039 * Get the font of the "Normal" group.
2040 * Returns "" when it's not found or not set.
2041 */
2042 char_u *
2043hl_get_font_name(void)
2044{
2045 int id;
2046 char_u *s;
2047
2048 id = syn_name2id((char_u *)"Normal");
2049 if (id > 0)
2050 {
2051 s = HL_TABLE()[id - 1].sg_font_name;
2052 if (s != NULL)
2053 return s;
2054 }
2055 return (char_u *)"";
2056}
2057
2058/*
2059 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2060 * actually chosen to be used.
2061 */
2062 void
2063hl_set_font_name(char_u *font_name)
2064{
2065 int id;
2066
2067 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002068 if (id <= 0)
2069 return;
2070
2071 vim_free(HL_TABLE()[id - 1].sg_font_name);
2072 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002073}
2074
2075/*
2076 * Set background color for "Normal" group. Called by gui_set_bg_color()
2077 * when the color is known.
2078 */
2079 void
2080hl_set_bg_color_name(
2081 char_u *name) // must have been allocated
2082{
2083 int id;
2084
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002085 if (name == NULL)
2086 return;
2087
2088 id = syn_name2id((char_u *)"Normal");
2089 if (id <= 0)
2090 return;
2091
2092 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2093 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002094}
2095
2096/*
2097 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2098 * when the color is known.
2099 */
2100 void
2101hl_set_fg_color_name(
2102 char_u *name) // must have been allocated
2103{
2104 int id;
2105
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002106 if (name == NULL)
2107 return;
2108
2109 id = syn_name2id((char_u *)"Normal");
2110 if (id <= 0)
2111 return;
2112
2113 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2114 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002115}
2116
2117/*
2118 * Return the handle for a font name.
2119 * Returns NOFONT when failed.
2120 */
2121 static GuiFont
2122font_name2handle(char_u *name)
2123{
2124 if (STRCMP(name, "NONE") == 0)
2125 return NOFONT;
2126
2127 return gui_mch_get_font(name, TRUE);
2128}
2129
2130# ifdef FEAT_XFONTSET
2131/*
2132 * Return the handle for a fontset name.
2133 * Returns NOFONTSET when failed.
2134 */
2135 static GuiFontset
2136fontset_name2handle(char_u *name, int fixed_width)
2137{
2138 if (STRCMP(name, "NONE") == 0)
2139 return NOFONTSET;
2140
2141 return gui_mch_get_fontset(name, TRUE, fixed_width);
2142}
2143# endif
2144
2145/*
2146 * Get the font or fontset for one highlight group.
2147 */
2148 static void
2149hl_do_font(
2150 int idx,
2151 char_u *arg,
2152 int do_normal, // set normal font
2153 int do_menu UNUSED, // set menu font
2154 int do_tooltip UNUSED, // set tooltip font
2155 int free_font) // free current font/fontset
2156{
2157# ifdef FEAT_XFONTSET
2158 // If 'guifontset' is not empty, first try using the name as a
2159 // fontset. If that doesn't work, use it as a font name.
2160 if (*p_guifontset != NUL
2161# ifdef FONTSET_ALWAYS
2162 || do_menu
2163# endif
2164# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002165 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002166 || do_tooltip
2167# endif
2168 )
2169 {
2170 if (free_font)
2171 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2172 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2173# ifdef FONTSET_ALWAYS
2174 || do_menu
2175# endif
2176# ifdef FEAT_BEVAL_TIP
2177 || do_tooltip
2178# endif
2179 );
2180 }
2181 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2182 {
2183 // If it worked and it's the Normal group, use it as the normal
2184 // fontset. Same for the Menu group.
2185 if (do_normal)
2186 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002187# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002188 if (do_menu)
2189 {
2190# ifdef FONTSET_ALWAYS
2191 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2192# else
2193 // YIKES! This is a bug waiting to crash the program
2194 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2195# endif
2196 gui_mch_new_menu_font();
2197 }
2198# ifdef FEAT_BEVAL_GUI
2199 if (do_tooltip)
2200 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002201 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002202 // displaying a single font and a fontset.
2203 // If the XtNinternational resource is set to True at widget
2204 // creation, then a fontset is always used, otherwise an
2205 // XFontStruct is used.
2206 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2207 gui_mch_new_tooltip_font();
2208 }
2209# endif
2210# endif
2211 }
2212 else
2213# endif
2214 {
2215 if (free_font)
2216 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2217 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2218 // If it worked and it's the Normal group, use it as the
2219 // normal font. Same for the Menu group.
2220 if (HL_TABLE()[idx].sg_font != NOFONT)
2221 {
2222 if (do_normal)
2223 gui_init_font(arg, FALSE);
2224#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002225# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002226 if (do_menu)
2227 {
2228 gui.menu_font = HL_TABLE()[idx].sg_font;
2229 gui_mch_new_menu_font();
2230 }
2231# endif
2232#endif
2233 }
2234 }
2235}
2236
2237#endif // FEAT_GUI
2238
2239#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2240/*
2241 * Return the handle for a color name.
2242 * Returns INVALCOLOR when failed.
2243 */
2244 guicolor_T
2245color_name2handle(char_u *name)
2246{
2247 if (STRCMP(name, "NONE") == 0)
2248 return INVALCOLOR;
2249
2250 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2251 {
2252#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2253 if (gui.in_use)
2254#endif
2255#ifdef FEAT_GUI
2256 return gui.norm_pixel;
2257#endif
2258#ifdef FEAT_TERMGUICOLORS
2259 if (cterm_normal_fg_gui_color != INVALCOLOR)
2260 return cterm_normal_fg_gui_color;
2261 // Guess that the foreground is black or white.
2262 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2263#endif
2264 }
2265 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2266 {
2267#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2268 if (gui.in_use)
2269#endif
2270#ifdef FEAT_GUI
2271 return gui.back_pixel;
2272#endif
2273#ifdef FEAT_TERMGUICOLORS
2274 if (cterm_normal_bg_gui_color != INVALCOLOR)
2275 return cterm_normal_bg_gui_color;
2276 // Guess that the background is white or black.
2277 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2278#endif
2279 }
2280
2281 return GUI_GET_COLOR(name);
2282}
Drew Vogele30d1022021-10-24 20:35:07 +01002283
2284// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2285// values as used by the MS-Windows GDI api. It should be used only for
2286// MS-Windows GDI builds.
2287# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2288# undef RGB
2289# endif
2290# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002291# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002292# endif
2293
2294# ifdef VIMDLL
2295 static guicolor_T
2296gui_adjust_rgb(guicolor_T c)
2297{
2298 if (gui.in_use)
2299 return c;
2300 else
2301 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2302}
2303# else
2304# define gui_adjust_rgb(c) (c)
2305# endif
2306
2307 static int
2308hex_digit(int c)
2309{
2310 if (isdigit(c))
2311 return c - '0';
2312 c = TOLOWER_ASC(c);
2313 if (c >= 'a' && c <= 'f')
2314 return c - 'a' + 10;
2315 return 0x1ffffff;
2316}
2317
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002318 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002319decode_hex_color(char_u *hex)
2320{
2321 guicolor_T color;
2322
2323 if (hex[0] != '#' || STRLEN(hex) != 7)
2324 return INVALCOLOR;
2325
2326 // Name is in "#rrggbb" format
2327 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2328 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2329 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2330 if (color > 0xffffff)
2331 return INVALCOLOR;
2332 return gui_adjust_rgb(color);
2333}
2334
Bram Moolenaar2a521962021-10-25 10:30:14 +01002335#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002336// Returns the color currently mapped to the given name or INVALCOLOR if no
2337// such name exists in the color table. The convention is to use lowercase for
2338// all keys in the v:colornames dictionary. The value can be either a string in
2339// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002340 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002341colorname2rgb(char_u *name)
2342{
2343 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2344 char_u *lc_name;
2345 dictitem_T *colentry;
2346 char_u *colstr;
2347 varnumber_T colnum;
2348
2349 lc_name = strlow_save(name);
2350 if (lc_name == NULL)
2351 return INVALCOLOR;
2352
2353 colentry = dict_find(colornames_table, lc_name, -1);
2354 vim_free(lc_name);
2355 if (colentry == NULL)
2356 return INVALCOLOR;
2357
2358 if (colentry->di_tv.v_type == VAR_STRING)
2359 {
2360 colstr = tv_get_string_strict(&colentry->di_tv);
2361 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2362 {
2363 return decode_hex_color(colstr);
2364 }
2365 else
2366 {
2367 semsg(_(e_bad_color_string_str), colstr);
2368 return INVALCOLOR;
2369 }
2370 }
2371
2372 if (colentry->di_tv.v_type == VAR_NUMBER)
2373 {
2374 colnum = tv_get_number(&colentry->di_tv);
2375 return (guicolor_T)colnum;
2376 }
2377
2378 return INVALCOLOR;
2379}
2380
Drew Vogele30d1022021-10-24 20:35:07 +01002381#endif
2382
2383 guicolor_T
2384gui_get_color_cmn(char_u *name)
2385{
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002386 int i;
Drew Vogele30d1022021-10-24 20:35:07 +01002387 guicolor_T color;
2388
2389 struct rgbcolor_table_S {
2390 char_u *color_name;
2391 guicolor_T color;
2392 };
2393
2394 // Only non X11 colors (not present in rgb.txt) and colors in
2395 // color_names[], useful when $VIMRUNTIME is not found,.
2396 static struct rgbcolor_table_S rgb_table[] = {
2397 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2398 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2399 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2400 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2401 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2402 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2403 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2404 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2405 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2406 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2407 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2408 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2409 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2410 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2411 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2412 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2413 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2414 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2415 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2416 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2417 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2418 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2419 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2420 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2421 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2422 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2423 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2424 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2425 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2426 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2427 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2428 };
2429
2430 color = decode_hex_color(name);
2431 if (color != INVALCOLOR)
2432 return color;
2433
2434 // Check if the name is one of the colors we know
2435 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2436 if (STRICMP(name, rgb_table[i].color_name) == 0)
2437 return gui_adjust_rgb(rgb_table[i].color);
2438
2439#if defined(FEAT_EVAL)
2440 /*
2441 * Not a traditional color. Load additional color aliases and then consult the alias table.
2442 */
2443
2444 color = colorname2rgb(name);
2445 if (color == INVALCOLOR)
2446 {
2447 load_default_colors_lists();
2448 color = colorname2rgb(name);
2449 }
2450
2451 return color;
2452#else
2453 return INVALCOLOR;
2454#endif
2455}
2456
2457 guicolor_T
2458gui_get_rgb_color_cmn(int r, int g, int b)
2459{
2460 guicolor_T color = RGB(r, g, b);
2461
2462 if (color > 0xffffff)
2463 return INVALCOLOR;
2464 return gui_adjust_rgb(color);
2465}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002466#endif
2467
2468/*
2469 * Table with the specifications for an attribute number.
2470 * Note that this table is used by ALL buffers. This is required because the
2471 * GUI can redraw at any time for any buffer.
2472 */
2473static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2474
2475#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2476
2477static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2478
2479#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2480
2481#ifdef FEAT_GUI
2482static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2483
2484#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2485#endif
2486
2487/*
2488 * Return the attr number for a set of colors and font.
2489 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2490 * if the combination is new.
2491 * Return 0 for error (no more room).
2492 */
2493 static int
2494get_attr_entry(garray_T *table, attrentry_T *aep)
2495{
2496 int i;
2497 attrentry_T *taep;
2498 static int recursive = FALSE;
2499
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002500 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002501 table->ga_itemsize = sizeof(attrentry_T);
2502 table->ga_growsize = 7;
2503
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002504 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002505 for (i = 0; i < table->ga_len; ++i)
2506 {
2507 taep = &(((attrentry_T *)table->ga_data)[i]);
2508 if ( aep->ae_attr == taep->ae_attr
2509 && (
2510#ifdef FEAT_GUI
2511 (table == &gui_attr_table
2512 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2513 && aep->ae_u.gui.bg_color
2514 == taep->ae_u.gui.bg_color
2515 && aep->ae_u.gui.sp_color
2516 == taep->ae_u.gui.sp_color
2517 && aep->ae_u.gui.font == taep->ae_u.gui.font
2518# ifdef FEAT_XFONTSET
2519 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2520# endif
2521 ))
2522 ||
2523#endif
2524 (table == &term_attr_table
2525 && (aep->ae_u.term.start == NULL)
2526 == (taep->ae_u.term.start == NULL)
2527 && (aep->ae_u.term.start == NULL
2528 || STRCMP(aep->ae_u.term.start,
2529 taep->ae_u.term.start) == 0)
2530 && (aep->ae_u.term.stop == NULL)
2531 == (taep->ae_u.term.stop == NULL)
2532 && (aep->ae_u.term.stop == NULL
2533 || STRCMP(aep->ae_u.term.stop,
2534 taep->ae_u.term.stop) == 0))
2535 || (table == &cterm_attr_table
2536 && aep->ae_u.cterm.fg_color
2537 == taep->ae_u.cterm.fg_color
2538 && aep->ae_u.cterm.bg_color
2539 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002540 && aep->ae_u.cterm.ul_color
2541 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002542#ifdef FEAT_TERMGUICOLORS
2543 && aep->ae_u.cterm.fg_rgb
2544 == taep->ae_u.cterm.fg_rgb
2545 && aep->ae_u.cterm.bg_rgb
2546 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002547 && aep->ae_u.cterm.ul_rgb
2548 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002549#endif
2550 )))
2551
2552 return i + ATTR_OFF;
2553 }
2554
2555 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2556 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002557 // Running out of attribute entries! remove all attributes, and
2558 // compute new ones for all groups.
2559 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002560 if (recursive)
2561 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002562 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002563 return 0;
2564 }
2565 recursive = TRUE;
2566
2567 clear_hl_tables();
2568
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002569 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002570
2571 for (i = 0; i < highlight_ga.ga_len; ++i)
2572 set_hl_attr(i);
2573
2574 recursive = FALSE;
2575 }
2576
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002577 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002578 if (ga_grow(table, 1) == FAIL)
2579 return 0;
2580
2581 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002582 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002583 taep->ae_attr = aep->ae_attr;
2584#ifdef FEAT_GUI
2585 if (table == &gui_attr_table)
2586 {
2587 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2588 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2589 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2590 taep->ae_u.gui.font = aep->ae_u.gui.font;
2591# ifdef FEAT_XFONTSET
2592 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2593# endif
2594 }
2595#endif
2596 if (table == &term_attr_table)
2597 {
2598 if (aep->ae_u.term.start == NULL)
2599 taep->ae_u.term.start = NULL;
2600 else
2601 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2602 if (aep->ae_u.term.stop == NULL)
2603 taep->ae_u.term.stop = NULL;
2604 else
2605 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2606 }
2607 else if (table == &cterm_attr_table)
2608 {
2609 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2610 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002611 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002612#ifdef FEAT_TERMGUICOLORS
2613 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2614 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002615 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002616#endif
2617 }
2618 ++table->ga_len;
2619 return (table->ga_len - 1 + ATTR_OFF);
2620}
2621
2622#if defined(FEAT_TERMINAL) || defined(PROTO)
2623/*
2624 * Get an attribute index for a cterm entry.
2625 * Uses an existing entry when possible or adds one when needed.
2626 */
2627 int
2628get_cterm_attr_idx(int attr, int fg, int bg)
2629{
2630 attrentry_T at_en;
2631
Bram Moolenaara80faa82020-04-12 19:37:17 +02002632 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002633#ifdef FEAT_TERMGUICOLORS
2634 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2635 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002636 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002637#endif
2638 at_en.ae_attr = attr;
2639 at_en.ae_u.cterm.fg_color = fg;
2640 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002641 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002642 return get_attr_entry(&cterm_attr_table, &at_en);
2643}
2644#endif
2645
2646#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2647/*
2648 * Get an attribute index for a 'termguicolors' entry.
2649 * Uses an existing entry when possible or adds one when needed.
2650 */
2651 int
2652get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2653{
2654 attrentry_T at_en;
2655
Bram Moolenaara80faa82020-04-12 19:37:17 +02002656 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002657 at_en.ae_attr = attr;
2658 if (fg == INVALCOLOR && bg == INVALCOLOR)
2659 {
2660 // If both GUI colors are not set fall back to the cterm colors. Helps
2661 // if the GUI only has an attribute, such as undercurl.
2662 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2663 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2664 }
2665 else
2666 {
2667 at_en.ae_u.cterm.fg_rgb = fg;
2668 at_en.ae_u.cterm.bg_rgb = bg;
2669 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002670 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002671 return get_attr_entry(&cterm_attr_table, &at_en);
2672}
2673#endif
2674
2675#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2676/*
2677 * Get an attribute index for a cterm entry.
2678 * Uses an existing entry when possible or adds one when needed.
2679 */
2680 int
2681get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2682{
2683 attrentry_T at_en;
2684
Bram Moolenaara80faa82020-04-12 19:37:17 +02002685 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002686 at_en.ae_attr = attr;
2687 at_en.ae_u.gui.fg_color = fg;
2688 at_en.ae_u.gui.bg_color = bg;
2689 return get_attr_entry(&gui_attr_table, &at_en);
2690}
2691#endif
2692
2693/*
2694 * Clear all highlight tables.
2695 */
2696 void
2697clear_hl_tables(void)
2698{
2699 int i;
2700 attrentry_T *taep;
2701
2702#ifdef FEAT_GUI
2703 ga_clear(&gui_attr_table);
2704#endif
2705 for (i = 0; i < term_attr_table.ga_len; ++i)
2706 {
2707 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2708 vim_free(taep->ae_u.term.start);
2709 vim_free(taep->ae_u.term.stop);
2710 }
2711 ga_clear(&term_attr_table);
2712 ga_clear(&cterm_attr_table);
2713}
2714
2715/*
2716 * Combine special attributes (e.g., for spelling) with other attributes
2717 * (e.g., for syntax highlighting).
2718 * "prim_attr" overrules "char_attr".
2719 * This creates a new group when required.
2720 * Since we expect there to be few spelling mistakes we don't cache the
2721 * result.
2722 * Return the resulting attributes.
2723 */
2724 int
2725hl_combine_attr(int char_attr, int prim_attr)
2726{
2727 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002728 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002729 attrentry_T new_en;
2730
2731 if (char_attr == 0)
2732 return prim_attr;
2733 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2734 return ATTR_COMBINE(char_attr, prim_attr);
2735#ifdef FEAT_GUI
2736 if (gui.in_use)
2737 {
2738 if (char_attr > HL_ALL)
2739 char_aep = syn_gui_attr2entry(char_attr);
2740 if (char_aep != NULL)
2741 new_en = *char_aep;
2742 else
2743 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002744 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002745 new_en.ae_u.gui.fg_color = INVALCOLOR;
2746 new_en.ae_u.gui.bg_color = INVALCOLOR;
2747 new_en.ae_u.gui.sp_color = INVALCOLOR;
2748 if (char_attr <= HL_ALL)
2749 new_en.ae_attr = char_attr;
2750 }
2751
2752 if (prim_attr <= HL_ALL)
2753 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2754 else
2755 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002756 prim_aep = syn_gui_attr2entry(prim_attr);
2757 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002758 {
2759 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002760 prim_aep->ae_attr);
2761 if (prim_aep->ae_u.gui.fg_color != INVALCOLOR)
2762 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2763 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2764 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2765 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2766 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2767 if (prim_aep->ae_u.gui.font != NOFONT)
2768 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002769# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002770 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2771 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002772# endif
2773 }
2774 }
2775 return get_attr_entry(&gui_attr_table, &new_en);
2776 }
2777#endif
2778
2779 if (IS_CTERM)
2780 {
2781 if (char_attr > HL_ALL)
2782 char_aep = syn_cterm_attr2entry(char_attr);
2783 if (char_aep != NULL)
2784 new_en = *char_aep;
2785 else
2786 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002787 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002788#ifdef FEAT_TERMGUICOLORS
2789 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2790 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002791 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002792#endif
2793 if (char_attr <= HL_ALL)
2794 new_en.ae_attr = char_attr;
2795 }
2796
2797 if (prim_attr <= HL_ALL)
2798 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2799 else
2800 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002801 prim_aep = syn_cterm_attr2entry(prim_attr);
2802 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002803 {
2804 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002805 prim_aep->ae_attr);
2806 if (prim_aep->ae_u.cterm.fg_color > 0)
2807 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2808 if (prim_aep->ae_u.cterm.bg_color > 0)
2809 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2810 if (prim_aep->ae_u.cterm.ul_color > 0)
2811 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002812#ifdef FEAT_TERMGUICOLORS
2813 // If both fg and bg are not set fall back to cterm colors.
2814 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002815 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2816 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002817 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002818 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002819 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002820 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002821 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2822 }
2823 else
2824 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002825 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2826 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2827 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2828 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002829 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002830 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2831 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002832#endif
2833 }
2834 }
2835 return get_attr_entry(&cterm_attr_table, &new_en);
2836 }
2837
2838 if (char_attr > HL_ALL)
2839 char_aep = syn_term_attr2entry(char_attr);
2840 if (char_aep != NULL)
2841 new_en = *char_aep;
2842 else
2843 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002844 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002845 if (char_attr <= HL_ALL)
2846 new_en.ae_attr = char_attr;
2847 }
2848
2849 if (prim_attr <= HL_ALL)
2850 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2851 else
2852 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002853 prim_aep = syn_term_attr2entry(prim_attr);
2854 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002855 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002856 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2857 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002858 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002859 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2860 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002861 }
2862 }
2863 }
2864 return get_attr_entry(&term_attr_table, &new_en);
2865}
2866
2867#ifdef FEAT_GUI
2868 attrentry_T *
2869syn_gui_attr2entry(int attr)
2870{
2871 attr -= ATTR_OFF;
2872 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2873 return NULL;
2874 return &(GUI_ATTR_ENTRY(attr));
2875}
2876#endif
2877
2878/*
2879 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2880 * Only to be used when "attr" > HL_ALL.
2881 */
2882 int
2883syn_attr2attr(int attr)
2884{
2885 attrentry_T *aep;
2886
2887#ifdef FEAT_GUI
2888 if (gui.in_use)
2889 aep = syn_gui_attr2entry(attr);
2890 else
2891#endif
2892 if (IS_CTERM)
2893 aep = syn_cterm_attr2entry(attr);
2894 else
2895 aep = syn_term_attr2entry(attr);
2896
2897 if (aep == NULL) // highlighting not set
2898 return 0;
2899 return aep->ae_attr;
2900}
2901
2902
2903 attrentry_T *
2904syn_term_attr2entry(int attr)
2905{
2906 attr -= ATTR_OFF;
2907 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2908 return NULL;
2909 return &(TERM_ATTR_ENTRY(attr));
2910}
2911
2912 attrentry_T *
2913syn_cterm_attr2entry(int attr)
2914{
2915 attr -= ATTR_OFF;
2916 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2917 return NULL;
2918 return &(CTERM_ATTR_ENTRY(attr));
2919}
2920
2921#define LIST_ATTR 1
2922#define LIST_STRING 2
2923#define LIST_INT 3
2924
2925 static void
2926highlight_list_one(int id)
2927{
2928 hl_group_T *sgp;
2929 int didh = FALSE;
2930
2931 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2932
2933 if (message_filtered(sgp->sg_name))
2934 return;
2935
2936 didh = highlight_list_arg(id, didh, LIST_ATTR,
2937 sgp->sg_term, NULL, "term");
2938 didh = highlight_list_arg(id, didh, LIST_STRING,
2939 0, sgp->sg_start, "start");
2940 didh = highlight_list_arg(id, didh, LIST_STRING,
2941 0, sgp->sg_stop, "stop");
2942
2943 didh = highlight_list_arg(id, didh, LIST_ATTR,
2944 sgp->sg_cterm, NULL, "cterm");
2945 didh = highlight_list_arg(id, didh, LIST_INT,
2946 sgp->sg_cterm_fg, NULL, "ctermfg");
2947 didh = highlight_list_arg(id, didh, LIST_INT,
2948 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002949 didh = highlight_list_arg(id, didh, LIST_INT,
2950 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002951
2952#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2953 didh = highlight_list_arg(id, didh, LIST_ATTR,
2954 sgp->sg_gui, NULL, "gui");
2955 didh = highlight_list_arg(id, didh, LIST_STRING,
2956 0, sgp->sg_gui_fg_name, "guifg");
2957 didh = highlight_list_arg(id, didh, LIST_STRING,
2958 0, sgp->sg_gui_bg_name, "guibg");
2959 didh = highlight_list_arg(id, didh, LIST_STRING,
2960 0, sgp->sg_gui_sp_name, "guisp");
2961#endif
2962#ifdef FEAT_GUI
2963 didh = highlight_list_arg(id, didh, LIST_STRING,
2964 0, sgp->sg_font_name, "font");
2965#endif
2966
2967 if (sgp->sg_link && !got_int)
2968 {
2969 (void)syn_list_header(didh, 9999, id);
2970 didh = TRUE;
2971 msg_puts_attr("links to", HL_ATTR(HLF_D));
2972 msg_putchar(' ');
2973 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2974 }
2975
2976 if (!didh)
2977 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2978#ifdef FEAT_EVAL
2979 if (p_verbose > 0)
2980 last_set_msg(sgp->sg_script_ctx);
2981#endif
2982}
2983
2984 static int
2985highlight_list_arg(
2986 int id,
2987 int didh,
2988 int type,
2989 int iarg,
2990 char_u *sarg,
2991 char *name)
2992{
Bram Moolenaar84f54632022-06-29 18:39:11 +01002993 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002994 char_u *ts;
2995 int i;
2996
2997 if (got_int)
2998 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002999
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003000 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3001 return didh;
3002
3003 ts = buf;
3004 if (type == LIST_INT)
3005 sprintf((char *)buf, "%d", iarg - 1);
3006 else if (type == LIST_STRING)
3007 ts = sarg;
3008 else // type == LIST_ATTR
3009 {
3010 buf[0] = NUL;
3011 for (i = 0; hl_attr_table[i] != 0; ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003012 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003013 if (iarg & hl_attr_table[i])
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003014 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003015 if (buf[0] != NUL)
3016 vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
3017 vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
3018 iarg &= ~hl_attr_table[i]; // don't want "inverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003019 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003020 }
3021 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003022
3023 (void)syn_list_header(didh,
3024 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3025 didh = TRUE;
3026 if (!got_int)
3027 {
3028 if (*name != NUL)
3029 {
3030 msg_puts_attr(name, HL_ATTR(HLF_D));
3031 msg_puts_attr("=", HL_ATTR(HLF_D));
3032 }
3033 msg_outtrans(ts);
3034 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003035 return didh;
3036}
3037
3038#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3039/*
3040 * Return "1" if highlight group "id" has attribute "flag".
3041 * Return NULL otherwise.
3042 */
3043 char_u *
3044highlight_has_attr(
3045 int id,
3046 int flag,
3047 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3048{
3049 int attr;
3050
3051 if (id <= 0 || id > highlight_ga.ga_len)
3052 return NULL;
3053
3054#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3055 if (modec == 'g')
3056 attr = HL_TABLE()[id - 1].sg_gui;
3057 else
3058#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003059 {
3060 if (modec == 'c')
3061 attr = HL_TABLE()[id - 1].sg_cterm;
3062 else
3063 attr = HL_TABLE()[id - 1].sg_term;
3064 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003065
3066 if (attr & flag)
3067 return (char_u *)"1";
3068 return NULL;
3069}
3070#endif
3071
3072#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3073/*
3074 * Return color name of highlight group "id".
3075 */
3076 char_u *
3077highlight_color(
3078 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003079 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003080 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3081{
3082 static char_u name[20];
3083 int n;
3084 int fg = FALSE;
3085 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003086 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003087 int font = FALSE;
3088
3089 if (id <= 0 || id > highlight_ga.ga_len)
3090 return NULL;
3091
3092 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3093 fg = TRUE;
3094 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3095 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3096 font = TRUE;
3097 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3098 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003099 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3100 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003101 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3102 return NULL;
3103 if (modec == 'g')
3104 {
3105# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3106# ifdef FEAT_GUI
3107 // return font name
3108 if (font)
3109 return HL_TABLE()[id - 1].sg_font_name;
3110# endif
3111
3112 // return #RRGGBB form (only possible when GUI is running)
3113 if ((USE_24BIT) && what[2] == '#')
3114 {
3115 guicolor_T color;
3116 long_u rgb;
3117 static char_u buf[10];
3118
3119 if (fg)
3120 color = HL_TABLE()[id - 1].sg_gui_fg;
3121 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003122 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003123 else
3124 color = HL_TABLE()[id - 1].sg_gui_bg;
3125 if (color == INVALCOLOR)
3126 return NULL;
3127 rgb = (long_u)GUI_MCH_GET_RGB(color);
3128 sprintf((char *)buf, "#%02x%02x%02x",
3129 (unsigned)(rgb >> 16),
3130 (unsigned)(rgb >> 8) & 255,
3131 (unsigned)rgb & 255);
3132 return buf;
3133 }
3134# endif
3135 if (fg)
3136 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3137 if (sp)
3138 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3139 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3140 }
3141 if (font || sp)
3142 return NULL;
3143 if (modec == 'c')
3144 {
3145 if (fg)
3146 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003147 else if (ul)
3148 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003149 else
3150 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3151 if (n < 0)
3152 return NULL;
3153 sprintf((char *)name, "%d", n);
3154 return name;
3155 }
3156 // term doesn't have color
3157 return NULL;
3158}
3159#endif
3160
3161#if (defined(FEAT_SYN_HL) \
3162 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3163 && defined(FEAT_PRINTER)) || defined(PROTO)
3164/*
3165 * Return color name of highlight group "id" as RGB value.
3166 */
3167 long_u
3168highlight_gui_color_rgb(
3169 int id,
3170 int fg) // TRUE = fg, FALSE = bg
3171{
3172 guicolor_T color;
3173
3174 if (id <= 0 || id > highlight_ga.ga_len)
3175 return 0L;
3176
3177 if (fg)
3178 color = HL_TABLE()[id - 1].sg_gui_fg;
3179 else
3180 color = HL_TABLE()[id - 1].sg_gui_bg;
3181
3182 if (color == INVALCOLOR)
3183 return 0L;
3184
3185 return GUI_MCH_GET_RGB(color);
3186}
3187#endif
3188
3189/*
3190 * Output the syntax list header.
3191 * Return TRUE when started a new line.
3192 */
3193 int
3194syn_list_header(
3195 int did_header, // did header already
3196 int outlen, // length of string that comes
3197 int id) // highlight group id
3198{
3199 int endcol = 19;
3200 int newline = TRUE;
3201 int name_col = 0;
3202
3203 if (!did_header)
3204 {
3205 msg_putchar('\n');
3206 if (got_int)
3207 return TRUE;
3208 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3209 name_col = msg_col;
3210 endcol = 15;
3211 }
3212 else if (msg_col + outlen + 1 >= Columns)
3213 {
3214 msg_putchar('\n');
3215 if (got_int)
3216 return TRUE;
3217 }
3218 else
3219 {
3220 if (msg_col >= endcol) // wrap around is like starting a new line
3221 newline = FALSE;
3222 }
3223
3224 if (msg_col >= endcol) // output at least one space
3225 endcol = msg_col + 1;
3226 if (Columns <= endcol) // avoid hang for tiny window
3227 endcol = Columns - 1;
3228
3229 msg_advance(endcol);
3230
3231 // Show "xxx" with the attributes.
3232 if (!did_header)
3233 {
3234 if (endcol == Columns - 1 && endcol <= name_col)
3235 msg_putchar(' ');
3236 msg_puts_attr("xxx", syn_id2attr(id));
3237 msg_putchar(' ');
3238 }
3239
3240 return newline;
3241}
3242
3243/*
3244 * Set the attribute numbers for a highlight group.
3245 * Called after one of the attributes has changed.
3246 */
3247 static void
3248set_hl_attr(
3249 int idx) // index in array
3250{
3251 attrentry_T at_en;
3252 hl_group_T *sgp = HL_TABLE() + idx;
3253
3254 // The "Normal" group doesn't need an attribute number
3255 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3256 return;
3257
3258#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003259 // For the GUI 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_gui_fg == INVALCOLOR
3262 && sgp->sg_gui_bg == INVALCOLOR
3263 && sgp->sg_gui_sp == INVALCOLOR
3264 && sgp->sg_font == NOFONT
3265# ifdef FEAT_XFONTSET
3266 && sgp->sg_fontset == NOFONTSET
3267# endif
3268 )
3269 {
3270 sgp->sg_gui_attr = sgp->sg_gui;
3271 }
3272 else
3273 {
3274 at_en.ae_attr = sgp->sg_gui;
3275 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3276 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3277 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3278 at_en.ae_u.gui.font = sgp->sg_font;
3279# ifdef FEAT_XFONTSET
3280 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3281# endif
3282 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3283 }
3284#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003285 // For the term mode: If there are other than "normal" highlighting
3286 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003287 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3288 sgp->sg_term_attr = sgp->sg_term;
3289 else
3290 {
3291 at_en.ae_attr = sgp->sg_term;
3292 at_en.ae_u.term.start = sgp->sg_start;
3293 at_en.ae_u.term.stop = sgp->sg_stop;
3294 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3295 }
3296
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003297 // For the color term mode: If there are other than "normal"
3298 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003299 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003300# ifdef FEAT_TERMGUICOLORS
3301 && sgp->sg_gui_fg == INVALCOLOR
3302 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003303 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003304# endif
3305 )
3306 sgp->sg_cterm_attr = sgp->sg_cterm;
3307 else
3308 {
3309 at_en.ae_attr = sgp->sg_cterm;
3310 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3311 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003312 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003313# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003314 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3315 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003316 // Only use the underline/undercurl color when used, it may clear the
3317 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003318 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3319 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003320 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3321 else
3322 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003323 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3324 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3325 {
3326 // If both fg and bg are invalid fall back to the cterm colors.
3327 // Helps when the GUI only uses an attribute, e.g. undercurl.
3328 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3329 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3330 }
3331# endif
3332 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3333 }
3334}
3335
3336/*
3337 * Lookup a highlight group name and return its ID.
3338 * If it is not found, 0 is returned.
3339 */
3340 int
3341syn_name2id(char_u *name)
3342{
3343 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003344 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003345
3346 // Avoid using stricmp() too much, it's slow on some systems
3347 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3348 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003349 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003350 vim_strup(name_u);
3351 for (i = highlight_ga.ga_len; --i >= 0; )
3352 if (HL_TABLE()[i].sg_name_u != NULL
3353 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3354 break;
3355 return i + 1;
3356}
3357
3358/*
3359 * Lookup a highlight group name and return its attributes.
3360 * Return zero if not found.
3361 */
3362 int
3363syn_name2attr(char_u *name)
3364{
3365 int id = syn_name2id(name);
3366
3367 if (id != 0)
3368 return syn_id2attr(id);
3369 return 0;
3370}
3371
3372#if defined(FEAT_EVAL) || defined(PROTO)
3373/*
3374 * Return TRUE if highlight group "name" exists.
3375 */
3376 int
3377highlight_exists(char_u *name)
3378{
3379 return (syn_name2id(name) > 0);
3380}
3381
3382# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3383/*
3384 * Return the name of highlight group "id".
3385 * When not a valid ID return an empty string.
3386 */
3387 char_u *
3388syn_id2name(int id)
3389{
3390 if (id <= 0 || id > highlight_ga.ga_len)
3391 return (char_u *)"";
3392 return HL_TABLE()[id - 1].sg_name;
3393}
3394# endif
3395#endif
3396
3397/*
3398 * Like syn_name2id(), but take a pointer + length argument.
3399 */
3400 int
3401syn_namen2id(char_u *linep, int len)
3402{
3403 char_u *name;
3404 int id = 0;
3405
3406 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003407 if (name == NULL)
3408 return 0;
3409
3410 id = syn_name2id(name);
3411 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003412 return id;
3413}
3414
3415/*
3416 * Find highlight group name in the table and return its ID.
3417 * The argument is a pointer to the name and the length of the name.
3418 * If it doesn't exist yet, a new entry is created.
3419 * Return 0 for failure.
3420 */
3421 int
3422syn_check_group(char_u *pp, int len)
3423{
3424 int id;
3425 char_u *name;
3426
erw7f7f7aaf2021-12-07 21:29:20 +00003427 if (len > MAX_SYN_NAME)
3428 {
3429 emsg(_(e_highlight_group_name_too_long));
3430 return 0;
3431 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003432 name = vim_strnsave(pp, len);
3433 if (name == NULL)
3434 return 0;
3435
3436 id = syn_name2id(name);
3437 if (id == 0) // doesn't exist yet
3438 id = syn_add_group(name);
3439 else
3440 vim_free(name);
3441 return id;
3442}
3443
3444/*
3445 * Add new highlight group and return its ID.
3446 * "name" must be an allocated string, it will be consumed.
3447 * Return 0 for failure.
3448 */
3449 static int
3450syn_add_group(char_u *name)
3451{
3452 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003453 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003454
3455 // Check that the name is ASCII letters, digits and underscore.
3456 for (p = name; *p != NUL; ++p)
3457 {
3458 if (!vim_isprintc(*p))
3459 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003460 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003461 vim_free(name);
3462 return 0;
3463 }
3464 else if (!ASCII_ISALNUM(*p) && *p != '_')
3465 {
3466 // This is an error, but since there previously was no check only
3467 // give a warning.
3468 msg_source(HL_ATTR(HLF_W));
3469 msg(_("W18: Invalid character in group name"));
3470 break;
3471 }
3472 }
3473
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003474 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003475 if (highlight_ga.ga_data == NULL)
3476 {
3477 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3478 highlight_ga.ga_growsize = 10;
3479 }
3480
3481 if (highlight_ga.ga_len >= MAX_HL_ID)
3482 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003483 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003484 vim_free(name);
3485 return 0;
3486 }
3487
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003488 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003489 if (ga_grow(&highlight_ga, 1) == FAIL)
3490 {
3491 vim_free(name);
3492 return 0;
3493 }
3494
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003495 name_up = vim_strsave_up(name);
3496 if (name_up == NULL)
3497 {
3498 vim_free(name);
3499 return 0;
3500 }
3501
Bram Moolenaara80faa82020-04-12 19:37:17 +02003502 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003503 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003504 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003505#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3506 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3507 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003508 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003509#endif
3510 ++highlight_ga.ga_len;
3511
3512 return highlight_ga.ga_len; // ID is index plus one
3513}
3514
3515/*
3516 * When, just after calling syn_add_group(), an error is discovered, this
3517 * function deletes the new name.
3518 */
3519 static void
3520syn_unadd_group(void)
3521{
3522 --highlight_ga.ga_len;
3523 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3524 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3525}
3526
3527/*
3528 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003529 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003530 */
3531 int
3532syn_id2attr(int hl_id)
3533{
3534 int attr;
3535 hl_group_T *sgp;
3536
3537 hl_id = syn_get_final_id(hl_id);
3538 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3539
3540#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003541 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003542 if (gui.in_use)
3543 attr = sgp->sg_gui_attr;
3544 else
3545#endif
3546 if (IS_CTERM)
3547 attr = sgp->sg_cterm_attr;
3548 else
3549 attr = sgp->sg_term_attr;
3550
3551 return attr;
3552}
3553
3554#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3555/*
3556 * Get the GUI colors and attributes for a group ID.
3557 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3558 */
3559 int
3560syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3561{
3562 hl_group_T *sgp;
3563
3564 hl_id = syn_get_final_id(hl_id);
3565 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3566
3567 *fgp = sgp->sg_gui_fg;
3568 *bgp = sgp->sg_gui_bg;
3569 return sgp->sg_gui;
3570}
3571#endif
3572
3573#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003574 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3575 && defined(FEAT_TERMGUICOLORS)) \
3576 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003577 void
3578syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3579{
3580 hl_group_T *sgp;
3581
3582 hl_id = syn_get_final_id(hl_id);
3583 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3584 *fgp = sgp->sg_cterm_fg - 1;
3585 *bgp = sgp->sg_cterm_bg - 1;
3586}
3587#endif
3588
3589/*
3590 * Translate a group ID to the final group ID (following links).
3591 */
3592 int
3593syn_get_final_id(int hl_id)
3594{
3595 int count;
3596 hl_group_T *sgp;
3597
3598 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3599 return 0; // Can be called from eval!!
3600
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003601 // Follow links until there is no more.
3602 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003603 for (count = 100; --count >= 0; )
3604 {
3605 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3606 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3607 break;
3608 hl_id = sgp->sg_link;
3609 }
3610
3611 return hl_id;
3612}
3613
3614#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3615/*
3616 * Call this function just after the GUI has started.
3617 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3618 * It finds the font and color handles for the highlighting groups.
3619 */
3620 void
3621highlight_gui_started(void)
3622{
3623 int idx;
3624
3625 // First get the colors from the "Normal" and "Menu" group, if set
3626 if (USE_24BIT)
3627 set_normal_colors();
3628
3629 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3630 gui_do_one_color(idx, FALSE, FALSE);
3631
3632 highlight_changed();
3633}
3634
3635 static void
3636gui_do_one_color(
3637 int idx,
3638 int do_menu UNUSED, // TRUE: might set the menu font
3639 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3640{
3641 int didit = FALSE;
3642
3643# ifdef FEAT_GUI
3644# ifdef FEAT_TERMGUICOLORS
3645 if (gui.in_use)
3646# endif
3647 if (HL_TABLE()[idx].sg_font_name != NULL)
3648 {
3649 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3650 do_tooltip, TRUE);
3651 didit = TRUE;
3652 }
3653# endif
3654 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3655 {
3656 HL_TABLE()[idx].sg_gui_fg =
3657 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3658 didit = TRUE;
3659 }
3660 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3661 {
3662 HL_TABLE()[idx].sg_gui_bg =
3663 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3664 didit = TRUE;
3665 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003666 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3667 {
3668 HL_TABLE()[idx].sg_gui_sp =
3669 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3670 didit = TRUE;
3671 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003672 if (didit) // need to get a new attr number
3673 set_hl_attr(idx);
3674}
3675#endif
3676
3677#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3678/*
3679 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3680 */
3681 static void
3682combine_stl_hlt(
3683 int id,
3684 int id_S,
3685 int id_alt,
3686 int hlcnt,
3687 int i,
3688 int hlf,
3689 int *table)
3690{
3691 hl_group_T *hlt = HL_TABLE();
3692
3693 if (id_alt == 0)
3694 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003695 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003696 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3697 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3698# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3699 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3700# endif
3701 }
3702 else
3703 mch_memmove(&hlt[hlcnt + i],
3704 &hlt[id_alt - 1],
3705 sizeof(hl_group_T));
3706 hlt[hlcnt + i].sg_link = 0;
3707
3708 hlt[hlcnt + i].sg_term ^=
3709 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3710 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3711 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3712 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3713 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3714 hlt[hlcnt + i].sg_cterm ^=
3715 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3716 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3717 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3718 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3719 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3720# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3721 hlt[hlcnt + i].sg_gui ^=
3722 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3723# endif
3724# ifdef FEAT_GUI
3725 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3726 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3727 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3728 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3729 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3730 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3731 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3732 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3733# ifdef FEAT_XFONTSET
3734 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3735 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3736# endif
3737# endif
3738 highlight_ga.ga_len = hlcnt + i + 1;
3739 set_hl_attr(hlcnt + i); // At long last we can apply
3740 table[i] = syn_id2attr(hlcnt + i + 1);
3741}
3742#endif
3743
3744/*
3745 * Translate the 'highlight' option into attributes in highlight_attr[] and
3746 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3747 * corresponding highlights to use on top of HLF_SNC is computed.
3748 * Called only when the 'highlight' option has been changed and upon first
3749 * screen redraw after any :highlight command.
3750 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3751 */
3752 int
3753highlight_changed(void)
3754{
3755 int hlf;
3756 int i;
3757 char_u *p;
3758 int attr;
3759 char_u *end;
3760 int id;
3761#ifdef USER_HIGHLIGHT
3762 char_u userhl[30]; // use 30 to avoid compiler warning
3763# ifdef FEAT_STL_OPT
3764 int id_S = -1;
3765 int id_SNC = 0;
3766# ifdef FEAT_TERMINAL
3767 int id_ST = 0;
3768 int id_STNC = 0;
3769# endif
3770 int hlcnt;
3771# endif
3772#endif
3773 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3774
3775 need_highlight_changed = FALSE;
3776
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003777#ifdef FEAT_TERMINAL
3778 term_update_colors_all();
3779 term_update_wincolor_all();
3780#endif
3781
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003782 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003783 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3784 highlight_attr[hlf] = 0;
3785
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003786 // First set all attributes to their default value.
3787 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003788 for (i = 0; i < 2; ++i)
3789 {
3790 if (i)
3791 p = p_hl;
3792 else
3793 p = get_highlight_default();
3794 if (p == NULL) // just in case
3795 continue;
3796
3797 while (*p)
3798 {
3799 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3800 if (hl_flags[hlf] == *p)
3801 break;
3802 ++p;
3803 if (hlf == (int)HLF_COUNT || *p == NUL)
3804 return FAIL;
3805
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003806 // Allow several hl_flags to be combined, like "bu" for
3807 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003808 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003809 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003810 {
3811 if (VIM_ISWHITE(*p)) // ignore white space
3812 continue;
3813
3814 if (attr > HL_ALL) // Combination with ':' is not allowed.
3815 return FAIL;
3816
3817 switch (*p)
3818 {
3819 case 'b': attr |= HL_BOLD;
3820 break;
3821 case 'i': attr |= HL_ITALIC;
3822 break;
3823 case '-':
3824 case 'n': // no highlighting
3825 break;
3826 case 'r': attr |= HL_INVERSE;
3827 break;
3828 case 's': attr |= HL_STANDOUT;
3829 break;
3830 case 'u': attr |= HL_UNDERLINE;
3831 break;
3832 case 'c': attr |= HL_UNDERCURL;
3833 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003834 case '2': attr |= HL_UNDERDOUBLE;
3835 break;
3836 case 'd': attr |= HL_UNDERDOTTED;
3837 break;
3838 case '=': attr |= HL_UNDERDASHED;
3839 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003840 case 't': attr |= HL_STRIKETHROUGH;
3841 break;
3842 case ':': ++p; // highlight group name
3843 if (attr || *p == NUL) // no combinations
3844 return FAIL;
3845 end = vim_strchr(p, ',');
3846 if (end == NULL)
3847 end = p + STRLEN(p);
3848 id = syn_check_group(p, (int)(end - p));
3849 if (id == 0)
3850 return FAIL;
3851 attr = syn_id2attr(id);
3852 p = end - 1;
3853#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3854 if (hlf == (int)HLF_SNC)
3855 id_SNC = syn_get_final_id(id);
3856# ifdef FEAT_TERMINAL
3857 else if (hlf == (int)HLF_ST)
3858 id_ST = syn_get_final_id(id);
3859 else if (hlf == (int)HLF_STNC)
3860 id_STNC = syn_get_final_id(id);
3861# endif
3862 else if (hlf == (int)HLF_S)
3863 id_S = syn_get_final_id(id);
3864#endif
3865 break;
3866 default: return FAIL;
3867 }
3868 }
3869 highlight_attr[hlf] = attr;
3870
3871 p = skip_to_option_part(p); // skip comma and spaces
3872 }
3873 }
3874
3875#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003876 // Setup the user highlights
3877 //
3878 // Temporarily utilize 28 more hl entries:
3879 // 9 for User1-User9 combined with StatusLineNC
3880 // 9 for User1-User9 combined with StatusLineTerm
3881 // 9 for User1-User9 combined with StatusLineTermNC
3882 // 1 for StatusLine default
3883 // Have to be in there simultaneously in case of table overflows in
3884 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003885# ifdef FEAT_STL_OPT
3886 if (ga_grow(&highlight_ga, 28) == FAIL)
3887 return FAIL;
3888 hlcnt = highlight_ga.ga_len;
3889 if (id_S == -1)
3890 {
3891 // Make sure id_S is always valid to simplify code below. Use the last
3892 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003893 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003894 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3895 id_S = hlcnt + 19;
3896 }
3897# endif
3898 for (i = 0; i < 9; i++)
3899 {
3900 sprintf((char *)userhl, "User%d", i + 1);
3901 id = syn_name2id(userhl);
3902 if (id == 0)
3903 {
3904 highlight_user[i] = 0;
3905# ifdef FEAT_STL_OPT
3906 highlight_stlnc[i] = 0;
3907# ifdef FEAT_TERMINAL
3908 highlight_stlterm[i] = 0;
3909 highlight_stltermnc[i] = 0;
3910# endif
3911# endif
3912 }
3913 else
3914 {
3915 highlight_user[i] = syn_id2attr(id);
3916# ifdef FEAT_STL_OPT
3917 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3918 HLF_SNC, highlight_stlnc);
3919# ifdef FEAT_TERMINAL
3920 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3921 HLF_ST, highlight_stlterm);
3922 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3923 HLF_STNC, highlight_stltermnc);
3924# endif
3925# endif
3926 }
3927 }
3928# ifdef FEAT_STL_OPT
3929 highlight_ga.ga_len = hlcnt;
3930# endif
3931
3932#endif // USER_HIGHLIGHT
3933
3934 return OK;
3935}
3936
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003937static void highlight_list(void);
3938static void highlight_list_two(int cnt, int attr);
3939
3940/*
3941 * Handle command line completion for :highlight command.
3942 */
3943 void
3944set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3945{
3946 char_u *p;
3947
3948 // Default: expand group names
3949 xp->xp_context = EXPAND_HIGHLIGHT;
3950 xp->xp_pattern = arg;
3951 include_link = 2;
3952 include_default = 1;
3953
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003954 if (*arg == NUL)
3955 return;
3956
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003957 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003958 p = skiptowhite(arg);
3959 if (*p == NUL)
3960 return;
3961
3962 // past "default" or group name
3963 include_default = 0;
3964 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003965 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003966 arg = skipwhite(p);
3967 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003968 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003969 }
3970 if (*p == NUL)
3971 return;
3972
3973 // past group name
3974 include_link = 0;
3975 if (arg[1] == 'i' && arg[0] == 'N')
3976 highlight_list();
3977 if (STRNCMP("link", arg, p - arg) == 0
3978 || STRNCMP("clear", arg, p - arg) == 0)
3979 {
3980 xp->xp_pattern = skipwhite(p);
3981 p = skiptowhite(xp->xp_pattern);
3982 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003983 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003984 xp->xp_pattern = skipwhite(p);
3985 p = skiptowhite(xp->xp_pattern);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003986 }
3987 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003988 if (*p != NUL) // past group name(s)
3989 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003990}
3991
3992/*
3993 * List highlighting matches in a nice way.
3994 */
3995 static void
3996highlight_list(void)
3997{
3998 int i;
3999
4000 for (i = 10; --i >= 0; )
4001 highlight_list_two(i, HL_ATTR(HLF_D));
4002 for (i = 40; --i >= 0; )
4003 highlight_list_two(99, 0);
4004}
4005
4006 static void
4007highlight_list_two(int cnt, int attr)
4008{
4009 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4010 msg_clr_eos();
4011 out_flush();
4012 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4013}
4014
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004015/*
4016 * Function given to ExpandGeneric() to obtain the list of group names.
4017 */
4018 char_u *
4019get_highlight_name(expand_T *xp UNUSED, int idx)
4020{
4021 return get_highlight_name_ext(xp, idx, TRUE);
4022}
4023
4024/*
4025 * Obtain a highlight group name.
4026 * When "skip_cleared" is TRUE don't return a cleared entry.
4027 */
4028 char_u *
4029get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4030{
4031 if (idx < 0)
4032 return NULL;
4033
4034 // Items are never removed from the table, skip the ones that were
4035 // cleared.
4036 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4037 return (char_u *)"";
4038
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004039 if (idx == highlight_ga.ga_len && include_none != 0)
4040 return (char_u *)"none";
4041 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4042 return (char_u *)"default";
4043 if (idx == highlight_ga.ga_len + include_none + include_default
4044 && include_link != 0)
4045 return (char_u *)"link";
4046 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4047 && include_link != 0)
4048 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004049 if (idx >= highlight_ga.ga_len)
4050 return NULL;
4051 return HL_TABLE()[idx].sg_name;
4052}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004053
4054#if defined(FEAT_GUI) || defined(PROTO)
4055/*
4056 * Free all the highlight group fonts.
4057 * Used when quitting for systems which need it.
4058 */
4059 void
4060free_highlight_fonts(void)
4061{
4062 int idx;
4063
4064 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4065 {
4066 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4067 HL_TABLE()[idx].sg_font = NOFONT;
4068# ifdef FEAT_XFONTSET
4069 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4070 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4071# endif
4072 }
4073
4074 gui_mch_free_font(gui.norm_font);
4075# ifdef FEAT_XFONTSET
4076 gui_mch_free_fontset(gui.fontset);
4077# endif
4078# ifndef FEAT_GUI_GTK
4079 gui_mch_free_font(gui.bold_font);
4080 gui_mch_free_font(gui.ital_font);
4081 gui_mch_free_font(gui.boldital_font);
4082# endif
4083}
4084#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004085
4086#if defined(FEAT_EVAL) || defined(PROTO)
4087/*
4088 * Convert each of the highlight attribute bits (bold, standout, underline,
4089 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4090 * the attribute name as the key.
4091 */
4092 static dict_T *
4093highlight_get_attr_dict(int hlattr)
4094{
4095 dict_T *dict;
4096 int i;
4097
4098 dict = dict_alloc();
4099 if (dict == NULL)
4100 return NULL;
4101
4102 for (i = 0; hl_attr_table[i] != 0; ++i)
4103 {
4104 if (hlattr & hl_attr_table[i])
4105 {
4106 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4107 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4108 }
4109 }
4110
4111 return dict;
4112}
4113
4114/*
4115 * Return the attributes of the highlight group at index 'hl_idx' as a
4116 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4117 * links recursively.
4118 */
4119 static dict_T *
4120highlight_get_info(int hl_idx, int resolve_link)
4121{
4122 dict_T *dict;
4123 hl_group_T *sgp;
4124 dict_T *attr_dict;
4125 int hlgid;
4126
4127 dict = dict_alloc();
4128 if (dict == NULL)
4129 return dict;
4130
4131 sgp = &HL_TABLE()[hl_idx];
4132 // highlight group id is 1-based
4133 hlgid = hl_idx + 1;
4134
4135 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4136 goto error;
4137 if (dict_add_number(dict, "id", hlgid) == FAIL)
4138 goto error;
4139
4140 if (sgp->sg_link && resolve_link)
4141 {
4142 // resolve the highlight group link recursively
4143 while (sgp->sg_link)
4144 {
4145 hlgid = sgp->sg_link;
4146 sgp = &HL_TABLE()[sgp->sg_link - 1];
4147 }
4148 }
4149
4150 if (sgp->sg_term != 0)
4151 {
4152 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4153 if (attr_dict != NULL)
4154 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4155 goto error;
4156 }
4157 if (sgp->sg_start != NULL)
4158 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4159 goto error;
4160 if (sgp->sg_stop != NULL)
4161 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4162 goto error;
4163 if (sgp->sg_cterm != 0)
4164 {
4165 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4166 if (attr_dict != NULL)
4167 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4168 goto error;
4169 }
4170 if (sgp->sg_cterm_fg != 0)
4171 if (dict_add_string(dict, "ctermfg",
4172 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4173 goto error;
4174 if (sgp->sg_cterm_bg != 0)
4175 if (dict_add_string(dict, "ctermbg",
4176 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4177 goto error;
4178 if (sgp->sg_cterm_ul != 0)
4179 if (dict_add_string(dict, "ctermul",
4180 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4181 goto error;
4182 if (sgp->sg_gui != 0)
4183 {
4184 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4185 if (attr_dict != NULL)
4186 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4187 goto error;
4188 }
4189 if (sgp->sg_gui_fg_name != NULL)
4190 if (dict_add_string(dict, "guifg",
4191 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4192 goto error;
4193 if (sgp->sg_gui_bg_name != NULL)
4194 if (dict_add_string(dict, "guibg",
4195 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4196 goto error;
4197 if (sgp->sg_gui_sp_name != NULL)
4198 if (dict_add_string(dict, "guisp",
4199 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4200 goto error;
4201# ifdef FEAT_GUI
4202 if (sgp->sg_font_name != NULL)
4203 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4204 goto error;
4205# endif
4206 if (sgp->sg_link)
4207 {
4208 char_u *link;
4209
4210 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4211 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4212 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004213
4214 if (sgp->sg_deflink)
4215 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004216 }
4217 if (dict_len(dict) == 2)
4218 // If only 'name' is present, then the highlight group is cleared.
4219 dict_add_bool(dict, "cleared", VVAL_TRUE);
4220
4221 return dict;
4222
4223error:
4224 vim_free(dict);
4225 return NULL;
4226}
4227
4228/*
4229 * "hlget([name])" function
4230 * Return the attributes of a specific highlight group (if specified) or all
4231 * the highlight groups.
4232 */
4233 void
4234f_hlget(typval_T *argvars, typval_T *rettv)
4235{
4236 list_T *list;
4237 dict_T *dict;
4238 int i;
4239 char_u *hlarg = NULL;
4240 int resolve_link = FALSE;
4241
4242 if (rettv_list_alloc(rettv) == FAIL)
4243 return;
4244
4245 if (check_for_opt_string_arg(argvars, 0) == FAIL
4246 || (argvars[0].v_type != VAR_UNKNOWN
4247 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4248 return;
4249
4250 if (argvars[0].v_type != VAR_UNKNOWN)
4251 {
4252 // highlight group name supplied
4253 hlarg = tv_get_string_chk(&argvars[0]);
4254 if (hlarg == NULL)
4255 return;
4256
4257 if (argvars[1].v_type != VAR_UNKNOWN)
4258 {
4259 int error = FALSE;
4260
4261 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4262 if (error)
4263 return;
4264 }
4265 }
4266
4267 list = rettv->vval.v_list;
4268 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4269 {
4270 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4271 {
4272 dict = highlight_get_info(i, resolve_link);
4273 if (dict != NULL)
4274 list_append_dict(list, dict);
4275 }
4276 }
4277}
4278
4279/*
4280 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4281 * 'dict' or the value is not a string type. If the value is not a string type
4282 * or is NULL, then 'error' is set to TRUE.
4283 */
4284 static char_u *
4285hldict_get_string(dict_T *dict, char_u *key, int *error)
4286{
4287 dictitem_T *di;
4288
4289 *error = FALSE;
4290 di = dict_find(dict, key, -1);
4291 if (di == NULL)
4292 return NULL;
4293
4294 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4295 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004296 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004297 *error = TRUE;
4298 return NULL;
4299 }
4300
4301 return di->di_tv.vval.v_string;
4302}
4303
4304/*
4305 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4306 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4307 * Dictionary or is NULL.
4308 */
4309 static int
4310hldict_attr_to_str(
4311 dict_T *dict,
4312 char_u *key,
4313 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004314 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004315{
4316 dictitem_T *di;
4317 dict_T *attrdict;
4318 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004319 char_u *p;
4320 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004321
4322 attr_str[0] = NUL;
4323 di = dict_find(dict, key, -1);
4324 if (di == NULL)
4325 return TRUE;
4326
4327 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4328 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004329 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004330 return FALSE;
4331 }
4332
4333 attrdict = di->di_tv.vval.v_dict;
4334
4335 // If the attribute dict is empty, then return NONE to clear the attributes
4336 if (dict_len(attrdict) == 0)
4337 {
4338 vim_strcat(attr_str, (char_u *)"NONE", len);
4339 return TRUE;
4340 }
4341
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004342 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004343 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4344 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01004345 if (dict_get_bool(attrdict, hl_name_table[i], VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004346 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004347 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4348 STRCPY(p, (char_u *)",");
4349 sz = STRLEN(hl_name_table[i]);
4350 if (p - attr_str + sz + 1 < len)
4351 {
4352 STRCPY(p, (char_u *)hl_name_table[i]);
4353 p += sz;
4354 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004355 }
4356 }
4357
4358 return TRUE;
4359}
4360
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004361// Temporary buffer used to store the command string produced by hlset().
4362// IObuff cannot be used for this as the error messages produced by hlset()
4363// internally use IObuff.
4364#define HLSETBUFSZ 512
4365static char_u hlsetBuf[HLSETBUFSZ + 1];
4366
4367/*
4368 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4369 * "dptr", which points into "hlsetBuf".
4370 * Returns the updated pointer.
4371 */
4372 static char_u *
4373add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4374{
4375 size_t vallen;
4376
4377 // Do nothing if the value is not specified or is empty
4378 if (value == NULL || *value == NUL)
4379 return dptr;
4380
4381 vallen = STRLEN(value);
4382 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4383 {
4384 STRCPY(dptr, attr);
4385 dptr += attrlen;
4386 STRCPY(dptr, value);
4387 dptr += vallen;
4388 }
4389
4390 return dptr;
4391}
4392
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004393/*
4394 * Add or update a highlight group using 'dict' items. Returns TRUE if
4395 * successfully updated the highlight group.
4396 */
4397 static int
4398hlg_add_or_update(dict_T *dict)
4399{
4400 char_u *name;
4401 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004402 char_u term_attr[MAX_ATTR_LEN];
4403 char_u cterm_attr[MAX_ATTR_LEN];
4404 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004405 char_u *start;
4406 char_u *stop;
4407 char_u *ctermfg;
4408 char_u *ctermbg;
4409 char_u *ctermul;
4410 char_u *guifg;
4411 char_u *guibg;
4412 char_u *guisp;
4413# ifdef FEAT_GUI
4414 char_u *font;
4415# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004416 int forceit = FALSE;
4417 int dodefault = FALSE;
4418 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004419 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004420
4421 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004422 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004423 return FALSE;
4424
Bram Moolenaard61efa52022-07-23 09:52:04 +01004425 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004426 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004427
Bram Moolenaard61efa52022-07-23 09:52:04 +01004428 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004429 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004430
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004431 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004432 {
4433 varnumber_T cleared;
4434
4435 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004436 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004437 if (cleared == TRUE)
4438 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004439 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4440 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004441 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004442 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004443 }
4444
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004445 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004446 {
4447 char_u *linksto;
4448
4449 // link highlight groups
4450 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004451 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004452 return FALSE;
4453
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004454 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004455 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004456 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004457
4458 done = TRUE;
4459 }
4460
4461 // If 'cleared' or 'linksto' are specified, then don't process the other
4462 // attributes.
4463 if (done)
4464 return TRUE;
4465
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004466 start = hldict_get_string(dict, (char_u *)"start", &error);
4467 if (error)
4468 return FALSE;
4469
4470 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4471 if (error)
4472 return FALSE;
4473
4474 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004475 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004476 return FALSE;
4477
4478 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004479 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004480 return FALSE;
4481
4482 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4483 if (error)
4484 return FALSE;
4485
4486 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4487 if (error)
4488 return FALSE;
4489
4490 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4491 if (error)
4492 return FALSE;
4493
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004494 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004495 return FALSE;
4496
4497 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4498 if (error)
4499 return FALSE;
4500
4501 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4502 if (error)
4503 return FALSE;
4504
4505 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4506 if (error)
4507 return FALSE;
4508
4509# ifdef FEAT_GUI
4510 font = hldict_get_string(dict, (char_u *)"font", &error);
4511 if (error)
4512 return FALSE;
4513# endif
4514
4515 // If none of the attributes are specified, then do nothing.
4516 if (term_attr[0] == NUL && start == NULL && stop == NULL
4517 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4518 && ctermul == NULL && gui_attr[0] == NUL
4519# ifdef FEAT_GUI
4520 && font == NULL
4521# endif
4522 && guifg == NULL && guibg == NULL && guisp == NULL
4523 )
4524 return TRUE;
4525
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004526 hlsetBuf[0] = NUL;
4527 p = hlsetBuf;
4528 if (dodefault)
4529 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4530 p = add_attr_and_value(p, (char_u *)"", 0, name);
4531 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4532 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4533 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4534 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4535 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4536 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4537 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4538 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004539# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004540 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004541# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004542 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4543 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004544 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004545
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004546 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004547
4548 return TRUE;
4549}
4550
4551/*
4552 * "hlset([{highlight_attr}])" function
4553 * Add or modify highlight groups
4554 */
4555 void
4556f_hlset(typval_T *argvars, typval_T *rettv)
4557{
4558 listitem_T *li;
4559 dict_T *dict;
4560
4561 rettv->vval.v_number = -1;
4562
4563 if (check_for_list_arg(argvars, 0) == FAIL)
4564 return;
4565
4566 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4567 {
4568 if (li->li_tv.v_type != VAR_DICT)
4569 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004570 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004571 return;
4572 }
4573
4574 dict = li->li_tv.vval.v_dict;
4575 if (!hlg_add_or_update(dict))
4576 return;
4577 }
4578
4579 rettv->vval.v_number = 0;
4580}
4581#endif