blob: 9b3b07244634d4ecf1f4b1b635e4af26def054a0 [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
PMuncha606f3a2023-11-15 15:35:49 +010062 int sg_cterm_font; // terminal alternative font (0 for normal)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020063// for when using the GUI
64#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
65 guicolor_T sg_gui_fg; // GUI foreground color handle
66 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020067 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020068#endif
69#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020070 GuiFont sg_font; // GUI font handle
71#ifdef FEAT_XFONTSET
72 GuiFontset sg_fontset; // GUI fontset handle
73#endif
74 char_u *sg_font_name; // GUI font or fontset name
75 int sg_gui_attr; // Screen attr for GUI mode
76#endif
77#if defined(FEAT_GUI) || defined(FEAT_EVAL)
78// Store the sp color name for the GUI or synIDattr()
79 int sg_gui; // "gui=" highlighting attributes
80 char_u *sg_gui_fg_name;// GUI foreground color name
81 char_u *sg_gui_bg_name;// GUI background color name
82 char_u *sg_gui_sp_name;// GUI special color name
83#endif
84 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +020085 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020086 int sg_set; // combination of SG_* flags
87#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +020088 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020089 sctx_T sg_script_ctx; // script in which the group was last set
90#endif
91} hl_group_T;
92
93// highlight groups for 'highlight' option
94static garray_T highlight_ga;
95#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
96
97/*
98 * An attribute number is the index in attr_table plus ATTR_OFF.
99 */
100#define ATTR_OFF (HL_ALL + 1)
101
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200102static void syn_unadd_group(void);
103static void set_hl_attr(int idx);
104static void highlight_list_one(int id);
105static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
106static int syn_add_group(char_u *name);
107static int hl_has_settings(int idx, int check_link);
108static void highlight_clear(int idx);
109
110#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
111static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
112#endif
113#ifdef FEAT_GUI
114static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
115static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
116#endif
117
118/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200119 * The default highlight groups. These are compiled-in for fast startup and
120 * they still work when the runtime files can't be found.
121 * When making changes here, also change runtime/colors/default.vim!
122 * The #ifdefs are needed to reduce the amount of static data. Helps to make
123 * the 16 bit DOS (museum) version compile.
124 */
125#if defined(FEAT_GUI) || defined(FEAT_EVAL)
126# define CENT(a, b) b
127#else
128# define CENT(a, b) a
129#endif
130static char *(highlight_init_both[]) = {
131 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
132 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
133 CENT("IncSearch term=reverse cterm=reverse",
134 "IncSearch term=reverse cterm=reverse gui=reverse"),
135 CENT("ModeMsg term=bold cterm=bold",
136 "ModeMsg term=bold cterm=bold gui=bold"),
137 CENT("NonText term=bold ctermfg=Blue",
138 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
139 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
140 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
141 CENT("StatusLineNC term=reverse cterm=reverse",
142 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
143 "default link EndOfBuffer NonText",
144 CENT("VertSplit term=reverse cterm=reverse",
145 "VertSplit term=reverse cterm=reverse gui=reverse"),
146#ifdef FEAT_CLIPBOARD
147 CENT("VisualNOS term=underline,bold cterm=underline,bold",
148 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
149#endif
150#ifdef FEAT_DIFF
151 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
152 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
153#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200154 CENT("PmenuSbar ctermbg=Grey",
155 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200156 CENT("TabLineSel term=bold cterm=bold",
157 "TabLineSel term=bold cterm=bold gui=bold"),
158 CENT("TabLineFill term=reverse cterm=reverse",
159 "TabLineFill term=reverse cterm=reverse gui=reverse"),
160#ifdef FEAT_GUI
161 "Cursor guibg=fg guifg=bg",
162 "lCursor guibg=fg guifg=bg", // should be different, but what?
163#endif
164 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000165 "default link CursorLineSign SignColumn",
166 "default link CursorLineFold FoldColumn",
LemonBoya4399382022-04-09 21:04:08 +0100167 "default link CurSearch Search",
Gianmaria Bajo6a7c7742023-03-10 16:35:53 +0000168 "default link PmenuKind Pmenu",
169 "default link PmenuKindSel PmenuSel",
170 "default link PmenuExtra Pmenu",
171 "default link PmenuExtraSel PmenuSel",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200172 CENT("Normal cterm=NONE", "Normal gui=NONE"),
173 NULL
174};
175
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200176// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200177static char *(highlight_init_light[]) = {
178 CENT("Directory term=bold ctermfg=DarkBlue",
179 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
180 CENT("LineNr term=underline ctermfg=Brown",
181 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200182 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
183 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200184 CENT("MoreMsg term=bold ctermfg=DarkGreen",
185 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
186 CENT("Question term=standout ctermfg=DarkGreen",
187 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
188 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
189 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
190#ifdef FEAT_SPELL
191 CENT("SpellBad term=reverse ctermbg=LightRed",
192 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
193 CENT("SpellCap term=reverse ctermbg=LightBlue",
194 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
195 CENT("SpellRare term=reverse ctermbg=LightMagenta",
196 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
197 CENT("SpellLocal term=underline ctermbg=Cyan",
198 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
199#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200200 CENT("PmenuThumb ctermbg=Black",
201 "PmenuThumb ctermbg=Black guibg=Black"),
202 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
203 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
204 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
205 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200206 CENT("SpecialKey term=bold ctermfg=DarkBlue",
207 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
208 CENT("Title term=bold ctermfg=DarkMagenta",
209 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
210 CENT("WarningMsg term=standout ctermfg=DarkRed",
211 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200212 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
213 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200214#ifdef FEAT_FOLDING
215 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
216 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
217 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
218 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
219#endif
220#ifdef FEAT_SIGNS
221 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
222 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
223#endif
Maxim Kim59bafc82024-02-01 21:07:51 +0100224 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100225 "Visual ctermbg=Grey ctermfg=Black guibg=LightGrey guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200226#ifdef FEAT_DIFF
227 CENT("DiffAdd term=bold ctermbg=LightBlue",
228 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
229 CENT("DiffChange term=bold ctermbg=LightMagenta",
230 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
231 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
232 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
233#endif
234 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
235 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
236#ifdef FEAT_SYN_HL
237 CENT("CursorColumn term=reverse ctermbg=LightGrey",
238 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
239 CENT("CursorLine term=underline cterm=underline",
240 "CursorLine term=underline cterm=underline guibg=Grey90"),
241 CENT("ColorColumn term=reverse ctermbg=LightRed",
242 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
243#endif
244#ifdef FEAT_CONCEAL
245 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
246 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
247#endif
248 CENT("MatchParen term=reverse ctermbg=Cyan",
249 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
250#ifdef FEAT_TERMINAL
251 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
252 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
253 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
254 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
255#endif
256#ifdef FEAT_MENU
257 CENT("ToolbarLine term=underline ctermbg=LightGrey",
258 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
259 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
260 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
261#endif
262 NULL
263};
264
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200265// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200266static char *(highlight_init_dark[]) = {
267 CENT("Directory term=bold ctermfg=LightCyan",
268 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
269 CENT("LineNr term=underline ctermfg=Yellow",
270 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200271 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
272 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200273 CENT("MoreMsg term=bold ctermfg=LightGreen",
274 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
275 CENT("Question term=standout ctermfg=LightGreen",
276 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
277 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
278 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
279 CENT("SpecialKey term=bold ctermfg=LightBlue",
280 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
281#ifdef FEAT_SPELL
282 CENT("SpellBad term=reverse ctermbg=Red",
283 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
284 CENT("SpellCap term=reverse ctermbg=Blue",
285 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
286 CENT("SpellRare term=reverse ctermbg=Magenta",
287 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
288 CENT("SpellLocal term=underline ctermbg=Cyan",
289 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
290#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200291 CENT("PmenuThumb ctermbg=White",
292 "PmenuThumb ctermbg=White guibg=White"),
293 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
294 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
295 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
296 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200297 CENT("Title term=bold ctermfg=LightMagenta",
298 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
299 CENT("WarningMsg term=standout ctermfg=LightRed",
300 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200301 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
302 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200303#ifdef FEAT_FOLDING
304 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
305 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
306 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
307 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
308#endif
309#ifdef FEAT_SIGNS
310 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
311 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
312#endif
Christian Brabandte6d8b462024-01-28 23:33:29 +0100313 CENT("Visual ctermbg=Grey ctermfg=Black",
Maxim Kim34e4a052024-02-14 20:28:17 +0100314 "Visual ctermbg=Grey ctermfg=Black guibg=#575757 guifg=LightGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200315#ifdef FEAT_DIFF
316 CENT("DiffAdd term=bold ctermbg=DarkBlue",
317 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
318 CENT("DiffChange term=bold ctermbg=DarkMagenta",
319 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
320 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
321 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
322#endif
323 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
324 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
325#ifdef FEAT_SYN_HL
326 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
327 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
328 CENT("CursorLine term=underline cterm=underline",
329 "CursorLine term=underline cterm=underline guibg=Grey40"),
330 CENT("ColorColumn term=reverse ctermbg=DarkRed",
331 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
332#endif
333 CENT("MatchParen term=reverse ctermbg=DarkCyan",
334 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
335#ifdef FEAT_CONCEAL
336 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
337 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
338#endif
339#ifdef FEAT_TERMINAL
340 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
341 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
342 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
343 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
344#endif
345#ifdef FEAT_MENU
346 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
347 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
348 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
349 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
350#endif
351 NULL
352};
353
Dominique Pelle748b3082022-01-08 12:41:16 +0000354#if defined(FEAT_SYN_HL) || defined(PROTO)
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200355/*
356 * Returns the number of highlight groups.
357 */
358 int
359highlight_num_groups(void)
360{
361 return highlight_ga.ga_len;
362}
363
364/*
365 * Returns the name of a highlight group.
366 */
367 char_u *
368highlight_group_name(int id)
369{
370 return HL_TABLE()[id].sg_name;
371}
372
373/*
374 * Returns the ID of the link to a highlight group.
375 */
376 int
377highlight_link_id(int id)
378{
379 return HL_TABLE()[id].sg_link;
380}
Dominique Pelle748b3082022-01-08 12:41:16 +0000381#endif
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200382
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200383 void
384init_highlight(
385 int both, // include groups where 'bg' doesn't matter
386 int reset) // clear group first
387{
388 int i;
389 char **pp;
390 static int had_both = FALSE;
391#ifdef FEAT_EVAL
392 char_u *p;
393
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100394 // Try finding the color scheme file. Used when a color file was loaded
395 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200396 p = get_var_value((char_u *)"g:colors_name");
397 if (p != NULL)
398 {
399 // The value of g:colors_name could be freed when sourcing the script,
400 // making "p" invalid, so copy it.
401 char_u *copy_p = vim_strsave(p);
402 int r;
403
404 if (copy_p != NULL)
405 {
406 r = load_colors(copy_p);
407 vim_free(copy_p);
408 if (r == OK)
409 return;
410 }
411 }
412
413#endif
414
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100415 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200416 if (both)
417 {
418 had_both = TRUE;
419 pp = highlight_init_both;
420 for (i = 0; pp[i] != NULL; ++i)
421 do_highlight((char_u *)pp[i], reset, TRUE);
422 }
423 else if (!had_both)
424 // Don't do anything before the call with both == TRUE from main().
425 // Not everything has been setup then, and that call will overrule
426 // everything anyway.
427 return;
428
429 if (*p_bg == 'l')
430 pp = highlight_init_light;
431 else
432 pp = highlight_init_dark;
433 for (i = 0; pp[i] != NULL; ++i)
434 do_highlight((char_u *)pp[i], reset, TRUE);
435
Maxim Kim59bafc82024-02-01 21:07:51 +0100436 // Reverse looks ugly, but grey may not work for less than 8 colors. Thus
437 // let it depend on the number of colors available.
438 if (t_colors < 8)
439 do_highlight((char_u *)"Visual term=reverse cterm=reverse ctermbg=NONE ctermfg=NONE",
440 FALSE, TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200441 // With 8 colors brown is equal to yellow, need to use black for Search fg
442 // to avoid Statement highlighted text disappears.
443 // Clear the attributes, needed when changing the t_Co value.
Christian Brabandte6d8b462024-01-28 23:33:29 +0100444 if (t_colors <= 8)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200445 {
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200446 if (*p_bg == 'l')
447 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
448 }
449
450#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100451 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200452 if (get_var_value((char_u *)"g:syntax_on") != NULL)
453 {
454 static int recursive = 0;
455
456 if (recursive >= 5)
Bram Moolenaara6f79292022-01-04 21:30:47 +0000457 emsg(_(e_recursive_loop_loading_syncolor_vim));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200458 else
459 {
460 ++recursive;
461 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
462 --recursive;
463 }
464 }
465#endif
466}
467
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000468#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
469/*
470 * Load a default color list. Intended to support legacy color names but allows
471 * the user to override the color values. Only loaded once.
472 */
473 static void
Yegappan Lakshmanana23a11b2023-02-21 14:27:41 +0000474load_default_colors_lists(void)
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +0000475{
476 // Lacking a default color list isn't the end of the world but it is likely
477 // an inconvenience so users should know when it is missing.
478 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
479 msg("failed to load colors/lists/default.vim");
480}
481#endif
482
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200483/*
484 * Load color file "name".
485 * Return OK for success, FAIL for failure.
486 */
487 int
488load_colors(char_u *name)
489{
490 char_u *buf;
491 int retval = FAIL;
492 static int recursive = FALSE;
493
494 // When being called recursively, this is probably because setting
495 // 'background' caused the highlighting to be reloaded. This means it is
496 // working, thus we should return OK.
497 if (recursive)
498 return OK;
499
500 recursive = TRUE;
501 buf = alloc(STRLEN(name) + 12);
502 if (buf != NULL)
503 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100504#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100505 load_default_colors_lists();
506#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200507 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
508 curbuf->b_fname, FALSE, curbuf);
509 sprintf((char *)buf, "colors/%s.vim", name);
510 retval = source_runtime(buf, DIP_START + DIP_OPT);
511 vim_free(buf);
Bram Moolenaar5d09a402022-08-31 21:17:10 +0100512 if (retval == OK)
513 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname,
514 FALSE, curbuf);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200515 }
516 recursive = FALSE;
517
518 return retval;
519}
520
521static char *(color_names[28]) = {
522 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
523 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
524 "Gray", "Grey", "LightGray", "LightGrey",
525 "DarkGray", "DarkGrey",
526 "Blue", "LightBlue", "Green", "LightGreen",
527 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
528 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
529 // indices:
530 // 0, 1, 2, 3,
531 // 4, 5, 6, 7,
532 // 8, 9, 10, 11,
533 // 12, 13,
534 // 14, 15, 16, 17,
535 // 18, 19, 20, 21, 22,
536 // 23, 24, 25, 26, 27
537static int color_numbers_16[28] = {0, 1, 2, 3,
538 4, 5, 6, 6,
539 7, 7, 7, 7,
540 8, 8,
541 9, 9, 10, 10,
542 11, 11, 12, 12, 13,
543 13, 14, 14, 15, -1};
544// for xterm with 88 colors...
545static int color_numbers_88[28] = {0, 4, 2, 6,
546 1, 5, 32, 72,
547 84, 84, 7, 7,
548 82, 82,
549 12, 43, 10, 61,
550 14, 63, 9, 74, 13,
551 75, 11, 78, 15, -1};
552// for xterm with 256 colors...
553static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200554 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200555 248, 248, 7, 7,
556 242, 242,
557 12, 81, 10, 121,
558 14, 159, 9, 224, 13,
559 225, 11, 229, 15, -1};
560// for terminals with less than 16 colors...
561static int color_numbers_8[28] = {0, 4, 2, 6,
562 1, 5, 3, 3,
563 7, 7, 7, 7,
564 0+8, 0+8,
565 4+8, 4+8, 2+8, 2+8,
566 6+8, 6+8, 1+8, 1+8, 5+8,
567 5+8, 3+8, 3+8, 7+8, -1};
568
569/*
570 * Lookup the "cterm" value to be used for color with index "idx" in
571 * color_names[].
572 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
573 * colors, otherwise it will be unchanged.
574 */
Yegappan Lakshmananee47eac2022-06-29 12:55:36 +0100575 static int
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200576lookup_color(int idx, int foreground, int *boldp)
577{
578 int color = color_numbers_16[idx];
579 char_u *p;
580
581 // Use the _16 table to check if it's a valid color name.
582 if (color < 0)
583 return -1;
584
585 if (t_colors == 8)
586 {
587 // t_Co is 8: use the 8 colors table
588#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100589 // On qnx, the 8 & 16 color arrays are the same
590 if (STRNCMP(T_NAME, "qansi", 5) == 0)
591 color = color_numbers_16[idx];
592 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200593#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100594 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200595 if (foreground)
596 {
597 // set/reset bold attribute to get light foreground
598 // colors (on some terminals, e.g. "linux")
599 if (color & 8)
600 *boldp = TRUE;
601 else
602 *boldp = FALSE;
603 }
604 color &= 7; // truncate to 8 colors
605 }
606 else if (t_colors == 16 || t_colors == 88
607 || t_colors >= 256)
608 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100609 // Guess: if the termcap entry ends in 'm', it is
610 // probably an xterm-like terminal. Use the changed
611 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200612 if (*T_CAF != NUL)
613 p = T_CAF;
614 else
615 p = T_CSF;
616 if (*p != NUL && (t_colors > 256
617 || *(p + STRLEN(p) - 1) == 'm'))
618 {
619 if (t_colors == 88)
620 color = color_numbers_88[idx];
621 else if (t_colors >= 256)
622 color = color_numbers_256[idx];
623 else
624 color = color_numbers_8[idx];
625 }
626#ifdef FEAT_TERMRESPONSE
627 if (t_colors >= 256 && color == 15 && is_mac_terminal)
628 // Terminal.app has a bug: 15 is light grey. Use white
629 // from the color cube instead.
630 color = 231;
631#endif
632 }
633 return color;
634}
635
636/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100637 * Link highlight group 'from_hg' to 'to_hg'.
638 * 'dodefault' is set to TRUE for ":highlight default link".
dundargocc57b5bc2022-11-02 13:30:51 +0000639 * 'forceit' is set to TRUE for ":highlight! link"
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100640 * 'init' is set to TRUE when initializing all the highlight groups.
641 */
642 static void
643highlight_group_link(
644 char_u *from_hg,
645 int from_len,
646 char_u *to_hg,
647 int to_len,
648 int dodefault,
649 int forceit,
650 int init)
651{
652 int from_id;
653 int to_id;
654 hl_group_T *hlgroup = NULL;
655
656 from_id = syn_check_group(from_hg, from_len);
657 if (STRNCMP(to_hg, "NONE", 4) == 0)
658 to_id = 0;
659 else
660 to_id = syn_check_group(to_hg, to_len);
661
662 if (from_id > 0)
663 {
664 hlgroup = &HL_TABLE()[from_id - 1];
665 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
666 {
667 hlgroup->sg_deflink = to_id;
668#ifdef FEAT_EVAL
669 hlgroup->sg_deflink_sctx = current_sctx;
670 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
671#endif
672 }
673 }
674
675 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
676 {
677 // Don't allow a link when there already is some highlighting
678 // for the group, unless '!' is used
679 if (to_id > 0 && !forceit && !init
680 && hl_has_settings(from_id - 1, dodefault))
681 {
682 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000683 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100684 }
685 else if (hlgroup->sg_link != to_id
686#ifdef FEAT_EVAL
687 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
688#endif
689 || hlgroup->sg_cleared)
690 {
691 if (!init)
692 hlgroup->sg_set |= SG_LINK;
693 hlgroup->sg_link = to_id;
694#ifdef FEAT_EVAL
695 hlgroup->sg_script_ctx = current_sctx;
696 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
697#endif
698 hlgroup->sg_cleared = FALSE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +0100699 redraw_all_later(UPD_SOME_VALID);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100700
701 // Only call highlight_changed() once after multiple changes.
702 need_highlight_changed = TRUE;
703 }
704 }
705
706}
707
708/*
709 * Reset all highlighting to the defaults. Removes all highlighting for the
710 * groups added by the user.
711 */
712 static void
713highlight_reset_all(void)
714{
715 int idx;
716
717#ifdef FEAT_GUI
718 // First, we do not destroy the old values, but allocate the new
719 // ones and update the display. THEN we destroy the old values.
720 // If we destroy the old values first, then the old values
721 // (such as GuiFont's or GuiFontset's) will still be displayed but
722 // invalid because they were free'd.
723 if (gui.in_use)
724 {
725# ifdef FEAT_BEVAL_TIP
726 gui_init_tooltip_font();
727# endif
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +0100728# if defined(FEAT_MENU) && defined(FEAT_GUI_MOTIF)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100729 gui_init_menu_font();
730# endif
731 }
732# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
733 gui_mch_def_colors();
734# endif
735# ifdef FEAT_GUI_X11
736# ifdef FEAT_MENU
737
738 // This only needs to be done when there is no Menu highlight
739 // group defined by default, which IS currently the case.
740 gui_mch_new_menu_colors();
741# endif
742 if (gui.in_use)
743 {
744 gui_new_scrollbar_colors();
745# ifdef FEAT_BEVAL_GUI
746 gui_mch_new_tooltip_colors();
747# endif
748# ifdef FEAT_MENU
749 gui_mch_new_menu_font();
750# endif
751 }
752# endif
753
754 // Ok, we're done allocating the new default graphics items.
755 // The screen should already be refreshed at this point.
756 // It is now Ok to clear out the old data.
757#endif
758#ifdef FEAT_EVAL
759 do_unlet((char_u *)"g:colors_name", TRUE);
760#endif
761 restore_cterm_colors();
762
763 // Clear all default highlight groups and load the defaults.
764 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
765 highlight_clear(idx);
766 init_highlight(TRUE, TRUE);
767#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
768 if (USE_24BIT)
769 highlight_gui_started();
770 else
771#endif
772 highlight_changed();
773 redraw_later_clear();
774}
775
776/*
777 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
778 * index 'idx'.
779 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
780 * 'arg' is the list of attribute names separated by comma.
781 * 'init' is set to TRUE when initializing all the highlight groups.
782 * Returns TRUE if the attributes are set.
783 */
784 static int
785highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
786{
787 int attr;
788 int off;
789 long i;
790 int len;
791
792 attr = 0;
793 off = 0;
794 while (arg[off] != NUL)
795 {
796 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
797 {
798 len = (int)STRLEN(hl_name_table[i]);
799 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
800 {
801 attr |= hl_attr_table[i];
802 off += len;
803 break;
804 }
805 }
806 if (i < 0)
807 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000808 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100809 return FALSE;
810 }
811 if (arg[off] == ',') // another one follows
812 ++off;
813 }
814 if (*key == 'T')
815 {
816 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
817 {
818 if (!init)
819 HL_TABLE()[idx].sg_set |= SG_TERM;
820 HL_TABLE()[idx].sg_term = attr;
821 }
822 }
823 else if (*key == 'C')
824 {
825 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
826 {
827 if (!init)
828 HL_TABLE()[idx].sg_set |= SG_CTERM;
829 HL_TABLE()[idx].sg_cterm = attr;
830 HL_TABLE()[idx].sg_cterm_bold = FALSE;
831 }
832 }
833#if defined(FEAT_GUI) || defined(FEAT_EVAL)
834 else
835 {
836 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
837 {
838 if (!init)
839 HL_TABLE()[idx].sg_set |= SG_GUI;
840 HL_TABLE()[idx].sg_gui = attr;
841 }
842 }
843#endif
844
845 return TRUE;
846}
847
848#ifdef FEAT_GUI
849/*
850 * Set the font for the highlight group at 'idx'.
851 * 'arg' is the font name.
852 * Returns TRUE if the font is changed.
853 */
854 static int
855highlight_set_font(
856 int idx,
857 char_u *arg,
858 int is_normal_group,
859 int is_menu_group,
860 int is_tooltip_group)
861{
862 int did_change = FALSE;
863
864 // in non-GUI fonts are simply ignored
865 if (HL_TABLE()[idx].sg_font_name != NULL
866 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
867 {
868 // Font name didn't change, ignore.
869 }
870 else if (!gui.shell_created)
871 {
872 // GUI not started yet, always accept the name.
873 vim_free(HL_TABLE()[idx].sg_font_name);
874 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
875 did_change = TRUE;
876 }
877 else
878 {
879 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
880# ifdef FEAT_XFONTSET
881 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
882# endif
883 // First, save the current font/fontset.
884 // Then try to allocate the font/fontset.
885 // If the allocation fails, HL_TABLE()[idx].sg_font OR
886 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
887
888 HL_TABLE()[idx].sg_font = NOFONT;
889# ifdef FEAT_XFONTSET
890 HL_TABLE()[idx].sg_fontset = NOFONTSET;
891# endif
892 hl_do_font(idx, arg, is_normal_group, is_menu_group,
893 is_tooltip_group, FALSE);
894
895# ifdef FEAT_XFONTSET
896 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
897 {
898 // New fontset was accepted. Free the old one, if there
899 // was one.
900 gui_mch_free_fontset(temp_sg_fontset);
901 vim_free(HL_TABLE()[idx].sg_font_name);
902 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
903 did_change = TRUE;
904 }
905 else
906 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
907# endif
908 if (HL_TABLE()[idx].sg_font != NOFONT)
909 {
910 // New font was accepted. Free the old one, if there was
911 // one.
912 gui_mch_free_font(temp_sg_font);
913 vim_free(HL_TABLE()[idx].sg_font_name);
914 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
915 did_change = TRUE;
916 }
917 else
918 HL_TABLE()[idx].sg_font = temp_sg_font;
919 }
920
921 return did_change;
922}
923#endif
924
925/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000926 * Set the cterm foreground color for the Normal highlight group to "color" and
927 * the bold attribute to "bold".
928 */
929 static void
930hl_set_ctermfg_normal_group(int color, int bold)
931{
932 cterm_normal_fg_color = color + 1;
933 cterm_normal_fg_bold = bold;
934#ifdef FEAT_GUI
935 // Don't do this if the GUI is used.
936 if (!gui.in_use && !gui.starting)
937#endif
938 {
939 set_must_redraw(UPD_CLEAR);
940 if (termcap_active && color >= 0)
941 term_fg_color(color);
942 }
943}
944
945/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100946 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100947 */
948 static void
949highlight_set_ctermfg(int idx, int color, int is_normal_group)
950{
951 HL_TABLE()[idx].sg_cterm_fg = color + 1;
952 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000953 hl_set_ctermfg_normal_group(color,
954 (HL_TABLE()[idx].sg_cterm & HL_BOLD));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100955}
956
957/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000958 * Set the cterm background color for the Normal highlight group to "color".
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100959 */
960 static void
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000961hl_set_ctermbg_normal_group(int color)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100962{
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000963 cterm_normal_bg_color = color + 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100964#ifdef FEAT_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000965 // Don't mess with 'background' if the GUI is used.
966 if (!gui.in_use && !gui.starting)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100967#endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000968 {
969 set_must_redraw(UPD_CLEAR);
970 if (color >= 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100971 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000972 int dark = -1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100973
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000974 if (termcap_active)
975 term_bg_color(color);
976 if (t_colors < 16)
977 dark = (color == 0 || color == 4);
978 // Limit the heuristic to the standard 16 colors
979 else if (color < 16)
980 dark = (color < 7 || color == 8);
981 // Set the 'background' option if the value is
982 // wrong.
983 if (dark != -1
984 && dark != (*p_bg == 'd')
985 && !option_was_set((char_u *)"bg"))
986 {
987 set_option_value_give_err((char_u *)"bg",
988 0L, (char_u *)(dark ? "dark" : "light"), 0);
989 reset_option_was_set((char_u *)"bg");
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100990 }
991 }
992 }
993}
994
995/*
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +0000996 * Set the cterm background color for the highlight group at 'idx' to 'color'.
997 */
998 static void
999highlight_set_ctermbg(int idx, int color, int is_normal_group)
1000{
1001 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1002 if (is_normal_group)
1003 hl_set_ctermbg_normal_group(color);
1004}
1005
1006/*
1007 * Set the cterm underline color for the Normal highlight group to "color".
1008 */
1009 static void
1010hl_set_ctermul_normal_group(int color)
1011{
1012 cterm_normal_ul_color = color + 1;
1013#ifdef FEAT_GUI
1014 // Don't do this if the GUI is used.
1015 if (!gui.in_use && !gui.starting)
1016#endif
1017 {
1018 set_must_redraw(UPD_CLEAR);
1019 if (termcap_active && color >= 0)
1020 term_ul_color(color);
1021 }
1022}
1023
1024/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001025 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001026 */
1027 static void
1028highlight_set_ctermul(int idx, int color, int is_normal_group)
1029{
1030 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1031 if (is_normal_group)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001032 hl_set_ctermul_normal_group(color);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001033}
1034
1035/*
PMuncha606f3a2023-11-15 15:35:49 +01001036 * Set the cterm font for the highlight group at 'idx'.
1037 * 'arg' is the color name or the numeric value as a string.
1038 * 'init' is set to TRUE when initializing highlighting.
1039 * Called for the ":highlight" command and the "hlset()" function.
1040 *
1041 * Returns TRUE if the font is set.
1042 */
1043 static int
1044highlight_set_cterm_font(
1045 int idx,
1046 char_u *arg,
1047 int init)
1048{
1049 int font;
1050
1051 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1052 return FALSE;
1053
1054 if (!init)
1055 HL_TABLE()[idx].sg_set |= SG_CTERM;
1056
1057 if (VIM_ISDIGIT(*arg))
1058 font = atoi((char *)arg);
1059 else if (STRICMP(arg, "NONE") == 0)
1060 font = -1;
1061 else
1062 return FALSE;
1063
1064 HL_TABLE()[idx].sg_cterm_font = font + 1;
1065 return TRUE;
1066}
1067
1068/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001069 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
1070 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
1071 * 'keystart' is the color name/value.
1072 * 'arg' is the color name or the numeric value as a string.
1073 * 'is_normal_group' is set if the highlight group is 'NORMAL'
1074 * 'init' is set to TRUE when initializing highlighting.
1075 * Called for the ":highlight" command and the "hlset()" function.
1076 *
1077 * Returns TRUE if the color is set.
1078 */
1079 static int
1080highlight_set_cterm_color(
1081 int idx,
1082 char_u *key,
1083 char_u *key_start,
1084 char_u *arg,
1085 int is_normal_group,
1086 int init)
1087{
1088 int color;
1089 long i;
1090 int off;
1091
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001092 if (init && (HL_TABLE()[idx].sg_set & SG_CTERM))
1093 return FALSE;
1094
1095 if (!init)
1096 HL_TABLE()[idx].sg_set |= SG_CTERM;
1097
1098 // When setting the foreground color, and previously the "bold"
1099 // flag was set for a light color, reset it now
1100 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001101 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001102 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1103 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1104 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001105
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001106 if (VIM_ISDIGIT(*arg))
1107 color = atoi((char *)arg);
1108 else if (STRICMP(arg, "fg") == 0)
1109 {
1110 if (cterm_normal_fg_color)
1111 color = cterm_normal_fg_color - 1;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001112 else
1113 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001114 emsg(_(e_fg_color_unknown));
1115 return FALSE;
1116 }
1117 }
1118 else if (STRICMP(arg, "bg") == 0)
1119 {
1120 if (cterm_normal_bg_color > 0)
1121 color = cterm_normal_bg_color - 1;
1122 else
1123 {
1124 emsg(_(e_bg_color_unknown));
1125 return FALSE;
1126 }
1127 }
1128 else if (STRICMP(arg, "ul") == 0)
1129 {
1130 if (cterm_normal_ul_color > 0)
1131 color = cterm_normal_ul_color - 1;
1132 else
1133 {
1134 emsg(_(e_ul_color_unknown));
1135 return FALSE;
1136 }
1137 }
1138 else
1139 {
1140 int bold = MAYBE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001141
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001142 // reduce calls to STRICMP a bit, it can be slow
1143 off = TOUPPER_ASC(*arg);
1144 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1145 if (off == color_names[i][0]
1146 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1147 break;
1148 if (i < 0)
1149 {
1150 semsg(_(e_color_name_or_number_not_recognized_str), key_start);
1151 return FALSE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001152 }
1153
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001154 color = lookup_color(i, key[5] == 'F', &bold);
1155
1156 // set/reset bold attribute to get light foreground
1157 // colors (on some terminals, e.g. "linux")
1158 if (bold == TRUE)
1159 {
1160 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1161 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1162 }
1163 else if (bold == FALSE)
1164 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001165 }
1166
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001167 // Add one to the argument, to avoid zero. Zero is used for
1168 // "NONE", then "color" is -1.
1169 if (key[5] == 'F')
1170 highlight_set_ctermfg(idx, color, is_normal_group);
1171 else if (key[5] == 'B')
1172 highlight_set_ctermbg(idx, color, is_normal_group);
1173 else // ctermul
1174 highlight_set_ctermul(idx, color, is_normal_group);
1175
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001176 return TRUE;
1177}
1178
1179#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1180/*
1181 * Set the GUI foreground color for the highlight group at 'idx'.
1182 * Returns TRUE if the color is set.
1183 */
1184 static int
1185highlight_set_guifg(
1186 int idx,
1187 char_u *arg,
1188 int is_menu_group UNUSED,
1189 int is_scrollbar_group UNUSED,
1190 int is_tooltip_group UNUSED,
1191 int *do_colors UNUSED,
1192 int init)
1193{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001194# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001195 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001196# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001197 char_u **namep;
1198 int did_change = FALSE;
1199
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001200 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1201 return FALSE;
1202
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001203 namep = &HL_TABLE()[idx].sg_gui_fg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001204 if (!init)
1205 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001206
1207# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001208 // In GUI guifg colors are only used when recognized
1209 i = color_name2handle(arg);
1210 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1211 {
1212 HL_TABLE()[idx].sg_gui_fg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001213# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001214 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1215 {
1216 vim_free(*namep);
1217 if (STRCMP(arg, "NONE") != 0)
1218 *namep = vim_strsave(arg);
1219 else
1220 *namep = NULL;
1221 did_change = TRUE;
1222 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001223# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1224# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001225 if (is_menu_group && gui.menu_fg_pixel != i)
1226 {
1227 gui.menu_fg_pixel = i;
1228 *do_colors = TRUE;
1229 }
1230 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1231 {
1232 gui.scroll_fg_pixel = i;
1233 *do_colors = TRUE;
1234 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001235# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001236 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1237 {
1238 gui.tooltip_fg_pixel = i;
1239 *do_colors = TRUE;
1240 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001241# endif
1242# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001243 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001244# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001245
1246 return did_change;
1247}
1248
1249/*
1250 * Set the GUI background color for the highlight group at 'idx'.
1251 * Returns TRUE if the color is set.
1252 */
1253 static int
1254highlight_set_guibg(
1255 int idx,
1256 char_u *arg,
1257 int is_menu_group UNUSED,
1258 int is_scrollbar_group UNUSED,
1259 int is_tooltip_group UNUSED,
1260 int *do_colors UNUSED,
1261 int init)
1262{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001263# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001264 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001265# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001266 char_u **namep;
1267 int did_change = FALSE;
1268
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001269 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1270 return FALSE;
1271
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001272 namep = &HL_TABLE()[idx].sg_gui_bg_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001273 if (!init)
1274 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001275
1276# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001277 // In GUI guibg colors are only used when recognized
1278 i = color_name2handle(arg);
1279 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1280 {
1281 HL_TABLE()[idx].sg_gui_bg = i;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001282# endif
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001283 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1284 {
1285 vim_free(*namep);
1286 if (STRCMP(arg, "NONE") != 0)
1287 *namep = vim_strsave(arg);
1288 else
1289 *namep = NULL;
1290 did_change = TRUE;
1291 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001292# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1293# ifdef FEAT_GUI_X11
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001294 if (is_menu_group && gui.menu_bg_pixel != i)
1295 {
1296 gui.menu_bg_pixel = i;
1297 *do_colors = TRUE;
1298 }
1299 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1300 {
1301 gui.scroll_bg_pixel = i;
1302 *do_colors = TRUE;
1303 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001304# ifdef FEAT_BEVAL_GUI
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001305 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1306 {
1307 gui.tooltip_bg_pixel = i;
1308 *do_colors = TRUE;
1309 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001310# endif
1311# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001312 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001313# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001314
1315 return did_change;
1316}
1317
1318/*
1319 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1320 * Returns TRUE if the color is set.
1321 */
1322 static int
1323highlight_set_guisp(int idx, char_u *arg, int init)
1324{
1325# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1326 int i;
1327# endif
1328 int did_change = FALSE;
1329 char_u **namep;
1330
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001331 if (init && (HL_TABLE()[idx].sg_set & SG_GUI))
1332 return FALSE;
1333
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001334 namep = &HL_TABLE()[idx].sg_gui_sp_name;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001335 if (!init)
1336 HL_TABLE()[idx].sg_set |= SG_GUI;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001337
1338# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001339 // In GUI guisp colors are only used when recognized
1340 i = color_name2handle(arg);
1341 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1342 {
1343 HL_TABLE()[idx].sg_gui_sp = i;
1344# endif
1345 if (*namep == NULL || STRCMP(*namep, arg) != 0)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001346 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001347 vim_free(*namep);
1348 if (STRCMP(arg, "NONE") != 0)
1349 *namep = vim_strsave(arg);
1350 else
1351 *namep = NULL;
1352 did_change = TRUE;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001353 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001354# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001355 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00001356# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001357
1358 return did_change;
1359}
1360#endif
1361
1362/*
1363 * Set the start/stop terminal codes for a highlight group.
1364 * Returns TRUE if the terminal code is set.
1365 */
1366 static int
1367highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1368{
1369 int off;
1370 char_u buf[100];
1371 int len;
1372 char_u *tname;
1373 char_u *p;
1374
1375 if (!init)
1376 HL_TABLE()[idx].sg_set |= SG_TERM;
1377
1378 // The "start" and "stop" arguments can be a literal escape
1379 // sequence, or a comma separated list of terminal codes.
1380 if (STRNCMP(arg, "t_", 2) == 0)
1381 {
1382 off = 0;
1383 buf[0] = 0;
1384 while (arg[off] != NUL)
1385 {
1386 // Isolate one termcap name
1387 for (len = 0; arg[off + len] &&
1388 arg[off + len] != ','; ++len)
1389 ;
1390 tname = vim_strnsave(arg + off, len);
1391 if (tname == NULL) // out of memory
1392 return FALSE;
1393 // lookup the escape sequence for the item
1394 p = get_term_code(tname);
1395 vim_free(tname);
1396 if (p == NULL) // ignore non-existing things
1397 p = (char_u *)"";
1398
1399 // Append it to the already found stuff
1400 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1401 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001402 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001403 return FALSE;
1404 }
1405 STRCAT(buf, p);
1406
1407 // Advance to the next item
1408 off += len;
1409 if (arg[off] == ',') // another one follows
1410 ++off;
1411 }
1412 }
1413 else
1414 {
1415 // Copy characters from arg[] to buf[], translating <> codes.
1416 for (p = arg, off = 0; off < 100 - 6 && *p; )
1417 {
zeertzjqdb088872022-05-02 22:53:45 +01001418 len = trans_special(&p, buf + off, FSK_SIMPLIFY, FALSE, NULL);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001419 if (len > 0) // recognized special char
1420 off += len;
1421 else // copy as normal char
1422 buf[off++] = *p++;
1423 }
1424 buf[off] = NUL;
1425 }
1426
1427 if (STRCMP(buf, "NONE") == 0) // resetting the value
1428 p = NULL;
1429 else
1430 p = vim_strsave(buf);
1431 if (key[2] == 'A')
1432 {
1433 vim_free(HL_TABLE()[idx].sg_start);
1434 HL_TABLE()[idx].sg_start = p;
1435 }
1436 else
1437 {
1438 vim_free(HL_TABLE()[idx].sg_stop);
1439 HL_TABLE()[idx].sg_stop = p;
1440 }
1441 return TRUE;
1442}
1443
1444/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001445 * Handle the ":highlight .." command.
1446 * When using ":hi clear" this is called recursively for each group with
1447 * "forceit" and "init" both TRUE.
1448 */
1449 void
1450do_highlight(
1451 char_u *line,
1452 int forceit,
1453 int init) // TRUE when called for initializing
1454{
1455 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001456 char_u *linep;
1457 char_u *key_start;
1458 char_u *arg_start;
1459 char_u *key = NULL, *arg = NULL;
1460 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001461 int id;
1462 int idx;
1463 hl_group_T item_before;
1464 int did_change = FALSE;
1465 int dodefault = FALSE;
1466 int doclear = FALSE;
1467 int dolink = FALSE;
1468 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001469 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001470#ifdef FEAT_GUI_X11
1471 int is_menu_group = FALSE; // "Menu" group
1472 int is_scrollbar_group = FALSE; // "Scrollbar" group
1473 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001474#else
1475# define is_menu_group 0
1476# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001477# define is_scrollbar_group 0
1478#endif
1479#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1480 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001481#endif
1482#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1483 int did_highlight_changed = FALSE;
1484#endif
1485
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001486 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001487 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001488 {
1489 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1490 // TODO: only call when the group has attributes set
1491 highlight_list_one((int)i);
1492 return;
1493 }
1494
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001495 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001496 name_end = skiptowhite(line);
1497 linep = skipwhite(name_end);
1498
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001499 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001500 if (STRNCMP(line, "default", name_end - line) == 0)
1501 {
1502 dodefault = TRUE;
1503 line = linep;
1504 name_end = skiptowhite(line);
1505 linep = skipwhite(name_end);
1506 }
1507
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001508 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001509 if (STRNCMP(line, "clear", name_end - line) == 0)
1510 doclear = TRUE;
1511 if (STRNCMP(line, "link", name_end - line) == 0)
1512 dolink = TRUE;
1513
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001514 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001515 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001516 {
1517 id = syn_namen2id(line, (int)(name_end - line));
1518 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001519 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001520 else
1521 highlight_list_one(id);
1522 return;
1523 }
1524
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001525 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001526 if (dolink)
1527 {
1528 char_u *from_start = linep;
1529 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001530 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001531 char_u *to_start;
1532 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001533 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001534
1535 from_end = skiptowhite(from_start);
1536 to_start = skipwhite(from_end);
1537 to_end = skiptowhite(to_start);
1538
Bram Moolenaar1966c242020-04-20 22:42:32 +02001539 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001540 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001541 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001542 return;
1543 }
1544
Bram Moolenaar1966c242020-04-20 22:42:32 +02001545 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001546 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001547 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001548 return;
1549 }
1550
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001551 from_len = (int)(from_end - from_start);
1552 to_len = (int)(to_end - to_start);
1553 highlight_group_link(from_start, from_len, to_start, to_len,
1554 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001555 return;
1556 }
1557
1558 if (doclear)
1559 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001560 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001561 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001562 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001563 // ":highlight clear" without group name
1564 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001565 return;
1566 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001567 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001568 name_end = skiptowhite(line);
1569 linep = skipwhite(name_end);
1570 }
1571
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001572 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001573 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001574 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001575 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001576 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001577
1578 // Return if "default" was used and the group already has settings.
1579 if (dodefault && hl_has_settings(idx, TRUE))
1580 return;
1581
1582 // Make a copy so we can check if any attribute actually changed.
1583 item_before = HL_TABLE()[idx];
1584
1585 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1586 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001587#ifdef FEAT_GUI_X11
1588 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1589 is_menu_group = TRUE;
1590 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1591 is_scrollbar_group = TRUE;
1592 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1593 is_tooltip_group = TRUE;
1594#endif
1595
1596 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1597 if (doclear || (forceit && init))
1598 {
1599 highlight_clear(idx);
1600 if (!doclear)
1601 HL_TABLE()[idx].sg_set = 0;
1602 }
1603
1604 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001605 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001606 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001607 key_start = linep;
1608 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001609 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001610 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001611 error = TRUE;
1612 break;
1613 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001614
1615 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1616 // or "guibg").
1617 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1618 ++linep;
1619 vim_free(key);
1620 key = vim_strnsave_up(key_start, linep - key_start);
1621 if (key == NULL)
1622 {
1623 error = TRUE;
1624 break;
1625 }
1626 linep = skipwhite(linep);
1627
1628 if (STRCMP(key, "NONE") == 0)
1629 {
1630 if (!init || HL_TABLE()[idx].sg_set == 0)
1631 {
1632 if (!init)
1633 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1634 highlight_clear(idx);
1635 }
1636 continue;
1637 }
1638
1639 // Check for the equal sign.
1640 if (*linep != '=')
1641 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001642 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001643 error = TRUE;
1644 break;
1645 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001646 ++linep;
1647
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001648 // Isolate the argument.
1649 linep = skipwhite(linep);
1650 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001651 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001652 arg_start = ++linep;
1653 linep = vim_strchr(linep, '\'');
1654 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001655 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001656 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001657 error = TRUE;
1658 break;
1659 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001660 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001661 else
1662 {
1663 arg_start = linep;
1664 linep = skiptowhite(linep);
1665 }
1666 if (linep == arg_start)
1667 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001668 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001669 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001670 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001671 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001672 vim_free(arg);
1673 arg = vim_strnsave(arg_start, linep - arg_start);
1674 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001675 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001676 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001677 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001678 }
1679 if (*linep == '\'')
1680 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001681
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001682 // Store the argument.
1683 if (STRCMP(key, "TERM") == 0
1684 || STRCMP(key, "CTERM") == 0
1685 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001686 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001687 if (!highlight_set_termgui_attr(idx, key, arg, init))
1688 {
1689 error = TRUE;
1690 break;
1691 }
1692 }
1693 else if (STRCMP(key, "FONT") == 0)
1694 {
1695 // in non-GUI fonts are simply ignored
1696#ifdef FEAT_GUI
1697 if (highlight_set_font(idx, arg, is_normal_group,
1698 is_menu_group, is_tooltip_group))
1699 did_change = TRUE;
1700#endif
1701 }
1702 else if (STRCMP(key, "CTERMFG") == 0
1703 || STRCMP(key, "CTERMBG") == 0
1704 || STRCMP(key, "CTERMUL") == 0)
1705 {
1706 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1707 is_normal_group, init))
1708 {
1709 error = TRUE;
1710 break;
1711 }
1712 }
PMuncha606f3a2023-11-15 15:35:49 +01001713 else if (STRCMP(key, "CTERMFONT") == 0)
1714 {
1715 if (!highlight_set_cterm_font(idx, arg, init))
1716 {
1717 error = TRUE;
1718 break;
1719 }
1720 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001721 else if (STRCMP(key, "GUIFG") == 0)
1722 {
1723#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1724 if (highlight_set_guifg(idx, arg, is_menu_group,
1725 is_scrollbar_group, is_tooltip_group,
1726 &do_colors, init))
1727 did_change = TRUE;
1728#endif
1729 }
1730 else if (STRCMP(key, "GUIBG") == 0)
1731 {
1732#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1733 if (highlight_set_guibg(idx, arg, is_menu_group,
1734 is_scrollbar_group, is_tooltip_group,
1735 &do_colors, init))
1736 did_change = TRUE;
1737#endif
1738 }
1739 else if (STRCMP(key, "GUISP") == 0)
1740 {
1741#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1742 if (highlight_set_guisp(idx, arg, init))
1743 did_change = TRUE;
1744#endif
1745 }
1746 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1747 {
1748 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1749 {
1750 error = TRUE;
1751 break;
1752 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001753 }
1754 else
1755 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001756 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001757 error = TRUE;
1758 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001759 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001760 HL_TABLE()[idx].sg_cleared = FALSE;
1761
1762 // When highlighting has been given for a group, don't link it.
1763 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1764 HL_TABLE()[idx].sg_link = 0;
1765
1766 // Continue with next argument.
1767 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001768 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001769
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001770 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001771 if (error && idx == highlight_ga.ga_len)
1772 syn_unadd_group();
1773 else
1774 {
1775 if (is_normal_group)
1776 {
1777 HL_TABLE()[idx].sg_term_attr = 0;
1778 HL_TABLE()[idx].sg_cterm_attr = 0;
1779#ifdef FEAT_GUI
1780 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001781 // Need to update all groups, because they might be using "bg"
1782 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001783#endif
1784#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1785 if (USE_24BIT)
1786 {
1787 highlight_gui_started();
1788 did_highlight_changed = TRUE;
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001789 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001790 }
1791#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001792#ifdef FEAT_VTP
1793 control_console_color_rgb();
1794#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001795 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001796#ifdef FEAT_GUI_X11
1797# ifdef FEAT_MENU
1798 else if (is_menu_group)
1799 {
1800 if (gui.in_use && do_colors)
1801 gui_mch_new_menu_colors();
1802 }
1803# endif
1804 else if (is_scrollbar_group)
1805 {
1806 if (gui.in_use && do_colors)
1807 gui_new_scrollbar_colors();
1808 else
1809 set_hl_attr(idx);
1810 }
1811# ifdef FEAT_BEVAL_GUI
1812 else if (is_tooltip_group)
1813 {
1814 if (gui.in_use && do_colors)
1815 gui_mch_new_tooltip_colors();
1816 }
1817# endif
1818#endif
1819 else
1820 set_hl_attr(idx);
1821#ifdef FEAT_EVAL
1822 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001823 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001824#endif
1825 }
1826
1827 vim_free(key);
1828 vim_free(arg);
1829
1830 // Only call highlight_changed() once, after a sequence of highlight
1831 // commands, and only if an attribute actually changed.
1832 if ((did_change
1833 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1834#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1835 && !did_highlight_changed
1836#endif
1837 )
1838 {
1839 // Do not trigger a redraw when highlighting is changed while
1840 // redrawing. This may happen when evaluating 'statusline' changes the
1841 // StatusLine group.
1842 if (!updating_screen)
Bram Moolenaara4d158b2022-08-14 14:17:45 +01001843 redraw_all_later(UPD_NOT_VALID);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001844 need_highlight_changed = TRUE;
1845 }
1846}
1847
1848#if defined(EXITFREE) || defined(PROTO)
1849 void
1850free_highlight(void)
1851{
1852 int i;
1853
1854 for (i = 0; i < highlight_ga.ga_len; ++i)
1855 {
1856 highlight_clear(i);
1857 vim_free(HL_TABLE()[i].sg_name);
1858 vim_free(HL_TABLE()[i].sg_name_u);
1859 }
1860 ga_clear(&highlight_ga);
1861}
1862#endif
1863
1864/*
1865 * Reset the cterm colors to what they were before Vim was started, if
1866 * possible. Otherwise reset them to zero.
1867 */
1868 void
1869restore_cterm_colors(void)
1870{
1871#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1872 // Since t_me has been set, this probably means that the user
1873 // wants to use this as default colors. Need to reset default
1874 // background/foreground colors.
1875 mch_set_normal_colors();
1876#else
1877# ifdef VIMDLL
1878 if (!gui.in_use)
1879 {
1880 mch_set_normal_colors();
1881 return;
1882 }
1883# endif
1884 cterm_normal_fg_color = 0;
1885 cterm_normal_fg_bold = 0;
1886 cterm_normal_bg_color = 0;
1887# ifdef FEAT_TERMGUICOLORS
1888 cterm_normal_fg_gui_color = INVALCOLOR;
1889 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001890 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001891# endif
1892#endif
1893}
1894
1895/*
1896 * Return TRUE if highlight group "idx" has any settings.
1897 * When "check_link" is TRUE also check for an existing link.
1898 */
1899 static int
1900hl_has_settings(int idx, int check_link)
1901{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001902 return HL_TABLE()[idx].sg_cleared == 0
1903 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001904 || HL_TABLE()[idx].sg_cterm_attr != 0
1905 || HL_TABLE()[idx].sg_cterm_fg != 0
1906 || HL_TABLE()[idx].sg_cterm_bg != 0
PMuncha606f3a2023-11-15 15:35:49 +01001907 || HL_TABLE()[idx].sg_cterm_font != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001908#ifdef FEAT_GUI
1909 || HL_TABLE()[idx].sg_gui_attr != 0
1910 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1911 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1912 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1913 || HL_TABLE()[idx].sg_font_name != NULL
1914#endif
1915 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1916}
1917
1918/*
1919 * Clear highlighting for one group.
1920 */
1921 static void
1922highlight_clear(int idx)
1923{
1924 HL_TABLE()[idx].sg_cleared = TRUE;
1925
1926 HL_TABLE()[idx].sg_term = 0;
1927 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1928 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1929 HL_TABLE()[idx].sg_term_attr = 0;
1930 HL_TABLE()[idx].sg_cterm = 0;
1931 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1932 HL_TABLE()[idx].sg_cterm_fg = 0;
1933 HL_TABLE()[idx].sg_cterm_bg = 0;
1934 HL_TABLE()[idx].sg_cterm_attr = 0;
PMuncha606f3a2023-11-15 15:35:49 +01001935 HL_TABLE()[idx].sg_cterm_font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001936#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1937 HL_TABLE()[idx].sg_gui = 0;
1938 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1939 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1940 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1941#endif
1942#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1943 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1944 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001945 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001946#endif
1947#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001948 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1949 HL_TABLE()[idx].sg_font = NOFONT;
1950# ifdef FEAT_XFONTSET
1951 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1952 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1953# endif
1954 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1955 HL_TABLE()[idx].sg_gui_attr = 0;
1956#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001957 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001958 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001959#ifdef FEAT_EVAL
1960 // Since we set the default link, set the location to where the default
1961 // link was set.
1962 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001963#endif
1964}
1965
1966#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1967/*
1968 * Set the normal foreground and background colors according to the "Normal"
1969 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1970 * "Tooltip" colors.
1971 */
1972 void
1973set_normal_colors(void)
1974{
1975# ifdef FEAT_GUI
1976# ifdef FEAT_TERMGUICOLORS
1977 if (gui.in_use)
1978# endif
1979 {
1980 if (set_group_colors((char_u *)"Normal",
1981 &gui.norm_pixel, &gui.back_pixel,
1982 FALSE, TRUE, FALSE))
1983 {
1984 gui_mch_new_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001985 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001986 }
1987# ifdef FEAT_GUI_X11
1988 if (set_group_colors((char_u *)"Menu",
1989 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1990 TRUE, FALSE, FALSE))
1991 {
1992# ifdef FEAT_MENU
1993 gui_mch_new_menu_colors();
1994# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01001995 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001996 }
1997# ifdef FEAT_BEVAL_GUI
1998 if (set_group_colors((char_u *)"Tooltip",
1999 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
2000 FALSE, FALSE, TRUE))
2001 {
2002# ifdef FEAT_TOOLBAR
2003 gui_mch_new_tooltip_colors();
2004# endif
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002005 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002006 }
2007# endif
2008 if (set_group_colors((char_u *)"Scrollbar",
2009 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
2010 FALSE, FALSE, FALSE))
2011 {
2012 gui_new_scrollbar_colors();
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002013 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002014 }
2015# endif
2016 }
2017# endif
2018# ifdef FEAT_TERMGUICOLORS
2019# ifdef FEAT_GUI
2020 else
2021# endif
2022 {
2023 int idx;
2024
2025 idx = syn_name2id((char_u *)"Normal") - 1;
2026 if (idx >= 0)
2027 {
2028 gui_do_one_color(idx, FALSE, FALSE);
2029
2030 // If the normal fg or bg color changed a complete redraw is
2031 // required.
2032 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
2033 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
2034 {
2035 // if the GUI color is INVALCOLOR then we use the default cterm
2036 // color
2037 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
2038 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002039 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002040 }
2041 }
2042 }
2043# endif
2044}
2045#endif
2046
2047#if defined(FEAT_GUI) || defined(PROTO)
2048/*
2049 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
2050 */
2051 static int
2052set_group_colors(
2053 char_u *name,
2054 guicolor_T *fgp,
2055 guicolor_T *bgp,
2056 int do_menu,
2057 int use_norm,
2058 int do_tooltip)
2059{
2060 int idx;
2061
2062 idx = syn_name2id(name) - 1;
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002063 if (idx < 0)
2064 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002065
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002066 gui_do_one_color(idx, do_menu, do_tooltip);
2067
2068 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
2069 *fgp = HL_TABLE()[idx].sg_gui_fg;
2070 else if (use_norm)
2071 *fgp = gui.def_norm_pixel;
2072 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
2073 *bgp = HL_TABLE()[idx].sg_gui_bg;
2074 else if (use_norm)
2075 *bgp = gui.def_back_pixel;
2076 return TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002077}
2078
2079/*
2080 * Get the font of the "Normal" group.
2081 * Returns "" when it's not found or not set.
2082 */
2083 char_u *
2084hl_get_font_name(void)
2085{
2086 int id;
2087 char_u *s;
2088
2089 id = syn_name2id((char_u *)"Normal");
2090 if (id > 0)
2091 {
2092 s = HL_TABLE()[id - 1].sg_font_name;
2093 if (s != NULL)
2094 return s;
2095 }
2096 return (char_u *)"";
2097}
2098
2099/*
2100 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2101 * actually chosen to be used.
2102 */
2103 void
2104hl_set_font_name(char_u *font_name)
2105{
2106 int id;
2107
2108 id = syn_name2id((char_u *)"Normal");
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002109 if (id <= 0)
2110 return;
2111
2112 vim_free(HL_TABLE()[id - 1].sg_font_name);
2113 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002114}
2115
2116/*
2117 * Set background color for "Normal" group. Called by gui_set_bg_color()
2118 * when the color is known.
2119 */
2120 void
2121hl_set_bg_color_name(
2122 char_u *name) // must have been allocated
2123{
2124 int id;
2125
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002126 if (name == NULL)
2127 return;
2128
2129 id = syn_name2id((char_u *)"Normal");
2130 if (id <= 0)
2131 return;
2132
2133 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2134 HL_TABLE()[id - 1].sg_gui_bg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002135}
2136
2137/*
2138 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2139 * when the color is known.
2140 */
2141 void
2142hl_set_fg_color_name(
2143 char_u *name) // must have been allocated
2144{
2145 int id;
2146
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00002147 if (name == NULL)
2148 return;
2149
2150 id = syn_name2id((char_u *)"Normal");
2151 if (id <= 0)
2152 return;
2153
2154 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2155 HL_TABLE()[id - 1].sg_gui_fg_name = name;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002156}
2157
2158/*
2159 * Return the handle for a font name.
2160 * Returns NOFONT when failed.
2161 */
2162 static GuiFont
2163font_name2handle(char_u *name)
2164{
2165 if (STRCMP(name, "NONE") == 0)
2166 return NOFONT;
2167
2168 return gui_mch_get_font(name, TRUE);
2169}
2170
2171# ifdef FEAT_XFONTSET
2172/*
2173 * Return the handle for a fontset name.
2174 * Returns NOFONTSET when failed.
2175 */
2176 static GuiFontset
2177fontset_name2handle(char_u *name, int fixed_width)
2178{
2179 if (STRCMP(name, "NONE") == 0)
2180 return NOFONTSET;
2181
2182 return gui_mch_get_fontset(name, TRUE, fixed_width);
2183}
2184# endif
2185
2186/*
2187 * Get the font or fontset for one highlight group.
2188 */
2189 static void
2190hl_do_font(
2191 int idx,
2192 char_u *arg,
2193 int do_normal, // set normal font
2194 int do_menu UNUSED, // set menu font
2195 int do_tooltip UNUSED, // set tooltip font
2196 int free_font) // free current font/fontset
2197{
2198# ifdef FEAT_XFONTSET
2199 // If 'guifontset' is not empty, first try using the name as a
2200 // fontset. If that doesn't work, use it as a font name.
2201 if (*p_guifontset != NUL
2202# ifdef FONTSET_ALWAYS
2203 || do_menu
2204# endif
2205# ifdef FEAT_BEVAL_TIP
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002206 // In Motif, the Tooltip highlight group is always a fontset
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002207 || do_tooltip
2208# endif
2209 )
2210 {
2211 if (free_font)
2212 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2213 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2214# ifdef FONTSET_ALWAYS
2215 || do_menu
2216# endif
2217# ifdef FEAT_BEVAL_TIP
2218 || do_tooltip
2219# endif
2220 );
2221 }
2222 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2223 {
2224 // If it worked and it's the Normal group, use it as the normal
2225 // fontset. Same for the Menu group.
2226 if (do_normal)
2227 gui_init_font(arg, TRUE);
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002228# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002229 if (do_menu)
2230 {
2231# ifdef FONTSET_ALWAYS
2232 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2233# else
2234 // YIKES! This is a bug waiting to crash the program
2235 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2236# endif
2237 gui_mch_new_menu_font();
2238 }
2239# ifdef FEAT_BEVAL_GUI
2240 if (do_tooltip)
2241 {
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002242 // The Athena widget set could not handle switching between
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002243 // displaying a single font and a fontset.
2244 // If the XtNinternational resource is set to True at widget
2245 // creation, then a fontset is always used, otherwise an
2246 // XFontStruct is used.
2247 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2248 gui_mch_new_tooltip_font();
2249 }
2250# endif
2251# endif
2252 }
2253 else
2254# endif
2255 {
2256 if (free_font)
2257 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2258 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2259 // If it worked and it's the Normal group, use it as the
2260 // normal font. Same for the Menu group.
2261 if (HL_TABLE()[idx].sg_font != NOFONT)
2262 {
2263 if (do_normal)
2264 gui_init_font(arg, FALSE);
2265#ifndef FONTSET_ALWAYS
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01002266# if defined(FEAT_GUI_MOTIF) && defined(FEAT_MENU)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002267 if (do_menu)
2268 {
2269 gui.menu_font = HL_TABLE()[idx].sg_font;
2270 gui_mch_new_menu_font();
2271 }
2272# endif
2273#endif
2274 }
2275 }
2276}
2277
2278#endif // FEAT_GUI
2279
2280#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2281/*
2282 * Return the handle for a color name.
2283 * Returns INVALCOLOR when failed.
2284 */
2285 guicolor_T
2286color_name2handle(char_u *name)
2287{
2288 if (STRCMP(name, "NONE") == 0)
2289 return INVALCOLOR;
2290
2291 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2292 {
2293#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2294 if (gui.in_use)
2295#endif
2296#ifdef FEAT_GUI
2297 return gui.norm_pixel;
2298#endif
2299#ifdef FEAT_TERMGUICOLORS
2300 if (cterm_normal_fg_gui_color != INVALCOLOR)
2301 return cterm_normal_fg_gui_color;
2302 // Guess that the foreground is black or white.
2303 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2304#endif
2305 }
2306 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2307 {
2308#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2309 if (gui.in_use)
2310#endif
2311#ifdef FEAT_GUI
2312 return gui.back_pixel;
2313#endif
2314#ifdef FEAT_TERMGUICOLORS
2315 if (cterm_normal_bg_gui_color != INVALCOLOR)
2316 return cterm_normal_bg_gui_color;
2317 // Guess that the background is white or black.
2318 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2319#endif
2320 }
2321
2322 return GUI_GET_COLOR(name);
2323}
Drew Vogele30d1022021-10-24 20:35:07 +01002324
2325// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2326// values as used by the MS-Windows GDI api. It should be used only for
2327// MS-Windows GDI builds.
2328# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2329# undef RGB
2330# endif
2331# ifndef RGB
kylo252ae6f1d82022-02-16 19:24:07 +00002332# define RGB(r, g, b) (((r)<<16) | ((g)<<8) | (b))
Drew Vogele30d1022021-10-24 20:35:07 +01002333# endif
2334
2335# ifdef VIMDLL
2336 static guicolor_T
2337gui_adjust_rgb(guicolor_T c)
2338{
2339 if (gui.in_use)
2340 return c;
2341 else
2342 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2343}
2344# else
2345# define gui_adjust_rgb(c) (c)
2346# endif
2347
2348 static int
2349hex_digit(int c)
2350{
Keith Thompson184f71c2024-01-04 21:19:04 +01002351 if (SAFE_isdigit(c))
Drew Vogele30d1022021-10-24 20:35:07 +01002352 return c - '0';
2353 c = TOLOWER_ASC(c);
2354 if (c >= 'a' && c <= 'f')
2355 return c - 'a' + 10;
2356 return 0x1ffffff;
2357}
2358
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002359 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002360decode_hex_color(char_u *hex)
2361{
2362 guicolor_T color;
2363
2364 if (hex[0] != '#' || STRLEN(hex) != 7)
2365 return INVALCOLOR;
2366
2367 // Name is in "#rrggbb" format
2368 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2369 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2370 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2371 if (color > 0xffffff)
2372 return INVALCOLOR;
2373 return gui_adjust_rgb(color);
2374}
2375
Bram Moolenaar2a521962021-10-25 10:30:14 +01002376#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002377// Returns the color currently mapped to the given name or INVALCOLOR if no
2378// such name exists in the color table. The convention is to use lowercase for
2379// all keys in the v:colornames dictionary. The value can be either a string in
2380// the form #rrggbb or a number, either of which is converted to a guicolor_T.
Yegappan Lakshmanan782b43d2022-01-08 18:43:40 +00002381 static guicolor_T
Drew Vogele30d1022021-10-24 20:35:07 +01002382colorname2rgb(char_u *name)
2383{
2384 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2385 char_u *lc_name;
2386 dictitem_T *colentry;
2387 char_u *colstr;
2388 varnumber_T colnum;
2389
2390 lc_name = strlow_save(name);
2391 if (lc_name == NULL)
2392 return INVALCOLOR;
2393
2394 colentry = dict_find(colornames_table, lc_name, -1);
2395 vim_free(lc_name);
2396 if (colentry == NULL)
2397 return INVALCOLOR;
2398
2399 if (colentry->di_tv.v_type == VAR_STRING)
2400 {
2401 colstr = tv_get_string_strict(&colentry->di_tv);
2402 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2403 {
2404 return decode_hex_color(colstr);
2405 }
2406 else
2407 {
2408 semsg(_(e_bad_color_string_str), colstr);
2409 return INVALCOLOR;
2410 }
2411 }
2412
2413 if (colentry->di_tv.v_type == VAR_NUMBER)
2414 {
2415 colnum = tv_get_number(&colentry->di_tv);
2416 return (guicolor_T)colnum;
2417 }
2418
2419 return INVALCOLOR;
2420}
2421
Drew Vogele30d1022021-10-24 20:35:07 +01002422#endif
2423
2424 guicolor_T
2425gui_get_color_cmn(char_u *name)
2426{
Bram Moolenaar6ed545e2022-05-09 20:09:23 +01002427 int i;
Drew Vogele30d1022021-10-24 20:35:07 +01002428 guicolor_T color;
2429
2430 struct rgbcolor_table_S {
2431 char_u *color_name;
2432 guicolor_T color;
2433 };
2434
2435 // Only non X11 colors (not present in rgb.txt) and colors in
2436 // color_names[], useful when $VIMRUNTIME is not found,.
2437 static struct rgbcolor_table_S rgb_table[] = {
2438 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2439 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2440 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2441 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2442 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2443 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2444 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2445 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2446 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2447 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2448 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2449 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2450 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2451 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2452 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2453 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2454 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2455 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2456 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2457 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2458 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2459 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2460 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2461 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2462 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2463 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2464 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2465 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2466 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2467 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2468 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2469 };
2470
2471 color = decode_hex_color(name);
2472 if (color != INVALCOLOR)
2473 return color;
2474
2475 // Check if the name is one of the colors we know
2476 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2477 if (STRICMP(name, rgb_table[i].color_name) == 0)
2478 return gui_adjust_rgb(rgb_table[i].color);
2479
2480#if defined(FEAT_EVAL)
2481 /*
2482 * Not a traditional color. Load additional color aliases and then consult the alias table.
2483 */
2484
2485 color = colorname2rgb(name);
2486 if (color == INVALCOLOR)
2487 {
2488 load_default_colors_lists();
2489 color = colorname2rgb(name);
2490 }
2491
2492 return color;
2493#else
2494 return INVALCOLOR;
2495#endif
2496}
2497
2498 guicolor_T
2499gui_get_rgb_color_cmn(int r, int g, int b)
2500{
2501 guicolor_T color = RGB(r, g, b);
2502
2503 if (color > 0xffffff)
2504 return INVALCOLOR;
2505 return gui_adjust_rgb(color);
2506}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002507#endif
2508
2509/*
2510 * Table with the specifications for an attribute number.
2511 * Note that this table is used by ALL buffers. This is required because the
2512 * GUI can redraw at any time for any buffer.
2513 */
2514static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2515
2516#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2517
2518static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2519
2520#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2521
2522#ifdef FEAT_GUI
2523static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2524
2525#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2526#endif
2527
2528/*
2529 * Return the attr number for a set of colors and font.
2530 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2531 * if the combination is new.
2532 * Return 0 for error (no more room).
2533 */
2534 static int
2535get_attr_entry(garray_T *table, attrentry_T *aep)
2536{
2537 int i;
2538 attrentry_T *taep;
2539 static int recursive = FALSE;
2540
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002541 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002542 table->ga_itemsize = sizeof(attrentry_T);
2543 table->ga_growsize = 7;
2544
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002545 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002546 for (i = 0; i < table->ga_len; ++i)
2547 {
2548 taep = &(((attrentry_T *)table->ga_data)[i]);
2549 if ( aep->ae_attr == taep->ae_attr
2550 && (
2551#ifdef FEAT_GUI
2552 (table == &gui_attr_table
2553 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2554 && aep->ae_u.gui.bg_color
2555 == taep->ae_u.gui.bg_color
2556 && aep->ae_u.gui.sp_color
2557 == taep->ae_u.gui.sp_color
2558 && aep->ae_u.gui.font == taep->ae_u.gui.font
2559# ifdef FEAT_XFONTSET
2560 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2561# endif
2562 ))
2563 ||
2564#endif
2565 (table == &term_attr_table
2566 && (aep->ae_u.term.start == NULL)
2567 == (taep->ae_u.term.start == NULL)
2568 && (aep->ae_u.term.start == NULL
2569 || STRCMP(aep->ae_u.term.start,
2570 taep->ae_u.term.start) == 0)
2571 && (aep->ae_u.term.stop == NULL)
2572 == (taep->ae_u.term.stop == NULL)
2573 && (aep->ae_u.term.stop == NULL
2574 || STRCMP(aep->ae_u.term.stop,
2575 taep->ae_u.term.stop) == 0))
2576 || (table == &cterm_attr_table
2577 && aep->ae_u.cterm.fg_color
2578 == taep->ae_u.cterm.fg_color
2579 && aep->ae_u.cterm.bg_color
2580 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002581 && aep->ae_u.cterm.ul_color
2582 == taep->ae_u.cterm.ul_color
PMuncha606f3a2023-11-15 15:35:49 +01002583 && aep->ae_u.cterm.font
2584 == taep->ae_u.cterm.font
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002585#ifdef FEAT_TERMGUICOLORS
2586 && aep->ae_u.cterm.fg_rgb
2587 == taep->ae_u.cterm.fg_rgb
2588 && aep->ae_u.cterm.bg_rgb
2589 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002590 && aep->ae_u.cterm.ul_rgb
2591 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002592#endif
2593 )))
2594
2595 return i + ATTR_OFF;
2596 }
2597
2598 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2599 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002600 // Running out of attribute entries! remove all attributes, and
2601 // compute new ones for all groups.
2602 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002603 if (recursive)
2604 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002605 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002606 return 0;
2607 }
2608 recursive = TRUE;
2609
2610 clear_hl_tables();
2611
Bram Moolenaar471c0fa2022-08-22 15:19:16 +01002612 set_must_redraw(UPD_CLEAR);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002613
2614 for (i = 0; i < highlight_ga.ga_len; ++i)
2615 set_hl_attr(i);
2616
2617 recursive = FALSE;
2618 }
2619
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002620 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002621 if (ga_grow(table, 1) == FAIL)
2622 return 0;
2623
2624 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002625 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002626 taep->ae_attr = aep->ae_attr;
2627#ifdef FEAT_GUI
2628 if (table == &gui_attr_table)
2629 {
2630 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2631 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2632 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2633 taep->ae_u.gui.font = aep->ae_u.gui.font;
2634# ifdef FEAT_XFONTSET
2635 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2636# endif
2637 }
2638#endif
2639 if (table == &term_attr_table)
2640 {
2641 if (aep->ae_u.term.start == NULL)
2642 taep->ae_u.term.start = NULL;
2643 else
2644 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2645 if (aep->ae_u.term.stop == NULL)
2646 taep->ae_u.term.stop = NULL;
2647 else
2648 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2649 }
2650 else if (table == &cterm_attr_table)
2651 {
2652 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2653 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002654 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002655 taep->ae_u.cterm.font = aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002656#ifdef FEAT_TERMGUICOLORS
2657 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2658 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002659 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002660#endif
2661 }
2662 ++table->ga_len;
2663 return (table->ga_len - 1 + ATTR_OFF);
2664}
2665
2666#if defined(FEAT_TERMINAL) || defined(PROTO)
2667/*
2668 * Get an attribute index for a cterm entry.
2669 * Uses an existing entry when possible or adds one when needed.
2670 */
2671 int
2672get_cterm_attr_idx(int attr, int fg, int bg)
2673{
2674 attrentry_T at_en;
2675
Bram Moolenaara80faa82020-04-12 19:37:17 +02002676 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002677#ifdef FEAT_TERMGUICOLORS
2678 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2679 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002680 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002681#endif
2682 at_en.ae_attr = attr;
2683 at_en.ae_u.cterm.fg_color = fg;
2684 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002685 at_en.ae_u.cterm.ul_color = 0;
PMuncha606f3a2023-11-15 15:35:49 +01002686 at_en.ae_u.cterm.font = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002687 return get_attr_entry(&cterm_attr_table, &at_en);
2688}
2689#endif
2690
2691#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2692/*
2693 * Get an attribute index for a 'termguicolors' entry.
2694 * Uses an existing entry when possible or adds one when needed.
2695 */
2696 int
2697get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2698{
2699 attrentry_T at_en;
2700
Bram Moolenaara80faa82020-04-12 19:37:17 +02002701 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002702 at_en.ae_attr = attr;
2703 if (fg == INVALCOLOR && bg == INVALCOLOR)
2704 {
2705 // If both GUI colors are not set fall back to the cterm colors. Helps
2706 // if the GUI only has an attribute, such as undercurl.
2707 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2708 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2709 }
2710 else
2711 {
2712 at_en.ae_u.cterm.fg_rgb = fg;
2713 at_en.ae_u.cterm.bg_rgb = bg;
2714 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002715 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002716 return get_attr_entry(&cterm_attr_table, &at_en);
2717}
2718#endif
2719
2720#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2721/*
2722 * Get an attribute index for a cterm entry.
2723 * Uses an existing entry when possible or adds one when needed.
2724 */
2725 int
2726get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2727{
2728 attrentry_T at_en;
2729
Bram Moolenaara80faa82020-04-12 19:37:17 +02002730 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002731 at_en.ae_attr = attr;
2732 at_en.ae_u.gui.fg_color = fg;
2733 at_en.ae_u.gui.bg_color = bg;
2734 return get_attr_entry(&gui_attr_table, &at_en);
2735}
2736#endif
2737
2738/*
2739 * Clear all highlight tables.
2740 */
2741 void
2742clear_hl_tables(void)
2743{
2744 int i;
2745 attrentry_T *taep;
2746
2747#ifdef FEAT_GUI
2748 ga_clear(&gui_attr_table);
2749#endif
2750 for (i = 0; i < term_attr_table.ga_len; ++i)
2751 {
2752 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2753 vim_free(taep->ae_u.term.start);
2754 vim_free(taep->ae_u.term.stop);
2755 }
2756 ga_clear(&term_attr_table);
2757 ga_clear(&cterm_attr_table);
2758}
2759
2760/*
2761 * Combine special attributes (e.g., for spelling) with other attributes
2762 * (e.g., for syntax highlighting).
2763 * "prim_attr" overrules "char_attr".
2764 * This creates a new group when required.
2765 * Since we expect there to be few spelling mistakes we don't cache the
2766 * result.
2767 * Return the resulting attributes.
2768 */
2769 int
2770hl_combine_attr(int char_attr, int prim_attr)
2771{
2772 attrentry_T *char_aep = NULL;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002773 attrentry_T *prim_aep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002774 attrentry_T new_en;
2775
2776 if (char_attr == 0)
2777 return prim_attr;
2778 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2779 return ATTR_COMBINE(char_attr, prim_attr);
2780#ifdef FEAT_GUI
2781 if (gui.in_use)
2782 {
2783 if (char_attr > HL_ALL)
2784 char_aep = syn_gui_attr2entry(char_attr);
2785 if (char_aep != NULL)
2786 new_en = *char_aep;
2787 else
2788 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002789 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002790 new_en.ae_u.gui.fg_color = INVALCOLOR;
2791 new_en.ae_u.gui.bg_color = INVALCOLOR;
2792 new_en.ae_u.gui.sp_color = INVALCOLOR;
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_gui_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.gui.fg_color != INVALCOLOR)
2807 new_en.ae_u.gui.fg_color = prim_aep->ae_u.gui.fg_color;
2808 if (prim_aep->ae_u.gui.bg_color != INVALCOLOR)
2809 new_en.ae_u.gui.bg_color = prim_aep->ae_u.gui.bg_color;
2810 if (prim_aep->ae_u.gui.sp_color != INVALCOLOR)
2811 new_en.ae_u.gui.sp_color = prim_aep->ae_u.gui.sp_color;
2812 if (prim_aep->ae_u.gui.font != NOFONT)
2813 new_en.ae_u.gui.font = prim_aep->ae_u.gui.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002814# ifdef FEAT_XFONTSET
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002815 if (prim_aep->ae_u.gui.fontset != NOFONTSET)
2816 new_en.ae_u.gui.fontset = prim_aep->ae_u.gui.fontset;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002817# endif
2818 }
2819 }
2820 return get_attr_entry(&gui_attr_table, &new_en);
2821 }
2822#endif
2823
2824 if (IS_CTERM)
2825 {
2826 if (char_attr > HL_ALL)
2827 char_aep = syn_cterm_attr2entry(char_attr);
2828 if (char_aep != NULL)
2829 new_en = *char_aep;
2830 else
2831 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002832 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002833#ifdef FEAT_TERMGUICOLORS
2834 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2835 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002836 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002837#endif
2838 if (char_attr <= HL_ALL)
2839 new_en.ae_attr = char_attr;
2840 }
2841
2842 if (prim_attr <= HL_ALL)
2843 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2844 else
2845 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002846 prim_aep = syn_cterm_attr2entry(prim_attr);
2847 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002848 {
2849 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002850 prim_aep->ae_attr);
2851 if (prim_aep->ae_u.cterm.fg_color > 0)
2852 new_en.ae_u.cterm.fg_color = prim_aep->ae_u.cterm.fg_color;
2853 if (prim_aep->ae_u.cterm.bg_color > 0)
2854 new_en.ae_u.cterm.bg_color = prim_aep->ae_u.cterm.bg_color;
2855 if (prim_aep->ae_u.cterm.ul_color > 0)
2856 new_en.ae_u.cterm.ul_color = prim_aep->ae_u.cterm.ul_color;
PMuncha606f3a2023-11-15 15:35:49 +01002857 if (prim_aep->ae_u.cterm.font > 0)
2858 new_en.ae_u.cterm.font = prim_aep->ae_u.cterm.font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002859#ifdef FEAT_TERMGUICOLORS
2860 // If both fg and bg are not set fall back to cterm colors.
2861 // Helps for SpellBad which uses undercurl in the GUI.
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002862 if (COLOR_INVALID(prim_aep->ae_u.cterm.fg_rgb)
2863 && COLOR_INVALID(prim_aep->ae_u.cterm.bg_rgb))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002864 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002865 if (prim_aep->ae_u.cterm.fg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002866 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002867 if (prim_aep->ae_u.cterm.bg_color > 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002868 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2869 }
2870 else
2871 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002872 if (prim_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2873 new_en.ae_u.cterm.fg_rgb = prim_aep->ae_u.cterm.fg_rgb;
2874 if (prim_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2875 new_en.ae_u.cterm.bg_rgb = prim_aep->ae_u.cterm.bg_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002876 }
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002877 if (prim_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2878 new_en.ae_u.cterm.ul_rgb = prim_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002879#endif
2880 }
2881 }
2882 return get_attr_entry(&cterm_attr_table, &new_en);
2883 }
2884
2885 if (char_attr > HL_ALL)
2886 char_aep = syn_term_attr2entry(char_attr);
2887 if (char_aep != NULL)
2888 new_en = *char_aep;
2889 else
2890 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002891 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002892 if (char_attr <= HL_ALL)
2893 new_en.ae_attr = char_attr;
2894 }
2895
2896 if (prim_attr <= HL_ALL)
2897 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2898 else
2899 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002900 prim_aep = syn_term_attr2entry(prim_attr);
2901 if (prim_aep != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002902 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002903 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_aep->ae_attr);
2904 if (prim_aep->ae_u.term.start != NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002905 {
Bram Moolenaarc9b65702022-08-13 21:37:29 +01002906 new_en.ae_u.term.start = prim_aep->ae_u.term.start;
2907 new_en.ae_u.term.stop = prim_aep->ae_u.term.stop;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002908 }
2909 }
2910 }
2911 return get_attr_entry(&term_attr_table, &new_en);
2912}
2913
2914#ifdef FEAT_GUI
2915 attrentry_T *
2916syn_gui_attr2entry(int attr)
2917{
2918 attr -= ATTR_OFF;
2919 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2920 return NULL;
2921 return &(GUI_ATTR_ENTRY(attr));
2922}
2923#endif
2924
2925/*
2926 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2927 * Only to be used when "attr" > HL_ALL.
2928 */
2929 int
2930syn_attr2attr(int attr)
2931{
2932 attrentry_T *aep;
2933
2934#ifdef FEAT_GUI
2935 if (gui.in_use)
2936 aep = syn_gui_attr2entry(attr);
2937 else
2938#endif
2939 if (IS_CTERM)
2940 aep = syn_cterm_attr2entry(attr);
2941 else
2942 aep = syn_term_attr2entry(attr);
2943
2944 if (aep == NULL) // highlighting not set
2945 return 0;
2946 return aep->ae_attr;
2947}
2948
2949
2950 attrentry_T *
2951syn_term_attr2entry(int attr)
2952{
2953 attr -= ATTR_OFF;
2954 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2955 return NULL;
2956 return &(TERM_ATTR_ENTRY(attr));
2957}
2958
2959 attrentry_T *
2960syn_cterm_attr2entry(int attr)
2961{
2962 attr -= ATTR_OFF;
2963 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2964 return NULL;
2965 return &(CTERM_ATTR_ENTRY(attr));
2966}
2967
2968#define LIST_ATTR 1
2969#define LIST_STRING 2
2970#define LIST_INT 3
2971
2972 static void
2973highlight_list_one(int id)
2974{
2975 hl_group_T *sgp;
2976 int didh = FALSE;
2977
2978 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2979
2980 if (message_filtered(sgp->sg_name))
2981 return;
2982
2983 didh = highlight_list_arg(id, didh, LIST_ATTR,
2984 sgp->sg_term, NULL, "term");
2985 didh = highlight_list_arg(id, didh, LIST_STRING,
2986 0, sgp->sg_start, "start");
2987 didh = highlight_list_arg(id, didh, LIST_STRING,
2988 0, sgp->sg_stop, "stop");
2989
2990 didh = highlight_list_arg(id, didh, LIST_ATTR,
2991 sgp->sg_cterm, NULL, "cterm");
2992 didh = highlight_list_arg(id, didh, LIST_INT,
2993 sgp->sg_cterm_fg, NULL, "ctermfg");
2994 didh = highlight_list_arg(id, didh, LIST_INT,
2995 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002996 didh = highlight_list_arg(id, didh, LIST_INT,
2997 sgp->sg_cterm_ul, NULL, "ctermul");
PMuncha606f3a2023-11-15 15:35:49 +01002998 didh = highlight_list_arg(id, didh, LIST_INT,
2999 sgp->sg_cterm_font, NULL, "ctermfont");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003000
3001#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3002 didh = highlight_list_arg(id, didh, LIST_ATTR,
3003 sgp->sg_gui, NULL, "gui");
3004 didh = highlight_list_arg(id, didh, LIST_STRING,
3005 0, sgp->sg_gui_fg_name, "guifg");
3006 didh = highlight_list_arg(id, didh, LIST_STRING,
3007 0, sgp->sg_gui_bg_name, "guibg");
3008 didh = highlight_list_arg(id, didh, LIST_STRING,
3009 0, sgp->sg_gui_sp_name, "guisp");
3010#endif
3011#ifdef FEAT_GUI
3012 didh = highlight_list_arg(id, didh, LIST_STRING,
3013 0, sgp->sg_font_name, "font");
3014#endif
3015
3016 if (sgp->sg_link && !got_int)
3017 {
3018 (void)syn_list_header(didh, 9999, id);
3019 didh = TRUE;
3020 msg_puts_attr("links to", HL_ATTR(HLF_D));
3021 msg_putchar(' ');
3022 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
3023 }
3024
3025 if (!didh)
3026 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
3027#ifdef FEAT_EVAL
3028 if (p_verbose > 0)
3029 last_set_msg(sgp->sg_script_ctx);
3030#endif
3031}
3032
3033 static int
3034highlight_list_arg(
3035 int id,
3036 int didh,
3037 int type,
3038 int iarg,
3039 char_u *sarg,
3040 char *name)
3041{
Bram Moolenaar84f54632022-06-29 18:39:11 +01003042 char_u buf[MAX_ATTR_LEN];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003043 char_u *ts;
3044 int i;
3045
3046 if (got_int)
3047 return FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003048
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003049 if (type == LIST_STRING ? (sarg == NULL) : (iarg == 0))
3050 return didh;
3051
3052 ts = buf;
3053 if (type == LIST_INT)
3054 sprintf((char *)buf, "%d", iarg - 1);
3055 else if (type == LIST_STRING)
3056 ts = sarg;
3057 else // type == LIST_ATTR
3058 {
3059 buf[0] = NUL;
3060 for (i = 0; hl_attr_table[i] != 0; ++i)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003061 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003062 if (iarg & hl_attr_table[i])
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003063 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003064 if (buf[0] != NUL)
3065 vim_strcat(buf, (char_u *)",", MAX_ATTR_LEN);
3066 vim_strcat(buf, (char_u *)hl_name_table[i], MAX_ATTR_LEN);
3067 iarg &= ~hl_attr_table[i]; // don't want "inverse"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003068 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003069 }
3070 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003071
3072 (void)syn_list_header(didh,
3073 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
3074 didh = TRUE;
3075 if (!got_int)
3076 {
3077 if (*name != NUL)
3078 {
3079 msg_puts_attr(name, HL_ATTR(HLF_D));
3080 msg_puts_attr("=", HL_ATTR(HLF_D));
3081 }
3082 msg_outtrans(ts);
3083 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003084 return didh;
3085}
3086
3087#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3088/*
3089 * Return "1" if highlight group "id" has attribute "flag".
3090 * Return NULL otherwise.
3091 */
3092 char_u *
3093highlight_has_attr(
3094 int id,
3095 int flag,
3096 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3097{
3098 int attr;
3099
3100 if (id <= 0 || id > highlight_ga.ga_len)
3101 return NULL;
3102
3103#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3104 if (modec == 'g')
3105 attr = HL_TABLE()[id - 1].sg_gui;
3106 else
3107#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003108 {
3109 if (modec == 'c')
3110 attr = HL_TABLE()[id - 1].sg_cterm;
3111 else
3112 attr = HL_TABLE()[id - 1].sg_term;
3113 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003114
3115 if (attr & flag)
3116 return (char_u *)"1";
3117 return NULL;
3118}
3119#endif
3120
3121#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3122/*
3123 * Return color name of highlight group "id".
3124 */
3125 char_u *
3126highlight_color(
3127 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003128 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003129 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3130{
3131 static char_u name[20];
3132 int n;
3133 int fg = FALSE;
3134 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003135 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003136 int font = FALSE;
3137
3138 if (id <= 0 || id > highlight_ga.ga_len)
3139 return NULL;
3140
3141 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3142 fg = TRUE;
3143 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3144 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3145 font = TRUE;
3146 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3147 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003148 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3149 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003150 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3151 return NULL;
3152 if (modec == 'g')
3153 {
3154# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3155# ifdef FEAT_GUI
3156 // return font name
3157 if (font)
3158 return HL_TABLE()[id - 1].sg_font_name;
3159# endif
3160
3161 // return #RRGGBB form (only possible when GUI is running)
3162 if ((USE_24BIT) && what[2] == '#')
3163 {
3164 guicolor_T color;
3165 long_u rgb;
3166 static char_u buf[10];
3167
3168 if (fg)
3169 color = HL_TABLE()[id - 1].sg_gui_fg;
3170 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003171 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003172 else
3173 color = HL_TABLE()[id - 1].sg_gui_bg;
3174 if (color == INVALCOLOR)
3175 return NULL;
3176 rgb = (long_u)GUI_MCH_GET_RGB(color);
3177 sprintf((char *)buf, "#%02x%02x%02x",
3178 (unsigned)(rgb >> 16),
3179 (unsigned)(rgb >> 8) & 255,
3180 (unsigned)rgb & 255);
3181 return buf;
3182 }
3183# endif
3184 if (fg)
3185 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3186 if (sp)
3187 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3188 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3189 }
PMuncha606f3a2023-11-15 15:35:49 +01003190 if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003191 return NULL;
3192 if (modec == 'c')
3193 {
3194 if (fg)
3195 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003196 else if (ul)
3197 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
PMuncha606f3a2023-11-15 15:35:49 +01003198 else if (font)
3199 n = HL_TABLE()[id - 1].sg_cterm_font - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003200 else
3201 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3202 if (n < 0)
3203 return NULL;
3204 sprintf((char *)name, "%d", n);
3205 return name;
3206 }
3207 // term doesn't have color
3208 return NULL;
3209}
3210#endif
3211
3212#if (defined(FEAT_SYN_HL) \
3213 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3214 && defined(FEAT_PRINTER)) || defined(PROTO)
3215/*
3216 * Return color name of highlight group "id" as RGB value.
3217 */
3218 long_u
3219highlight_gui_color_rgb(
3220 int id,
3221 int fg) // TRUE = fg, FALSE = bg
3222{
3223 guicolor_T color;
3224
3225 if (id <= 0 || id > highlight_ga.ga_len)
3226 return 0L;
3227
3228 if (fg)
3229 color = HL_TABLE()[id - 1].sg_gui_fg;
3230 else
3231 color = HL_TABLE()[id - 1].sg_gui_bg;
3232
3233 if (color == INVALCOLOR)
3234 return 0L;
3235
3236 return GUI_MCH_GET_RGB(color);
3237}
3238#endif
3239
3240/*
3241 * Output the syntax list header.
3242 * Return TRUE when started a new line.
3243 */
3244 int
3245syn_list_header(
3246 int did_header, // did header already
3247 int outlen, // length of string that comes
3248 int id) // highlight group id
3249{
3250 int endcol = 19;
3251 int newline = TRUE;
3252 int name_col = 0;
3253
3254 if (!did_header)
3255 {
3256 msg_putchar('\n');
3257 if (got_int)
3258 return TRUE;
3259 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3260 name_col = msg_col;
3261 endcol = 15;
3262 }
3263 else if (msg_col + outlen + 1 >= Columns)
3264 {
3265 msg_putchar('\n');
3266 if (got_int)
3267 return TRUE;
3268 }
3269 else
3270 {
3271 if (msg_col >= endcol) // wrap around is like starting a new line
3272 newline = FALSE;
3273 }
3274
3275 if (msg_col >= endcol) // output at least one space
3276 endcol = msg_col + 1;
3277 if (Columns <= endcol) // avoid hang for tiny window
3278 endcol = Columns - 1;
3279
3280 msg_advance(endcol);
3281
3282 // Show "xxx" with the attributes.
3283 if (!did_header)
3284 {
3285 if (endcol == Columns - 1 && endcol <= name_col)
3286 msg_putchar(' ');
3287 msg_puts_attr("xxx", syn_id2attr(id));
3288 msg_putchar(' ');
3289 }
3290
3291 return newline;
3292}
3293
3294/*
3295 * Set the attribute numbers for a highlight group.
3296 * Called after one of the attributes has changed.
3297 */
3298 static void
3299set_hl_attr(
3300 int idx) // index in array
3301{
3302 attrentry_T at_en;
3303 hl_group_T *sgp = HL_TABLE() + idx;
3304
3305 // The "Normal" group doesn't need an attribute number
3306 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3307 return;
3308
3309#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003310 // For the GUI mode: If there are other than "normal" highlighting
3311 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003312 if (sgp->sg_gui_fg == INVALCOLOR
3313 && sgp->sg_gui_bg == INVALCOLOR
3314 && sgp->sg_gui_sp == INVALCOLOR
3315 && sgp->sg_font == NOFONT
3316# ifdef FEAT_XFONTSET
3317 && sgp->sg_fontset == NOFONTSET
3318# endif
3319 )
3320 {
3321 sgp->sg_gui_attr = sgp->sg_gui;
3322 }
3323 else
3324 {
3325 at_en.ae_attr = sgp->sg_gui;
3326 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3327 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3328 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3329 at_en.ae_u.gui.font = sgp->sg_font;
3330# ifdef FEAT_XFONTSET
3331 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3332# endif
3333 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3334 }
3335#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003336 // For the term mode: If there are other than "normal" highlighting
3337 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003338 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3339 sgp->sg_term_attr = sgp->sg_term;
3340 else
3341 {
3342 at_en.ae_attr = sgp->sg_term;
3343 at_en.ae_u.term.start = sgp->sg_start;
3344 at_en.ae_u.term.stop = sgp->sg_stop;
3345 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3346 }
3347
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003348 // For the color term mode: If there are other than "normal"
3349 // highlighting attributes, need to allocate an attr number.
PMuncha606f3a2023-11-15 15:35:49 +01003350 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 &&
3351 sgp->sg_cterm_ul == 0 && sgp->sg_cterm_font == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003352# ifdef FEAT_TERMGUICOLORS
3353 && sgp->sg_gui_fg == INVALCOLOR
3354 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003355 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003356# endif
3357 )
3358 sgp->sg_cterm_attr = sgp->sg_cterm;
3359 else
3360 {
3361 at_en.ae_attr = sgp->sg_cterm;
3362 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3363 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003364 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
PMuncha606f3a2023-11-15 15:35:49 +01003365 at_en.ae_u.cterm.font = sgp->sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003366# ifdef FEAT_TERMGUICOLORS
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003367 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3368 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003369 // Only use the underline/undercurl color when used, it may clear the
3370 // background color if not supported.
Bram Moolenaar84f54632022-06-29 18:39:11 +01003371 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL
3372 | HL_UNDERDOUBLE | HL_UNDERDOTTED | HL_UNDERDASHED))
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003373 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3374 else
3375 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003376 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3377 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3378 {
3379 // If both fg and bg are invalid fall back to the cterm colors.
3380 // Helps when the GUI only uses an attribute, e.g. undercurl.
3381 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3382 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3383 }
3384# endif
3385 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3386 }
3387}
3388
3389/*
3390 * Lookup a highlight group name and return its ID.
3391 * If it is not found, 0 is returned.
3392 */
3393 int
3394syn_name2id(char_u *name)
3395{
3396 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003397 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003398
3399 // Avoid using stricmp() too much, it's slow on some systems
3400 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3401 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003402 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003403 vim_strup(name_u);
3404 for (i = highlight_ga.ga_len; --i >= 0; )
3405 if (HL_TABLE()[i].sg_name_u != NULL
3406 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3407 break;
3408 return i + 1;
3409}
3410
3411/*
3412 * Lookup a highlight group name and return its attributes.
3413 * Return zero if not found.
3414 */
3415 int
3416syn_name2attr(char_u *name)
3417{
3418 int id = syn_name2id(name);
3419
3420 if (id != 0)
3421 return syn_id2attr(id);
3422 return 0;
3423}
3424
3425#if defined(FEAT_EVAL) || defined(PROTO)
3426/*
3427 * Return TRUE if highlight group "name" exists.
3428 */
3429 int
3430highlight_exists(char_u *name)
3431{
3432 return (syn_name2id(name) > 0);
3433}
3434
3435# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3436/*
3437 * Return the name of highlight group "id".
3438 * When not a valid ID return an empty string.
3439 */
3440 char_u *
3441syn_id2name(int id)
3442{
3443 if (id <= 0 || id > highlight_ga.ga_len)
3444 return (char_u *)"";
3445 return HL_TABLE()[id - 1].sg_name;
3446}
3447# endif
3448#endif
3449
3450/*
3451 * Like syn_name2id(), but take a pointer + length argument.
3452 */
3453 int
3454syn_namen2id(char_u *linep, int len)
3455{
3456 char_u *name;
3457 int id = 0;
3458
3459 name = vim_strnsave(linep, len);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00003460 if (name == NULL)
3461 return 0;
3462
3463 id = syn_name2id(name);
3464 vim_free(name);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003465 return id;
3466}
3467
3468/*
3469 * Find highlight group name in the table and return its ID.
3470 * The argument is a pointer to the name and the length of the name.
3471 * If it doesn't exist yet, a new entry is created.
3472 * Return 0 for failure.
3473 */
3474 int
3475syn_check_group(char_u *pp, int len)
3476{
3477 int id;
3478 char_u *name;
3479
erw7f7f7aaf2021-12-07 21:29:20 +00003480 if (len > MAX_SYN_NAME)
3481 {
3482 emsg(_(e_highlight_group_name_too_long));
3483 return 0;
3484 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003485 name = vim_strnsave(pp, len);
3486 if (name == NULL)
3487 return 0;
3488
3489 id = syn_name2id(name);
3490 if (id == 0) // doesn't exist yet
3491 id = syn_add_group(name);
3492 else
3493 vim_free(name);
3494 return id;
3495}
3496
3497/*
3498 * Add new highlight group and return its ID.
3499 * "name" must be an allocated string, it will be consumed.
3500 * Return 0 for failure.
3501 */
3502 static int
3503syn_add_group(char_u *name)
3504{
3505 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003506 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003507
Gregory Andersd4376dc2023-08-20 19:14:03 +02003508 // Check that the name is valid (ASCII letters, digits, underscores, dots, or hyphens).
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003509 for (p = name; *p != NUL; ++p)
3510 {
3511 if (!vim_isprintc(*p))
3512 {
Bram Moolenaara6f79292022-01-04 21:30:47 +00003513 emsg(_(e_unprintable_character_in_group_name));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003514 vim_free(name);
3515 return 0;
3516 }
Gregory Andersd4376dc2023-08-20 19:14:03 +02003517 else if (!ASCII_ISALNUM(*p) && *p != '_' && *p != '.' && *p != '-')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003518 {
3519 // This is an error, but since there previously was no check only
3520 // give a warning.
3521 msg_source(HL_ATTR(HLF_W));
3522 msg(_("W18: Invalid character in group name"));
3523 break;
3524 }
3525 }
3526
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003527 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003528 if (highlight_ga.ga_data == NULL)
3529 {
3530 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3531 highlight_ga.ga_growsize = 10;
3532 }
3533
3534 if (highlight_ga.ga_len >= MAX_HL_ID)
3535 {
Bram Moolenaar9d00e4a2022-01-05 17:49:15 +00003536 emsg(_(e_too_many_highlight_and_syntax_groups));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003537 vim_free(name);
3538 return 0;
3539 }
3540
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003541 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003542 if (ga_grow(&highlight_ga, 1) == FAIL)
3543 {
3544 vim_free(name);
3545 return 0;
3546 }
3547
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003548 name_up = vim_strsave_up(name);
3549 if (name_up == NULL)
3550 {
3551 vim_free(name);
3552 return 0;
3553 }
3554
Bram Moolenaara80faa82020-04-12 19:37:17 +02003555 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003556 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003557 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003558#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3559 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3560 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003561 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003562#endif
3563 ++highlight_ga.ga_len;
3564
3565 return highlight_ga.ga_len; // ID is index plus one
3566}
3567
3568/*
3569 * When, just after calling syn_add_group(), an error is discovered, this
3570 * function deletes the new name.
3571 */
3572 static void
3573syn_unadd_group(void)
3574{
3575 --highlight_ga.ga_len;
3576 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3577 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3578}
3579
3580/*
3581 * Translate a group ID to highlight attributes.
Bram Moolenaar87f3a2c2022-08-10 20:50:23 +01003582 * "hl_id" must be valid: > 0, caller must check.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003583 */
3584 int
3585syn_id2attr(int hl_id)
3586{
3587 int attr;
3588 hl_group_T *sgp;
3589
3590 hl_id = syn_get_final_id(hl_id);
3591 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3592
3593#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003594 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003595 if (gui.in_use)
3596 attr = sgp->sg_gui_attr;
3597 else
3598#endif
3599 if (IS_CTERM)
3600 attr = sgp->sg_cterm_attr;
3601 else
3602 attr = sgp->sg_term_attr;
3603
3604 return attr;
3605}
3606
3607#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3608/*
3609 * Get the GUI colors and attributes for a group ID.
3610 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3611 */
3612 int
3613syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3614{
3615 hl_group_T *sgp;
3616
3617 hl_id = syn_get_final_id(hl_id);
3618 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3619
3620 *fgp = sgp->sg_gui_fg;
3621 *bgp = sgp->sg_gui_bg;
3622 return sgp->sg_gui;
3623}
3624#endif
3625
3626#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003627 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3628 && defined(FEAT_TERMGUICOLORS)) \
3629 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003630 void
3631syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3632{
3633 hl_group_T *sgp;
3634
3635 hl_id = syn_get_final_id(hl_id);
3636 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3637 *fgp = sgp->sg_cterm_fg - 1;
3638 *bgp = sgp->sg_cterm_bg - 1;
3639}
3640#endif
3641
3642/*
3643 * Translate a group ID to the final group ID (following links).
3644 */
3645 int
3646syn_get_final_id(int hl_id)
3647{
3648 int count;
3649 hl_group_T *sgp;
3650
3651 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3652 return 0; // Can be called from eval!!
3653
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003654 // Follow links until there is no more.
3655 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003656 for (count = 100; --count >= 0; )
3657 {
3658 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3659 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3660 break;
3661 hl_id = sgp->sg_link;
3662 }
3663
3664 return hl_id;
3665}
3666
3667#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3668/*
3669 * Call this function just after the GUI has started.
3670 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3671 * It finds the font and color handles for the highlighting groups.
3672 */
3673 void
3674highlight_gui_started(void)
3675{
3676 int idx;
3677
3678 // First get the colors from the "Normal" and "Menu" group, if set
3679 if (USE_24BIT)
3680 set_normal_colors();
3681
3682 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3683 gui_do_one_color(idx, FALSE, FALSE);
3684
3685 highlight_changed();
3686}
3687
3688 static void
3689gui_do_one_color(
3690 int idx,
3691 int do_menu UNUSED, // TRUE: might set the menu font
3692 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3693{
3694 int didit = FALSE;
3695
3696# ifdef FEAT_GUI
3697# ifdef FEAT_TERMGUICOLORS
3698 if (gui.in_use)
3699# endif
3700 if (HL_TABLE()[idx].sg_font_name != NULL)
3701 {
3702 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3703 do_tooltip, TRUE);
3704 didit = TRUE;
3705 }
3706# endif
3707 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3708 {
3709 HL_TABLE()[idx].sg_gui_fg =
3710 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3711 didit = TRUE;
3712 }
3713 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3714 {
3715 HL_TABLE()[idx].sg_gui_bg =
3716 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3717 didit = TRUE;
3718 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003719 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3720 {
3721 HL_TABLE()[idx].sg_gui_sp =
3722 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3723 didit = TRUE;
3724 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003725 if (didit) // need to get a new attr number
3726 set_hl_attr(idx);
3727}
3728#endif
3729
3730#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3731/*
3732 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3733 */
3734 static void
3735combine_stl_hlt(
3736 int id,
3737 int id_S,
3738 int id_alt,
3739 int hlcnt,
3740 int i,
3741 int hlf,
3742 int *table)
3743{
3744 hl_group_T *hlt = HL_TABLE();
3745
3746 if (id_alt == 0)
3747 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003748 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003749 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3750 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3751# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3752 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3753# endif
3754 }
3755 else
3756 mch_memmove(&hlt[hlcnt + i],
3757 &hlt[id_alt - 1],
3758 sizeof(hl_group_T));
3759 hlt[hlcnt + i].sg_link = 0;
3760
3761 hlt[hlcnt + i].sg_term ^=
3762 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3763 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3764 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3765 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3766 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3767 hlt[hlcnt + i].sg_cterm ^=
3768 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3769 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3770 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3771 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3772 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
PMuncha606f3a2023-11-15 15:35:49 +01003773 if (hlt[id - 1].sg_cterm_font != hlt[id_S - 1].sg_cterm_font)
3774 hlt[hlcnt + i].sg_cterm_font = hlt[id - 1].sg_cterm_font;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003775# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3776 hlt[hlcnt + i].sg_gui ^=
3777 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3778# endif
3779# ifdef FEAT_GUI
3780 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3781 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3782 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3783 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3784 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3785 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3786 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3787 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3788# ifdef FEAT_XFONTSET
3789 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3790 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3791# endif
3792# endif
3793 highlight_ga.ga_len = hlcnt + i + 1;
3794 set_hl_attr(hlcnt + i); // At long last we can apply
3795 table[i] = syn_id2attr(hlcnt + i + 1);
3796}
3797#endif
3798
3799/*
3800 * Translate the 'highlight' option into attributes in highlight_attr[] and
3801 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3802 * corresponding highlights to use on top of HLF_SNC is computed.
3803 * Called only when the 'highlight' option has been changed and upon first
3804 * screen redraw after any :highlight command.
3805 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3806 */
3807 int
3808highlight_changed(void)
3809{
3810 int hlf;
3811 int i;
3812 char_u *p;
3813 int attr;
3814 char_u *end;
3815 int id;
3816#ifdef USER_HIGHLIGHT
3817 char_u userhl[30]; // use 30 to avoid compiler warning
3818# ifdef FEAT_STL_OPT
3819 int id_S = -1;
3820 int id_SNC = 0;
3821# ifdef FEAT_TERMINAL
3822 int id_ST = 0;
3823 int id_STNC = 0;
3824# endif
3825 int hlcnt;
3826# endif
3827#endif
3828 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3829
3830 need_highlight_changed = FALSE;
3831
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003832#ifdef FEAT_TERMINAL
3833 term_update_colors_all();
3834 term_update_wincolor_all();
3835#endif
3836
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003837 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003838 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3839 highlight_attr[hlf] = 0;
3840
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003841 // First set all attributes to their default value.
3842 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003843 for (i = 0; i < 2; ++i)
3844 {
3845 if (i)
3846 p = p_hl;
3847 else
3848 p = get_highlight_default();
3849 if (p == NULL) // just in case
3850 continue;
3851
3852 while (*p)
3853 {
3854 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3855 if (hl_flags[hlf] == *p)
3856 break;
3857 ++p;
3858 if (hlf == (int)HLF_COUNT || *p == NUL)
3859 return FAIL;
3860
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003861 // Allow several hl_flags to be combined, like "bu" for
3862 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003863 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003864 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003865 {
3866 if (VIM_ISWHITE(*p)) // ignore white space
3867 continue;
3868
3869 if (attr > HL_ALL) // Combination with ':' is not allowed.
3870 return FAIL;
3871
Yee Cheng Chin900894b2023-09-29 20:42:32 +02003872 // Note: Keep this in sync with expand_set_highlight().
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003873 switch (*p)
3874 {
3875 case 'b': attr |= HL_BOLD;
3876 break;
3877 case 'i': attr |= HL_ITALIC;
3878 break;
3879 case '-':
3880 case 'n': // no highlighting
3881 break;
3882 case 'r': attr |= HL_INVERSE;
3883 break;
3884 case 's': attr |= HL_STANDOUT;
3885 break;
3886 case 'u': attr |= HL_UNDERLINE;
3887 break;
3888 case 'c': attr |= HL_UNDERCURL;
3889 break;
Bram Moolenaar84f54632022-06-29 18:39:11 +01003890 case '2': attr |= HL_UNDERDOUBLE;
3891 break;
3892 case 'd': attr |= HL_UNDERDOTTED;
3893 break;
3894 case '=': attr |= HL_UNDERDASHED;
3895 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003896 case 't': attr |= HL_STRIKETHROUGH;
3897 break;
3898 case ':': ++p; // highlight group name
3899 if (attr || *p == NUL) // no combinations
3900 return FAIL;
3901 end = vim_strchr(p, ',');
3902 if (end == NULL)
3903 end = p + STRLEN(p);
3904 id = syn_check_group(p, (int)(end - p));
3905 if (id == 0)
3906 return FAIL;
3907 attr = syn_id2attr(id);
3908 p = end - 1;
3909#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3910 if (hlf == (int)HLF_SNC)
3911 id_SNC = syn_get_final_id(id);
3912# ifdef FEAT_TERMINAL
3913 else if (hlf == (int)HLF_ST)
3914 id_ST = syn_get_final_id(id);
3915 else if (hlf == (int)HLF_STNC)
3916 id_STNC = syn_get_final_id(id);
3917# endif
3918 else if (hlf == (int)HLF_S)
3919 id_S = syn_get_final_id(id);
3920#endif
3921 break;
3922 default: return FAIL;
3923 }
3924 }
3925 highlight_attr[hlf] = attr;
3926
3927 p = skip_to_option_part(p); // skip comma and spaces
3928 }
3929 }
3930
3931#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003932 // Setup the user highlights
3933 //
3934 // Temporarily utilize 28 more hl entries:
3935 // 9 for User1-User9 combined with StatusLineNC
3936 // 9 for User1-User9 combined with StatusLineTerm
3937 // 9 for User1-User9 combined with StatusLineTermNC
3938 // 1 for StatusLine default
3939 // Have to be in there simultaneously in case of table overflows in
3940 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003941# ifdef FEAT_STL_OPT
3942 if (ga_grow(&highlight_ga, 28) == FAIL)
3943 return FAIL;
3944 hlcnt = highlight_ga.ga_len;
3945 if (id_S == -1)
3946 {
3947 // Make sure id_S is always valid to simplify code below. Use the last
3948 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003949 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003950 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3951 id_S = hlcnt + 19;
3952 }
3953# endif
3954 for (i = 0; i < 9; i++)
3955 {
3956 sprintf((char *)userhl, "User%d", i + 1);
3957 id = syn_name2id(userhl);
3958 if (id == 0)
3959 {
3960 highlight_user[i] = 0;
3961# ifdef FEAT_STL_OPT
3962 highlight_stlnc[i] = 0;
3963# ifdef FEAT_TERMINAL
3964 highlight_stlterm[i] = 0;
3965 highlight_stltermnc[i] = 0;
3966# endif
3967# endif
3968 }
3969 else
3970 {
3971 highlight_user[i] = syn_id2attr(id);
3972# ifdef FEAT_STL_OPT
3973 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3974 HLF_SNC, highlight_stlnc);
3975# ifdef FEAT_TERMINAL
3976 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3977 HLF_ST, highlight_stlterm);
3978 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3979 HLF_STNC, highlight_stltermnc);
3980# endif
3981# endif
3982 }
3983 }
3984# ifdef FEAT_STL_OPT
3985 highlight_ga.ga_len = hlcnt;
3986# endif
3987
3988#endif // USER_HIGHLIGHT
3989
3990 return OK;
3991}
3992
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003993static void highlight_list(void);
3994static void highlight_list_two(int cnt, int attr);
3995
3996/*
3997 * Handle command line completion for :highlight command.
3998 */
3999 void
4000set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
4001{
4002 char_u *p;
4003
4004 // Default: expand group names
4005 xp->xp_context = EXPAND_HIGHLIGHT;
4006 xp->xp_pattern = arg;
4007 include_link = 2;
4008 include_default = 1;
4009
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004010 if (*arg == NUL)
4011 return;
4012
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004013 // (part of) subcommand already typed
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004014 p = skiptowhite(arg);
4015 if (*p == NUL)
4016 return;
4017
4018 // past "default" or group name
4019 include_default = 0;
4020 if (STRNCMP("default", arg, p - arg) == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004021 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004022 arg = skipwhite(p);
4023 xp->xp_pattern = arg;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004024 p = skiptowhite(arg);
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004025 }
4026 if (*p == NUL)
4027 return;
4028
4029 // past group name
4030 include_link = 0;
4031 if (arg[1] == 'i' && arg[0] == 'N')
4032 highlight_list();
4033 if (STRNCMP("link", arg, p - arg) == 0
4034 || STRNCMP("clear", arg, p - arg) == 0)
4035 {
4036 xp->xp_pattern = skipwhite(p);
4037 p = skiptowhite(xp->xp_pattern);
4038 if (*p != NUL) // past first group name
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004039 {
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004040 xp->xp_pattern = skipwhite(p);
4041 p = skiptowhite(xp->xp_pattern);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004042 }
4043 }
Yegappan Lakshmanan1cfb14a2023-01-09 19:04:23 +00004044 if (*p != NUL) // past group name(s)
4045 xp->xp_context = EXPAND_NOTHING;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004046}
4047
4048/*
4049 * List highlighting matches in a nice way.
4050 */
4051 static void
4052highlight_list(void)
4053{
4054 int i;
4055
4056 for (i = 10; --i >= 0; )
4057 highlight_list_two(i, HL_ATTR(HLF_D));
4058 for (i = 40; --i >= 0; )
4059 highlight_list_two(99, 0);
4060}
4061
4062 static void
4063highlight_list_two(int cnt, int attr)
4064{
4065 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
4066 msg_clr_eos();
4067 out_flush();
4068 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
4069}
4070
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004071/*
4072 * Function given to ExpandGeneric() to obtain the list of group names.
4073 */
4074 char_u *
4075get_highlight_name(expand_T *xp UNUSED, int idx)
4076{
4077 return get_highlight_name_ext(xp, idx, TRUE);
4078}
4079
4080/*
4081 * Obtain a highlight group name.
4082 * When "skip_cleared" is TRUE don't return a cleared entry.
4083 */
4084 char_u *
4085get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4086{
4087 if (idx < 0)
4088 return NULL;
4089
4090 // Items are never removed from the table, skip the ones that were
4091 // cleared.
4092 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4093 return (char_u *)"";
4094
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004095 if (idx == highlight_ga.ga_len && include_none != 0)
4096 return (char_u *)"none";
4097 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4098 return (char_u *)"default";
4099 if (idx == highlight_ga.ga_len + include_none + include_default
4100 && include_link != 0)
4101 return (char_u *)"link";
4102 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4103 && include_link != 0)
4104 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004105 if (idx >= highlight_ga.ga_len)
4106 return NULL;
4107 return HL_TABLE()[idx].sg_name;
4108}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004109
4110#if defined(FEAT_GUI) || defined(PROTO)
4111/*
4112 * Free all the highlight group fonts.
4113 * Used when quitting for systems which need it.
4114 */
4115 void
4116free_highlight_fonts(void)
4117{
4118 int idx;
4119
4120 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4121 {
4122 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4123 HL_TABLE()[idx].sg_font = NOFONT;
4124# ifdef FEAT_XFONTSET
4125 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4126 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4127# endif
4128 }
4129
4130 gui_mch_free_font(gui.norm_font);
4131# ifdef FEAT_XFONTSET
4132 gui_mch_free_fontset(gui.fontset);
4133# endif
4134# ifndef FEAT_GUI_GTK
4135 gui_mch_free_font(gui.bold_font);
4136 gui_mch_free_font(gui.ital_font);
4137 gui_mch_free_font(gui.boldital_font);
4138# endif
4139}
4140#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004141
4142#if defined(FEAT_EVAL) || defined(PROTO)
4143/*
4144 * Convert each of the highlight attribute bits (bold, standout, underline,
4145 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4146 * the attribute name as the key.
4147 */
4148 static dict_T *
4149highlight_get_attr_dict(int hlattr)
4150{
4151 dict_T *dict;
4152 int i;
4153
4154 dict = dict_alloc();
4155 if (dict == NULL)
4156 return NULL;
4157
4158 for (i = 0; hl_attr_table[i] != 0; ++i)
4159 {
4160 if (hlattr & hl_attr_table[i])
4161 {
4162 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4163 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4164 }
4165 }
4166
4167 return dict;
4168}
4169
4170/*
4171 * Return the attributes of the highlight group at index 'hl_idx' as a
4172 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4173 * links recursively.
4174 */
4175 static dict_T *
4176highlight_get_info(int hl_idx, int resolve_link)
4177{
4178 dict_T *dict;
4179 hl_group_T *sgp;
4180 dict_T *attr_dict;
4181 int hlgid;
4182
4183 dict = dict_alloc();
4184 if (dict == NULL)
4185 return dict;
4186
4187 sgp = &HL_TABLE()[hl_idx];
4188 // highlight group id is 1-based
4189 hlgid = hl_idx + 1;
4190
4191 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4192 goto error;
4193 if (dict_add_number(dict, "id", hlgid) == FAIL)
4194 goto error;
4195
4196 if (sgp->sg_link && resolve_link)
4197 {
4198 // resolve the highlight group link recursively
4199 while (sgp->sg_link)
4200 {
4201 hlgid = sgp->sg_link;
4202 sgp = &HL_TABLE()[sgp->sg_link - 1];
4203 }
4204 }
4205
4206 if (sgp->sg_term != 0)
4207 {
4208 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4209 if (attr_dict != NULL)
4210 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4211 goto error;
4212 }
4213 if (sgp->sg_start != NULL)
4214 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4215 goto error;
4216 if (sgp->sg_stop != NULL)
4217 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4218 goto error;
4219 if (sgp->sg_cterm != 0)
4220 {
4221 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4222 if (attr_dict != NULL)
4223 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4224 goto error;
4225 }
4226 if (sgp->sg_cterm_fg != 0)
4227 if (dict_add_string(dict, "ctermfg",
4228 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4229 goto error;
4230 if (sgp->sg_cterm_bg != 0)
4231 if (dict_add_string(dict, "ctermbg",
4232 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4233 goto error;
4234 if (sgp->sg_cterm_ul != 0)
4235 if (dict_add_string(dict, "ctermul",
4236 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4237 goto error;
PMuncha606f3a2023-11-15 15:35:49 +01004238 if (sgp->sg_cterm_font != 0)
4239 if (dict_add_string(dict, "ctermfont",
4240 highlight_color(hlgid, (char_u *)"font", 'c')) == FAIL)
4241 goto error;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004242 if (sgp->sg_gui != 0)
4243 {
4244 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4245 if (attr_dict != NULL)
4246 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4247 goto error;
4248 }
4249 if (sgp->sg_gui_fg_name != NULL)
4250 if (dict_add_string(dict, "guifg",
4251 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4252 goto error;
4253 if (sgp->sg_gui_bg_name != NULL)
4254 if (dict_add_string(dict, "guibg",
4255 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4256 goto error;
4257 if (sgp->sg_gui_sp_name != NULL)
4258 if (dict_add_string(dict, "guisp",
4259 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4260 goto error;
4261# ifdef FEAT_GUI
4262 if (sgp->sg_font_name != NULL)
4263 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4264 goto error;
4265# endif
4266 if (sgp->sg_link)
4267 {
4268 char_u *link;
4269
4270 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4271 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4272 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004273
4274 if (sgp->sg_deflink)
4275 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004276 }
4277 if (dict_len(dict) == 2)
4278 // If only 'name' is present, then the highlight group is cleared.
4279 dict_add_bool(dict, "cleared", VVAL_TRUE);
4280
4281 return dict;
4282
4283error:
4284 vim_free(dict);
4285 return NULL;
4286}
4287
4288/*
4289 * "hlget([name])" function
4290 * Return the attributes of a specific highlight group (if specified) or all
4291 * the highlight groups.
4292 */
4293 void
4294f_hlget(typval_T *argvars, typval_T *rettv)
4295{
4296 list_T *list;
4297 dict_T *dict;
4298 int i;
4299 char_u *hlarg = NULL;
4300 int resolve_link = FALSE;
4301
4302 if (rettv_list_alloc(rettv) == FAIL)
4303 return;
4304
4305 if (check_for_opt_string_arg(argvars, 0) == FAIL
4306 || (argvars[0].v_type != VAR_UNKNOWN
4307 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4308 return;
4309
4310 if (argvars[0].v_type != VAR_UNKNOWN)
4311 {
4312 // highlight group name supplied
4313 hlarg = tv_get_string_chk(&argvars[0]);
4314 if (hlarg == NULL)
4315 return;
4316
4317 if (argvars[1].v_type != VAR_UNKNOWN)
4318 {
4319 int error = FALSE;
4320
4321 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4322 if (error)
4323 return;
4324 }
4325 }
4326
4327 list = rettv->vval.v_list;
4328 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4329 {
4330 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4331 {
4332 dict = highlight_get_info(i, resolve_link);
4333 if (dict != NULL)
4334 list_append_dict(list, dict);
4335 }
4336 }
4337}
4338
4339/*
4340 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4341 * 'dict' or the value is not a string type. If the value is not a string type
4342 * or is NULL, then 'error' is set to TRUE.
4343 */
4344 static char_u *
4345hldict_get_string(dict_T *dict, char_u *key, int *error)
4346{
4347 dictitem_T *di;
4348
4349 *error = FALSE;
4350 di = dict_find(dict, key, -1);
4351 if (di == NULL)
4352 return NULL;
4353
4354 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4355 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004356 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004357 *error = TRUE;
4358 return NULL;
4359 }
4360
4361 return di->di_tv.vval.v_string;
4362}
4363
4364/*
4365 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4366 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4367 * Dictionary or is NULL.
4368 */
4369 static int
4370hldict_attr_to_str(
4371 dict_T *dict,
4372 char_u *key,
4373 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004374 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004375{
4376 dictitem_T *di;
4377 dict_T *attrdict;
4378 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004379 char_u *p;
4380 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004381
4382 attr_str[0] = NUL;
4383 di = dict_find(dict, key, -1);
4384 if (di == NULL)
4385 return TRUE;
4386
4387 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4388 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004389 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004390 return FALSE;
4391 }
4392
4393 attrdict = di->di_tv.vval.v_dict;
4394
4395 // If the attribute dict is empty, then return NONE to clear the attributes
4396 if (dict_len(attrdict) == 0)
4397 {
4398 vim_strcat(attr_str, (char_u *)"NONE", len);
4399 return TRUE;
4400 }
4401
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004402 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004403 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4404 {
Bram Moolenaard61efa52022-07-23 09:52:04 +01004405 if (dict_get_bool(attrdict, hl_name_table[i], VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004406 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004407 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4408 STRCPY(p, (char_u *)",");
4409 sz = STRLEN(hl_name_table[i]);
4410 if (p - attr_str + sz + 1 < len)
4411 {
4412 STRCPY(p, (char_u *)hl_name_table[i]);
4413 p += sz;
4414 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004415 }
4416 }
4417
4418 return TRUE;
4419}
4420
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004421// Temporary buffer used to store the command string produced by hlset().
4422// IObuff cannot be used for this as the error messages produced by hlset()
4423// internally use IObuff.
4424#define HLSETBUFSZ 512
4425static char_u hlsetBuf[HLSETBUFSZ + 1];
4426
4427/*
4428 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4429 * "dptr", which points into "hlsetBuf".
4430 * Returns the updated pointer.
4431 */
4432 static char_u *
4433add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4434{
4435 size_t vallen;
4436
4437 // Do nothing if the value is not specified or is empty
4438 if (value == NULL || *value == NUL)
4439 return dptr;
4440
4441 vallen = STRLEN(value);
4442 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4443 {
4444 STRCPY(dptr, attr);
4445 dptr += attrlen;
4446 STRCPY(dptr, value);
4447 dptr += vallen;
4448 }
4449
4450 return dptr;
4451}
4452
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004453/*
4454 * Add or update a highlight group using 'dict' items. Returns TRUE if
4455 * successfully updated the highlight group.
4456 */
4457 static int
4458hlg_add_or_update(dict_T *dict)
4459{
4460 char_u *name;
4461 int error;
Bram Moolenaar84f54632022-06-29 18:39:11 +01004462 char_u term_attr[MAX_ATTR_LEN];
4463 char_u cterm_attr[MAX_ATTR_LEN];
4464 char_u gui_attr[MAX_ATTR_LEN];
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004465 char_u *start;
4466 char_u *stop;
4467 char_u *ctermfg;
4468 char_u *ctermbg;
4469 char_u *ctermul;
PMuncha606f3a2023-11-15 15:35:49 +01004470 char_u *ctermfont;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004471 char_u *guifg;
4472 char_u *guibg;
4473 char_u *guisp;
4474# ifdef FEAT_GUI
4475 char_u *font;
4476# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004477 int forceit = FALSE;
4478 int dodefault = FALSE;
4479 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004480 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004481
4482 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004483 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004484 return FALSE;
4485
Bram Moolenaard61efa52022-07-23 09:52:04 +01004486 if (dict_get_bool(dict, "force", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004487 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004488
Bram Moolenaard61efa52022-07-23 09:52:04 +01004489 if (dict_get_bool(dict, "default", VVAL_FALSE) == VVAL_TRUE)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004490 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004491
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004492 if (dict_has_key(dict, "cleared"))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004493 {
4494 varnumber_T cleared;
4495
4496 // clear a highlight group
Bram Moolenaard61efa52022-07-23 09:52:04 +01004497 cleared = dict_get_bool(dict, "cleared", FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004498 if (cleared == TRUE)
4499 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004500 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4501 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004502 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004503 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004504 }
4505
Yegappan Lakshmanan4829c1c2022-04-04 15:16:54 +01004506 if (dict_has_key(dict, "linksto"))
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004507 {
4508 char_u *linksto;
4509
4510 // link highlight groups
4511 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004512 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004513 return FALSE;
4514
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004515 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004516 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004517 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004518
4519 done = TRUE;
4520 }
4521
4522 // If 'cleared' or 'linksto' are specified, then don't process the other
4523 // attributes.
4524 if (done)
4525 return TRUE;
4526
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004527 start = hldict_get_string(dict, (char_u *)"start", &error);
4528 if (error)
4529 return FALSE;
4530
4531 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4532 if (error)
4533 return FALSE;
4534
4535 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004536 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004537 return FALSE;
4538
4539 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004540 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004541 return FALSE;
4542
4543 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4544 if (error)
4545 return FALSE;
4546
4547 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4548 if (error)
4549 return FALSE;
4550
4551 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4552 if (error)
4553 return FALSE;
4554
PMuncha606f3a2023-11-15 15:35:49 +01004555 ctermfont = hldict_get_string(dict, (char_u *)"ctermfont", &error);
4556 if (error)
4557 return FALSE;
4558
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004559 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004560 return FALSE;
4561
4562 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4563 if (error)
4564 return FALSE;
4565
4566 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4567 if (error)
4568 return FALSE;
4569
4570 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4571 if (error)
4572 return FALSE;
4573
4574# ifdef FEAT_GUI
4575 font = hldict_get_string(dict, (char_u *)"font", &error);
4576 if (error)
4577 return FALSE;
4578# endif
4579
4580 // If none of the attributes are specified, then do nothing.
4581 if (term_attr[0] == NUL && start == NULL && stop == NULL
4582 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
PMuncha606f3a2023-11-15 15:35:49 +01004583 && ctermul == NULL && ctermfont == NULL && gui_attr[0] == NUL
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004584# ifdef FEAT_GUI
4585 && font == NULL
4586# endif
4587 && guifg == NULL && guibg == NULL && guisp == NULL
4588 )
4589 return TRUE;
4590
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004591 hlsetBuf[0] = NUL;
4592 p = hlsetBuf;
4593 if (dodefault)
4594 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4595 p = add_attr_and_value(p, (char_u *)"", 0, name);
4596 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4597 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4598 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4599 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4600 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4601 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4602 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
PMuncha606f3a2023-11-15 15:35:49 +01004603 p = add_attr_and_value(p, (char_u *)" ctermfont=", 9, ctermfont);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004604 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004605# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004606 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004607# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004608 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4609 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
Yegappan Lakshmananc99e1822022-09-03 10:52:24 +01004610 (void)add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004611
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004612 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004613
4614 return TRUE;
4615}
4616
4617/*
4618 * "hlset([{highlight_attr}])" function
4619 * Add or modify highlight groups
4620 */
4621 void
4622f_hlset(typval_T *argvars, typval_T *rettv)
4623{
4624 listitem_T *li;
4625 dict_T *dict;
4626
4627 rettv->vval.v_number = -1;
4628
4629 if (check_for_list_arg(argvars, 0) == FAIL)
4630 return;
4631
4632 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4633 {
4634 if (li->li_tv.v_type != VAR_DICT)
4635 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004636 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004637 return;
4638 }
4639
4640 dict = li->li_tv.vval.v_dict;
4641 if (!hlg_add_or_update(dict))
4642 return;
4643 }
4644
4645 rettv->vval.v_number = 0;
4646}
4647#endif