blob: 253ed5bf298c2e7b7ba0d5941a2452c5d74297a3 [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[]) =
28 {"bold", "standout", "underline", "undercurl",
29 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
30static int hl_attr_table[] =
31 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
32#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
33
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020034/*
35 * Structure that stores information about a highlight group.
36 * The ID of a highlight group is also called group ID. It is the index in
37 * the highlight_ga array PLUS ONE.
38 */
39typedef struct
40{
41 char_u *sg_name; // highlight group name
42 char_u *sg_name_u; // uppercase of sg_name
43 int sg_cleared; // "hi clear" was used
44// for normal terminals
45 int sg_term; // "term=" highlighting attributes
46 char_u *sg_start; // terminal string for start highl
47 char_u *sg_stop; // terminal string for stop highl
48 int sg_term_attr; // Screen attr for term mode
49// for color terminals
50 int sg_cterm; // "cterm=" highlighting attr
51 int sg_cterm_bold; // bold attr was set for light color
52 int sg_cterm_fg; // terminal fg color number + 1
53 int sg_cterm_bg; // terminal bg color number + 1
Bram Moolenaare023e882020-05-31 16:42:30 +020054 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020055 int sg_cterm_attr; // Screen attr for color term mode
56// for when using the GUI
57#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
58 guicolor_T sg_gui_fg; // GUI foreground color handle
59 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020060 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020061#endif
62#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020063 GuiFont sg_font; // GUI font handle
64#ifdef FEAT_XFONTSET
65 GuiFontset sg_fontset; // GUI fontset handle
66#endif
67 char_u *sg_font_name; // GUI font or fontset name
68 int sg_gui_attr; // Screen attr for GUI mode
69#endif
70#if defined(FEAT_GUI) || defined(FEAT_EVAL)
71// Store the sp color name for the GUI or synIDattr()
72 int sg_gui; // "gui=" highlighting attributes
73 char_u *sg_gui_fg_name;// GUI foreground color name
74 char_u *sg_gui_bg_name;// GUI background color name
75 char_u *sg_gui_sp_name;// GUI special color name
76#endif
77 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +020078 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020079 int sg_set; // combination of SG_* flags
80#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +020081 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020082 sctx_T sg_script_ctx; // script in which the group was last set
83#endif
84} hl_group_T;
85
86// highlight groups for 'highlight' option
87static garray_T highlight_ga;
88#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
89
90/*
91 * An attribute number is the index in attr_table plus ATTR_OFF.
92 */
93#define ATTR_OFF (HL_ALL + 1)
94
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020095static void syn_unadd_group(void);
96static void set_hl_attr(int idx);
97static void highlight_list_one(int id);
98static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
99static int syn_add_group(char_u *name);
100static int hl_has_settings(int idx, int check_link);
101static void highlight_clear(int idx);
102
103#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
104static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
105#endif
106#ifdef FEAT_GUI
107static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
108static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
109#endif
110
111/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200112 * The default highlight groups. These are compiled-in for fast startup and
113 * they still work when the runtime files can't be found.
114 * When making changes here, also change runtime/colors/default.vim!
115 * The #ifdefs are needed to reduce the amount of static data. Helps to make
116 * the 16 bit DOS (museum) version compile.
117 */
118#if defined(FEAT_GUI) || defined(FEAT_EVAL)
119# define CENT(a, b) b
120#else
121# define CENT(a, b) a
122#endif
123static char *(highlight_init_both[]) = {
124 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
125 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
126 CENT("IncSearch term=reverse cterm=reverse",
127 "IncSearch term=reverse cterm=reverse gui=reverse"),
128 CENT("ModeMsg term=bold cterm=bold",
129 "ModeMsg term=bold cterm=bold gui=bold"),
130 CENT("NonText term=bold ctermfg=Blue",
131 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
132 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
133 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
134 CENT("StatusLineNC term=reverse cterm=reverse",
135 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
136 "default link EndOfBuffer NonText",
137 CENT("VertSplit term=reverse cterm=reverse",
138 "VertSplit term=reverse cterm=reverse gui=reverse"),
139#ifdef FEAT_CLIPBOARD
140 CENT("VisualNOS term=underline,bold cterm=underline,bold",
141 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
142#endif
143#ifdef FEAT_DIFF
144 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
145 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
146#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200147 CENT("PmenuSbar ctermbg=Grey",
148 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200149 CENT("TabLineSel term=bold cterm=bold",
150 "TabLineSel term=bold cterm=bold gui=bold"),
151 CENT("TabLineFill term=reverse cterm=reverse",
152 "TabLineFill term=reverse cterm=reverse gui=reverse"),
153#ifdef FEAT_GUI
154 "Cursor guibg=fg guifg=bg",
155 "lCursor guibg=fg guifg=bg", // should be different, but what?
156#endif
157 "default link QuickFixLine Search",
Bram Moolenaare413ea02021-11-24 16:20:13 +0000158 "default link CursorLineSign SignColumn",
159 "default link CursorLineFold FoldColumn",
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200160 CENT("Normal cterm=NONE", "Normal gui=NONE"),
161 NULL
162};
163
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200164// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200165static char *(highlight_init_light[]) = {
166 CENT("Directory term=bold ctermfg=DarkBlue",
167 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
168 CENT("LineNr term=underline ctermfg=Brown",
169 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200170 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
171 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200172 CENT("MoreMsg term=bold ctermfg=DarkGreen",
173 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
174 CENT("Question term=standout ctermfg=DarkGreen",
175 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
176 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
177 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
178#ifdef FEAT_SPELL
179 CENT("SpellBad term=reverse ctermbg=LightRed",
180 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
181 CENT("SpellCap term=reverse ctermbg=LightBlue",
182 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
183 CENT("SpellRare term=reverse ctermbg=LightMagenta",
184 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
185 CENT("SpellLocal term=underline ctermbg=Cyan",
186 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
187#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200188 CENT("PmenuThumb ctermbg=Black",
189 "PmenuThumb ctermbg=Black guibg=Black"),
190 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
191 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
192 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
193 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200194 CENT("SpecialKey term=bold ctermfg=DarkBlue",
195 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
196 CENT("Title term=bold ctermfg=DarkMagenta",
197 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
198 CENT("WarningMsg term=standout ctermfg=DarkRed",
199 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
200#ifdef FEAT_WILDMENU
201 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
202 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
203#endif
204#ifdef FEAT_FOLDING
205 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
206 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
207 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
208 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
209#endif
210#ifdef FEAT_SIGNS
211 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
212 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
213#endif
214 CENT("Visual term=reverse",
215 "Visual term=reverse guibg=LightGrey"),
216#ifdef FEAT_DIFF
217 CENT("DiffAdd term=bold ctermbg=LightBlue",
218 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
219 CENT("DiffChange term=bold ctermbg=LightMagenta",
220 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
221 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
222 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
223#endif
224 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
225 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
226#ifdef FEAT_SYN_HL
227 CENT("CursorColumn term=reverse ctermbg=LightGrey",
228 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
229 CENT("CursorLine term=underline cterm=underline",
230 "CursorLine term=underline cterm=underline guibg=Grey90"),
231 CENT("ColorColumn term=reverse ctermbg=LightRed",
232 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
233#endif
234#ifdef FEAT_CONCEAL
235 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
236 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
237#endif
238 CENT("MatchParen term=reverse ctermbg=Cyan",
239 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
240#ifdef FEAT_TERMINAL
241 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
242 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
243 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
244 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
245#endif
246#ifdef FEAT_MENU
247 CENT("ToolbarLine term=underline ctermbg=LightGrey",
248 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
249 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
250 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
251#endif
252 NULL
253};
254
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200255// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200256static char *(highlight_init_dark[]) = {
257 CENT("Directory term=bold ctermfg=LightCyan",
258 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
259 CENT("LineNr term=underline ctermfg=Yellow",
260 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200261 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
262 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200263 CENT("MoreMsg term=bold ctermfg=LightGreen",
264 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
265 CENT("Question term=standout ctermfg=LightGreen",
266 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
267 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
268 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
269 CENT("SpecialKey term=bold ctermfg=LightBlue",
270 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
271#ifdef FEAT_SPELL
272 CENT("SpellBad term=reverse ctermbg=Red",
273 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
274 CENT("SpellCap term=reverse ctermbg=Blue",
275 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
276 CENT("SpellRare term=reverse ctermbg=Magenta",
277 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
278 CENT("SpellLocal term=underline ctermbg=Cyan",
279 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
280#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200281 CENT("PmenuThumb ctermbg=White",
282 "PmenuThumb ctermbg=White guibg=White"),
283 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
284 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
285 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
286 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200287 CENT("Title term=bold ctermfg=LightMagenta",
288 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
289 CENT("WarningMsg term=standout ctermfg=LightRed",
290 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
291#ifdef FEAT_WILDMENU
292 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
293 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
294#endif
295#ifdef FEAT_FOLDING
296 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
297 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
298 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
299 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
300#endif
301#ifdef FEAT_SIGNS
302 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
303 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
304#endif
305 CENT("Visual term=reverse",
306 "Visual term=reverse guibg=DarkGrey"),
307#ifdef FEAT_DIFF
308 CENT("DiffAdd term=bold ctermbg=DarkBlue",
309 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
310 CENT("DiffChange term=bold ctermbg=DarkMagenta",
311 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
312 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
313 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
314#endif
315 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
316 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
317#ifdef FEAT_SYN_HL
318 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
319 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
320 CENT("CursorLine term=underline cterm=underline",
321 "CursorLine term=underline cterm=underline guibg=Grey40"),
322 CENT("ColorColumn term=reverse ctermbg=DarkRed",
323 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
324#endif
325 CENT("MatchParen term=reverse ctermbg=DarkCyan",
326 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
327#ifdef FEAT_CONCEAL
328 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
329 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
330#endif
331#ifdef FEAT_TERMINAL
332 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
333 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
334 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
335 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
336#endif
337#ifdef FEAT_MENU
338 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
339 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
340 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
341 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
342#endif
343 NULL
344};
345
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200346/*
347 * Returns the number of highlight groups.
348 */
349 int
350highlight_num_groups(void)
351{
352 return highlight_ga.ga_len;
353}
354
355/*
356 * Returns the name of a highlight group.
357 */
358 char_u *
359highlight_group_name(int id)
360{
361 return HL_TABLE()[id].sg_name;
362}
363
364/*
365 * Returns the ID of the link to a highlight group.
366 */
367 int
368highlight_link_id(int id)
369{
370 return HL_TABLE()[id].sg_link;
371}
372
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200373 void
374init_highlight(
375 int both, // include groups where 'bg' doesn't matter
376 int reset) // clear group first
377{
378 int i;
379 char **pp;
380 static int had_both = FALSE;
381#ifdef FEAT_EVAL
382 char_u *p;
383
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100384 // Try finding the color scheme file. Used when a color file was loaded
385 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200386 p = get_var_value((char_u *)"g:colors_name");
387 if (p != NULL)
388 {
389 // The value of g:colors_name could be freed when sourcing the script,
390 // making "p" invalid, so copy it.
391 char_u *copy_p = vim_strsave(p);
392 int r;
393
394 if (copy_p != NULL)
395 {
396 r = load_colors(copy_p);
397 vim_free(copy_p);
398 if (r == OK)
399 return;
400 }
401 }
402
403#endif
404
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100405 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200406 if (both)
407 {
408 had_both = TRUE;
409 pp = highlight_init_both;
410 for (i = 0; pp[i] != NULL; ++i)
411 do_highlight((char_u *)pp[i], reset, TRUE);
412 }
413 else if (!had_both)
414 // Don't do anything before the call with both == TRUE from main().
415 // Not everything has been setup then, and that call will overrule
416 // everything anyway.
417 return;
418
419 if (*p_bg == 'l')
420 pp = highlight_init_light;
421 else
422 pp = highlight_init_dark;
423 for (i = 0; pp[i] != NULL; ++i)
424 do_highlight((char_u *)pp[i], reset, TRUE);
425
426 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
427 // depend on the number of colors available.
428 // With 8 colors brown is equal to yellow, need to use black for Search fg
429 // to avoid Statement highlighted text disappears.
430 // Clear the attributes, needed when changing the t_Co value.
431 if (t_colors > 8)
432 do_highlight((char_u *)(*p_bg == 'l'
433 ? "Visual cterm=NONE ctermbg=LightGrey"
434 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
435 else
436 {
437 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
438 FALSE, TRUE);
439 if (*p_bg == 'l')
440 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
441 }
442
443#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100444 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200445 if (get_var_value((char_u *)"g:syntax_on") != NULL)
446 {
447 static int recursive = 0;
448
449 if (recursive >= 5)
450 emsg(_("E679: recursive loop loading syncolor.vim"));
451 else
452 {
453 ++recursive;
454 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
455 --recursive;
456 }
457 }
458#endif
459}
460
461/*
462 * Load color file "name".
463 * Return OK for success, FAIL for failure.
464 */
465 int
466load_colors(char_u *name)
467{
468 char_u *buf;
469 int retval = FAIL;
470 static int recursive = FALSE;
471
472 // When being called recursively, this is probably because setting
473 // 'background' caused the highlighting to be reloaded. This means it is
474 // working, thus we should return OK.
475 if (recursive)
476 return OK;
477
478 recursive = TRUE;
479 buf = alloc(STRLEN(name) + 12);
480 if (buf != NULL)
481 {
Bram Moolenaar2a521962021-10-25 10:30:14 +0100482#if defined(FEAT_EVAL) && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS))
Drew Vogele30d1022021-10-24 20:35:07 +0100483 load_default_colors_lists();
484#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200485 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
486 curbuf->b_fname, FALSE, curbuf);
487 sprintf((char *)buf, "colors/%s.vim", name);
488 retval = source_runtime(buf, DIP_START + DIP_OPT);
489 vim_free(buf);
490 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
491 }
492 recursive = FALSE;
493
494 return retval;
495}
496
497static char *(color_names[28]) = {
498 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
499 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
500 "Gray", "Grey", "LightGray", "LightGrey",
501 "DarkGray", "DarkGrey",
502 "Blue", "LightBlue", "Green", "LightGreen",
503 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
504 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
505 // indices:
506 // 0, 1, 2, 3,
507 // 4, 5, 6, 7,
508 // 8, 9, 10, 11,
509 // 12, 13,
510 // 14, 15, 16, 17,
511 // 18, 19, 20, 21, 22,
512 // 23, 24, 25, 26, 27
513static int color_numbers_16[28] = {0, 1, 2, 3,
514 4, 5, 6, 6,
515 7, 7, 7, 7,
516 8, 8,
517 9, 9, 10, 10,
518 11, 11, 12, 12, 13,
519 13, 14, 14, 15, -1};
520// for xterm with 88 colors...
521static int color_numbers_88[28] = {0, 4, 2, 6,
522 1, 5, 32, 72,
523 84, 84, 7, 7,
524 82, 82,
525 12, 43, 10, 61,
526 14, 63, 9, 74, 13,
527 75, 11, 78, 15, -1};
528// for xterm with 256 colors...
529static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200530 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200531 248, 248, 7, 7,
532 242, 242,
533 12, 81, 10, 121,
534 14, 159, 9, 224, 13,
535 225, 11, 229, 15, -1};
536// for terminals with less than 16 colors...
537static int color_numbers_8[28] = {0, 4, 2, 6,
538 1, 5, 3, 3,
539 7, 7, 7, 7,
540 0+8, 0+8,
541 4+8, 4+8, 2+8, 2+8,
542 6+8, 6+8, 1+8, 1+8, 5+8,
543 5+8, 3+8, 3+8, 7+8, -1};
544
545/*
546 * Lookup the "cterm" value to be used for color with index "idx" in
547 * color_names[].
548 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
549 * colors, otherwise it will be unchanged.
550 */
551 int
552lookup_color(int idx, int foreground, int *boldp)
553{
554 int color = color_numbers_16[idx];
555 char_u *p;
556
557 // Use the _16 table to check if it's a valid color name.
558 if (color < 0)
559 return -1;
560
561 if (t_colors == 8)
562 {
563 // t_Co is 8: use the 8 colors table
564#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100565 // On qnx, the 8 & 16 color arrays are the same
566 if (STRNCMP(T_NAME, "qansi", 5) == 0)
567 color = color_numbers_16[idx];
568 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200569#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100570 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200571 if (foreground)
572 {
573 // set/reset bold attribute to get light foreground
574 // colors (on some terminals, e.g. "linux")
575 if (color & 8)
576 *boldp = TRUE;
577 else
578 *boldp = FALSE;
579 }
580 color &= 7; // truncate to 8 colors
581 }
582 else if (t_colors == 16 || t_colors == 88
583 || t_colors >= 256)
584 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100585 // Guess: if the termcap entry ends in 'm', it is
586 // probably an xterm-like terminal. Use the changed
587 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200588 if (*T_CAF != NUL)
589 p = T_CAF;
590 else
591 p = T_CSF;
592 if (*p != NUL && (t_colors > 256
593 || *(p + STRLEN(p) - 1) == 'm'))
594 {
595 if (t_colors == 88)
596 color = color_numbers_88[idx];
597 else if (t_colors >= 256)
598 color = color_numbers_256[idx];
599 else
600 color = color_numbers_8[idx];
601 }
602#ifdef FEAT_TERMRESPONSE
603 if (t_colors >= 256 && color == 15 && is_mac_terminal)
604 // Terminal.app has a bug: 15 is light grey. Use white
605 // from the color cube instead.
606 color = 231;
607#endif
608 }
609 return color;
610}
611
612/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100613 * Link highlight group 'from_hg' to 'to_hg'.
614 * 'dodefault' is set to TRUE for ":highlight default link".
615 * 'forceit' is set to TRUE for ":higlight! link"
616 * 'init' is set to TRUE when initializing all the highlight groups.
617 */
618 static void
619highlight_group_link(
620 char_u *from_hg,
621 int from_len,
622 char_u *to_hg,
623 int to_len,
624 int dodefault,
625 int forceit,
626 int init)
627{
628 int from_id;
629 int to_id;
630 hl_group_T *hlgroup = NULL;
631
632 from_id = syn_check_group(from_hg, from_len);
633 if (STRNCMP(to_hg, "NONE", 4) == 0)
634 to_id = 0;
635 else
636 to_id = syn_check_group(to_hg, to_len);
637
638 if (from_id > 0)
639 {
640 hlgroup = &HL_TABLE()[from_id - 1];
641 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
642 {
643 hlgroup->sg_deflink = to_id;
644#ifdef FEAT_EVAL
645 hlgroup->sg_deflink_sctx = current_sctx;
646 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
647#endif
648 }
649 }
650
651 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
652 {
653 // Don't allow a link when there already is some highlighting
654 // for the group, unless '!' is used
655 if (to_id > 0 && !forceit && !init
656 && hl_has_settings(from_id - 1, dodefault))
657 {
658 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000659 emsg(_(e_group_has_settings_highlight_link_ignored));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100660 }
661 else if (hlgroup->sg_link != to_id
662#ifdef FEAT_EVAL
663 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
664#endif
665 || hlgroup->sg_cleared)
666 {
667 if (!init)
668 hlgroup->sg_set |= SG_LINK;
669 hlgroup->sg_link = to_id;
670#ifdef FEAT_EVAL
671 hlgroup->sg_script_ctx = current_sctx;
672 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
673#endif
674 hlgroup->sg_cleared = FALSE;
675 redraw_all_later(SOME_VALID);
676
677 // Only call highlight_changed() once after multiple changes.
678 need_highlight_changed = TRUE;
679 }
680 }
681
682}
683
684/*
685 * Reset all highlighting to the defaults. Removes all highlighting for the
686 * groups added by the user.
687 */
688 static void
689highlight_reset_all(void)
690{
691 int idx;
692
693#ifdef FEAT_GUI
694 // First, we do not destroy the old values, but allocate the new
695 // ones and update the display. THEN we destroy the old values.
696 // If we destroy the old values first, then the old values
697 // (such as GuiFont's or GuiFontset's) will still be displayed but
698 // invalid because they were free'd.
699 if (gui.in_use)
700 {
701# ifdef FEAT_BEVAL_TIP
702 gui_init_tooltip_font();
703# endif
704# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
705 gui_init_menu_font();
706# endif
707 }
708# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
709 gui_mch_def_colors();
710# endif
711# ifdef FEAT_GUI_X11
712# ifdef FEAT_MENU
713
714 // This only needs to be done when there is no Menu highlight
715 // group defined by default, which IS currently the case.
716 gui_mch_new_menu_colors();
717# endif
718 if (gui.in_use)
719 {
720 gui_new_scrollbar_colors();
721# ifdef FEAT_BEVAL_GUI
722 gui_mch_new_tooltip_colors();
723# endif
724# ifdef FEAT_MENU
725 gui_mch_new_menu_font();
726# endif
727 }
728# endif
729
730 // Ok, we're done allocating the new default graphics items.
731 // The screen should already be refreshed at this point.
732 // It is now Ok to clear out the old data.
733#endif
734#ifdef FEAT_EVAL
735 do_unlet((char_u *)"g:colors_name", TRUE);
736#endif
737 restore_cterm_colors();
738
739 // Clear all default highlight groups and load the defaults.
740 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
741 highlight_clear(idx);
742 init_highlight(TRUE, TRUE);
743#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
744 if (USE_24BIT)
745 highlight_gui_started();
746 else
747#endif
748 highlight_changed();
749 redraw_later_clear();
750}
751
752/*
753 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
754 * index 'idx'.
755 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
756 * 'arg' is the list of attribute names separated by comma.
757 * 'init' is set to TRUE when initializing all the highlight groups.
758 * Returns TRUE if the attributes are set.
759 */
760 static int
761highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
762{
763 int attr;
764 int off;
765 long i;
766 int len;
767
768 attr = 0;
769 off = 0;
770 while (arg[off] != NUL)
771 {
772 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
773 {
774 len = (int)STRLEN(hl_name_table[i]);
775 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
776 {
777 attr |= hl_attr_table[i];
778 off += len;
779 break;
780 }
781 }
782 if (i < 0)
783 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +0000784 semsg(_(e_illegal_value_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100785 return FALSE;
786 }
787 if (arg[off] == ',') // another one follows
788 ++off;
789 }
790 if (*key == 'T')
791 {
792 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
793 {
794 if (!init)
795 HL_TABLE()[idx].sg_set |= SG_TERM;
796 HL_TABLE()[idx].sg_term = attr;
797 }
798 }
799 else if (*key == 'C')
800 {
801 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
802 {
803 if (!init)
804 HL_TABLE()[idx].sg_set |= SG_CTERM;
805 HL_TABLE()[idx].sg_cterm = attr;
806 HL_TABLE()[idx].sg_cterm_bold = FALSE;
807 }
808 }
809#if defined(FEAT_GUI) || defined(FEAT_EVAL)
810 else
811 {
812 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
813 {
814 if (!init)
815 HL_TABLE()[idx].sg_set |= SG_GUI;
816 HL_TABLE()[idx].sg_gui = attr;
817 }
818 }
819#endif
820
821 return TRUE;
822}
823
824#ifdef FEAT_GUI
825/*
826 * Set the font for the highlight group at 'idx'.
827 * 'arg' is the font name.
828 * Returns TRUE if the font is changed.
829 */
830 static int
831highlight_set_font(
832 int idx,
833 char_u *arg,
834 int is_normal_group,
835 int is_menu_group,
836 int is_tooltip_group)
837{
838 int did_change = FALSE;
839
840 // in non-GUI fonts are simply ignored
841 if (HL_TABLE()[idx].sg_font_name != NULL
842 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
843 {
844 // Font name didn't change, ignore.
845 }
846 else if (!gui.shell_created)
847 {
848 // GUI not started yet, always accept the name.
849 vim_free(HL_TABLE()[idx].sg_font_name);
850 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
851 did_change = TRUE;
852 }
853 else
854 {
855 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
856# ifdef FEAT_XFONTSET
857 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
858# endif
859 // First, save the current font/fontset.
860 // Then try to allocate the font/fontset.
861 // If the allocation fails, HL_TABLE()[idx].sg_font OR
862 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
863
864 HL_TABLE()[idx].sg_font = NOFONT;
865# ifdef FEAT_XFONTSET
866 HL_TABLE()[idx].sg_fontset = NOFONTSET;
867# endif
868 hl_do_font(idx, arg, is_normal_group, is_menu_group,
869 is_tooltip_group, FALSE);
870
871# ifdef FEAT_XFONTSET
872 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
873 {
874 // New fontset was accepted. Free the old one, if there
875 // was one.
876 gui_mch_free_fontset(temp_sg_fontset);
877 vim_free(HL_TABLE()[idx].sg_font_name);
878 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
879 did_change = TRUE;
880 }
881 else
882 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
883# endif
884 if (HL_TABLE()[idx].sg_font != NOFONT)
885 {
886 // New font was accepted. Free the old one, if there was
887 // one.
888 gui_mch_free_font(temp_sg_font);
889 vim_free(HL_TABLE()[idx].sg_font_name);
890 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
891 did_change = TRUE;
892 }
893 else
894 HL_TABLE()[idx].sg_font = temp_sg_font;
895 }
896
897 return did_change;
898}
899#endif
900
901/*
902 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
903 * Returns TRUE if the foreground color is set.
904 */
905 static void
906highlight_set_ctermfg(int idx, int color, int is_normal_group)
907{
908 HL_TABLE()[idx].sg_cterm_fg = color + 1;
909 if (is_normal_group)
910 {
911 cterm_normal_fg_color = color + 1;
912 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
913#ifdef FEAT_GUI
914 // Don't do this if the GUI is used.
915 if (!gui.in_use && !gui.starting)
916#endif
917 {
918 must_redraw = CLEAR;
919 if (termcap_active && color >= 0)
920 term_fg_color(color);
921 }
922 }
923}
924
925/*
926 * Set the cterm background color for the highlight group at 'idx' to 'color'.
927 * Returns TRUE if the background color is set.
928 */
929 static void
930highlight_set_ctermbg(int idx, int color, int is_normal_group)
931{
932 HL_TABLE()[idx].sg_cterm_bg = color + 1;
933 if (is_normal_group)
934 {
935 cterm_normal_bg_color = color + 1;
936#ifdef FEAT_GUI
937 // Don't mess with 'background' if the GUI is used.
938 if (!gui.in_use && !gui.starting)
939#endif
940 {
941 must_redraw = CLEAR;
942 if (color >= 0)
943 {
944 int dark = -1;
945
946 if (termcap_active)
947 term_bg_color(color);
948 if (t_colors < 16)
949 dark = (color == 0 || color == 4);
950 // Limit the heuristic to the standard 16 colors
951 else if (color < 16)
952 dark = (color < 7 || color == 8);
953 // Set the 'background' option if the value is
954 // wrong.
955 if (dark != -1
956 && dark != (*p_bg == 'd')
957 && !option_was_set((char_u *)"bg"))
958 {
959 set_option_value((char_u *)"bg", 0L,
960 (char_u *)(dark ? "dark" : "light"), 0);
961 reset_option_was_set((char_u *)"bg");
962 }
963 }
964 }
965 }
966}
967
968/*
969 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
970 * Returns TRUE if the underline color is set.
971 */
972 static void
973highlight_set_ctermul(int idx, int color, int is_normal_group)
974{
975 HL_TABLE()[idx].sg_cterm_ul = color + 1;
976 if (is_normal_group)
977 {
978 cterm_normal_ul_color = color + 1;
979#ifdef FEAT_GUI
980 // Don't do this if the GUI is used.
981 if (!gui.in_use && !gui.starting)
982#endif
983 {
984 must_redraw = CLEAR;
985 if (termcap_active && color >= 0)
986 term_ul_color(color);
987 }
988 }
989}
990
991/*
992 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
993 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
994 * 'keystart' is the color name/value.
995 * 'arg' is the color name or the numeric value as a string.
996 * 'is_normal_group' is set if the highlight group is 'NORMAL'
997 * 'init' is set to TRUE when initializing highlighting.
998 * Called for the ":highlight" command and the "hlset()" function.
999 *
1000 * Returns TRUE if the color is set.
1001 */
1002 static int
1003highlight_set_cterm_color(
1004 int idx,
1005 char_u *key,
1006 char_u *key_start,
1007 char_u *arg,
1008 int is_normal_group,
1009 int init)
1010{
1011 int color;
1012 long i;
1013 int off;
1014
1015 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1016 {
1017 if (!init)
1018 HL_TABLE()[idx].sg_set |= SG_CTERM;
1019
1020 // When setting the foreground color, and previously the "bold"
1021 // flag was set for a light color, reset it now
1022 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1023 {
1024 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1025 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1026 }
1027
1028 if (VIM_ISDIGIT(*arg))
1029 color = atoi((char *)arg);
1030 else if (STRICMP(arg, "fg") == 0)
1031 {
1032 if (cterm_normal_fg_color)
1033 color = cterm_normal_fg_color - 1;
1034 else
1035 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001036 emsg(_(e_fg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001037 return FALSE;
1038 }
1039 }
1040 else if (STRICMP(arg, "bg") == 0)
1041 {
1042 if (cterm_normal_bg_color > 0)
1043 color = cterm_normal_bg_color - 1;
1044 else
1045 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001046 emsg(_(e_bg_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001047 return FALSE;
1048 }
1049 }
1050 else if (STRICMP(arg, "ul") == 0)
1051 {
1052 if (cterm_normal_ul_color > 0)
1053 color = cterm_normal_ul_color - 1;
1054 else
1055 {
Bram Moolenaarb09feaa2022-01-02 20:20:45 +00001056 emsg(_(e_ul_color_unknown));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001057 return FALSE;
1058 }
1059 }
1060 else
1061 {
1062 int bold = MAYBE;
1063
1064 // reduce calls to STRICMP a bit, it can be slow
1065 off = TOUPPER_ASC(*arg);
1066 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1067 if (off == color_names[i][0]
1068 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1069 break;
1070 if (i < 0)
1071 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001072 semsg(_(e_color_name_or_number_not_recognized), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001073 return FALSE;
1074 }
1075
1076 color = lookup_color(i, key[5] == 'F', &bold);
1077
1078 // set/reset bold attribute to get light foreground
1079 // colors (on some terminals, e.g. "linux")
1080 if (bold == TRUE)
1081 {
1082 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1083 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1084 }
1085 else if (bold == FALSE)
1086 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1087 }
1088
1089 // Add one to the argument, to avoid zero. Zero is used for
1090 // "NONE", then "color" is -1.
1091 if (key[5] == 'F')
1092 highlight_set_ctermfg(idx, color, is_normal_group);
1093 else if (key[5] == 'B')
1094 highlight_set_ctermbg(idx, color, is_normal_group);
1095 else // ctermul
1096 highlight_set_ctermul(idx, color, is_normal_group);
1097 }
1098
1099 return TRUE;
1100}
1101
1102#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1103/*
1104 * Set the GUI foreground color for the highlight group at 'idx'.
1105 * Returns TRUE if the color is set.
1106 */
1107 static int
1108highlight_set_guifg(
1109 int idx,
1110 char_u *arg,
1111 int is_menu_group UNUSED,
1112 int is_scrollbar_group UNUSED,
1113 int is_tooltip_group UNUSED,
1114 int *do_colors UNUSED,
1115 int init)
1116{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001117# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001118 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001119# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001120 char_u **namep;
1121 int did_change = FALSE;
1122
1123 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1124 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1125 {
1126 if (!init)
1127 HL_TABLE()[idx].sg_set |= SG_GUI;
1128
1129# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1130 // In GUI guifg colors are only used when recognized
1131 i = color_name2handle(arg);
1132 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1133 {
1134 HL_TABLE()[idx].sg_gui_fg = i;
1135# endif
1136 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1137 {
1138 vim_free(*namep);
1139 if (STRCMP(arg, "NONE") != 0)
1140 *namep = vim_strsave(arg);
1141 else
1142 *namep = NULL;
1143 did_change = TRUE;
1144 }
1145# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1146# ifdef FEAT_GUI_X11
1147 if (is_menu_group && gui.menu_fg_pixel != i)
1148 {
1149 gui.menu_fg_pixel = i;
1150 *do_colors = TRUE;
1151 }
1152 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1153 {
1154 gui.scroll_fg_pixel = i;
1155 *do_colors = TRUE;
1156 }
1157# ifdef FEAT_BEVAL_GUI
1158 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1159 {
1160 gui.tooltip_fg_pixel = i;
1161 *do_colors = TRUE;
1162 }
1163# endif
1164# endif
1165 }
1166# endif
1167 }
1168
1169 return did_change;
1170}
1171
1172/*
1173 * Set the GUI background color for the highlight group at 'idx'.
1174 * Returns TRUE if the color is set.
1175 */
1176 static int
1177highlight_set_guibg(
1178 int idx,
1179 char_u *arg,
1180 int is_menu_group UNUSED,
1181 int is_scrollbar_group UNUSED,
1182 int is_tooltip_group UNUSED,
1183 int *do_colors UNUSED,
1184 int init)
1185{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001186# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001187 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001188# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001189 char_u **namep;
1190 int did_change = FALSE;
1191
1192 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1193 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1194 {
1195 if (!init)
1196 HL_TABLE()[idx].sg_set |= SG_GUI;
1197
1198# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Drew Vogele30d1022021-10-24 20:35:07 +01001199 // In GUI guibg colors are only used when recognized
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001200 i = color_name2handle(arg);
1201 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1202 {
1203 HL_TABLE()[idx].sg_gui_bg = i;
1204# endif
1205 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1206 {
1207 vim_free(*namep);
1208 if (STRCMP(arg, "NONE") != 0)
1209 *namep = vim_strsave(arg);
1210 else
1211 *namep = NULL;
1212 did_change = TRUE;
1213 }
1214# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1215# ifdef FEAT_GUI_X11
1216 if (is_menu_group && gui.menu_bg_pixel != i)
1217 {
1218 gui.menu_bg_pixel = i;
1219 *do_colors = TRUE;
1220 }
1221 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1222 {
1223 gui.scroll_bg_pixel = i;
1224 *do_colors = TRUE;
1225 }
1226# ifdef FEAT_BEVAL_GUI
1227 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1228 {
1229 gui.tooltip_bg_pixel = i;
1230 *do_colors = TRUE;
1231 }
1232# endif
1233# endif
1234 }
1235# endif
1236 }
1237
1238 return did_change;
1239}
1240
1241/*
1242 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1243 * Returns TRUE if the color is set.
1244 */
1245 static int
1246highlight_set_guisp(int idx, char_u *arg, int init)
1247{
1248# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1249 int i;
1250# endif
1251 int did_change = FALSE;
1252 char_u **namep;
1253
1254 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1255 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1256 {
1257 if (!init)
1258 HL_TABLE()[idx].sg_set |= SG_GUI;
1259
1260# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1261 // In GUI guisp colors are only used when recognized
1262 i = color_name2handle(arg);
1263 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1264 {
1265 HL_TABLE()[idx].sg_gui_sp = i;
1266# endif
1267 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1268 {
1269 vim_free(*namep);
1270 if (STRCMP(arg, "NONE") != 0)
1271 *namep = vim_strsave(arg);
1272 else
1273 *namep = NULL;
1274 did_change = TRUE;
1275 }
1276# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1277 }
1278# endif
1279 }
1280
1281 return did_change;
1282}
1283#endif
1284
1285/*
1286 * Set the start/stop terminal codes for a highlight group.
1287 * Returns TRUE if the terminal code is set.
1288 */
1289 static int
1290highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1291{
1292 int off;
1293 char_u buf[100];
1294 int len;
1295 char_u *tname;
1296 char_u *p;
1297
1298 if (!init)
1299 HL_TABLE()[idx].sg_set |= SG_TERM;
1300
1301 // The "start" and "stop" arguments can be a literal escape
1302 // sequence, or a comma separated list of terminal codes.
1303 if (STRNCMP(arg, "t_", 2) == 0)
1304 {
1305 off = 0;
1306 buf[0] = 0;
1307 while (arg[off] != NUL)
1308 {
1309 // Isolate one termcap name
1310 for (len = 0; arg[off + len] &&
1311 arg[off + len] != ','; ++len)
1312 ;
1313 tname = vim_strnsave(arg + off, len);
1314 if (tname == NULL) // out of memory
1315 return FALSE;
1316 // lookup the escape sequence for the item
1317 p = get_term_code(tname);
1318 vim_free(tname);
1319 if (p == NULL) // ignore non-existing things
1320 p = (char_u *)"";
1321
1322 // Append it to the already found stuff
1323 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1324 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001325 semsg(_(e_terminal_code_too_long_str), arg);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001326 return FALSE;
1327 }
1328 STRCAT(buf, p);
1329
1330 // Advance to the next item
1331 off += len;
1332 if (arg[off] == ',') // another one follows
1333 ++off;
1334 }
1335 }
1336 else
1337 {
1338 // Copy characters from arg[] to buf[], translating <> codes.
1339 for (p = arg, off = 0; off < 100 - 6 && *p; )
1340 {
1341 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
1342 if (len > 0) // recognized special char
1343 off += len;
1344 else // copy as normal char
1345 buf[off++] = *p++;
1346 }
1347 buf[off] = NUL;
1348 }
1349
1350 if (STRCMP(buf, "NONE") == 0) // resetting the value
1351 p = NULL;
1352 else
1353 p = vim_strsave(buf);
1354 if (key[2] == 'A')
1355 {
1356 vim_free(HL_TABLE()[idx].sg_start);
1357 HL_TABLE()[idx].sg_start = p;
1358 }
1359 else
1360 {
1361 vim_free(HL_TABLE()[idx].sg_stop);
1362 HL_TABLE()[idx].sg_stop = p;
1363 }
1364 return TRUE;
1365}
1366
1367/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001368 * Handle the ":highlight .." command.
1369 * When using ":hi clear" this is called recursively for each group with
1370 * "forceit" and "init" both TRUE.
1371 */
1372 void
1373do_highlight(
1374 char_u *line,
1375 int forceit,
1376 int init) // TRUE when called for initializing
1377{
1378 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001379 char_u *linep;
1380 char_u *key_start;
1381 char_u *arg_start;
1382 char_u *key = NULL, *arg = NULL;
1383 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001384 int id;
1385 int idx;
1386 hl_group_T item_before;
1387 int did_change = FALSE;
1388 int dodefault = FALSE;
1389 int doclear = FALSE;
1390 int dolink = FALSE;
1391 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001392 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001393#ifdef FEAT_GUI_X11
1394 int is_menu_group = FALSE; // "Menu" group
1395 int is_scrollbar_group = FALSE; // "Scrollbar" group
1396 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001397#else
1398# define is_menu_group 0
1399# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001400# define is_scrollbar_group 0
1401#endif
1402#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1403 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001404#endif
1405#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1406 int did_highlight_changed = FALSE;
1407#endif
1408
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001409 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001410 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001411 {
1412 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1413 // TODO: only call when the group has attributes set
1414 highlight_list_one((int)i);
1415 return;
1416 }
1417
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001418 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001419 name_end = skiptowhite(line);
1420 linep = skipwhite(name_end);
1421
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001422 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001423 if (STRNCMP(line, "default", name_end - line) == 0)
1424 {
1425 dodefault = TRUE;
1426 line = linep;
1427 name_end = skiptowhite(line);
1428 linep = skipwhite(name_end);
1429 }
1430
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001431 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001432 if (STRNCMP(line, "clear", name_end - line) == 0)
1433 doclear = TRUE;
1434 if (STRNCMP(line, "link", name_end - line) == 0)
1435 dolink = TRUE;
1436
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001437 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001438 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001439 {
1440 id = syn_namen2id(line, (int)(name_end - line));
1441 if (id == 0)
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001442 semsg(_(e_highlight_group_name_not_found_str), line);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001443 else
1444 highlight_list_one(id);
1445 return;
1446 }
1447
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001448 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001449 if (dolink)
1450 {
1451 char_u *from_start = linep;
1452 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001453 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001454 char_u *to_start;
1455 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001456 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001457
1458 from_end = skiptowhite(from_start);
1459 to_start = skipwhite(from_end);
1460 to_end = skiptowhite(to_start);
1461
Bram Moolenaar1966c242020-04-20 22:42:32 +02001462 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001463 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001464 semsg(_(e_not_enough_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001465 return;
1466 }
1467
Bram Moolenaar1966c242020-04-20 22:42:32 +02001468 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001469 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001470 semsg(_(e_too_many_arguments_highlight_link_str), from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001471 return;
1472 }
1473
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001474 from_len = (int)(from_end - from_start);
1475 to_len = (int)(to_end - to_start);
1476 highlight_group_link(from_start, from_len, to_start, to_len,
1477 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001478 return;
1479 }
1480
1481 if (doclear)
1482 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001483 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001484 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001485 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001486 // ":highlight clear" without group name
1487 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001488 return;
1489 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001490 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001491 name_end = skiptowhite(line);
1492 linep = skipwhite(name_end);
1493 }
1494
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001495 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001496 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001497 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001498 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001499 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001500
1501 // Return if "default" was used and the group already has settings.
1502 if (dodefault && hl_has_settings(idx, TRUE))
1503 return;
1504
1505 // Make a copy so we can check if any attribute actually changed.
1506 item_before = HL_TABLE()[idx];
1507
1508 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1509 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001510#ifdef FEAT_GUI_X11
1511 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1512 is_menu_group = TRUE;
1513 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1514 is_scrollbar_group = TRUE;
1515 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1516 is_tooltip_group = TRUE;
1517#endif
1518
1519 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1520 if (doclear || (forceit && init))
1521 {
1522 highlight_clear(idx);
1523 if (!doclear)
1524 HL_TABLE()[idx].sg_set = 0;
1525 }
1526
1527 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001528 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001529 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001530 key_start = linep;
1531 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001532 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001533 semsg(_(e_unexpected_equal_sign_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001534 error = TRUE;
1535 break;
1536 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001537
1538 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1539 // or "guibg").
1540 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1541 ++linep;
1542 vim_free(key);
1543 key = vim_strnsave_up(key_start, linep - key_start);
1544 if (key == NULL)
1545 {
1546 error = TRUE;
1547 break;
1548 }
1549 linep = skipwhite(linep);
1550
1551 if (STRCMP(key, "NONE") == 0)
1552 {
1553 if (!init || HL_TABLE()[idx].sg_set == 0)
1554 {
1555 if (!init)
1556 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1557 highlight_clear(idx);
1558 }
1559 continue;
1560 }
1561
1562 // Check for the equal sign.
1563 if (*linep != '=')
1564 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001565 semsg(_(e_missing_equal_sign_str_2), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001566 error = TRUE;
1567 break;
1568 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001569 ++linep;
1570
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001571 // Isolate the argument.
1572 linep = skipwhite(linep);
1573 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001574 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001575 arg_start = ++linep;
1576 linep = vim_strchr(linep, '\'');
1577 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001578 {
Bram Moolenaar436b5ad2021-12-31 22:49:24 +00001579 semsg(_(e_invalid_argument_str), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001580 error = TRUE;
1581 break;
1582 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001583 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001584 else
1585 {
1586 arg_start = linep;
1587 linep = skiptowhite(linep);
1588 }
1589 if (linep == arg_start)
1590 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001591 semsg(_(e_missing_argument_str), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001592 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001593 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001594 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001595 vim_free(arg);
1596 arg = vim_strnsave(arg_start, linep - arg_start);
1597 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001598 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001599 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001600 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001601 }
1602 if (*linep == '\'')
1603 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001604
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001605 // Store the argument.
1606 if (STRCMP(key, "TERM") == 0
1607 || STRCMP(key, "CTERM") == 0
1608 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001609 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001610 if (!highlight_set_termgui_attr(idx, key, arg, init))
1611 {
1612 error = TRUE;
1613 break;
1614 }
1615 }
1616 else if (STRCMP(key, "FONT") == 0)
1617 {
1618 // in non-GUI fonts are simply ignored
1619#ifdef FEAT_GUI
1620 if (highlight_set_font(idx, arg, is_normal_group,
1621 is_menu_group, is_tooltip_group))
1622 did_change = TRUE;
1623#endif
1624 }
1625 else if (STRCMP(key, "CTERMFG") == 0
1626 || STRCMP(key, "CTERMBG") == 0
1627 || STRCMP(key, "CTERMUL") == 0)
1628 {
1629 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1630 is_normal_group, init))
1631 {
1632 error = TRUE;
1633 break;
1634 }
1635 }
1636 else if (STRCMP(key, "GUIFG") == 0)
1637 {
1638#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1639 if (highlight_set_guifg(idx, arg, is_menu_group,
1640 is_scrollbar_group, is_tooltip_group,
1641 &do_colors, init))
1642 did_change = TRUE;
1643#endif
1644 }
1645 else if (STRCMP(key, "GUIBG") == 0)
1646 {
1647#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1648 if (highlight_set_guibg(idx, arg, is_menu_group,
1649 is_scrollbar_group, is_tooltip_group,
1650 &do_colors, init))
1651 did_change = TRUE;
1652#endif
1653 }
1654 else if (STRCMP(key, "GUISP") == 0)
1655 {
1656#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1657 if (highlight_set_guisp(idx, arg, init))
1658 did_change = TRUE;
1659#endif
1660 }
1661 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1662 {
1663 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1664 {
1665 error = TRUE;
1666 break;
1667 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001668 }
1669 else
1670 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00001671 semsg(_(e_illegal_argument_str_3), key_start);
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001672 error = TRUE;
1673 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001674 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001675 HL_TABLE()[idx].sg_cleared = FALSE;
1676
1677 // When highlighting has been given for a group, don't link it.
1678 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1679 HL_TABLE()[idx].sg_link = 0;
1680
1681 // Continue with next argument.
1682 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001683 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001684
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001685 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001686 if (error && idx == highlight_ga.ga_len)
1687 syn_unadd_group();
1688 else
1689 {
1690 if (is_normal_group)
1691 {
1692 HL_TABLE()[idx].sg_term_attr = 0;
1693 HL_TABLE()[idx].sg_cterm_attr = 0;
1694#ifdef FEAT_GUI
1695 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001696 // Need to update all groups, because they might be using "bg"
1697 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001698#endif
1699#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1700 if (USE_24BIT)
1701 {
1702 highlight_gui_started();
1703 did_highlight_changed = TRUE;
1704 redraw_all_later(NOT_VALID);
1705 }
1706#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001707#ifdef FEAT_VTP
1708 control_console_color_rgb();
1709#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001710 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001711#ifdef FEAT_GUI_X11
1712# ifdef FEAT_MENU
1713 else if (is_menu_group)
1714 {
1715 if (gui.in_use && do_colors)
1716 gui_mch_new_menu_colors();
1717 }
1718# endif
1719 else if (is_scrollbar_group)
1720 {
1721 if (gui.in_use && do_colors)
1722 gui_new_scrollbar_colors();
1723 else
1724 set_hl_attr(idx);
1725 }
1726# ifdef FEAT_BEVAL_GUI
1727 else if (is_tooltip_group)
1728 {
1729 if (gui.in_use && do_colors)
1730 gui_mch_new_tooltip_colors();
1731 }
1732# endif
1733#endif
1734 else
1735 set_hl_attr(idx);
1736#ifdef FEAT_EVAL
1737 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001738 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001739#endif
1740 }
1741
1742 vim_free(key);
1743 vim_free(arg);
1744
1745 // Only call highlight_changed() once, after a sequence of highlight
1746 // commands, and only if an attribute actually changed.
1747 if ((did_change
1748 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1749#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1750 && !did_highlight_changed
1751#endif
1752 )
1753 {
1754 // Do not trigger a redraw when highlighting is changed while
1755 // redrawing. This may happen when evaluating 'statusline' changes the
1756 // StatusLine group.
1757 if (!updating_screen)
1758 redraw_all_later(NOT_VALID);
1759 need_highlight_changed = TRUE;
1760 }
1761}
1762
1763#if defined(EXITFREE) || defined(PROTO)
1764 void
1765free_highlight(void)
1766{
1767 int i;
1768
1769 for (i = 0; i < highlight_ga.ga_len; ++i)
1770 {
1771 highlight_clear(i);
1772 vim_free(HL_TABLE()[i].sg_name);
1773 vim_free(HL_TABLE()[i].sg_name_u);
1774 }
1775 ga_clear(&highlight_ga);
1776}
1777#endif
1778
1779/*
1780 * Reset the cterm colors to what they were before Vim was started, if
1781 * possible. Otherwise reset them to zero.
1782 */
1783 void
1784restore_cterm_colors(void)
1785{
1786#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1787 // Since t_me has been set, this probably means that the user
1788 // wants to use this as default colors. Need to reset default
1789 // background/foreground colors.
1790 mch_set_normal_colors();
1791#else
1792# ifdef VIMDLL
1793 if (!gui.in_use)
1794 {
1795 mch_set_normal_colors();
1796 return;
1797 }
1798# endif
1799 cterm_normal_fg_color = 0;
1800 cterm_normal_fg_bold = 0;
1801 cterm_normal_bg_color = 0;
1802# ifdef FEAT_TERMGUICOLORS
1803 cterm_normal_fg_gui_color = INVALCOLOR;
1804 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001805 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001806# endif
1807#endif
1808}
1809
1810/*
1811 * Return TRUE if highlight group "idx" has any settings.
1812 * When "check_link" is TRUE also check for an existing link.
1813 */
1814 static int
1815hl_has_settings(int idx, int check_link)
1816{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001817 return HL_TABLE()[idx].sg_cleared == 0
1818 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001819 || HL_TABLE()[idx].sg_cterm_attr != 0
1820 || HL_TABLE()[idx].sg_cterm_fg != 0
1821 || HL_TABLE()[idx].sg_cterm_bg != 0
1822#ifdef FEAT_GUI
1823 || HL_TABLE()[idx].sg_gui_attr != 0
1824 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1825 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1826 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1827 || HL_TABLE()[idx].sg_font_name != NULL
1828#endif
1829 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1830}
1831
1832/*
1833 * Clear highlighting for one group.
1834 */
1835 static void
1836highlight_clear(int idx)
1837{
1838 HL_TABLE()[idx].sg_cleared = TRUE;
1839
1840 HL_TABLE()[idx].sg_term = 0;
1841 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1842 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1843 HL_TABLE()[idx].sg_term_attr = 0;
1844 HL_TABLE()[idx].sg_cterm = 0;
1845 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1846 HL_TABLE()[idx].sg_cterm_fg = 0;
1847 HL_TABLE()[idx].sg_cterm_bg = 0;
1848 HL_TABLE()[idx].sg_cterm_attr = 0;
1849#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1850 HL_TABLE()[idx].sg_gui = 0;
1851 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1852 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1853 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1854#endif
1855#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1856 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1857 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001858 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001859#endif
1860#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001861 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1862 HL_TABLE()[idx].sg_font = NOFONT;
1863# ifdef FEAT_XFONTSET
1864 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1865 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1866# endif
1867 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1868 HL_TABLE()[idx].sg_gui_attr = 0;
1869#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001870 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001871 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001872#ifdef FEAT_EVAL
1873 // Since we set the default link, set the location to where the default
1874 // link was set.
1875 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001876#endif
1877}
1878
1879#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1880/*
1881 * Set the normal foreground and background colors according to the "Normal"
1882 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1883 * "Tooltip" colors.
1884 */
1885 void
1886set_normal_colors(void)
1887{
1888# ifdef FEAT_GUI
1889# ifdef FEAT_TERMGUICOLORS
1890 if (gui.in_use)
1891# endif
1892 {
1893 if (set_group_colors((char_u *)"Normal",
1894 &gui.norm_pixel, &gui.back_pixel,
1895 FALSE, TRUE, FALSE))
1896 {
1897 gui_mch_new_colors();
1898 must_redraw = CLEAR;
1899 }
1900# ifdef FEAT_GUI_X11
1901 if (set_group_colors((char_u *)"Menu",
1902 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1903 TRUE, FALSE, FALSE))
1904 {
1905# ifdef FEAT_MENU
1906 gui_mch_new_menu_colors();
1907# endif
1908 must_redraw = CLEAR;
1909 }
1910# ifdef FEAT_BEVAL_GUI
1911 if (set_group_colors((char_u *)"Tooltip",
1912 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1913 FALSE, FALSE, TRUE))
1914 {
1915# ifdef FEAT_TOOLBAR
1916 gui_mch_new_tooltip_colors();
1917# endif
1918 must_redraw = CLEAR;
1919 }
1920# endif
1921 if (set_group_colors((char_u *)"Scrollbar",
1922 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1923 FALSE, FALSE, FALSE))
1924 {
1925 gui_new_scrollbar_colors();
1926 must_redraw = CLEAR;
1927 }
1928# endif
1929 }
1930# endif
1931# ifdef FEAT_TERMGUICOLORS
1932# ifdef FEAT_GUI
1933 else
1934# endif
1935 {
1936 int idx;
1937
1938 idx = syn_name2id((char_u *)"Normal") - 1;
1939 if (idx >= 0)
1940 {
1941 gui_do_one_color(idx, FALSE, FALSE);
1942
1943 // If the normal fg or bg color changed a complete redraw is
1944 // required.
1945 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1946 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1947 {
1948 // if the GUI color is INVALCOLOR then we use the default cterm
1949 // color
1950 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1951 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1952 must_redraw = CLEAR;
1953 }
1954 }
1955 }
1956# endif
1957}
1958#endif
1959
1960#if defined(FEAT_GUI) || defined(PROTO)
1961/*
1962 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1963 */
1964 static int
1965set_group_colors(
1966 char_u *name,
1967 guicolor_T *fgp,
1968 guicolor_T *bgp,
1969 int do_menu,
1970 int use_norm,
1971 int do_tooltip)
1972{
1973 int idx;
1974
1975 idx = syn_name2id(name) - 1;
1976 if (idx >= 0)
1977 {
1978 gui_do_one_color(idx, do_menu, do_tooltip);
1979
1980 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1981 *fgp = HL_TABLE()[idx].sg_gui_fg;
1982 else if (use_norm)
1983 *fgp = gui.def_norm_pixel;
1984 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1985 *bgp = HL_TABLE()[idx].sg_gui_bg;
1986 else if (use_norm)
1987 *bgp = gui.def_back_pixel;
1988 return TRUE;
1989 }
1990 return FALSE;
1991}
1992
1993/*
1994 * Get the font of the "Normal" group.
1995 * Returns "" when it's not found or not set.
1996 */
1997 char_u *
1998hl_get_font_name(void)
1999{
2000 int id;
2001 char_u *s;
2002
2003 id = syn_name2id((char_u *)"Normal");
2004 if (id > 0)
2005 {
2006 s = HL_TABLE()[id - 1].sg_font_name;
2007 if (s != NULL)
2008 return s;
2009 }
2010 return (char_u *)"";
2011}
2012
2013/*
2014 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2015 * actually chosen to be used.
2016 */
2017 void
2018hl_set_font_name(char_u *font_name)
2019{
2020 int id;
2021
2022 id = syn_name2id((char_u *)"Normal");
2023 if (id > 0)
2024 {
2025 vim_free(HL_TABLE()[id - 1].sg_font_name);
2026 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2027 }
2028}
2029
2030/*
2031 * Set background color for "Normal" group. Called by gui_set_bg_color()
2032 * when the color is known.
2033 */
2034 void
2035hl_set_bg_color_name(
2036 char_u *name) // must have been allocated
2037{
2038 int id;
2039
2040 if (name != NULL)
2041 {
2042 id = syn_name2id((char_u *)"Normal");
2043 if (id > 0)
2044 {
2045 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2046 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2047 }
2048 }
2049}
2050
2051/*
2052 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2053 * when the color is known.
2054 */
2055 void
2056hl_set_fg_color_name(
2057 char_u *name) // must have been allocated
2058{
2059 int id;
2060
2061 if (name != NULL)
2062 {
2063 id = syn_name2id((char_u *)"Normal");
2064 if (id > 0)
2065 {
2066 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2067 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2068 }
2069 }
2070}
2071
2072/*
2073 * Return the handle for a font name.
2074 * Returns NOFONT when failed.
2075 */
2076 static GuiFont
2077font_name2handle(char_u *name)
2078{
2079 if (STRCMP(name, "NONE") == 0)
2080 return NOFONT;
2081
2082 return gui_mch_get_font(name, TRUE);
2083}
2084
2085# ifdef FEAT_XFONTSET
2086/*
2087 * Return the handle for a fontset name.
2088 * Returns NOFONTSET when failed.
2089 */
2090 static GuiFontset
2091fontset_name2handle(char_u *name, int fixed_width)
2092{
2093 if (STRCMP(name, "NONE") == 0)
2094 return NOFONTSET;
2095
2096 return gui_mch_get_fontset(name, TRUE, fixed_width);
2097}
2098# endif
2099
2100/*
2101 * Get the font or fontset for one highlight group.
2102 */
2103 static void
2104hl_do_font(
2105 int idx,
2106 char_u *arg,
2107 int do_normal, // set normal font
2108 int do_menu UNUSED, // set menu font
2109 int do_tooltip UNUSED, // set tooltip font
2110 int free_font) // free current font/fontset
2111{
2112# ifdef FEAT_XFONTSET
2113 // If 'guifontset' is not empty, first try using the name as a
2114 // fontset. If that doesn't work, use it as a font name.
2115 if (*p_guifontset != NUL
2116# ifdef FONTSET_ALWAYS
2117 || do_menu
2118# endif
2119# ifdef FEAT_BEVAL_TIP
2120 // In Athena & Motif, the Tooltip highlight group is always a fontset
2121 || do_tooltip
2122# endif
2123 )
2124 {
2125 if (free_font)
2126 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2127 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2128# ifdef FONTSET_ALWAYS
2129 || do_menu
2130# endif
2131# ifdef FEAT_BEVAL_TIP
2132 || do_tooltip
2133# endif
2134 );
2135 }
2136 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2137 {
2138 // If it worked and it's the Normal group, use it as the normal
2139 // fontset. Same for the Menu group.
2140 if (do_normal)
2141 gui_init_font(arg, TRUE);
2142# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2143 if (do_menu)
2144 {
2145# ifdef FONTSET_ALWAYS
2146 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2147# else
2148 // YIKES! This is a bug waiting to crash the program
2149 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2150# endif
2151 gui_mch_new_menu_font();
2152 }
2153# ifdef FEAT_BEVAL_GUI
2154 if (do_tooltip)
2155 {
2156 // The Athena widget set cannot currently handle switching between
2157 // displaying a single font and a fontset.
2158 // If the XtNinternational resource is set to True at widget
2159 // creation, then a fontset is always used, otherwise an
2160 // XFontStruct is used.
2161 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2162 gui_mch_new_tooltip_font();
2163 }
2164# endif
2165# endif
2166 }
2167 else
2168# endif
2169 {
2170 if (free_font)
2171 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2172 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2173 // If it worked and it's the Normal group, use it as the
2174 // normal font. Same for the Menu group.
2175 if (HL_TABLE()[idx].sg_font != NOFONT)
2176 {
2177 if (do_normal)
2178 gui_init_font(arg, FALSE);
2179#ifndef FONTSET_ALWAYS
2180# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2181 if (do_menu)
2182 {
2183 gui.menu_font = HL_TABLE()[idx].sg_font;
2184 gui_mch_new_menu_font();
2185 }
2186# endif
2187#endif
2188 }
2189 }
2190}
2191
2192#endif // FEAT_GUI
2193
2194#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2195/*
2196 * Return the handle for a color name.
2197 * Returns INVALCOLOR when failed.
2198 */
2199 guicolor_T
2200color_name2handle(char_u *name)
2201{
2202 if (STRCMP(name, "NONE") == 0)
2203 return INVALCOLOR;
2204
2205 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2206 {
2207#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2208 if (gui.in_use)
2209#endif
2210#ifdef FEAT_GUI
2211 return gui.norm_pixel;
2212#endif
2213#ifdef FEAT_TERMGUICOLORS
2214 if (cterm_normal_fg_gui_color != INVALCOLOR)
2215 return cterm_normal_fg_gui_color;
2216 // Guess that the foreground is black or white.
2217 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2218#endif
2219 }
2220 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2221 {
2222#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2223 if (gui.in_use)
2224#endif
2225#ifdef FEAT_GUI
2226 return gui.back_pixel;
2227#endif
2228#ifdef FEAT_TERMGUICOLORS
2229 if (cterm_normal_bg_gui_color != INVALCOLOR)
2230 return cterm_normal_bg_gui_color;
2231 // Guess that the background is white or black.
2232 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2233#endif
2234 }
2235
2236 return GUI_GET_COLOR(name);
2237}
Drew Vogele30d1022021-10-24 20:35:07 +01002238
2239// On MS-Windows an RGB macro is available and it produces 0x00bbggrr color
2240// values as used by the MS-Windows GDI api. It should be used only for
2241// MS-Windows GDI builds.
2242# if defined(RGB) && defined(MSWIN) && !defined(FEAT_GUI)
2243# undef RGB
2244# endif
2245# ifndef RGB
2246# define RGB(r, g, b) ((r<<16) | (g<<8) | (b))
2247# endif
2248
2249# ifdef VIMDLL
2250 static guicolor_T
2251gui_adjust_rgb(guicolor_T c)
2252{
2253 if (gui.in_use)
2254 return c;
2255 else
2256 return ((c & 0xff) << 16) | (c & 0x00ff00) | ((c >> 16) & 0xff);
2257}
2258# else
2259# define gui_adjust_rgb(c) (c)
2260# endif
2261
2262 static int
2263hex_digit(int c)
2264{
2265 if (isdigit(c))
2266 return c - '0';
2267 c = TOLOWER_ASC(c);
2268 if (c >= 'a' && c <= 'f')
2269 return c - 'a' + 10;
2270 return 0x1ffffff;
2271}
2272
2273 guicolor_T
2274decode_hex_color(char_u *hex)
2275{
2276 guicolor_T color;
2277
2278 if (hex[0] != '#' || STRLEN(hex) != 7)
2279 return INVALCOLOR;
2280
2281 // Name is in "#rrggbb" format
2282 color = RGB(((hex_digit(hex[1]) << 4) + hex_digit(hex[2])),
2283 ((hex_digit(hex[3]) << 4) + hex_digit(hex[4])),
2284 ((hex_digit(hex[5]) << 4) + hex_digit(hex[6])));
2285 if (color > 0xffffff)
2286 return INVALCOLOR;
2287 return gui_adjust_rgb(color);
2288}
2289
Bram Moolenaar2a521962021-10-25 10:30:14 +01002290#ifdef FEAT_EVAL
Drew Vogele30d1022021-10-24 20:35:07 +01002291// Returns the color currently mapped to the given name or INVALCOLOR if no
2292// such name exists in the color table. The convention is to use lowercase for
2293// all keys in the v:colornames dictionary. The value can be either a string in
2294// the form #rrggbb or a number, either of which is converted to a guicolor_T.
2295 guicolor_T
2296colorname2rgb(char_u *name)
2297{
2298 dict_T *colornames_table = get_vim_var_dict(VV_COLORNAMES);
2299 char_u *lc_name;
2300 dictitem_T *colentry;
2301 char_u *colstr;
2302 varnumber_T colnum;
2303
2304 lc_name = strlow_save(name);
2305 if (lc_name == NULL)
2306 return INVALCOLOR;
2307
2308 colentry = dict_find(colornames_table, lc_name, -1);
2309 vim_free(lc_name);
2310 if (colentry == NULL)
2311 return INVALCOLOR;
2312
2313 if (colentry->di_tv.v_type == VAR_STRING)
2314 {
2315 colstr = tv_get_string_strict(&colentry->di_tv);
2316 if ((STRLEN(colstr) == 7) && (*colstr == '#'))
2317 {
2318 return decode_hex_color(colstr);
2319 }
2320 else
2321 {
2322 semsg(_(e_bad_color_string_str), colstr);
2323 return INVALCOLOR;
2324 }
2325 }
2326
2327 if (colentry->di_tv.v_type == VAR_NUMBER)
2328 {
2329 colnum = tv_get_number(&colentry->di_tv);
2330 return (guicolor_T)colnum;
2331 }
2332
2333 return INVALCOLOR;
2334}
2335
Drew Vogele30d1022021-10-24 20:35:07 +01002336/*
2337 * Load a default color list. Intended to support legacy color names but allows
2338 * the user to override the color values. Only loaded once.
2339 */
2340 void
2341load_default_colors_lists()
2342{
2343 // Lacking a default color list isn't the end of the world but it is likely
2344 // an inconvenience so users should know when it is missing.
2345 if (source_runtime((char_u *)"colors/lists/default.vim", DIP_ALL) != OK)
2346 msg("failed to load colors/lists/default.vim");
2347}
2348#endif
2349
2350 guicolor_T
2351gui_get_color_cmn(char_u *name)
2352{
2353 int i;
2354 guicolor_T color;
2355
2356 struct rgbcolor_table_S {
2357 char_u *color_name;
2358 guicolor_T color;
2359 };
2360
2361 // Only non X11 colors (not present in rgb.txt) and colors in
2362 // color_names[], useful when $VIMRUNTIME is not found,.
2363 static struct rgbcolor_table_S rgb_table[] = {
2364 {(char_u *)"black", RGB(0x00, 0x00, 0x00)},
2365 {(char_u *)"blue", RGB(0x00, 0x00, 0xFF)},
2366 {(char_u *)"brown", RGB(0xA5, 0x2A, 0x2A)},
2367 {(char_u *)"cyan", RGB(0x00, 0xFF, 0xFF)},
2368 {(char_u *)"darkblue", RGB(0x00, 0x00, 0x8B)},
2369 {(char_u *)"darkcyan", RGB(0x00, 0x8B, 0x8B)},
2370 {(char_u *)"darkgray", RGB(0xA9, 0xA9, 0xA9)},
2371 {(char_u *)"darkgreen", RGB(0x00, 0x64, 0x00)},
2372 {(char_u *)"darkgrey", RGB(0xA9, 0xA9, 0xA9)},
2373 {(char_u *)"darkmagenta", RGB(0x8B, 0x00, 0x8B)},
2374 {(char_u *)"darkred", RGB(0x8B, 0x00, 0x00)},
2375 {(char_u *)"darkyellow", RGB(0x8B, 0x8B, 0x00)}, // No X11
2376 {(char_u *)"gray", RGB(0xBE, 0xBE, 0xBE)},
2377 {(char_u *)"green", RGB(0x00, 0xFF, 0x00)},
2378 {(char_u *)"grey", RGB(0xBE, 0xBE, 0xBE)},
2379 {(char_u *)"grey40", RGB(0x66, 0x66, 0x66)},
2380 {(char_u *)"grey50", RGB(0x7F, 0x7F, 0x7F)},
2381 {(char_u *)"grey90", RGB(0xE5, 0xE5, 0xE5)},
2382 {(char_u *)"lightblue", RGB(0xAD, 0xD8, 0xE6)},
2383 {(char_u *)"lightcyan", RGB(0xE0, 0xFF, 0xFF)},
2384 {(char_u *)"lightgray", RGB(0xD3, 0xD3, 0xD3)},
2385 {(char_u *)"lightgreen", RGB(0x90, 0xEE, 0x90)},
2386 {(char_u *)"lightgrey", RGB(0xD3, 0xD3, 0xD3)},
2387 {(char_u *)"lightmagenta", RGB(0xFF, 0x8B, 0xFF)}, // No X11
2388 {(char_u *)"lightred", RGB(0xFF, 0x8B, 0x8B)}, // No X11
2389 {(char_u *)"lightyellow", RGB(0xFF, 0xFF, 0xE0)},
2390 {(char_u *)"magenta", RGB(0xFF, 0x00, 0xFF)},
2391 {(char_u *)"red", RGB(0xFF, 0x00, 0x00)},
2392 {(char_u *)"seagreen", RGB(0x2E, 0x8B, 0x57)},
2393 {(char_u *)"white", RGB(0xFF, 0xFF, 0xFF)},
2394 {(char_u *)"yellow", RGB(0xFF, 0xFF, 0x00)},
2395 };
2396
2397 color = decode_hex_color(name);
2398 if (color != INVALCOLOR)
2399 return color;
2400
2401 // Check if the name is one of the colors we know
2402 for (i = 0; i < (int)ARRAY_LENGTH(rgb_table); i++)
2403 if (STRICMP(name, rgb_table[i].color_name) == 0)
2404 return gui_adjust_rgb(rgb_table[i].color);
2405
2406#if defined(FEAT_EVAL)
2407 /*
2408 * Not a traditional color. Load additional color aliases and then consult the alias table.
2409 */
2410
2411 color = colorname2rgb(name);
2412 if (color == INVALCOLOR)
2413 {
2414 load_default_colors_lists();
2415 color = colorname2rgb(name);
2416 }
2417
2418 return color;
2419#else
2420 return INVALCOLOR;
2421#endif
2422}
2423
2424 guicolor_T
2425gui_get_rgb_color_cmn(int r, int g, int b)
2426{
2427 guicolor_T color = RGB(r, g, b);
2428
2429 if (color > 0xffffff)
2430 return INVALCOLOR;
2431 return gui_adjust_rgb(color);
2432}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002433#endif
2434
2435/*
2436 * Table with the specifications for an attribute number.
2437 * Note that this table is used by ALL buffers. This is required because the
2438 * GUI can redraw at any time for any buffer.
2439 */
2440static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2441
2442#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2443
2444static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2445
2446#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2447
2448#ifdef FEAT_GUI
2449static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2450
2451#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2452#endif
2453
2454/*
2455 * Return the attr number for a set of colors and font.
2456 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2457 * if the combination is new.
2458 * Return 0 for error (no more room).
2459 */
2460 static int
2461get_attr_entry(garray_T *table, attrentry_T *aep)
2462{
2463 int i;
2464 attrentry_T *taep;
2465 static int recursive = FALSE;
2466
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002467 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002468 table->ga_itemsize = sizeof(attrentry_T);
2469 table->ga_growsize = 7;
2470
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002471 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002472 for (i = 0; i < table->ga_len; ++i)
2473 {
2474 taep = &(((attrentry_T *)table->ga_data)[i]);
2475 if ( aep->ae_attr == taep->ae_attr
2476 && (
2477#ifdef FEAT_GUI
2478 (table == &gui_attr_table
2479 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2480 && aep->ae_u.gui.bg_color
2481 == taep->ae_u.gui.bg_color
2482 && aep->ae_u.gui.sp_color
2483 == taep->ae_u.gui.sp_color
2484 && aep->ae_u.gui.font == taep->ae_u.gui.font
2485# ifdef FEAT_XFONTSET
2486 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2487# endif
2488 ))
2489 ||
2490#endif
2491 (table == &term_attr_table
2492 && (aep->ae_u.term.start == NULL)
2493 == (taep->ae_u.term.start == NULL)
2494 && (aep->ae_u.term.start == NULL
2495 || STRCMP(aep->ae_u.term.start,
2496 taep->ae_u.term.start) == 0)
2497 && (aep->ae_u.term.stop == NULL)
2498 == (taep->ae_u.term.stop == NULL)
2499 && (aep->ae_u.term.stop == NULL
2500 || STRCMP(aep->ae_u.term.stop,
2501 taep->ae_u.term.stop) == 0))
2502 || (table == &cterm_attr_table
2503 && aep->ae_u.cterm.fg_color
2504 == taep->ae_u.cterm.fg_color
2505 && aep->ae_u.cterm.bg_color
2506 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002507 && aep->ae_u.cterm.ul_color
2508 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002509#ifdef FEAT_TERMGUICOLORS
2510 && aep->ae_u.cterm.fg_rgb
2511 == taep->ae_u.cterm.fg_rgb
2512 && aep->ae_u.cterm.bg_rgb
2513 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002514 && aep->ae_u.cterm.ul_rgb
2515 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002516#endif
2517 )))
2518
2519 return i + ATTR_OFF;
2520 }
2521
2522 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2523 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002524 // Running out of attribute entries! remove all attributes, and
2525 // compute new ones for all groups.
2526 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002527 if (recursive)
2528 {
Bram Moolenaarac78dd42022-01-02 19:25:26 +00002529 emsg(_(e_too_many_different_highlighting_attributes_in_use));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002530 return 0;
2531 }
2532 recursive = TRUE;
2533
2534 clear_hl_tables();
2535
2536 must_redraw = CLEAR;
2537
2538 for (i = 0; i < highlight_ga.ga_len; ++i)
2539 set_hl_attr(i);
2540
2541 recursive = FALSE;
2542 }
2543
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002544 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002545 if (ga_grow(table, 1) == FAIL)
2546 return 0;
2547
2548 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002549 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002550 taep->ae_attr = aep->ae_attr;
2551#ifdef FEAT_GUI
2552 if (table == &gui_attr_table)
2553 {
2554 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2555 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2556 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2557 taep->ae_u.gui.font = aep->ae_u.gui.font;
2558# ifdef FEAT_XFONTSET
2559 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2560# endif
2561 }
2562#endif
2563 if (table == &term_attr_table)
2564 {
2565 if (aep->ae_u.term.start == NULL)
2566 taep->ae_u.term.start = NULL;
2567 else
2568 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2569 if (aep->ae_u.term.stop == NULL)
2570 taep->ae_u.term.stop = NULL;
2571 else
2572 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2573 }
2574 else if (table == &cterm_attr_table)
2575 {
2576 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2577 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002578 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002579#ifdef FEAT_TERMGUICOLORS
2580 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2581 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002582 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002583#endif
2584 }
2585 ++table->ga_len;
2586 return (table->ga_len - 1 + ATTR_OFF);
2587}
2588
2589#if defined(FEAT_TERMINAL) || defined(PROTO)
2590/*
2591 * Get an attribute index for a cterm entry.
2592 * Uses an existing entry when possible or adds one when needed.
2593 */
2594 int
2595get_cterm_attr_idx(int attr, int fg, int bg)
2596{
2597 attrentry_T at_en;
2598
Bram Moolenaara80faa82020-04-12 19:37:17 +02002599 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002600#ifdef FEAT_TERMGUICOLORS
2601 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2602 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002603 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002604#endif
2605 at_en.ae_attr = attr;
2606 at_en.ae_u.cterm.fg_color = fg;
2607 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002608 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002609 return get_attr_entry(&cterm_attr_table, &at_en);
2610}
2611#endif
2612
2613#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2614/*
2615 * Get an attribute index for a 'termguicolors' entry.
2616 * Uses an existing entry when possible or adds one when needed.
2617 */
2618 int
2619get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2620{
2621 attrentry_T at_en;
2622
Bram Moolenaara80faa82020-04-12 19:37:17 +02002623 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002624 at_en.ae_attr = attr;
2625 if (fg == INVALCOLOR && bg == INVALCOLOR)
2626 {
2627 // If both GUI colors are not set fall back to the cterm colors. Helps
2628 // if the GUI only has an attribute, such as undercurl.
2629 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2630 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2631 }
2632 else
2633 {
2634 at_en.ae_u.cterm.fg_rgb = fg;
2635 at_en.ae_u.cterm.bg_rgb = bg;
2636 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002637 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002638 return get_attr_entry(&cterm_attr_table, &at_en);
2639}
2640#endif
2641
2642#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2643/*
2644 * Get an attribute index for a cterm entry.
2645 * Uses an existing entry when possible or adds one when needed.
2646 */
2647 int
2648get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2649{
2650 attrentry_T at_en;
2651
Bram Moolenaara80faa82020-04-12 19:37:17 +02002652 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002653 at_en.ae_attr = attr;
2654 at_en.ae_u.gui.fg_color = fg;
2655 at_en.ae_u.gui.bg_color = bg;
2656 return get_attr_entry(&gui_attr_table, &at_en);
2657}
2658#endif
2659
2660/*
2661 * Clear all highlight tables.
2662 */
2663 void
2664clear_hl_tables(void)
2665{
2666 int i;
2667 attrentry_T *taep;
2668
2669#ifdef FEAT_GUI
2670 ga_clear(&gui_attr_table);
2671#endif
2672 for (i = 0; i < term_attr_table.ga_len; ++i)
2673 {
2674 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2675 vim_free(taep->ae_u.term.start);
2676 vim_free(taep->ae_u.term.stop);
2677 }
2678 ga_clear(&term_attr_table);
2679 ga_clear(&cterm_attr_table);
2680}
2681
2682/*
2683 * Combine special attributes (e.g., for spelling) with other attributes
2684 * (e.g., for syntax highlighting).
2685 * "prim_attr" overrules "char_attr".
2686 * This creates a new group when required.
2687 * Since we expect there to be few spelling mistakes we don't cache the
2688 * result.
2689 * Return the resulting attributes.
2690 */
2691 int
2692hl_combine_attr(int char_attr, int prim_attr)
2693{
2694 attrentry_T *char_aep = NULL;
2695 attrentry_T *spell_aep;
2696 attrentry_T new_en;
2697
2698 if (char_attr == 0)
2699 return prim_attr;
2700 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2701 return ATTR_COMBINE(char_attr, prim_attr);
2702#ifdef FEAT_GUI
2703 if (gui.in_use)
2704 {
2705 if (char_attr > HL_ALL)
2706 char_aep = syn_gui_attr2entry(char_attr);
2707 if (char_aep != NULL)
2708 new_en = *char_aep;
2709 else
2710 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002711 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002712 new_en.ae_u.gui.fg_color = INVALCOLOR;
2713 new_en.ae_u.gui.bg_color = INVALCOLOR;
2714 new_en.ae_u.gui.sp_color = INVALCOLOR;
2715 if (char_attr <= HL_ALL)
2716 new_en.ae_attr = char_attr;
2717 }
2718
2719 if (prim_attr <= HL_ALL)
2720 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2721 else
2722 {
2723 spell_aep = syn_gui_attr2entry(prim_attr);
2724 if (spell_aep != NULL)
2725 {
2726 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2727 spell_aep->ae_attr);
2728 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2729 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2730 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2731 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2732 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2733 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2734 if (spell_aep->ae_u.gui.font != NOFONT)
2735 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2736# ifdef FEAT_XFONTSET
2737 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2738 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2739# endif
2740 }
2741 }
2742 return get_attr_entry(&gui_attr_table, &new_en);
2743 }
2744#endif
2745
2746 if (IS_CTERM)
2747 {
2748 if (char_attr > HL_ALL)
2749 char_aep = syn_cterm_attr2entry(char_attr);
2750 if (char_aep != NULL)
2751 new_en = *char_aep;
2752 else
2753 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002754 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002755#ifdef FEAT_TERMGUICOLORS
2756 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2757 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002758 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002759#endif
2760 if (char_attr <= HL_ALL)
2761 new_en.ae_attr = char_attr;
2762 }
2763
2764 if (prim_attr <= HL_ALL)
2765 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2766 else
2767 {
2768 spell_aep = syn_cterm_attr2entry(prim_attr);
2769 if (spell_aep != NULL)
2770 {
2771 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2772 spell_aep->ae_attr);
2773 if (spell_aep->ae_u.cterm.fg_color > 0)
2774 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2775 if (spell_aep->ae_u.cterm.bg_color > 0)
2776 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002777 if (spell_aep->ae_u.cterm.ul_color > 0)
2778 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002779#ifdef FEAT_TERMGUICOLORS
2780 // If both fg and bg are not set fall back to cterm colors.
2781 // Helps for SpellBad which uses undercurl in the GUI.
2782 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2783 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2784 {
2785 if (spell_aep->ae_u.cterm.fg_color > 0)
2786 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2787 if (spell_aep->ae_u.cterm.bg_color > 0)
2788 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2789 }
2790 else
2791 {
2792 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2793 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2794 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2795 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2796 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002797 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2798 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002799#endif
2800 }
2801 }
2802 return get_attr_entry(&cterm_attr_table, &new_en);
2803 }
2804
2805 if (char_attr > HL_ALL)
2806 char_aep = syn_term_attr2entry(char_attr);
2807 if (char_aep != NULL)
2808 new_en = *char_aep;
2809 else
2810 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002811 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002812 if (char_attr <= HL_ALL)
2813 new_en.ae_attr = char_attr;
2814 }
2815
2816 if (prim_attr <= HL_ALL)
2817 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2818 else
2819 {
2820 spell_aep = syn_term_attr2entry(prim_attr);
2821 if (spell_aep != NULL)
2822 {
2823 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2824 if (spell_aep->ae_u.term.start != NULL)
2825 {
2826 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2827 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2828 }
2829 }
2830 }
2831 return get_attr_entry(&term_attr_table, &new_en);
2832}
2833
2834#ifdef FEAT_GUI
2835 attrentry_T *
2836syn_gui_attr2entry(int attr)
2837{
2838 attr -= ATTR_OFF;
2839 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2840 return NULL;
2841 return &(GUI_ATTR_ENTRY(attr));
2842}
2843#endif
2844
2845/*
2846 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2847 * Only to be used when "attr" > HL_ALL.
2848 */
2849 int
2850syn_attr2attr(int attr)
2851{
2852 attrentry_T *aep;
2853
2854#ifdef FEAT_GUI
2855 if (gui.in_use)
2856 aep = syn_gui_attr2entry(attr);
2857 else
2858#endif
2859 if (IS_CTERM)
2860 aep = syn_cterm_attr2entry(attr);
2861 else
2862 aep = syn_term_attr2entry(attr);
2863
2864 if (aep == NULL) // highlighting not set
2865 return 0;
2866 return aep->ae_attr;
2867}
2868
2869
2870 attrentry_T *
2871syn_term_attr2entry(int attr)
2872{
2873 attr -= ATTR_OFF;
2874 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2875 return NULL;
2876 return &(TERM_ATTR_ENTRY(attr));
2877}
2878
2879 attrentry_T *
2880syn_cterm_attr2entry(int attr)
2881{
2882 attr -= ATTR_OFF;
2883 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2884 return NULL;
2885 return &(CTERM_ATTR_ENTRY(attr));
2886}
2887
2888#define LIST_ATTR 1
2889#define LIST_STRING 2
2890#define LIST_INT 3
2891
2892 static void
2893highlight_list_one(int id)
2894{
2895 hl_group_T *sgp;
2896 int didh = FALSE;
2897
2898 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2899
2900 if (message_filtered(sgp->sg_name))
2901 return;
2902
2903 didh = highlight_list_arg(id, didh, LIST_ATTR,
2904 sgp->sg_term, NULL, "term");
2905 didh = highlight_list_arg(id, didh, LIST_STRING,
2906 0, sgp->sg_start, "start");
2907 didh = highlight_list_arg(id, didh, LIST_STRING,
2908 0, sgp->sg_stop, "stop");
2909
2910 didh = highlight_list_arg(id, didh, LIST_ATTR,
2911 sgp->sg_cterm, NULL, "cterm");
2912 didh = highlight_list_arg(id, didh, LIST_INT,
2913 sgp->sg_cterm_fg, NULL, "ctermfg");
2914 didh = highlight_list_arg(id, didh, LIST_INT,
2915 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002916 didh = highlight_list_arg(id, didh, LIST_INT,
2917 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002918
2919#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2920 didh = highlight_list_arg(id, didh, LIST_ATTR,
2921 sgp->sg_gui, NULL, "gui");
2922 didh = highlight_list_arg(id, didh, LIST_STRING,
2923 0, sgp->sg_gui_fg_name, "guifg");
2924 didh = highlight_list_arg(id, didh, LIST_STRING,
2925 0, sgp->sg_gui_bg_name, "guibg");
2926 didh = highlight_list_arg(id, didh, LIST_STRING,
2927 0, sgp->sg_gui_sp_name, "guisp");
2928#endif
2929#ifdef FEAT_GUI
2930 didh = highlight_list_arg(id, didh, LIST_STRING,
2931 0, sgp->sg_font_name, "font");
2932#endif
2933
2934 if (sgp->sg_link && !got_int)
2935 {
2936 (void)syn_list_header(didh, 9999, id);
2937 didh = TRUE;
2938 msg_puts_attr("links to", HL_ATTR(HLF_D));
2939 msg_putchar(' ');
2940 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2941 }
2942
2943 if (!didh)
2944 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2945#ifdef FEAT_EVAL
2946 if (p_verbose > 0)
2947 last_set_msg(sgp->sg_script_ctx);
2948#endif
2949}
2950
2951 static int
2952highlight_list_arg(
2953 int id,
2954 int didh,
2955 int type,
2956 int iarg,
2957 char_u *sarg,
2958 char *name)
2959{
2960 char_u buf[100];
2961 char_u *ts;
2962 int i;
2963
2964 if (got_int)
2965 return FALSE;
2966 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2967 {
2968 ts = buf;
2969 if (type == LIST_INT)
2970 sprintf((char *)buf, "%d", iarg - 1);
2971 else if (type == LIST_STRING)
2972 ts = sarg;
2973 else // type == LIST_ATTR
2974 {
2975 buf[0] = NUL;
2976 for (i = 0; hl_attr_table[i] != 0; ++i)
2977 {
2978 if (iarg & hl_attr_table[i])
2979 {
2980 if (buf[0] != NUL)
2981 vim_strcat(buf, (char_u *)",", 100);
2982 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2983 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2984 }
2985 }
2986 }
2987
2988 (void)syn_list_header(didh,
2989 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2990 didh = TRUE;
2991 if (!got_int)
2992 {
2993 if (*name != NUL)
2994 {
2995 msg_puts_attr(name, HL_ATTR(HLF_D));
2996 msg_puts_attr("=", HL_ATTR(HLF_D));
2997 }
2998 msg_outtrans(ts);
2999 }
3000 }
3001 return didh;
3002}
3003
3004#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
3005/*
3006 * Return "1" if highlight group "id" has attribute "flag".
3007 * Return NULL otherwise.
3008 */
3009 char_u *
3010highlight_has_attr(
3011 int id,
3012 int flag,
3013 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3014{
3015 int attr;
3016
3017 if (id <= 0 || id > highlight_ga.ga_len)
3018 return NULL;
3019
3020#if defined(FEAT_GUI) || defined(FEAT_EVAL)
3021 if (modec == 'g')
3022 attr = HL_TABLE()[id - 1].sg_gui;
3023 else
3024#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01003025 {
3026 if (modec == 'c')
3027 attr = HL_TABLE()[id - 1].sg_cterm;
3028 else
3029 attr = HL_TABLE()[id - 1].sg_term;
3030 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003031
3032 if (attr & flag)
3033 return (char_u *)"1";
3034 return NULL;
3035}
3036#endif
3037
3038#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
3039/*
3040 * Return color name of highlight group "id".
3041 */
3042 char_u *
3043highlight_color(
3044 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02003045 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003046 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
3047{
3048 static char_u name[20];
3049 int n;
3050 int fg = FALSE;
3051 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003052 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003053 int font = FALSE;
3054
3055 if (id <= 0 || id > highlight_ga.ga_len)
3056 return NULL;
3057
3058 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
3059 fg = TRUE;
3060 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
3061 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
3062 font = TRUE;
3063 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
3064 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003065 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
3066 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003067 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
3068 return NULL;
3069 if (modec == 'g')
3070 {
3071# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3072# ifdef FEAT_GUI
3073 // return font name
3074 if (font)
3075 return HL_TABLE()[id - 1].sg_font_name;
3076# endif
3077
3078 // return #RRGGBB form (only possible when GUI is running)
3079 if ((USE_24BIT) && what[2] == '#')
3080 {
3081 guicolor_T color;
3082 long_u rgb;
3083 static char_u buf[10];
3084
3085 if (fg)
3086 color = HL_TABLE()[id - 1].sg_gui_fg;
3087 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003088 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003089 else
3090 color = HL_TABLE()[id - 1].sg_gui_bg;
3091 if (color == INVALCOLOR)
3092 return NULL;
3093 rgb = (long_u)GUI_MCH_GET_RGB(color);
3094 sprintf((char *)buf, "#%02x%02x%02x",
3095 (unsigned)(rgb >> 16),
3096 (unsigned)(rgb >> 8) & 255,
3097 (unsigned)rgb & 255);
3098 return buf;
3099 }
3100# endif
3101 if (fg)
3102 return (HL_TABLE()[id - 1].sg_gui_fg_name);
3103 if (sp)
3104 return (HL_TABLE()[id - 1].sg_gui_sp_name);
3105 return (HL_TABLE()[id - 1].sg_gui_bg_name);
3106 }
3107 if (font || sp)
3108 return NULL;
3109 if (modec == 'c')
3110 {
3111 if (fg)
3112 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02003113 else if (ul)
3114 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003115 else
3116 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
3117 if (n < 0)
3118 return NULL;
3119 sprintf((char *)name, "%d", n);
3120 return name;
3121 }
3122 // term doesn't have color
3123 return NULL;
3124}
3125#endif
3126
3127#if (defined(FEAT_SYN_HL) \
3128 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
3129 && defined(FEAT_PRINTER)) || defined(PROTO)
3130/*
3131 * Return color name of highlight group "id" as RGB value.
3132 */
3133 long_u
3134highlight_gui_color_rgb(
3135 int id,
3136 int fg) // TRUE = fg, FALSE = bg
3137{
3138 guicolor_T color;
3139
3140 if (id <= 0 || id > highlight_ga.ga_len)
3141 return 0L;
3142
3143 if (fg)
3144 color = HL_TABLE()[id - 1].sg_gui_fg;
3145 else
3146 color = HL_TABLE()[id - 1].sg_gui_bg;
3147
3148 if (color == INVALCOLOR)
3149 return 0L;
3150
3151 return GUI_MCH_GET_RGB(color);
3152}
3153#endif
3154
3155/*
3156 * Output the syntax list header.
3157 * Return TRUE when started a new line.
3158 */
3159 int
3160syn_list_header(
3161 int did_header, // did header already
3162 int outlen, // length of string that comes
3163 int id) // highlight group id
3164{
3165 int endcol = 19;
3166 int newline = TRUE;
3167 int name_col = 0;
3168
3169 if (!did_header)
3170 {
3171 msg_putchar('\n');
3172 if (got_int)
3173 return TRUE;
3174 msg_outtrans(HL_TABLE()[id - 1].sg_name);
3175 name_col = msg_col;
3176 endcol = 15;
3177 }
3178 else if (msg_col + outlen + 1 >= Columns)
3179 {
3180 msg_putchar('\n');
3181 if (got_int)
3182 return TRUE;
3183 }
3184 else
3185 {
3186 if (msg_col >= endcol) // wrap around is like starting a new line
3187 newline = FALSE;
3188 }
3189
3190 if (msg_col >= endcol) // output at least one space
3191 endcol = msg_col + 1;
3192 if (Columns <= endcol) // avoid hang for tiny window
3193 endcol = Columns - 1;
3194
3195 msg_advance(endcol);
3196
3197 // Show "xxx" with the attributes.
3198 if (!did_header)
3199 {
3200 if (endcol == Columns - 1 && endcol <= name_col)
3201 msg_putchar(' ');
3202 msg_puts_attr("xxx", syn_id2attr(id));
3203 msg_putchar(' ');
3204 }
3205
3206 return newline;
3207}
3208
3209/*
3210 * Set the attribute numbers for a highlight group.
3211 * Called after one of the attributes has changed.
3212 */
3213 static void
3214set_hl_attr(
3215 int idx) // index in array
3216{
3217 attrentry_T at_en;
3218 hl_group_T *sgp = HL_TABLE() + idx;
3219
3220 // The "Normal" group doesn't need an attribute number
3221 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3222 return;
3223
3224#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003225 // For the GUI mode: If there are other than "normal" highlighting
3226 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003227 if (sgp->sg_gui_fg == INVALCOLOR
3228 && sgp->sg_gui_bg == INVALCOLOR
3229 && sgp->sg_gui_sp == INVALCOLOR
3230 && sgp->sg_font == NOFONT
3231# ifdef FEAT_XFONTSET
3232 && sgp->sg_fontset == NOFONTSET
3233# endif
3234 )
3235 {
3236 sgp->sg_gui_attr = sgp->sg_gui;
3237 }
3238 else
3239 {
3240 at_en.ae_attr = sgp->sg_gui;
3241 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3242 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3243 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3244 at_en.ae_u.gui.font = sgp->sg_font;
3245# ifdef FEAT_XFONTSET
3246 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3247# endif
3248 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3249 }
3250#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003251 // For the term mode: If there are other than "normal" highlighting
3252 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003253 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3254 sgp->sg_term_attr = sgp->sg_term;
3255 else
3256 {
3257 at_en.ae_attr = sgp->sg_term;
3258 at_en.ae_u.term.start = sgp->sg_start;
3259 at_en.ae_u.term.stop = sgp->sg_stop;
3260 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3261 }
3262
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003263 // For the color term mode: If there are other than "normal"
3264 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003265 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003266# ifdef FEAT_TERMGUICOLORS
3267 && sgp->sg_gui_fg == INVALCOLOR
3268 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003269 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003270# endif
3271 )
3272 sgp->sg_cterm_attr = sgp->sg_cterm;
3273 else
3274 {
3275 at_en.ae_attr = sgp->sg_cterm;
3276 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3277 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003278 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003279# ifdef FEAT_TERMGUICOLORS
3280# ifdef MSWIN
3281# ifdef VIMDLL
3282 // Only when not using the GUI.
3283 if (!gui.in_use && !gui.starting)
3284# endif
3285 {
3286 int id;
3287 guicolor_T fg, bg;
3288
3289 id = syn_name2id((char_u *)"Normal");
3290 if (id > 0)
3291 {
3292 syn_id2colors(id, &fg, &bg);
3293 if (sgp->sg_gui_fg == INVALCOLOR)
3294 sgp->sg_gui_fg = fg;
3295 if (sgp->sg_gui_bg == INVALCOLOR)
3296 sgp->sg_gui_bg = bg;
3297 }
3298
3299 }
3300# endif
3301 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3302 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003303 // Only use the underline/undercurl color when used, it may clear the
3304 // background color if not supported.
3305 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
3306 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3307 else
3308 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003309 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3310 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3311 {
3312 // If both fg and bg are invalid fall back to the cterm colors.
3313 // Helps when the GUI only uses an attribute, e.g. undercurl.
3314 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3315 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3316 }
3317# endif
3318 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3319 }
3320}
3321
3322/*
3323 * Lookup a highlight group name and return its ID.
3324 * If it is not found, 0 is returned.
3325 */
3326 int
3327syn_name2id(char_u *name)
3328{
3329 int i;
erw7f7f7aaf2021-12-07 21:29:20 +00003330 char_u name_u[MAX_SYN_NAME + 1];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003331
3332 // Avoid using stricmp() too much, it's slow on some systems
3333 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3334 // don't deserve to be found!
erw7f7f7aaf2021-12-07 21:29:20 +00003335 vim_strncpy(name_u, name, MAX_SYN_NAME);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003336 vim_strup(name_u);
3337 for (i = highlight_ga.ga_len; --i >= 0; )
3338 if (HL_TABLE()[i].sg_name_u != NULL
3339 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3340 break;
3341 return i + 1;
3342}
3343
3344/*
3345 * Lookup a highlight group name and return its attributes.
3346 * Return zero if not found.
3347 */
3348 int
3349syn_name2attr(char_u *name)
3350{
3351 int id = syn_name2id(name);
3352
3353 if (id != 0)
3354 return syn_id2attr(id);
3355 return 0;
3356}
3357
3358#if defined(FEAT_EVAL) || defined(PROTO)
3359/*
3360 * Return TRUE if highlight group "name" exists.
3361 */
3362 int
3363highlight_exists(char_u *name)
3364{
3365 return (syn_name2id(name) > 0);
3366}
3367
3368# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3369/*
3370 * Return the name of highlight group "id".
3371 * When not a valid ID return an empty string.
3372 */
3373 char_u *
3374syn_id2name(int id)
3375{
3376 if (id <= 0 || id > highlight_ga.ga_len)
3377 return (char_u *)"";
3378 return HL_TABLE()[id - 1].sg_name;
3379}
3380# endif
3381#endif
3382
3383/*
3384 * Like syn_name2id(), but take a pointer + length argument.
3385 */
3386 int
3387syn_namen2id(char_u *linep, int len)
3388{
3389 char_u *name;
3390 int id = 0;
3391
3392 name = vim_strnsave(linep, len);
3393 if (name != NULL)
3394 {
3395 id = syn_name2id(name);
3396 vim_free(name);
3397 }
3398 return id;
3399}
3400
3401/*
3402 * Find highlight group name in the table and return its ID.
3403 * The argument is a pointer to the name and the length of the name.
3404 * If it doesn't exist yet, a new entry is created.
3405 * Return 0 for failure.
3406 */
3407 int
3408syn_check_group(char_u *pp, int len)
3409{
3410 int id;
3411 char_u *name;
3412
erw7f7f7aaf2021-12-07 21:29:20 +00003413 if (len > MAX_SYN_NAME)
3414 {
3415 emsg(_(e_highlight_group_name_too_long));
3416 return 0;
3417 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003418 name = vim_strnsave(pp, len);
3419 if (name == NULL)
3420 return 0;
3421
3422 id = syn_name2id(name);
3423 if (id == 0) // doesn't exist yet
3424 id = syn_add_group(name);
3425 else
3426 vim_free(name);
3427 return id;
3428}
3429
3430/*
3431 * Add new highlight group and return its ID.
3432 * "name" must be an allocated string, it will be consumed.
3433 * Return 0 for failure.
3434 */
3435 static int
3436syn_add_group(char_u *name)
3437{
3438 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003439 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003440
3441 // Check that the name is ASCII letters, digits and underscore.
3442 for (p = name; *p != NUL; ++p)
3443 {
3444 if (!vim_isprintc(*p))
3445 {
3446 emsg(_("E669: Unprintable character in group name"));
3447 vim_free(name);
3448 return 0;
3449 }
3450 else if (!ASCII_ISALNUM(*p) && *p != '_')
3451 {
3452 // This is an error, but since there previously was no check only
3453 // give a warning.
3454 msg_source(HL_ATTR(HLF_W));
3455 msg(_("W18: Invalid character in group name"));
3456 break;
3457 }
3458 }
3459
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003460 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003461 if (highlight_ga.ga_data == NULL)
3462 {
3463 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3464 highlight_ga.ga_growsize = 10;
3465 }
3466
3467 if (highlight_ga.ga_len >= MAX_HL_ID)
3468 {
3469 emsg(_("E849: Too many highlight and syntax groups"));
3470 vim_free(name);
3471 return 0;
3472 }
3473
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003474 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003475 if (ga_grow(&highlight_ga, 1) == FAIL)
3476 {
3477 vim_free(name);
3478 return 0;
3479 }
3480
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003481 name_up = vim_strsave_up(name);
3482 if (name_up == NULL)
3483 {
3484 vim_free(name);
3485 return 0;
3486 }
3487
Bram Moolenaara80faa82020-04-12 19:37:17 +02003488 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003489 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003490 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003491#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3492 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3493 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003494 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003495#endif
3496 ++highlight_ga.ga_len;
3497
3498 return highlight_ga.ga_len; // ID is index plus one
3499}
3500
3501/*
3502 * When, just after calling syn_add_group(), an error is discovered, this
3503 * function deletes the new name.
3504 */
3505 static void
3506syn_unadd_group(void)
3507{
3508 --highlight_ga.ga_len;
3509 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3510 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3511}
3512
3513/*
3514 * Translate a group ID to highlight attributes.
3515 */
3516 int
3517syn_id2attr(int hl_id)
3518{
3519 int attr;
3520 hl_group_T *sgp;
3521
3522 hl_id = syn_get_final_id(hl_id);
3523 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3524
3525#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003526 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003527 if (gui.in_use)
3528 attr = sgp->sg_gui_attr;
3529 else
3530#endif
3531 if (IS_CTERM)
3532 attr = sgp->sg_cterm_attr;
3533 else
3534 attr = sgp->sg_term_attr;
3535
3536 return attr;
3537}
3538
3539#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3540/*
3541 * Get the GUI colors and attributes for a group ID.
3542 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3543 */
3544 int
3545syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3546{
3547 hl_group_T *sgp;
3548
3549 hl_id = syn_get_final_id(hl_id);
3550 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3551
3552 *fgp = sgp->sg_gui_fg;
3553 *bgp = sgp->sg_gui_bg;
3554 return sgp->sg_gui;
3555}
3556#endif
3557
3558#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003559 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3560 && defined(FEAT_TERMGUICOLORS)) \
3561 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003562 void
3563syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3564{
3565 hl_group_T *sgp;
3566
3567 hl_id = syn_get_final_id(hl_id);
3568 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3569 *fgp = sgp->sg_cterm_fg - 1;
3570 *bgp = sgp->sg_cterm_bg - 1;
3571}
3572#endif
3573
3574/*
3575 * Translate a group ID to the final group ID (following links).
3576 */
3577 int
3578syn_get_final_id(int hl_id)
3579{
3580 int count;
3581 hl_group_T *sgp;
3582
3583 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3584 return 0; // Can be called from eval!!
3585
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003586 // Follow links until there is no more.
3587 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003588 for (count = 100; --count >= 0; )
3589 {
3590 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3591 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3592 break;
3593 hl_id = sgp->sg_link;
3594 }
3595
3596 return hl_id;
3597}
3598
3599#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3600/*
3601 * Call this function just after the GUI has started.
3602 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3603 * It finds the font and color handles for the highlighting groups.
3604 */
3605 void
3606highlight_gui_started(void)
3607{
3608 int idx;
3609
3610 // First get the colors from the "Normal" and "Menu" group, if set
3611 if (USE_24BIT)
3612 set_normal_colors();
3613
3614 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3615 gui_do_one_color(idx, FALSE, FALSE);
3616
3617 highlight_changed();
3618}
3619
3620 static void
3621gui_do_one_color(
3622 int idx,
3623 int do_menu UNUSED, // TRUE: might set the menu font
3624 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3625{
3626 int didit = FALSE;
3627
3628# ifdef FEAT_GUI
3629# ifdef FEAT_TERMGUICOLORS
3630 if (gui.in_use)
3631# endif
3632 if (HL_TABLE()[idx].sg_font_name != NULL)
3633 {
3634 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3635 do_tooltip, TRUE);
3636 didit = TRUE;
3637 }
3638# endif
3639 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3640 {
3641 HL_TABLE()[idx].sg_gui_fg =
3642 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3643 didit = TRUE;
3644 }
3645 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3646 {
3647 HL_TABLE()[idx].sg_gui_bg =
3648 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3649 didit = TRUE;
3650 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003651 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3652 {
3653 HL_TABLE()[idx].sg_gui_sp =
3654 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3655 didit = TRUE;
3656 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003657 if (didit) // need to get a new attr number
3658 set_hl_attr(idx);
3659}
3660#endif
3661
3662#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3663/*
3664 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3665 */
3666 static void
3667combine_stl_hlt(
3668 int id,
3669 int id_S,
3670 int id_alt,
3671 int hlcnt,
3672 int i,
3673 int hlf,
3674 int *table)
3675{
3676 hl_group_T *hlt = HL_TABLE();
3677
3678 if (id_alt == 0)
3679 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003680 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003681 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3682 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3683# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3684 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3685# endif
3686 }
3687 else
3688 mch_memmove(&hlt[hlcnt + i],
3689 &hlt[id_alt - 1],
3690 sizeof(hl_group_T));
3691 hlt[hlcnt + i].sg_link = 0;
3692
3693 hlt[hlcnt + i].sg_term ^=
3694 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3695 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3696 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3697 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3698 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3699 hlt[hlcnt + i].sg_cterm ^=
3700 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3701 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3702 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3703 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3704 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3705# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3706 hlt[hlcnt + i].sg_gui ^=
3707 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3708# endif
3709# ifdef FEAT_GUI
3710 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3711 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3712 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3713 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3714 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3715 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3716 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3717 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3718# ifdef FEAT_XFONTSET
3719 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3720 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3721# endif
3722# endif
3723 highlight_ga.ga_len = hlcnt + i + 1;
3724 set_hl_attr(hlcnt + i); // At long last we can apply
3725 table[i] = syn_id2attr(hlcnt + i + 1);
3726}
3727#endif
3728
3729/*
3730 * Translate the 'highlight' option into attributes in highlight_attr[] and
3731 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3732 * corresponding highlights to use on top of HLF_SNC is computed.
3733 * Called only when the 'highlight' option has been changed and upon first
3734 * screen redraw after any :highlight command.
3735 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3736 */
3737 int
3738highlight_changed(void)
3739{
3740 int hlf;
3741 int i;
3742 char_u *p;
3743 int attr;
3744 char_u *end;
3745 int id;
3746#ifdef USER_HIGHLIGHT
3747 char_u userhl[30]; // use 30 to avoid compiler warning
3748# ifdef FEAT_STL_OPT
3749 int id_S = -1;
3750 int id_SNC = 0;
3751# ifdef FEAT_TERMINAL
3752 int id_ST = 0;
3753 int id_STNC = 0;
3754# endif
3755 int hlcnt;
3756# endif
3757#endif
3758 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3759
3760 need_highlight_changed = FALSE;
3761
Bram Moolenaar87fd0922021-11-20 13:47:45 +00003762#ifdef FEAT_TERMINAL
3763 term_update_colors_all();
3764 term_update_wincolor_all();
3765#endif
3766
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003767 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003768 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3769 highlight_attr[hlf] = 0;
3770
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003771 // First set all attributes to their default value.
3772 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003773 for (i = 0; i < 2; ++i)
3774 {
3775 if (i)
3776 p = p_hl;
3777 else
3778 p = get_highlight_default();
3779 if (p == NULL) // just in case
3780 continue;
3781
3782 while (*p)
3783 {
3784 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3785 if (hl_flags[hlf] == *p)
3786 break;
3787 ++p;
3788 if (hlf == (int)HLF_COUNT || *p == NUL)
3789 return FAIL;
3790
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003791 // Allow several hl_flags to be combined, like "bu" for
3792 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003793 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003794 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003795 {
3796 if (VIM_ISWHITE(*p)) // ignore white space
3797 continue;
3798
3799 if (attr > HL_ALL) // Combination with ':' is not allowed.
3800 return FAIL;
3801
3802 switch (*p)
3803 {
3804 case 'b': attr |= HL_BOLD;
3805 break;
3806 case 'i': attr |= HL_ITALIC;
3807 break;
3808 case '-':
3809 case 'n': // no highlighting
3810 break;
3811 case 'r': attr |= HL_INVERSE;
3812 break;
3813 case 's': attr |= HL_STANDOUT;
3814 break;
3815 case 'u': attr |= HL_UNDERLINE;
3816 break;
3817 case 'c': attr |= HL_UNDERCURL;
3818 break;
3819 case 't': attr |= HL_STRIKETHROUGH;
3820 break;
3821 case ':': ++p; // highlight group name
3822 if (attr || *p == NUL) // no combinations
3823 return FAIL;
3824 end = vim_strchr(p, ',');
3825 if (end == NULL)
3826 end = p + STRLEN(p);
3827 id = syn_check_group(p, (int)(end - p));
3828 if (id == 0)
3829 return FAIL;
3830 attr = syn_id2attr(id);
3831 p = end - 1;
3832#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3833 if (hlf == (int)HLF_SNC)
3834 id_SNC = syn_get_final_id(id);
3835# ifdef FEAT_TERMINAL
3836 else if (hlf == (int)HLF_ST)
3837 id_ST = syn_get_final_id(id);
3838 else if (hlf == (int)HLF_STNC)
3839 id_STNC = syn_get_final_id(id);
3840# endif
3841 else if (hlf == (int)HLF_S)
3842 id_S = syn_get_final_id(id);
3843#endif
3844 break;
3845 default: return FAIL;
3846 }
3847 }
3848 highlight_attr[hlf] = attr;
3849
3850 p = skip_to_option_part(p); // skip comma and spaces
3851 }
3852 }
3853
3854#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003855 // Setup the user highlights
3856 //
3857 // Temporarily utilize 28 more hl entries:
3858 // 9 for User1-User9 combined with StatusLineNC
3859 // 9 for User1-User9 combined with StatusLineTerm
3860 // 9 for User1-User9 combined with StatusLineTermNC
3861 // 1 for StatusLine default
3862 // Have to be in there simultaneously in case of table overflows in
3863 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003864# ifdef FEAT_STL_OPT
3865 if (ga_grow(&highlight_ga, 28) == FAIL)
3866 return FAIL;
3867 hlcnt = highlight_ga.ga_len;
3868 if (id_S == -1)
3869 {
3870 // Make sure id_S is always valid to simplify code below. Use the last
3871 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003872 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003873 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3874 id_S = hlcnt + 19;
3875 }
3876# endif
3877 for (i = 0; i < 9; i++)
3878 {
3879 sprintf((char *)userhl, "User%d", i + 1);
3880 id = syn_name2id(userhl);
3881 if (id == 0)
3882 {
3883 highlight_user[i] = 0;
3884# ifdef FEAT_STL_OPT
3885 highlight_stlnc[i] = 0;
3886# ifdef FEAT_TERMINAL
3887 highlight_stlterm[i] = 0;
3888 highlight_stltermnc[i] = 0;
3889# endif
3890# endif
3891 }
3892 else
3893 {
3894 highlight_user[i] = syn_id2attr(id);
3895# ifdef FEAT_STL_OPT
3896 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3897 HLF_SNC, highlight_stlnc);
3898# ifdef FEAT_TERMINAL
3899 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3900 HLF_ST, highlight_stlterm);
3901 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3902 HLF_STNC, highlight_stltermnc);
3903# endif
3904# endif
3905 }
3906 }
3907# ifdef FEAT_STL_OPT
3908 highlight_ga.ga_len = hlcnt;
3909# endif
3910
3911#endif // USER_HIGHLIGHT
3912
3913 return OK;
3914}
3915
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003916static void highlight_list(void);
3917static void highlight_list_two(int cnt, int attr);
3918
3919/*
3920 * Handle command line completion for :highlight command.
3921 */
3922 void
3923set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3924{
3925 char_u *p;
3926
3927 // Default: expand group names
3928 xp->xp_context = EXPAND_HIGHLIGHT;
3929 xp->xp_pattern = arg;
3930 include_link = 2;
3931 include_default = 1;
3932
3933 // (part of) subcommand already typed
3934 if (*arg != NUL)
3935 {
3936 p = skiptowhite(arg);
3937 if (*p != NUL) // past "default" or group name
3938 {
3939 include_default = 0;
3940 if (STRNCMP("default", arg, p - arg) == 0)
3941 {
3942 arg = skipwhite(p);
3943 xp->xp_pattern = arg;
3944 p = skiptowhite(arg);
3945 }
3946 if (*p != NUL) // past group name
3947 {
3948 include_link = 0;
3949 if (arg[1] == 'i' && arg[0] == 'N')
3950 highlight_list();
3951 if (STRNCMP("link", arg, p - arg) == 0
3952 || STRNCMP("clear", arg, p - arg) == 0)
3953 {
3954 xp->xp_pattern = skipwhite(p);
3955 p = skiptowhite(xp->xp_pattern);
3956 if (*p != NUL) // past first group name
3957 {
3958 xp->xp_pattern = skipwhite(p);
3959 p = skiptowhite(xp->xp_pattern);
3960 }
3961 }
3962 if (*p != NUL) // past group name(s)
3963 xp->xp_context = EXPAND_NOTHING;
3964 }
3965 }
3966 }
3967}
3968
3969/*
3970 * List highlighting matches in a nice way.
3971 */
3972 static void
3973highlight_list(void)
3974{
3975 int i;
3976
3977 for (i = 10; --i >= 0; )
3978 highlight_list_two(i, HL_ATTR(HLF_D));
3979 for (i = 40; --i >= 0; )
3980 highlight_list_two(99, 0);
3981}
3982
3983 static void
3984highlight_list_two(int cnt, int attr)
3985{
3986 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3987 msg_clr_eos();
3988 out_flush();
3989 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3990}
3991
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003992/*
3993 * Function given to ExpandGeneric() to obtain the list of group names.
3994 */
3995 char_u *
3996get_highlight_name(expand_T *xp UNUSED, int idx)
3997{
3998 return get_highlight_name_ext(xp, idx, TRUE);
3999}
4000
4001/*
4002 * Obtain a highlight group name.
4003 * When "skip_cleared" is TRUE don't return a cleared entry.
4004 */
4005 char_u *
4006get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
4007{
4008 if (idx < 0)
4009 return NULL;
4010
4011 // Items are never removed from the table, skip the ones that were
4012 // cleared.
4013 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
4014 return (char_u *)"";
4015
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004016 if (idx == highlight_ga.ga_len && include_none != 0)
4017 return (char_u *)"none";
4018 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
4019 return (char_u *)"default";
4020 if (idx == highlight_ga.ga_len + include_none + include_default
4021 && include_link != 0)
4022 return (char_u *)"link";
4023 if (idx == highlight_ga.ga_len + include_none + include_default + 1
4024 && include_link != 0)
4025 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004026 if (idx >= highlight_ga.ga_len)
4027 return NULL;
4028 return HL_TABLE()[idx].sg_name;
4029}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02004030
4031#if defined(FEAT_GUI) || defined(PROTO)
4032/*
4033 * Free all the highlight group fonts.
4034 * Used when quitting for systems which need it.
4035 */
4036 void
4037free_highlight_fonts(void)
4038{
4039 int idx;
4040
4041 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
4042 {
4043 gui_mch_free_font(HL_TABLE()[idx].sg_font);
4044 HL_TABLE()[idx].sg_font = NOFONT;
4045# ifdef FEAT_XFONTSET
4046 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
4047 HL_TABLE()[idx].sg_fontset = NOFONTSET;
4048# endif
4049 }
4050
4051 gui_mch_free_font(gui.norm_font);
4052# ifdef FEAT_XFONTSET
4053 gui_mch_free_fontset(gui.fontset);
4054# endif
4055# ifndef FEAT_GUI_GTK
4056 gui_mch_free_font(gui.bold_font);
4057 gui_mch_free_font(gui.ital_font);
4058 gui_mch_free_font(gui.boldital_font);
4059# endif
4060}
4061#endif
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004062
4063#if defined(FEAT_EVAL) || defined(PROTO)
4064/*
4065 * Convert each of the highlight attribute bits (bold, standout, underline,
4066 * etc.) set in 'hlattr' into a separate boolean item in a Dictionary with
4067 * the attribute name as the key.
4068 */
4069 static dict_T *
4070highlight_get_attr_dict(int hlattr)
4071{
4072 dict_T *dict;
4073 int i;
4074
4075 dict = dict_alloc();
4076 if (dict == NULL)
4077 return NULL;
4078
4079 for (i = 0; hl_attr_table[i] != 0; ++i)
4080 {
4081 if (hlattr & hl_attr_table[i])
4082 {
4083 dict_add_bool(dict, hl_name_table[i], VVAL_TRUE);
4084 hlattr &= ~hl_attr_table[i]; // don't want "inverse"
4085 }
4086 }
4087
4088 return dict;
4089}
4090
4091/*
4092 * Return the attributes of the highlight group at index 'hl_idx' as a
4093 * Dictionary. If 'resolve_link' is TRUE, then resolves the highlight group
4094 * links recursively.
4095 */
4096 static dict_T *
4097highlight_get_info(int hl_idx, int resolve_link)
4098{
4099 dict_T *dict;
4100 hl_group_T *sgp;
4101 dict_T *attr_dict;
4102 int hlgid;
4103
4104 dict = dict_alloc();
4105 if (dict == NULL)
4106 return dict;
4107
4108 sgp = &HL_TABLE()[hl_idx];
4109 // highlight group id is 1-based
4110 hlgid = hl_idx + 1;
4111
4112 if (dict_add_string(dict, "name", sgp->sg_name) == FAIL)
4113 goto error;
4114 if (dict_add_number(dict, "id", hlgid) == FAIL)
4115 goto error;
4116
4117 if (sgp->sg_link && resolve_link)
4118 {
4119 // resolve the highlight group link recursively
4120 while (sgp->sg_link)
4121 {
4122 hlgid = sgp->sg_link;
4123 sgp = &HL_TABLE()[sgp->sg_link - 1];
4124 }
4125 }
4126
4127 if (sgp->sg_term != 0)
4128 {
4129 attr_dict = highlight_get_attr_dict(sgp->sg_term);
4130 if (attr_dict != NULL)
4131 if (dict_add_dict(dict, "term", attr_dict) == FAIL)
4132 goto error;
4133 }
4134 if (sgp->sg_start != NULL)
4135 if (dict_add_string(dict, "start", sgp->sg_start) == FAIL)
4136 goto error;
4137 if (sgp->sg_stop != NULL)
4138 if (dict_add_string(dict, "stop", sgp->sg_stop) == FAIL)
4139 goto error;
4140 if (sgp->sg_cterm != 0)
4141 {
4142 attr_dict = highlight_get_attr_dict(sgp->sg_cterm);
4143 if (attr_dict != NULL)
4144 if (dict_add_dict(dict, "cterm", attr_dict) == FAIL)
4145 goto error;
4146 }
4147 if (sgp->sg_cterm_fg != 0)
4148 if (dict_add_string(dict, "ctermfg",
4149 highlight_color(hlgid, (char_u *)"fg", 'c')) == FAIL)
4150 goto error;
4151 if (sgp->sg_cterm_bg != 0)
4152 if (dict_add_string(dict, "ctermbg",
4153 highlight_color(hlgid, (char_u *)"bg", 'c')) == FAIL)
4154 goto error;
4155 if (sgp->sg_cterm_ul != 0)
4156 if (dict_add_string(dict, "ctermul",
4157 highlight_color(hlgid, (char_u *)"ul", 'c')) == FAIL)
4158 goto error;
4159 if (sgp->sg_gui != 0)
4160 {
4161 attr_dict = highlight_get_attr_dict(sgp->sg_gui);
4162 if (attr_dict != NULL)
4163 if (dict_add_dict(dict, "gui", attr_dict) == FAIL)
4164 goto error;
4165 }
4166 if (sgp->sg_gui_fg_name != NULL)
4167 if (dict_add_string(dict, "guifg",
4168 highlight_color(hlgid, (char_u *)"fg", 'g')) == FAIL)
4169 goto error;
4170 if (sgp->sg_gui_bg_name != NULL)
4171 if (dict_add_string(dict, "guibg",
4172 highlight_color(hlgid, (char_u *)"bg", 'g')) == FAIL)
4173 goto error;
4174 if (sgp->sg_gui_sp_name != NULL)
4175 if (dict_add_string(dict, "guisp",
4176 highlight_color(hlgid, (char_u *)"sp", 'g')) == FAIL)
4177 goto error;
4178# ifdef FEAT_GUI
4179 if (sgp->sg_font_name != NULL)
4180 if (dict_add_string(dict, "font", sgp->sg_font_name) == FAIL)
4181 goto error;
4182# endif
4183 if (sgp->sg_link)
4184 {
4185 char_u *link;
4186
4187 link = HL_TABLE()[sgp->sg_link - 1].sg_name;
4188 if (link != NULL && dict_add_string(dict, "linksto", link) == FAIL)
4189 goto error;
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004190
4191 if (sgp->sg_deflink)
4192 dict_add_bool(dict, "default", VVAL_TRUE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004193 }
4194 if (dict_len(dict) == 2)
4195 // If only 'name' is present, then the highlight group is cleared.
4196 dict_add_bool(dict, "cleared", VVAL_TRUE);
4197
4198 return dict;
4199
4200error:
4201 vim_free(dict);
4202 return NULL;
4203}
4204
4205/*
4206 * "hlget([name])" function
4207 * Return the attributes of a specific highlight group (if specified) or all
4208 * the highlight groups.
4209 */
4210 void
4211f_hlget(typval_T *argvars, typval_T *rettv)
4212{
4213 list_T *list;
4214 dict_T *dict;
4215 int i;
4216 char_u *hlarg = NULL;
4217 int resolve_link = FALSE;
4218
4219 if (rettv_list_alloc(rettv) == FAIL)
4220 return;
4221
4222 if (check_for_opt_string_arg(argvars, 0) == FAIL
4223 || (argvars[0].v_type != VAR_UNKNOWN
4224 && check_for_opt_bool_arg(argvars, 1) == FAIL))
4225 return;
4226
4227 if (argvars[0].v_type != VAR_UNKNOWN)
4228 {
4229 // highlight group name supplied
4230 hlarg = tv_get_string_chk(&argvars[0]);
4231 if (hlarg == NULL)
4232 return;
4233
4234 if (argvars[1].v_type != VAR_UNKNOWN)
4235 {
4236 int error = FALSE;
4237
4238 resolve_link = tv_get_bool_chk(&argvars[1], &error);
4239 if (error)
4240 return;
4241 }
4242 }
4243
4244 list = rettv->vval.v_list;
4245 for (i = 0; i < highlight_ga.ga_len && !got_int; ++i)
4246 {
4247 if (hlarg == NULL || STRICMP(hlarg, HL_TABLE()[i].sg_name) == 0)
4248 {
4249 dict = highlight_get_info(i, resolve_link);
4250 if (dict != NULL)
4251 list_append_dict(list, dict);
4252 }
4253 }
4254}
4255
4256/*
4257 * Returns the string value at 'dict[key]'. Returns NULL, if 'key' is not in
4258 * 'dict' or the value is not a string type. If the value is not a string type
4259 * or is NULL, then 'error' is set to TRUE.
4260 */
4261 static char_u *
4262hldict_get_string(dict_T *dict, char_u *key, int *error)
4263{
4264 dictitem_T *di;
4265
4266 *error = FALSE;
4267 di = dict_find(dict, key, -1);
4268 if (di == NULL)
4269 return NULL;
4270
4271 if (di->di_tv.v_type != VAR_STRING || di->di_tv.vval.v_string == NULL)
4272 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004273 emsg(_(e_string_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004274 *error = TRUE;
4275 return NULL;
4276 }
4277
4278 return di->di_tv.vval.v_string;
4279}
4280
4281/*
4282 * Convert the highlight attribute Dictionary at 'dict[key]' into a string
4283 * value in 'attr_str' of length 'len'. Returns FALSE if 'dict[key]' is not a
4284 * Dictionary or is NULL.
4285 */
4286 static int
4287hldict_attr_to_str(
4288 dict_T *dict,
4289 char_u *key,
4290 char_u *attr_str,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004291 size_t len)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004292{
4293 dictitem_T *di;
4294 dict_T *attrdict;
4295 int i;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004296 char_u *p;
4297 size_t sz;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004298
4299 attr_str[0] = NUL;
4300 di = dict_find(dict, key, -1);
4301 if (di == NULL)
4302 return TRUE;
4303
4304 if (di->di_tv.v_type != VAR_DICT || di->di_tv.vval.v_dict == NULL)
4305 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004306 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004307 return FALSE;
4308 }
4309
4310 attrdict = di->di_tv.vval.v_dict;
4311
4312 // If the attribute dict is empty, then return NONE to clear the attributes
4313 if (dict_len(attrdict) == 0)
4314 {
4315 vim_strcat(attr_str, (char_u *)"NONE", len);
4316 return TRUE;
4317 }
4318
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004319 p = attr_str;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004320 for (i = 0; i < (int)ARRAY_LENGTH(hl_name_table); i++)
4321 {
4322 if (dict_get_bool(attrdict, (char_u *)hl_name_table[i],
4323 VVAL_FALSE) == VVAL_TRUE)
4324 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004325 if (p != attr_str && (size_t)(p - attr_str + 2) < len)
4326 STRCPY(p, (char_u *)",");
4327 sz = STRLEN(hl_name_table[i]);
4328 if (p - attr_str + sz + 1 < len)
4329 {
4330 STRCPY(p, (char_u *)hl_name_table[i]);
4331 p += sz;
4332 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004333 }
4334 }
4335
4336 return TRUE;
4337}
4338
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004339// Temporary buffer used to store the command string produced by hlset().
4340// IObuff cannot be used for this as the error messages produced by hlset()
4341// internally use IObuff.
4342#define HLSETBUFSZ 512
4343static char_u hlsetBuf[HLSETBUFSZ + 1];
4344
4345/*
4346 * Add the highlight attribute "attr" of length "attrlen" and "value" at
4347 * "dptr", which points into "hlsetBuf".
4348 * Returns the updated pointer.
4349 */
4350 static char_u *
4351add_attr_and_value(char_u *dptr, char_u *attr, int attrlen, char_u *value)
4352{
4353 size_t vallen;
4354
4355 // Do nothing if the value is not specified or is empty
4356 if (value == NULL || *value == NUL)
4357 return dptr;
4358
4359 vallen = STRLEN(value);
4360 if (dptr + attrlen + vallen + 1 < hlsetBuf + HLSETBUFSZ)
4361 {
4362 STRCPY(dptr, attr);
4363 dptr += attrlen;
4364 STRCPY(dptr, value);
4365 dptr += vallen;
4366 }
4367
4368 return dptr;
4369}
4370
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004371/*
4372 * Add or update a highlight group using 'dict' items. Returns TRUE if
4373 * successfully updated the highlight group.
4374 */
4375 static int
4376hlg_add_or_update(dict_T *dict)
4377{
4378 char_u *name;
4379 int error;
4380 char_u term_attr[80];
4381 char_u cterm_attr[80];
4382 char_u gui_attr[80];
4383 char_u *start;
4384 char_u *stop;
4385 char_u *ctermfg;
4386 char_u *ctermbg;
4387 char_u *ctermul;
4388 char_u *guifg;
4389 char_u *guibg;
4390 char_u *guisp;
4391# ifdef FEAT_GUI
4392 char_u *font;
4393# endif
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004394 int forceit = FALSE;
4395 int dodefault = FALSE;
4396 int done = FALSE;
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004397 char_u *p;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004398
4399 name = hldict_get_string(dict, (char_u *)"name", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004400 if (name == NULL || *name == NUL || error)
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004401 return FALSE;
4402
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004403 if (dict_get_bool(dict, (char_u *)"force", VVAL_FALSE) == VVAL_TRUE)
4404 forceit = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004405
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004406 if (dict_get_bool(dict, (char_u *)"default", VVAL_FALSE) == VVAL_TRUE)
4407 dodefault = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004408
4409 if (dict_find(dict, (char_u *)"cleared", -1) != NULL)
4410 {
4411 varnumber_T cleared;
4412
4413 // clear a highlight group
4414 cleared = dict_get_bool(dict, (char_u *)"cleared", FALSE);
4415 if (cleared == TRUE)
4416 {
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004417 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "clear %s", name);
4418 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004419 done = TRUE;
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004420 }
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004421 }
4422
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004423 if (dict_find(dict, (char_u *)"linksto", -1) != NULL)
4424 {
4425 char_u *linksto;
4426
4427 // link highlight groups
4428 linksto = hldict_get_string(dict, (char_u *)"linksto", &error);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004429 if (linksto == NULL || *linksto == NUL || error)
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004430 return FALSE;
4431
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004432 vim_snprintf((char *)hlsetBuf, HLSETBUFSZ, "%slink %s %s",
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004433 dodefault ? "default " : "", name, linksto);
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004434 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanan2a16dc62021-11-16 17:19:30 +00004435
4436 done = TRUE;
4437 }
4438
4439 // If 'cleared' or 'linksto' are specified, then don't process the other
4440 // attributes.
4441 if (done)
4442 return TRUE;
4443
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004444 start = hldict_get_string(dict, (char_u *)"start", &error);
4445 if (error)
4446 return FALSE;
4447
4448 stop = hldict_get_string(dict, (char_u *)"stop", &error);
4449 if (error)
4450 return FALSE;
4451
4452 if (!hldict_attr_to_str(dict, (char_u *)"term", term_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004453 sizeof(term_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004454 return FALSE;
4455
4456 if (!hldict_attr_to_str(dict, (char_u *)"cterm", cterm_attr,
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004457 sizeof(cterm_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004458 return FALSE;
4459
4460 ctermfg = hldict_get_string(dict, (char_u *)"ctermfg", &error);
4461 if (error)
4462 return FALSE;
4463
4464 ctermbg = hldict_get_string(dict, (char_u *)"ctermbg", &error);
4465 if (error)
4466 return FALSE;
4467
4468 ctermul = hldict_get_string(dict, (char_u *)"ctermul", &error);
4469 if (error)
4470 return FALSE;
4471
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004472 if (!hldict_attr_to_str(dict, (char_u *)"gui", gui_attr, sizeof(gui_attr)))
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004473 return FALSE;
4474
4475 guifg = hldict_get_string(dict, (char_u *)"guifg", &error);
4476 if (error)
4477 return FALSE;
4478
4479 guibg = hldict_get_string(dict, (char_u *)"guibg", &error);
4480 if (error)
4481 return FALSE;
4482
4483 guisp = hldict_get_string(dict, (char_u *)"guisp", &error);
4484 if (error)
4485 return FALSE;
4486
4487# ifdef FEAT_GUI
4488 font = hldict_get_string(dict, (char_u *)"font", &error);
4489 if (error)
4490 return FALSE;
4491# endif
4492
4493 // If none of the attributes are specified, then do nothing.
4494 if (term_attr[0] == NUL && start == NULL && stop == NULL
4495 && cterm_attr[0] == NUL && ctermfg == NULL && ctermbg == NULL
4496 && ctermul == NULL && gui_attr[0] == NUL
4497# ifdef FEAT_GUI
4498 && font == NULL
4499# endif
4500 && guifg == NULL && guibg == NULL && guisp == NULL
4501 )
4502 return TRUE;
4503
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004504 hlsetBuf[0] = NUL;
4505 p = hlsetBuf;
4506 if (dodefault)
4507 p = add_attr_and_value(p, (char_u *)"default", 7, (char_u *)" ");
4508 p = add_attr_and_value(p, (char_u *)"", 0, name);
4509 p = add_attr_and_value(p, (char_u *)" term=", 6, term_attr);
4510 p = add_attr_and_value(p, (char_u *)" start=", 7, start);
4511 p = add_attr_and_value(p, (char_u *)" stop=", 6, stop);
4512 p = add_attr_and_value(p, (char_u *)" cterm=", 7, cterm_attr);
4513 p = add_attr_and_value(p, (char_u *)" ctermfg=", 9, ctermfg);
4514 p = add_attr_and_value(p, (char_u *)" ctermbg=", 9, ctermbg);
4515 p = add_attr_and_value(p, (char_u *)" ctermul=", 9, ctermul);
4516 p = add_attr_and_value(p, (char_u *)" gui=", 5, gui_attr);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004517# ifdef FEAT_GUI
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004518 p = add_attr_and_value(p, (char_u *)" font=", 6, font);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004519# endif
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004520 p = add_attr_and_value(p, (char_u *)" guifg=", 7, guifg);
4521 p = add_attr_and_value(p, (char_u *)" guibg=", 7, guibg);
4522 p = add_attr_and_value(p, (char_u *)" guisp=", 7, guisp);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004523
Yegappan Lakshmananbb277fd2021-11-24 20:28:31 +00004524 do_highlight(hlsetBuf, forceit, FALSE);
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004525
4526 return TRUE;
4527}
4528
4529/*
4530 * "hlset([{highlight_attr}])" function
4531 * Add or modify highlight groups
4532 */
4533 void
4534f_hlset(typval_T *argvars, typval_T *rettv)
4535{
4536 listitem_T *li;
4537 dict_T *dict;
4538
4539 rettv->vval.v_number = -1;
4540
4541 if (check_for_list_arg(argvars, 0) == FAIL)
4542 return;
4543
4544 FOR_ALL_LIST_ITEMS(argvars->vval.v_list, li)
4545 {
4546 if (li->li_tv.v_type != VAR_DICT)
4547 {
Bram Moolenaar460ae5d2022-01-01 14:19:49 +00004548 emsg(_(e_dictionary_required));
Yegappan Lakshmanand1a8d652021-11-03 21:56:45 +00004549 return;
4550 }
4551
4552 dict = li->li_tv.vval.v_dict;
4553 if (!hlg_add_or_update(dict))
4554 return;
4555 }
4556
4557 rettv->vval.v_number = 0;
4558}
4559#endif