blob: 76d9a7e82eb6ce52408639e44fb9a7dad3a15d3a [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
21/*
22 * The "term", "cterm" and "gui" arguments can be any combination of the
23 * following names, separated by commas (but no spaces!).
24 */
25static char *(hl_name_table[]) =
26 {"bold", "standout", "underline", "undercurl",
27 "italic", "reverse", "inverse", "nocombine", "strikethrough", "NONE"};
28static int hl_attr_table[] =
29 {HL_BOLD, HL_STANDOUT, HL_UNDERLINE, HL_UNDERCURL, HL_ITALIC, HL_INVERSE, HL_INVERSE, HL_NOCOMBINE, HL_STRIKETHROUGH, 0};
30#define ATTR_COMBINE(attr_a, attr_b) ((((attr_b) & HL_NOCOMBINE) ? attr_b : (attr_a)) | (attr_b))
31
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020032/*
33 * Structure that stores information about a highlight group.
34 * The ID of a highlight group is also called group ID. It is the index in
35 * the highlight_ga array PLUS ONE.
36 */
37typedef struct
38{
39 char_u *sg_name; // highlight group name
40 char_u *sg_name_u; // uppercase of sg_name
41 int sg_cleared; // "hi clear" was used
42// for normal terminals
43 int sg_term; // "term=" highlighting attributes
44 char_u *sg_start; // terminal string for start highl
45 char_u *sg_stop; // terminal string for stop highl
46 int sg_term_attr; // Screen attr for term mode
47// for color terminals
48 int sg_cterm; // "cterm=" highlighting attr
49 int sg_cterm_bold; // bold attr was set for light color
50 int sg_cterm_fg; // terminal fg color number + 1
51 int sg_cterm_bg; // terminal bg color number + 1
Bram Moolenaare023e882020-05-31 16:42:30 +020052 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020053 int sg_cterm_attr; // Screen attr for color term mode
54// for when using the GUI
55#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
56 guicolor_T sg_gui_fg; // GUI foreground color handle
57 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020058 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020059#endif
60#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020061 GuiFont sg_font; // GUI font handle
62#ifdef FEAT_XFONTSET
63 GuiFontset sg_fontset; // GUI fontset handle
64#endif
65 char_u *sg_font_name; // GUI font or fontset name
66 int sg_gui_attr; // Screen attr for GUI mode
67#endif
68#if defined(FEAT_GUI) || defined(FEAT_EVAL)
69// Store the sp color name for the GUI or synIDattr()
70 int sg_gui; // "gui=" highlighting attributes
71 char_u *sg_gui_fg_name;// GUI foreground color name
72 char_u *sg_gui_bg_name;// GUI background color name
73 char_u *sg_gui_sp_name;// GUI special color name
74#endif
75 int sg_link; // link to this highlight group ID
Bram Moolenaar213da552020-09-17 19:59:26 +020076 int sg_deflink; // default link; restored in highlight_clear()
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020077 int sg_set; // combination of SG_* flags
78#ifdef FEAT_EVAL
Bram Moolenaare8df0102020-09-18 19:40:45 +020079 sctx_T sg_deflink_sctx; // script where the default link was set
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020080 sctx_T sg_script_ctx; // script in which the group was last set
81#endif
82} hl_group_T;
83
84// highlight groups for 'highlight' option
85static garray_T highlight_ga;
86#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
87
88/*
89 * An attribute number is the index in attr_table plus ATTR_OFF.
90 */
91#define ATTR_OFF (HL_ALL + 1)
92
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020093static void syn_unadd_group(void);
94static void set_hl_attr(int idx);
95static void highlight_list_one(int id);
96static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
97static int syn_add_group(char_u *name);
98static int hl_has_settings(int idx, int check_link);
99static void highlight_clear(int idx);
100
101#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
102static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
103#endif
104#ifdef FEAT_GUI
105static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
106static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
107#endif
108
109/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200110 * The default highlight groups. These are compiled-in for fast startup and
111 * they still work when the runtime files can't be found.
112 * When making changes here, also change runtime/colors/default.vim!
113 * The #ifdefs are needed to reduce the amount of static data. Helps to make
114 * the 16 bit DOS (museum) version compile.
115 */
116#if defined(FEAT_GUI) || defined(FEAT_EVAL)
117# define CENT(a, b) b
118#else
119# define CENT(a, b) a
120#endif
121static char *(highlight_init_both[]) = {
122 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
123 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
124 CENT("IncSearch term=reverse cterm=reverse",
125 "IncSearch term=reverse cterm=reverse gui=reverse"),
126 CENT("ModeMsg term=bold cterm=bold",
127 "ModeMsg term=bold cterm=bold gui=bold"),
128 CENT("NonText term=bold ctermfg=Blue",
129 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
130 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
131 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
132 CENT("StatusLineNC term=reverse cterm=reverse",
133 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
134 "default link EndOfBuffer NonText",
135 CENT("VertSplit term=reverse cterm=reverse",
136 "VertSplit term=reverse cterm=reverse gui=reverse"),
137#ifdef FEAT_CLIPBOARD
138 CENT("VisualNOS term=underline,bold cterm=underline,bold",
139 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
140#endif
141#ifdef FEAT_DIFF
142 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
143 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
144#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200145 CENT("PmenuSbar ctermbg=Grey",
146 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200147 CENT("TabLineSel term=bold cterm=bold",
148 "TabLineSel term=bold cterm=bold gui=bold"),
149 CENT("TabLineFill term=reverse cterm=reverse",
150 "TabLineFill term=reverse cterm=reverse gui=reverse"),
151#ifdef FEAT_GUI
152 "Cursor guibg=fg guifg=bg",
153 "lCursor guibg=fg guifg=bg", // should be different, but what?
154#endif
155 "default link QuickFixLine Search",
156 CENT("Normal cterm=NONE", "Normal gui=NONE"),
157 NULL
158};
159
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200160// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200161static char *(highlight_init_light[]) = {
162 CENT("Directory term=bold ctermfg=DarkBlue",
163 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
164 CENT("LineNr term=underline ctermfg=Brown",
165 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200166 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
167 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200168 CENT("MoreMsg term=bold ctermfg=DarkGreen",
169 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
170 CENT("Question term=standout ctermfg=DarkGreen",
171 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
172 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
173 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
174#ifdef FEAT_SPELL
175 CENT("SpellBad term=reverse ctermbg=LightRed",
176 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
177 CENT("SpellCap term=reverse ctermbg=LightBlue",
178 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
179 CENT("SpellRare term=reverse ctermbg=LightMagenta",
180 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
181 CENT("SpellLocal term=underline ctermbg=Cyan",
182 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
183#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200184 CENT("PmenuThumb ctermbg=Black",
185 "PmenuThumb ctermbg=Black guibg=Black"),
186 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
187 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
188 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
189 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200190 CENT("SpecialKey term=bold ctermfg=DarkBlue",
191 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
192 CENT("Title term=bold ctermfg=DarkMagenta",
193 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
194 CENT("WarningMsg term=standout ctermfg=DarkRed",
195 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
196#ifdef FEAT_WILDMENU
197 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
198 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
199#endif
200#ifdef FEAT_FOLDING
201 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
202 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
203 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
204 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
205#endif
206#ifdef FEAT_SIGNS
207 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
208 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
209#endif
210 CENT("Visual term=reverse",
211 "Visual term=reverse guibg=LightGrey"),
212#ifdef FEAT_DIFF
213 CENT("DiffAdd term=bold ctermbg=LightBlue",
214 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
215 CENT("DiffChange term=bold ctermbg=LightMagenta",
216 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
217 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
218 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
219#endif
220 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
221 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
222#ifdef FEAT_SYN_HL
223 CENT("CursorColumn term=reverse ctermbg=LightGrey",
224 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
225 CENT("CursorLine term=underline cterm=underline",
226 "CursorLine term=underline cterm=underline guibg=Grey90"),
227 CENT("ColorColumn term=reverse ctermbg=LightRed",
228 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
229#endif
230#ifdef FEAT_CONCEAL
231 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
232 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
233#endif
234 CENT("MatchParen term=reverse ctermbg=Cyan",
235 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
236#ifdef FEAT_TERMINAL
237 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
238 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
239 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
240 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
241#endif
242#ifdef FEAT_MENU
243 CENT("ToolbarLine term=underline ctermbg=LightGrey",
244 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
245 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
246 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
247#endif
248 NULL
249};
250
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200251// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200252static char *(highlight_init_dark[]) = {
253 CENT("Directory term=bold ctermfg=LightCyan",
254 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
255 CENT("LineNr term=underline ctermfg=Yellow",
256 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200257 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
258 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200259 CENT("MoreMsg term=bold ctermfg=LightGreen",
260 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
261 CENT("Question term=standout ctermfg=LightGreen",
262 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
263 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
264 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
265 CENT("SpecialKey term=bold ctermfg=LightBlue",
266 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
267#ifdef FEAT_SPELL
268 CENT("SpellBad term=reverse ctermbg=Red",
269 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
270 CENT("SpellCap term=reverse ctermbg=Blue",
271 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
272 CENT("SpellRare term=reverse ctermbg=Magenta",
273 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
274 CENT("SpellLocal term=underline ctermbg=Cyan",
275 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
276#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200277 CENT("PmenuThumb ctermbg=White",
278 "PmenuThumb ctermbg=White guibg=White"),
279 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
280 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
281 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
282 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200283 CENT("Title term=bold ctermfg=LightMagenta",
284 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
285 CENT("WarningMsg term=standout ctermfg=LightRed",
286 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
287#ifdef FEAT_WILDMENU
288 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
289 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
290#endif
291#ifdef FEAT_FOLDING
292 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
293 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
294 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
295 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
296#endif
297#ifdef FEAT_SIGNS
298 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
299 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
300#endif
301 CENT("Visual term=reverse",
302 "Visual term=reverse guibg=DarkGrey"),
303#ifdef FEAT_DIFF
304 CENT("DiffAdd term=bold ctermbg=DarkBlue",
305 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
306 CENT("DiffChange term=bold ctermbg=DarkMagenta",
307 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
308 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
309 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
310#endif
311 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
312 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
313#ifdef FEAT_SYN_HL
314 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
315 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
316 CENT("CursorLine term=underline cterm=underline",
317 "CursorLine term=underline cterm=underline guibg=Grey40"),
318 CENT("ColorColumn term=reverse ctermbg=DarkRed",
319 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
320#endif
321 CENT("MatchParen term=reverse ctermbg=DarkCyan",
322 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
323#ifdef FEAT_CONCEAL
324 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
325 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
326#endif
327#ifdef FEAT_TERMINAL
328 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
329 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
330 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
331 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
332#endif
333#ifdef FEAT_MENU
334 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
335 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
336 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
337 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
338#endif
339 NULL
340};
341
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200342/*
343 * Returns the number of highlight groups.
344 */
345 int
346highlight_num_groups(void)
347{
348 return highlight_ga.ga_len;
349}
350
351/*
352 * Returns the name of a highlight group.
353 */
354 char_u *
355highlight_group_name(int id)
356{
357 return HL_TABLE()[id].sg_name;
358}
359
360/*
361 * Returns the ID of the link to a highlight group.
362 */
363 int
364highlight_link_id(int id)
365{
366 return HL_TABLE()[id].sg_link;
367}
368
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200369 void
370init_highlight(
371 int both, // include groups where 'bg' doesn't matter
372 int reset) // clear group first
373{
374 int i;
375 char **pp;
376 static int had_both = FALSE;
377#ifdef FEAT_EVAL
378 char_u *p;
379
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100380 // Try finding the color scheme file. Used when a color file was loaded
381 // and 'background' or 't_Co' is changed.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200382 p = get_var_value((char_u *)"g:colors_name");
383 if (p != NULL)
384 {
385 // The value of g:colors_name could be freed when sourcing the script,
386 // making "p" invalid, so copy it.
387 char_u *copy_p = vim_strsave(p);
388 int r;
389
390 if (copy_p != NULL)
391 {
392 r = load_colors(copy_p);
393 vim_free(copy_p);
394 if (r == OK)
395 return;
396 }
397 }
398
399#endif
400
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100401 // Didn't use a color file, use the compiled-in colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200402 if (both)
403 {
404 had_both = TRUE;
405 pp = highlight_init_both;
406 for (i = 0; pp[i] != NULL; ++i)
407 do_highlight((char_u *)pp[i], reset, TRUE);
408 }
409 else if (!had_both)
410 // Don't do anything before the call with both == TRUE from main().
411 // Not everything has been setup then, and that call will overrule
412 // everything anyway.
413 return;
414
415 if (*p_bg == 'l')
416 pp = highlight_init_light;
417 else
418 pp = highlight_init_dark;
419 for (i = 0; pp[i] != NULL; ++i)
420 do_highlight((char_u *)pp[i], reset, TRUE);
421
422 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
423 // depend on the number of colors available.
424 // With 8 colors brown is equal to yellow, need to use black for Search fg
425 // to avoid Statement highlighted text disappears.
426 // Clear the attributes, needed when changing the t_Co value.
427 if (t_colors > 8)
428 do_highlight((char_u *)(*p_bg == 'l'
429 ? "Visual cterm=NONE ctermbg=LightGrey"
430 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
431 else
432 {
433 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
434 FALSE, TRUE);
435 if (*p_bg == 'l')
436 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
437 }
438
439#ifdef FEAT_SYN_HL
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100440 // If syntax highlighting is enabled load the highlighting for it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200441 if (get_var_value((char_u *)"g:syntax_on") != NULL)
442 {
443 static int recursive = 0;
444
445 if (recursive >= 5)
446 emsg(_("E679: recursive loop loading syncolor.vim"));
447 else
448 {
449 ++recursive;
450 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
451 --recursive;
452 }
453 }
454#endif
455}
456
457/*
458 * Load color file "name".
459 * Return OK for success, FAIL for failure.
460 */
461 int
462load_colors(char_u *name)
463{
464 char_u *buf;
465 int retval = FAIL;
466 static int recursive = FALSE;
467
468 // When being called recursively, this is probably because setting
469 // 'background' caused the highlighting to be reloaded. This means it is
470 // working, thus we should return OK.
471 if (recursive)
472 return OK;
473
474 recursive = TRUE;
475 buf = alloc(STRLEN(name) + 12);
476 if (buf != NULL)
477 {
478 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
479 curbuf->b_fname, FALSE, curbuf);
480 sprintf((char *)buf, "colors/%s.vim", name);
481 retval = source_runtime(buf, DIP_START + DIP_OPT);
482 vim_free(buf);
483 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
484 }
485 recursive = FALSE;
486
487 return retval;
488}
489
490static char *(color_names[28]) = {
491 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
492 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
493 "Gray", "Grey", "LightGray", "LightGrey",
494 "DarkGray", "DarkGrey",
495 "Blue", "LightBlue", "Green", "LightGreen",
496 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
497 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
498 // indices:
499 // 0, 1, 2, 3,
500 // 4, 5, 6, 7,
501 // 8, 9, 10, 11,
502 // 12, 13,
503 // 14, 15, 16, 17,
504 // 18, 19, 20, 21, 22,
505 // 23, 24, 25, 26, 27
506static int color_numbers_16[28] = {0, 1, 2, 3,
507 4, 5, 6, 6,
508 7, 7, 7, 7,
509 8, 8,
510 9, 9, 10, 10,
511 11, 11, 12, 12, 13,
512 13, 14, 14, 15, -1};
513// for xterm with 88 colors...
514static int color_numbers_88[28] = {0, 4, 2, 6,
515 1, 5, 32, 72,
516 84, 84, 7, 7,
517 82, 82,
518 12, 43, 10, 61,
519 14, 63, 9, 74, 13,
520 75, 11, 78, 15, -1};
521// for xterm with 256 colors...
522static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200523 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200524 248, 248, 7, 7,
525 242, 242,
526 12, 81, 10, 121,
527 14, 159, 9, 224, 13,
528 225, 11, 229, 15, -1};
529// for terminals with less than 16 colors...
530static int color_numbers_8[28] = {0, 4, 2, 6,
531 1, 5, 3, 3,
532 7, 7, 7, 7,
533 0+8, 0+8,
534 4+8, 4+8, 2+8, 2+8,
535 6+8, 6+8, 1+8, 1+8, 5+8,
536 5+8, 3+8, 3+8, 7+8, -1};
537
538/*
539 * Lookup the "cterm" value to be used for color with index "idx" in
540 * color_names[].
541 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
542 * colors, otherwise it will be unchanged.
543 */
544 int
545lookup_color(int idx, int foreground, int *boldp)
546{
547 int color = color_numbers_16[idx];
548 char_u *p;
549
550 // Use the _16 table to check if it's a valid color name.
551 if (color < 0)
552 return -1;
553
554 if (t_colors == 8)
555 {
556 // t_Co is 8: use the 8 colors table
557#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100558 // On qnx, the 8 & 16 color arrays are the same
559 if (STRNCMP(T_NAME, "qansi", 5) == 0)
560 color = color_numbers_16[idx];
561 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200562#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100563 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200564 if (foreground)
565 {
566 // set/reset bold attribute to get light foreground
567 // colors (on some terminals, e.g. "linux")
568 if (color & 8)
569 *boldp = TRUE;
570 else
571 *boldp = FALSE;
572 }
573 color &= 7; // truncate to 8 colors
574 }
575 else if (t_colors == 16 || t_colors == 88
576 || t_colors >= 256)
577 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100578 // Guess: if the termcap entry ends in 'm', it is
579 // probably an xterm-like terminal. Use the changed
580 // order for colors.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200581 if (*T_CAF != NUL)
582 p = T_CAF;
583 else
584 p = T_CSF;
585 if (*p != NUL && (t_colors > 256
586 || *(p + STRLEN(p) - 1) == 'm'))
587 {
588 if (t_colors == 88)
589 color = color_numbers_88[idx];
590 else if (t_colors >= 256)
591 color = color_numbers_256[idx];
592 else
593 color = color_numbers_8[idx];
594 }
595#ifdef FEAT_TERMRESPONSE
596 if (t_colors >= 256 && color == 15 && is_mac_terminal)
597 // Terminal.app has a bug: 15 is light grey. Use white
598 // from the color cube instead.
599 color = 231;
600#endif
601 }
602 return color;
603}
604
605/*
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +0100606 * Link highlight group 'from_hg' to 'to_hg'.
607 * 'dodefault' is set to TRUE for ":highlight default link".
608 * 'forceit' is set to TRUE for ":higlight! link"
609 * 'init' is set to TRUE when initializing all the highlight groups.
610 */
611 static void
612highlight_group_link(
613 char_u *from_hg,
614 int from_len,
615 char_u *to_hg,
616 int to_len,
617 int dodefault,
618 int forceit,
619 int init)
620{
621 int from_id;
622 int to_id;
623 hl_group_T *hlgroup = NULL;
624
625 from_id = syn_check_group(from_hg, from_len);
626 if (STRNCMP(to_hg, "NONE", 4) == 0)
627 to_id = 0;
628 else
629 to_id = syn_check_group(to_hg, to_len);
630
631 if (from_id > 0)
632 {
633 hlgroup = &HL_TABLE()[from_id - 1];
634 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
635 {
636 hlgroup->sg_deflink = to_id;
637#ifdef FEAT_EVAL
638 hlgroup->sg_deflink_sctx = current_sctx;
639 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
640#endif
641 }
642 }
643
644 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
645 {
646 // Don't allow a link when there already is some highlighting
647 // for the group, unless '!' is used
648 if (to_id > 0 && !forceit && !init
649 && hl_has_settings(from_id - 1, dodefault))
650 {
651 if (SOURCING_NAME == NULL && !dodefault)
652 emsg(_("E414: group has settings, highlight link ignored"));
653 }
654 else if (hlgroup->sg_link != to_id
655#ifdef FEAT_EVAL
656 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
657#endif
658 || hlgroup->sg_cleared)
659 {
660 if (!init)
661 hlgroup->sg_set |= SG_LINK;
662 hlgroup->sg_link = to_id;
663#ifdef FEAT_EVAL
664 hlgroup->sg_script_ctx = current_sctx;
665 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
666#endif
667 hlgroup->sg_cleared = FALSE;
668 redraw_all_later(SOME_VALID);
669
670 // Only call highlight_changed() once after multiple changes.
671 need_highlight_changed = TRUE;
672 }
673 }
674
675}
676
677/*
678 * Reset all highlighting to the defaults. Removes all highlighting for the
679 * groups added by the user.
680 */
681 static void
682highlight_reset_all(void)
683{
684 int idx;
685
686#ifdef FEAT_GUI
687 // First, we do not destroy the old values, but allocate the new
688 // ones and update the display. THEN we destroy the old values.
689 // If we destroy the old values first, then the old values
690 // (such as GuiFont's or GuiFontset's) will still be displayed but
691 // invalid because they were free'd.
692 if (gui.in_use)
693 {
694# ifdef FEAT_BEVAL_TIP
695 gui_init_tooltip_font();
696# endif
697# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
698 gui_init_menu_font();
699# endif
700 }
701# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
702 gui_mch_def_colors();
703# endif
704# ifdef FEAT_GUI_X11
705# ifdef FEAT_MENU
706
707 // This only needs to be done when there is no Menu highlight
708 // group defined by default, which IS currently the case.
709 gui_mch_new_menu_colors();
710# endif
711 if (gui.in_use)
712 {
713 gui_new_scrollbar_colors();
714# ifdef FEAT_BEVAL_GUI
715 gui_mch_new_tooltip_colors();
716# endif
717# ifdef FEAT_MENU
718 gui_mch_new_menu_font();
719# endif
720 }
721# endif
722
723 // Ok, we're done allocating the new default graphics items.
724 // The screen should already be refreshed at this point.
725 // It is now Ok to clear out the old data.
726#endif
727#ifdef FEAT_EVAL
728 do_unlet((char_u *)"g:colors_name", TRUE);
729#endif
730 restore_cterm_colors();
731
732 // Clear all default highlight groups and load the defaults.
733 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
734 highlight_clear(idx);
735 init_highlight(TRUE, TRUE);
736#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
737 if (USE_24BIT)
738 highlight_gui_started();
739 else
740#endif
741 highlight_changed();
742 redraw_later_clear();
743}
744
745/*
746 * Set the 'term' or 'cterm' or 'gui' attributes for the highlight group at
747 * index 'idx'.
748 * 'key' is one of 'TERM' or 'CTERM' or 'GUI'
749 * 'arg' is the list of attribute names separated by comma.
750 * 'init' is set to TRUE when initializing all the highlight groups.
751 * Returns TRUE if the attributes are set.
752 */
753 static int
754highlight_set_termgui_attr(int idx, char_u *key, char_u *arg, int init)
755{
756 int attr;
757 int off;
758 long i;
759 int len;
760
761 attr = 0;
762 off = 0;
763 while (arg[off] != NUL)
764 {
765 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
766 {
767 len = (int)STRLEN(hl_name_table[i]);
768 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
769 {
770 attr |= hl_attr_table[i];
771 off += len;
772 break;
773 }
774 }
775 if (i < 0)
776 {
777 semsg(_("E418: Illegal value: %s"), arg);
778 return FALSE;
779 }
780 if (arg[off] == ',') // another one follows
781 ++off;
782 }
783 if (*key == 'T')
784 {
785 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
786 {
787 if (!init)
788 HL_TABLE()[idx].sg_set |= SG_TERM;
789 HL_TABLE()[idx].sg_term = attr;
790 }
791 }
792 else if (*key == 'C')
793 {
794 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
795 {
796 if (!init)
797 HL_TABLE()[idx].sg_set |= SG_CTERM;
798 HL_TABLE()[idx].sg_cterm = attr;
799 HL_TABLE()[idx].sg_cterm_bold = FALSE;
800 }
801 }
802#if defined(FEAT_GUI) || defined(FEAT_EVAL)
803 else
804 {
805 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
806 {
807 if (!init)
808 HL_TABLE()[idx].sg_set |= SG_GUI;
809 HL_TABLE()[idx].sg_gui = attr;
810 }
811 }
812#endif
813
814 return TRUE;
815}
816
817#ifdef FEAT_GUI
818/*
819 * Set the font for the highlight group at 'idx'.
820 * 'arg' is the font name.
821 * Returns TRUE if the font is changed.
822 */
823 static int
824highlight_set_font(
825 int idx,
826 char_u *arg,
827 int is_normal_group,
828 int is_menu_group,
829 int is_tooltip_group)
830{
831 int did_change = FALSE;
832
833 // in non-GUI fonts are simply ignored
834 if (HL_TABLE()[idx].sg_font_name != NULL
835 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
836 {
837 // Font name didn't change, ignore.
838 }
839 else if (!gui.shell_created)
840 {
841 // GUI not started yet, always accept the name.
842 vim_free(HL_TABLE()[idx].sg_font_name);
843 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
844 did_change = TRUE;
845 }
846 else
847 {
848 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
849# ifdef FEAT_XFONTSET
850 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
851# endif
852 // First, save the current font/fontset.
853 // Then try to allocate the font/fontset.
854 // If the allocation fails, HL_TABLE()[idx].sg_font OR
855 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
856
857 HL_TABLE()[idx].sg_font = NOFONT;
858# ifdef FEAT_XFONTSET
859 HL_TABLE()[idx].sg_fontset = NOFONTSET;
860# endif
861 hl_do_font(idx, arg, is_normal_group, is_menu_group,
862 is_tooltip_group, FALSE);
863
864# ifdef FEAT_XFONTSET
865 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
866 {
867 // New fontset was accepted. Free the old one, if there
868 // was one.
869 gui_mch_free_fontset(temp_sg_fontset);
870 vim_free(HL_TABLE()[idx].sg_font_name);
871 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
872 did_change = TRUE;
873 }
874 else
875 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
876# endif
877 if (HL_TABLE()[idx].sg_font != NOFONT)
878 {
879 // New font was accepted. Free the old one, if there was
880 // one.
881 gui_mch_free_font(temp_sg_font);
882 vim_free(HL_TABLE()[idx].sg_font_name);
883 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
884 did_change = TRUE;
885 }
886 else
887 HL_TABLE()[idx].sg_font = temp_sg_font;
888 }
889
890 return did_change;
891}
892#endif
893
894/*
895 * Set the cterm foreground color for the highlight group at 'idx' to 'color'.
896 * Returns TRUE if the foreground color is set.
897 */
898 static void
899highlight_set_ctermfg(int idx, int color, int is_normal_group)
900{
901 HL_TABLE()[idx].sg_cterm_fg = color + 1;
902 if (is_normal_group)
903 {
904 cterm_normal_fg_color = color + 1;
905 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
906#ifdef FEAT_GUI
907 // Don't do this if the GUI is used.
908 if (!gui.in_use && !gui.starting)
909#endif
910 {
911 must_redraw = CLEAR;
912 if (termcap_active && color >= 0)
913 term_fg_color(color);
914 }
915 }
916}
917
918/*
919 * Set the cterm background color for the highlight group at 'idx' to 'color'.
920 * Returns TRUE if the background color is set.
921 */
922 static void
923highlight_set_ctermbg(int idx, int color, int is_normal_group)
924{
925 HL_TABLE()[idx].sg_cterm_bg = color + 1;
926 if (is_normal_group)
927 {
928 cterm_normal_bg_color = color + 1;
929#ifdef FEAT_GUI
930 // Don't mess with 'background' if the GUI is used.
931 if (!gui.in_use && !gui.starting)
932#endif
933 {
934 must_redraw = CLEAR;
935 if (color >= 0)
936 {
937 int dark = -1;
938
939 if (termcap_active)
940 term_bg_color(color);
941 if (t_colors < 16)
942 dark = (color == 0 || color == 4);
943 // Limit the heuristic to the standard 16 colors
944 else if (color < 16)
945 dark = (color < 7 || color == 8);
946 // Set the 'background' option if the value is
947 // wrong.
948 if (dark != -1
949 && dark != (*p_bg == 'd')
950 && !option_was_set((char_u *)"bg"))
951 {
952 set_option_value((char_u *)"bg", 0L,
953 (char_u *)(dark ? "dark" : "light"), 0);
954 reset_option_was_set((char_u *)"bg");
955 }
956 }
957 }
958 }
959}
960
961/*
962 * Set the cterm underline color for the highlight group at 'idx' to 'color'.
963 * Returns TRUE if the underline color is set.
964 */
965 static void
966highlight_set_ctermul(int idx, int color, int is_normal_group)
967{
968 HL_TABLE()[idx].sg_cterm_ul = color + 1;
969 if (is_normal_group)
970 {
971 cterm_normal_ul_color = color + 1;
972#ifdef FEAT_GUI
973 // Don't do this if the GUI is used.
974 if (!gui.in_use && !gui.starting)
975#endif
976 {
977 must_redraw = CLEAR;
978 if (termcap_active && color >= 0)
979 term_ul_color(color);
980 }
981 }
982}
983
984/*
985 * Set the cterm fg/bg/ul color for the highlight group at 'idx'.
986 * 'key' is one of 'CTERMFG' or 'CTERMBG' or 'CTERMUL'.
987 * 'keystart' is the color name/value.
988 * 'arg' is the color name or the numeric value as a string.
989 * 'is_normal_group' is set if the highlight group is 'NORMAL'
990 * 'init' is set to TRUE when initializing highlighting.
991 * Called for the ":highlight" command and the "hlset()" function.
992 *
993 * Returns TRUE if the color is set.
994 */
995 static int
996highlight_set_cterm_color(
997 int idx,
998 char_u *key,
999 char_u *key_start,
1000 char_u *arg,
1001 int is_normal_group,
1002 int init)
1003{
1004 int color;
1005 long i;
1006 int off;
1007
1008 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1009 {
1010 if (!init)
1011 HL_TABLE()[idx].sg_set |= SG_CTERM;
1012
1013 // When setting the foreground color, and previously the "bold"
1014 // flag was set for a light color, reset it now
1015 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1016 {
1017 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1018 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1019 }
1020
1021 if (VIM_ISDIGIT(*arg))
1022 color = atoi((char *)arg);
1023 else if (STRICMP(arg, "fg") == 0)
1024 {
1025 if (cterm_normal_fg_color)
1026 color = cterm_normal_fg_color - 1;
1027 else
1028 {
1029 emsg(_("E419: FG color unknown"));
1030 return FALSE;
1031 }
1032 }
1033 else if (STRICMP(arg, "bg") == 0)
1034 {
1035 if (cterm_normal_bg_color > 0)
1036 color = cterm_normal_bg_color - 1;
1037 else
1038 {
1039 emsg(_("E420: BG color unknown"));
1040 return FALSE;
1041 }
1042 }
1043 else if (STRICMP(arg, "ul") == 0)
1044 {
1045 if (cterm_normal_ul_color > 0)
1046 color = cterm_normal_ul_color - 1;
1047 else
1048 {
1049 emsg(_("E453: UL color unknown"));
1050 return FALSE;
1051 }
1052 }
1053 else
1054 {
1055 int bold = MAYBE;
1056
1057 // reduce calls to STRICMP a bit, it can be slow
1058 off = TOUPPER_ASC(*arg);
1059 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
1060 if (off == color_names[i][0]
1061 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1062 break;
1063 if (i < 0)
1064 {
1065 semsg(_("E421: Color name or number not recognized: %s"),
1066 key_start);
1067 return FALSE;
1068 }
1069
1070 color = lookup_color(i, key[5] == 'F', &bold);
1071
1072 // set/reset bold attribute to get light foreground
1073 // colors (on some terminals, e.g. "linux")
1074 if (bold == TRUE)
1075 {
1076 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1077 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1078 }
1079 else if (bold == FALSE)
1080 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1081 }
1082
1083 // Add one to the argument, to avoid zero. Zero is used for
1084 // "NONE", then "color" is -1.
1085 if (key[5] == 'F')
1086 highlight_set_ctermfg(idx, color, is_normal_group);
1087 else if (key[5] == 'B')
1088 highlight_set_ctermbg(idx, color, is_normal_group);
1089 else // ctermul
1090 highlight_set_ctermul(idx, color, is_normal_group);
1091 }
1092
1093 return TRUE;
1094}
1095
1096#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1097/*
1098 * Set the GUI foreground color for the highlight group at 'idx'.
1099 * Returns TRUE if the color is set.
1100 */
1101 static int
1102highlight_set_guifg(
1103 int idx,
1104 char_u *arg,
1105 int is_menu_group UNUSED,
1106 int is_scrollbar_group UNUSED,
1107 int is_tooltip_group UNUSED,
1108 int *do_colors UNUSED,
1109 int init)
1110{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001111# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001112 long i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001113# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001114 char_u **namep;
1115 int did_change = FALSE;
1116
1117 namep = &HL_TABLE()[idx].sg_gui_fg_name;
1118 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1119 {
1120 if (!init)
1121 HL_TABLE()[idx].sg_set |= SG_GUI;
1122
1123# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1124 // In GUI guifg colors are only used when recognized
1125 i = color_name2handle(arg);
1126 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1127 {
1128 HL_TABLE()[idx].sg_gui_fg = i;
1129# endif
1130 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1131 {
1132 vim_free(*namep);
1133 if (STRCMP(arg, "NONE") != 0)
1134 *namep = vim_strsave(arg);
1135 else
1136 *namep = NULL;
1137 did_change = TRUE;
1138 }
1139# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1140# ifdef FEAT_GUI_X11
1141 if (is_menu_group && gui.menu_fg_pixel != i)
1142 {
1143 gui.menu_fg_pixel = i;
1144 *do_colors = TRUE;
1145 }
1146 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1147 {
1148 gui.scroll_fg_pixel = i;
1149 *do_colors = TRUE;
1150 }
1151# ifdef FEAT_BEVAL_GUI
1152 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1153 {
1154 gui.tooltip_fg_pixel = i;
1155 *do_colors = TRUE;
1156 }
1157# endif
1158# endif
1159 }
1160# endif
1161 }
1162
1163 return did_change;
1164}
1165
1166/*
1167 * Set the GUI background color for the highlight group at 'idx'.
1168 * Returns TRUE if the color is set.
1169 */
1170 static int
1171highlight_set_guibg(
1172 int idx,
1173 char_u *arg,
1174 int is_menu_group UNUSED,
1175 int is_scrollbar_group UNUSED,
1176 int is_tooltip_group UNUSED,
1177 int *do_colors UNUSED,
1178 int init)
1179{
Bram Moolenaar731fba12021-10-19 20:24:34 +01001180# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001181 int i;
Bram Moolenaar731fba12021-10-19 20:24:34 +01001182# endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001183 char_u **namep;
1184 int did_change = FALSE;
1185
1186 namep = &HL_TABLE()[idx].sg_gui_bg_name;
1187 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1188 {
1189 if (!init)
1190 HL_TABLE()[idx].sg_set |= SG_GUI;
1191
1192# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1193 // In GUI guifg colors are only used when recognized
1194 i = color_name2handle(arg);
1195 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1196 {
1197 HL_TABLE()[idx].sg_gui_bg = i;
1198# endif
1199 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1200 {
1201 vim_free(*namep);
1202 if (STRCMP(arg, "NONE") != 0)
1203 *namep = vim_strsave(arg);
1204 else
1205 *namep = NULL;
1206 did_change = TRUE;
1207 }
1208# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1209# ifdef FEAT_GUI_X11
1210 if (is_menu_group && gui.menu_bg_pixel != i)
1211 {
1212 gui.menu_bg_pixel = i;
1213 *do_colors = TRUE;
1214 }
1215 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1216 {
1217 gui.scroll_bg_pixel = i;
1218 *do_colors = TRUE;
1219 }
1220# ifdef FEAT_BEVAL_GUI
1221 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1222 {
1223 gui.tooltip_bg_pixel = i;
1224 *do_colors = TRUE;
1225 }
1226# endif
1227# endif
1228 }
1229# endif
1230 }
1231
1232 return did_change;
1233}
1234
1235/*
1236 * Set the GUI undercurl/strikethrough color for the highlight group at 'idx'.
1237 * Returns TRUE if the color is set.
1238 */
1239 static int
1240highlight_set_guisp(int idx, char_u *arg, int init)
1241{
1242# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1243 int i;
1244# endif
1245 int did_change = FALSE;
1246 char_u **namep;
1247
1248 namep = &HL_TABLE()[idx].sg_gui_sp_name;
1249 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1250 {
1251 if (!init)
1252 HL_TABLE()[idx].sg_set |= SG_GUI;
1253
1254# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1255 // In GUI guisp colors are only used when recognized
1256 i = color_name2handle(arg);
1257 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1258 {
1259 HL_TABLE()[idx].sg_gui_sp = i;
1260# endif
1261 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1262 {
1263 vim_free(*namep);
1264 if (STRCMP(arg, "NONE") != 0)
1265 *namep = vim_strsave(arg);
1266 else
1267 *namep = NULL;
1268 did_change = TRUE;
1269 }
1270# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1271 }
1272# endif
1273 }
1274
1275 return did_change;
1276}
1277#endif
1278
1279/*
1280 * Set the start/stop terminal codes for a highlight group.
1281 * Returns TRUE if the terminal code is set.
1282 */
1283 static int
1284highlight_set_startstop_termcode(int idx, char_u *key, char_u *arg, int init)
1285{
1286 int off;
1287 char_u buf[100];
1288 int len;
1289 char_u *tname;
1290 char_u *p;
1291
1292 if (!init)
1293 HL_TABLE()[idx].sg_set |= SG_TERM;
1294
1295 // The "start" and "stop" arguments can be a literal escape
1296 // sequence, or a comma separated list of terminal codes.
1297 if (STRNCMP(arg, "t_", 2) == 0)
1298 {
1299 off = 0;
1300 buf[0] = 0;
1301 while (arg[off] != NUL)
1302 {
1303 // Isolate one termcap name
1304 for (len = 0; arg[off + len] &&
1305 arg[off + len] != ','; ++len)
1306 ;
1307 tname = vim_strnsave(arg + off, len);
1308 if (tname == NULL) // out of memory
1309 return FALSE;
1310 // lookup the escape sequence for the item
1311 p = get_term_code(tname);
1312 vim_free(tname);
1313 if (p == NULL) // ignore non-existing things
1314 p = (char_u *)"";
1315
1316 // Append it to the already found stuff
1317 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1318 {
1319 semsg(_("E422: terminal code too long: %s"), arg);
1320 return FALSE;
1321 }
1322 STRCAT(buf, p);
1323
1324 // Advance to the next item
1325 off += len;
1326 if (arg[off] == ',') // another one follows
1327 ++off;
1328 }
1329 }
1330 else
1331 {
1332 // Copy characters from arg[] to buf[], translating <> codes.
1333 for (p = arg, off = 0; off < 100 - 6 && *p; )
1334 {
1335 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
1336 if (len > 0) // recognized special char
1337 off += len;
1338 else // copy as normal char
1339 buf[off++] = *p++;
1340 }
1341 buf[off] = NUL;
1342 }
1343
1344 if (STRCMP(buf, "NONE") == 0) // resetting the value
1345 p = NULL;
1346 else
1347 p = vim_strsave(buf);
1348 if (key[2] == 'A')
1349 {
1350 vim_free(HL_TABLE()[idx].sg_start);
1351 HL_TABLE()[idx].sg_start = p;
1352 }
1353 else
1354 {
1355 vim_free(HL_TABLE()[idx].sg_stop);
1356 HL_TABLE()[idx].sg_stop = p;
1357 }
1358 return TRUE;
1359}
1360
1361/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001362 * Handle the ":highlight .." command.
1363 * When using ":hi clear" this is called recursively for each group with
1364 * "forceit" and "init" both TRUE.
1365 */
1366 void
1367do_highlight(
1368 char_u *line,
1369 int forceit,
1370 int init) // TRUE when called for initializing
1371{
1372 char_u *name_end;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001373 char_u *linep;
1374 char_u *key_start;
1375 char_u *arg_start;
1376 char_u *key = NULL, *arg = NULL;
1377 long i;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001378 int id;
1379 int idx;
1380 hl_group_T item_before;
1381 int did_change = FALSE;
1382 int dodefault = FALSE;
1383 int doclear = FALSE;
1384 int dolink = FALSE;
1385 int error = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001386 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001387#ifdef FEAT_GUI_X11
1388 int is_menu_group = FALSE; // "Menu" group
1389 int is_scrollbar_group = FALSE; // "Scrollbar" group
1390 int is_tooltip_group = FALSE; // "Tooltip" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001391#else
1392# define is_menu_group 0
1393# define is_tooltip_group 0
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001394# define is_scrollbar_group 0
1395#endif
1396#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1397 int do_colors = FALSE; // need to update colors?
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001398#endif
1399#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1400 int did_highlight_changed = FALSE;
1401#endif
1402
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001403 // If no argument, list current highlighting.
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +02001404 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001405 {
1406 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
1407 // TODO: only call when the group has attributes set
1408 highlight_list_one((int)i);
1409 return;
1410 }
1411
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001412 // Isolate the name.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001413 name_end = skiptowhite(line);
1414 linep = skipwhite(name_end);
1415
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001416 // Check for "default" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001417 if (STRNCMP(line, "default", name_end - line) == 0)
1418 {
1419 dodefault = TRUE;
1420 line = linep;
1421 name_end = skiptowhite(line);
1422 linep = skipwhite(name_end);
1423 }
1424
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001425 // Check for "clear" or "link" argument.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001426 if (STRNCMP(line, "clear", name_end - line) == 0)
1427 doclear = TRUE;
1428 if (STRNCMP(line, "link", name_end - line) == 0)
1429 dolink = TRUE;
1430
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001431 // ":highlight {group-name}": list highlighting for one group.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001432 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001433 {
1434 id = syn_namen2id(line, (int)(name_end - line));
1435 if (id == 0)
1436 semsg(_("E411: highlight group not found: %s"), line);
1437 else
1438 highlight_list_one(id);
1439 return;
1440 }
1441
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001442 // Handle ":highlight link {from} {to}" command.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001443 if (dolink)
1444 {
1445 char_u *from_start = linep;
1446 char_u *from_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001447 int from_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001448 char_u *to_start;
1449 char_u *to_end;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001450 int to_len;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001451
1452 from_end = skiptowhite(from_start);
1453 to_start = skipwhite(from_end);
1454 to_end = skiptowhite(to_start);
1455
Bram Moolenaar1966c242020-04-20 22:42:32 +02001456 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001457 {
1458 semsg(_("E412: Not enough arguments: \":highlight link %s\""),
1459 from_start);
1460 return;
1461 }
1462
Bram Moolenaar1966c242020-04-20 22:42:32 +02001463 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001464 {
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001465 semsg(_("E413: Too many arguments: \":highlight link %s\""),
1466 from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001467 return;
1468 }
1469
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001470 from_len = (int)(from_end - from_start);
1471 to_len = (int)(to_end - to_start);
1472 highlight_group_link(from_start, from_len, to_start, to_len,
1473 dodefault, forceit, init);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001474 return;
1475 }
1476
1477 if (doclear)
1478 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001479 // ":highlight clear [group]" command.
Bram Moolenaar1966c242020-04-20 22:42:32 +02001480 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001481 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001482 // ":highlight clear" without group name
1483 highlight_reset_all();
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001484 return;
1485 }
Bram Moolenaar1966c242020-04-20 22:42:32 +02001486 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001487 name_end = skiptowhite(line);
1488 linep = skipwhite(name_end);
1489 }
1490
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001491 // Find the group name in the table. If it does not exist yet, add it.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001492 id = syn_check_group(line, (int)(name_end - line));
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001493 if (id == 0) // failed (out of memory)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001494 return;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001495 idx = id - 1; // index is ID minus one
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001496
1497 // Return if "default" was used and the group already has settings.
1498 if (dodefault && hl_has_settings(idx, TRUE))
1499 return;
1500
1501 // Make a copy so we can check if any attribute actually changed.
1502 item_before = HL_TABLE()[idx];
1503
1504 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
1505 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001506#ifdef FEAT_GUI_X11
1507 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
1508 is_menu_group = TRUE;
1509 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
1510 is_scrollbar_group = TRUE;
1511 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
1512 is_tooltip_group = TRUE;
1513#endif
1514
1515 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
1516 if (doclear || (forceit && init))
1517 {
1518 highlight_clear(idx);
1519 if (!doclear)
1520 HL_TABLE()[idx].sg_set = 0;
1521 }
1522
1523 if (!doclear)
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001524 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001525 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001526 key_start = linep;
1527 if (*linep == '=')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001528 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001529 semsg(_("E415: unexpected equal sign: %s"), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001530 error = TRUE;
1531 break;
1532 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001533
1534 // Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg"
1535 // or "guibg").
1536 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
1537 ++linep;
1538 vim_free(key);
1539 key = vim_strnsave_up(key_start, linep - key_start);
1540 if (key == NULL)
1541 {
1542 error = TRUE;
1543 break;
1544 }
1545 linep = skipwhite(linep);
1546
1547 if (STRCMP(key, "NONE") == 0)
1548 {
1549 if (!init || HL_TABLE()[idx].sg_set == 0)
1550 {
1551 if (!init)
1552 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
1553 highlight_clear(idx);
1554 }
1555 continue;
1556 }
1557
1558 // Check for the equal sign.
1559 if (*linep != '=')
1560 {
1561 semsg(_("E416: missing equal sign: %s"), key_start);
1562 error = TRUE;
1563 break;
1564 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001565 ++linep;
1566
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001567 // Isolate the argument.
1568 linep = skipwhite(linep);
1569 if (*linep == '\'') // guifg='color name'
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001570 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001571 arg_start = ++linep;
1572 linep = vim_strchr(linep, '\'');
1573 if (linep == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001574 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001575 semsg(_(e_invarg2), key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001576 error = TRUE;
1577 break;
1578 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001579 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001580 else
1581 {
1582 arg_start = linep;
1583 linep = skiptowhite(linep);
1584 }
1585 if (linep == arg_start)
1586 {
1587 semsg(_("E417: missing argument: %s"), key_start);
1588 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001589 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001590 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001591 vim_free(arg);
1592 arg = vim_strnsave(arg_start, linep - arg_start);
1593 if (arg == NULL)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001594 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001595 error = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001596 break;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001597 }
1598 if (*linep == '\'')
1599 ++linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001600
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001601 // Store the argument.
1602 if (STRCMP(key, "TERM") == 0
1603 || STRCMP(key, "CTERM") == 0
1604 || STRCMP(key, "GUI") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001605 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001606 if (!highlight_set_termgui_attr(idx, key, arg, init))
1607 {
1608 error = TRUE;
1609 break;
1610 }
1611 }
1612 else if (STRCMP(key, "FONT") == 0)
1613 {
1614 // in non-GUI fonts are simply ignored
1615#ifdef FEAT_GUI
1616 if (highlight_set_font(idx, arg, is_normal_group,
1617 is_menu_group, is_tooltip_group))
1618 did_change = TRUE;
1619#endif
1620 }
1621 else if (STRCMP(key, "CTERMFG") == 0
1622 || STRCMP(key, "CTERMBG") == 0
1623 || STRCMP(key, "CTERMUL") == 0)
1624 {
1625 if (!highlight_set_cterm_color(idx, key, key_start, arg,
1626 is_normal_group, init))
1627 {
1628 error = TRUE;
1629 break;
1630 }
1631 }
1632 else if (STRCMP(key, "GUIFG") == 0)
1633 {
1634#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1635 if (highlight_set_guifg(idx, arg, is_menu_group,
1636 is_scrollbar_group, is_tooltip_group,
1637 &do_colors, init))
1638 did_change = TRUE;
1639#endif
1640 }
1641 else if (STRCMP(key, "GUIBG") == 0)
1642 {
1643#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1644 if (highlight_set_guibg(idx, arg, is_menu_group,
1645 is_scrollbar_group, is_tooltip_group,
1646 &do_colors, init))
1647 did_change = TRUE;
1648#endif
1649 }
1650 else if (STRCMP(key, "GUISP") == 0)
1651 {
1652#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1653 if (highlight_set_guisp(idx, arg, init))
1654 did_change = TRUE;
1655#endif
1656 }
1657 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1658 {
1659 if (!highlight_set_startstop_termcode(idx, key, arg, init))
1660 {
1661 error = TRUE;
1662 break;
1663 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001664 }
1665 else
1666 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001667 semsg(_("E423: Illegal argument: %s"), key_start);
1668 error = TRUE;
1669 break;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001670 }
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001671 HL_TABLE()[idx].sg_cleared = FALSE;
1672
1673 // When highlighting has been given for a group, don't link it.
1674 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1675 HL_TABLE()[idx].sg_link = 0;
1676
1677 // Continue with next argument.
1678 linep = skipwhite(linep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001679 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001680
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001681 // If there is an error, and it's a new entry, remove it from the table.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001682 if (error && idx == highlight_ga.ga_len)
1683 syn_unadd_group();
1684 else
1685 {
1686 if (is_normal_group)
1687 {
1688 HL_TABLE()[idx].sg_term_attr = 0;
1689 HL_TABLE()[idx].sg_cterm_attr = 0;
1690#ifdef FEAT_GUI
1691 HL_TABLE()[idx].sg_gui_attr = 0;
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01001692 // Need to update all groups, because they might be using "bg"
1693 // and/or "fg", which have been changed now.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001694#endif
1695#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1696 if (USE_24BIT)
1697 {
1698 highlight_gui_started();
1699 did_highlight_changed = TRUE;
1700 redraw_all_later(NOT_VALID);
1701 }
1702#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001703#ifdef FEAT_VTP
1704 control_console_color_rgb();
1705#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001706 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001707#ifdef FEAT_GUI_X11
1708# ifdef FEAT_MENU
1709 else if (is_menu_group)
1710 {
1711 if (gui.in_use && do_colors)
1712 gui_mch_new_menu_colors();
1713 }
1714# endif
1715 else if (is_scrollbar_group)
1716 {
1717 if (gui.in_use && do_colors)
1718 gui_new_scrollbar_colors();
1719 else
1720 set_hl_attr(idx);
1721 }
1722# ifdef FEAT_BEVAL_GUI
1723 else if (is_tooltip_group)
1724 {
1725 if (gui.in_use && do_colors)
1726 gui_mch_new_tooltip_colors();
1727 }
1728# endif
1729#endif
1730 else
1731 set_hl_attr(idx);
1732#ifdef FEAT_EVAL
1733 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001734 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001735#endif
1736 }
1737
1738 vim_free(key);
1739 vim_free(arg);
1740
1741 // Only call highlight_changed() once, after a sequence of highlight
1742 // commands, and only if an attribute actually changed.
1743 if ((did_change
1744 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1745#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1746 && !did_highlight_changed
1747#endif
1748 )
1749 {
1750 // Do not trigger a redraw when highlighting is changed while
1751 // redrawing. This may happen when evaluating 'statusline' changes the
1752 // StatusLine group.
1753 if (!updating_screen)
1754 redraw_all_later(NOT_VALID);
1755 need_highlight_changed = TRUE;
1756 }
1757}
1758
1759#if defined(EXITFREE) || defined(PROTO)
1760 void
1761free_highlight(void)
1762{
1763 int i;
1764
1765 for (i = 0; i < highlight_ga.ga_len; ++i)
1766 {
1767 highlight_clear(i);
1768 vim_free(HL_TABLE()[i].sg_name);
1769 vim_free(HL_TABLE()[i].sg_name_u);
1770 }
1771 ga_clear(&highlight_ga);
1772}
1773#endif
1774
1775/*
1776 * Reset the cterm colors to what they were before Vim was started, if
1777 * possible. Otherwise reset them to zero.
1778 */
1779 void
1780restore_cterm_colors(void)
1781{
1782#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1783 // Since t_me has been set, this probably means that the user
1784 // wants to use this as default colors. Need to reset default
1785 // background/foreground colors.
1786 mch_set_normal_colors();
1787#else
1788# ifdef VIMDLL
1789 if (!gui.in_use)
1790 {
1791 mch_set_normal_colors();
1792 return;
1793 }
1794# endif
1795 cterm_normal_fg_color = 0;
1796 cterm_normal_fg_bold = 0;
1797 cterm_normal_bg_color = 0;
1798# ifdef FEAT_TERMGUICOLORS
1799 cterm_normal_fg_gui_color = INVALCOLOR;
1800 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001801 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001802# endif
1803#endif
1804}
1805
1806/*
1807 * Return TRUE if highlight group "idx" has any settings.
1808 * When "check_link" is TRUE also check for an existing link.
1809 */
1810 static int
1811hl_has_settings(int idx, int check_link)
1812{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001813 return HL_TABLE()[idx].sg_cleared == 0
1814 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001815 || HL_TABLE()[idx].sg_cterm_attr != 0
1816 || HL_TABLE()[idx].sg_cterm_fg != 0
1817 || HL_TABLE()[idx].sg_cterm_bg != 0
1818#ifdef FEAT_GUI
1819 || HL_TABLE()[idx].sg_gui_attr != 0
1820 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1821 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1822 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1823 || HL_TABLE()[idx].sg_font_name != NULL
1824#endif
1825 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1826}
1827
1828/*
1829 * Clear highlighting for one group.
1830 */
1831 static void
1832highlight_clear(int idx)
1833{
1834 HL_TABLE()[idx].sg_cleared = TRUE;
1835
1836 HL_TABLE()[idx].sg_term = 0;
1837 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1838 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1839 HL_TABLE()[idx].sg_term_attr = 0;
1840 HL_TABLE()[idx].sg_cterm = 0;
1841 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1842 HL_TABLE()[idx].sg_cterm_fg = 0;
1843 HL_TABLE()[idx].sg_cterm_bg = 0;
1844 HL_TABLE()[idx].sg_cterm_attr = 0;
1845#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1846 HL_TABLE()[idx].sg_gui = 0;
1847 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1848 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1849 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1850#endif
1851#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1852 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1853 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001854 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001855#endif
1856#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001857 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1858 HL_TABLE()[idx].sg_font = NOFONT;
1859# ifdef FEAT_XFONTSET
1860 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1861 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1862# endif
1863 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1864 HL_TABLE()[idx].sg_gui_attr = 0;
1865#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001866 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001867 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001868#ifdef FEAT_EVAL
1869 // Since we set the default link, set the location to where the default
1870 // link was set.
1871 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001872#endif
1873}
1874
1875#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1876/*
1877 * Set the normal foreground and background colors according to the "Normal"
1878 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1879 * "Tooltip" colors.
1880 */
1881 void
1882set_normal_colors(void)
1883{
1884# ifdef FEAT_GUI
1885# ifdef FEAT_TERMGUICOLORS
1886 if (gui.in_use)
1887# endif
1888 {
1889 if (set_group_colors((char_u *)"Normal",
1890 &gui.norm_pixel, &gui.back_pixel,
1891 FALSE, TRUE, FALSE))
1892 {
1893 gui_mch_new_colors();
1894 must_redraw = CLEAR;
1895 }
1896# ifdef FEAT_GUI_X11
1897 if (set_group_colors((char_u *)"Menu",
1898 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1899 TRUE, FALSE, FALSE))
1900 {
1901# ifdef FEAT_MENU
1902 gui_mch_new_menu_colors();
1903# endif
1904 must_redraw = CLEAR;
1905 }
1906# ifdef FEAT_BEVAL_GUI
1907 if (set_group_colors((char_u *)"Tooltip",
1908 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1909 FALSE, FALSE, TRUE))
1910 {
1911# ifdef FEAT_TOOLBAR
1912 gui_mch_new_tooltip_colors();
1913# endif
1914 must_redraw = CLEAR;
1915 }
1916# endif
1917 if (set_group_colors((char_u *)"Scrollbar",
1918 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1919 FALSE, FALSE, FALSE))
1920 {
1921 gui_new_scrollbar_colors();
1922 must_redraw = CLEAR;
1923 }
1924# endif
1925 }
1926# endif
1927# ifdef FEAT_TERMGUICOLORS
1928# ifdef FEAT_GUI
1929 else
1930# endif
1931 {
1932 int idx;
1933
1934 idx = syn_name2id((char_u *)"Normal") - 1;
1935 if (idx >= 0)
1936 {
1937 gui_do_one_color(idx, FALSE, FALSE);
1938
1939 // If the normal fg or bg color changed a complete redraw is
1940 // required.
1941 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1942 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1943 {
1944 // if the GUI color is INVALCOLOR then we use the default cterm
1945 // color
1946 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1947 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1948 must_redraw = CLEAR;
1949 }
1950 }
1951 }
1952# endif
1953}
1954#endif
1955
1956#if defined(FEAT_GUI) || defined(PROTO)
1957/*
1958 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1959 */
1960 static int
1961set_group_colors(
1962 char_u *name,
1963 guicolor_T *fgp,
1964 guicolor_T *bgp,
1965 int do_menu,
1966 int use_norm,
1967 int do_tooltip)
1968{
1969 int idx;
1970
1971 idx = syn_name2id(name) - 1;
1972 if (idx >= 0)
1973 {
1974 gui_do_one_color(idx, do_menu, do_tooltip);
1975
1976 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1977 *fgp = HL_TABLE()[idx].sg_gui_fg;
1978 else if (use_norm)
1979 *fgp = gui.def_norm_pixel;
1980 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1981 *bgp = HL_TABLE()[idx].sg_gui_bg;
1982 else if (use_norm)
1983 *bgp = gui.def_back_pixel;
1984 return TRUE;
1985 }
1986 return FALSE;
1987}
1988
1989/*
1990 * Get the font of the "Normal" group.
1991 * Returns "" when it's not found or not set.
1992 */
1993 char_u *
1994hl_get_font_name(void)
1995{
1996 int id;
1997 char_u *s;
1998
1999 id = syn_name2id((char_u *)"Normal");
2000 if (id > 0)
2001 {
2002 s = HL_TABLE()[id - 1].sg_font_name;
2003 if (s != NULL)
2004 return s;
2005 }
2006 return (char_u *)"";
2007}
2008
2009/*
2010 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
2011 * actually chosen to be used.
2012 */
2013 void
2014hl_set_font_name(char_u *font_name)
2015{
2016 int id;
2017
2018 id = syn_name2id((char_u *)"Normal");
2019 if (id > 0)
2020 {
2021 vim_free(HL_TABLE()[id - 1].sg_font_name);
2022 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
2023 }
2024}
2025
2026/*
2027 * Set background color for "Normal" group. Called by gui_set_bg_color()
2028 * when the color is known.
2029 */
2030 void
2031hl_set_bg_color_name(
2032 char_u *name) // must have been allocated
2033{
2034 int id;
2035
2036 if (name != NULL)
2037 {
2038 id = syn_name2id((char_u *)"Normal");
2039 if (id > 0)
2040 {
2041 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
2042 HL_TABLE()[id - 1].sg_gui_bg_name = name;
2043 }
2044 }
2045}
2046
2047/*
2048 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
2049 * when the color is known.
2050 */
2051 void
2052hl_set_fg_color_name(
2053 char_u *name) // must have been allocated
2054{
2055 int id;
2056
2057 if (name != NULL)
2058 {
2059 id = syn_name2id((char_u *)"Normal");
2060 if (id > 0)
2061 {
2062 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
2063 HL_TABLE()[id - 1].sg_gui_fg_name = name;
2064 }
2065 }
2066}
2067
2068/*
2069 * Return the handle for a font name.
2070 * Returns NOFONT when failed.
2071 */
2072 static GuiFont
2073font_name2handle(char_u *name)
2074{
2075 if (STRCMP(name, "NONE") == 0)
2076 return NOFONT;
2077
2078 return gui_mch_get_font(name, TRUE);
2079}
2080
2081# ifdef FEAT_XFONTSET
2082/*
2083 * Return the handle for a fontset name.
2084 * Returns NOFONTSET when failed.
2085 */
2086 static GuiFontset
2087fontset_name2handle(char_u *name, int fixed_width)
2088{
2089 if (STRCMP(name, "NONE") == 0)
2090 return NOFONTSET;
2091
2092 return gui_mch_get_fontset(name, TRUE, fixed_width);
2093}
2094# endif
2095
2096/*
2097 * Get the font or fontset for one highlight group.
2098 */
2099 static void
2100hl_do_font(
2101 int idx,
2102 char_u *arg,
2103 int do_normal, // set normal font
2104 int do_menu UNUSED, // set menu font
2105 int do_tooltip UNUSED, // set tooltip font
2106 int free_font) // free current font/fontset
2107{
2108# ifdef FEAT_XFONTSET
2109 // If 'guifontset' is not empty, first try using the name as a
2110 // fontset. If that doesn't work, use it as a font name.
2111 if (*p_guifontset != NUL
2112# ifdef FONTSET_ALWAYS
2113 || do_menu
2114# endif
2115# ifdef FEAT_BEVAL_TIP
2116 // In Athena & Motif, the Tooltip highlight group is always a fontset
2117 || do_tooltip
2118# endif
2119 )
2120 {
2121 if (free_font)
2122 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
2123 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
2124# ifdef FONTSET_ALWAYS
2125 || do_menu
2126# endif
2127# ifdef FEAT_BEVAL_TIP
2128 || do_tooltip
2129# endif
2130 );
2131 }
2132 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
2133 {
2134 // If it worked and it's the Normal group, use it as the normal
2135 // fontset. Same for the Menu group.
2136 if (do_normal)
2137 gui_init_font(arg, TRUE);
2138# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2139 if (do_menu)
2140 {
2141# ifdef FONTSET_ALWAYS
2142 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
2143# else
2144 // YIKES! This is a bug waiting to crash the program
2145 gui.menu_font = HL_TABLE()[idx].sg_fontset;
2146# endif
2147 gui_mch_new_menu_font();
2148 }
2149# ifdef FEAT_BEVAL_GUI
2150 if (do_tooltip)
2151 {
2152 // The Athena widget set cannot currently handle switching between
2153 // displaying a single font and a fontset.
2154 // If the XtNinternational resource is set to True at widget
2155 // creation, then a fontset is always used, otherwise an
2156 // XFontStruct is used.
2157 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
2158 gui_mch_new_tooltip_font();
2159 }
2160# endif
2161# endif
2162 }
2163 else
2164# endif
2165 {
2166 if (free_font)
2167 gui_mch_free_font(HL_TABLE()[idx].sg_font);
2168 HL_TABLE()[idx].sg_font = font_name2handle(arg);
2169 // If it worked and it's the Normal group, use it as the
2170 // normal font. Same for the Menu group.
2171 if (HL_TABLE()[idx].sg_font != NOFONT)
2172 {
2173 if (do_normal)
2174 gui_init_font(arg, FALSE);
2175#ifndef FONTSET_ALWAYS
2176# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2177 if (do_menu)
2178 {
2179 gui.menu_font = HL_TABLE()[idx].sg_font;
2180 gui_mch_new_menu_font();
2181 }
2182# endif
2183#endif
2184 }
2185 }
2186}
2187
2188#endif // FEAT_GUI
2189
2190#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2191/*
2192 * Return the handle for a color name.
2193 * Returns INVALCOLOR when failed.
2194 */
2195 guicolor_T
2196color_name2handle(char_u *name)
2197{
2198 if (STRCMP(name, "NONE") == 0)
2199 return INVALCOLOR;
2200
2201 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2202 {
2203#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2204 if (gui.in_use)
2205#endif
2206#ifdef FEAT_GUI
2207 return gui.norm_pixel;
2208#endif
2209#ifdef FEAT_TERMGUICOLORS
2210 if (cterm_normal_fg_gui_color != INVALCOLOR)
2211 return cterm_normal_fg_gui_color;
2212 // Guess that the foreground is black or white.
2213 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2214#endif
2215 }
2216 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2217 {
2218#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2219 if (gui.in_use)
2220#endif
2221#ifdef FEAT_GUI
2222 return gui.back_pixel;
2223#endif
2224#ifdef FEAT_TERMGUICOLORS
2225 if (cterm_normal_bg_gui_color != INVALCOLOR)
2226 return cterm_normal_bg_gui_color;
2227 // Guess that the background is white or black.
2228 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2229#endif
2230 }
2231
2232 return GUI_GET_COLOR(name);
2233}
2234#endif
2235
2236/*
2237 * Table with the specifications for an attribute number.
2238 * Note that this table is used by ALL buffers. This is required because the
2239 * GUI can redraw at any time for any buffer.
2240 */
2241static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2242
2243#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2244
2245static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2246
2247#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2248
2249#ifdef FEAT_GUI
2250static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2251
2252#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2253#endif
2254
2255/*
2256 * Return the attr number for a set of colors and font.
2257 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2258 * if the combination is new.
2259 * Return 0 for error (no more room).
2260 */
2261 static int
2262get_attr_entry(garray_T *table, attrentry_T *aep)
2263{
2264 int i;
2265 attrentry_T *taep;
2266 static int recursive = FALSE;
2267
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002268 // Init the table, in case it wasn't done yet.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002269 table->ga_itemsize = sizeof(attrentry_T);
2270 table->ga_growsize = 7;
2271
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002272 // Try to find an entry with the same specifications.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002273 for (i = 0; i < table->ga_len; ++i)
2274 {
2275 taep = &(((attrentry_T *)table->ga_data)[i]);
2276 if ( aep->ae_attr == taep->ae_attr
2277 && (
2278#ifdef FEAT_GUI
2279 (table == &gui_attr_table
2280 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2281 && aep->ae_u.gui.bg_color
2282 == taep->ae_u.gui.bg_color
2283 && aep->ae_u.gui.sp_color
2284 == taep->ae_u.gui.sp_color
2285 && aep->ae_u.gui.font == taep->ae_u.gui.font
2286# ifdef FEAT_XFONTSET
2287 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2288# endif
2289 ))
2290 ||
2291#endif
2292 (table == &term_attr_table
2293 && (aep->ae_u.term.start == NULL)
2294 == (taep->ae_u.term.start == NULL)
2295 && (aep->ae_u.term.start == NULL
2296 || STRCMP(aep->ae_u.term.start,
2297 taep->ae_u.term.start) == 0)
2298 && (aep->ae_u.term.stop == NULL)
2299 == (taep->ae_u.term.stop == NULL)
2300 && (aep->ae_u.term.stop == NULL
2301 || STRCMP(aep->ae_u.term.stop,
2302 taep->ae_u.term.stop) == 0))
2303 || (table == &cterm_attr_table
2304 && aep->ae_u.cterm.fg_color
2305 == taep->ae_u.cterm.fg_color
2306 && aep->ae_u.cterm.bg_color
2307 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002308 && aep->ae_u.cterm.ul_color
2309 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002310#ifdef FEAT_TERMGUICOLORS
2311 && aep->ae_u.cterm.fg_rgb
2312 == taep->ae_u.cterm.fg_rgb
2313 && aep->ae_u.cterm.bg_rgb
2314 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002315 && aep->ae_u.cterm.ul_rgb
2316 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002317#endif
2318 )))
2319
2320 return i + ATTR_OFF;
2321 }
2322
2323 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2324 {
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002325 // Running out of attribute entries! remove all attributes, and
2326 // compute new ones for all groups.
2327 // When called recursively, we are really out of numbers.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002328 if (recursive)
2329 {
2330 emsg(_("E424: Too many different highlighting attributes in use"));
2331 return 0;
2332 }
2333 recursive = TRUE;
2334
2335 clear_hl_tables();
2336
2337 must_redraw = CLEAR;
2338
2339 for (i = 0; i < highlight_ga.ga_len; ++i)
2340 set_hl_attr(i);
2341
2342 recursive = FALSE;
2343 }
2344
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01002345 // This is a new combination of colors and font, add an entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002346 if (ga_grow(table, 1) == FAIL)
2347 return 0;
2348
2349 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002350 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002351 taep->ae_attr = aep->ae_attr;
2352#ifdef FEAT_GUI
2353 if (table == &gui_attr_table)
2354 {
2355 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2356 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2357 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2358 taep->ae_u.gui.font = aep->ae_u.gui.font;
2359# ifdef FEAT_XFONTSET
2360 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2361# endif
2362 }
2363#endif
2364 if (table == &term_attr_table)
2365 {
2366 if (aep->ae_u.term.start == NULL)
2367 taep->ae_u.term.start = NULL;
2368 else
2369 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2370 if (aep->ae_u.term.stop == NULL)
2371 taep->ae_u.term.stop = NULL;
2372 else
2373 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2374 }
2375 else if (table == &cterm_attr_table)
2376 {
2377 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2378 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002379 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002380#ifdef FEAT_TERMGUICOLORS
2381 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2382 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002383 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002384#endif
2385 }
2386 ++table->ga_len;
2387 return (table->ga_len - 1 + ATTR_OFF);
2388}
2389
2390#if defined(FEAT_TERMINAL) || defined(PROTO)
2391/*
2392 * Get an attribute index for a cterm entry.
2393 * Uses an existing entry when possible or adds one when needed.
2394 */
2395 int
2396get_cterm_attr_idx(int attr, int fg, int bg)
2397{
2398 attrentry_T at_en;
2399
Bram Moolenaara80faa82020-04-12 19:37:17 +02002400 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002401#ifdef FEAT_TERMGUICOLORS
2402 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2403 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002404 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002405#endif
2406 at_en.ae_attr = attr;
2407 at_en.ae_u.cterm.fg_color = fg;
2408 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002409 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002410 return get_attr_entry(&cterm_attr_table, &at_en);
2411}
2412#endif
2413
2414#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2415/*
2416 * Get an attribute index for a 'termguicolors' entry.
2417 * Uses an existing entry when possible or adds one when needed.
2418 */
2419 int
2420get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2421{
2422 attrentry_T at_en;
2423
Bram Moolenaara80faa82020-04-12 19:37:17 +02002424 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002425 at_en.ae_attr = attr;
2426 if (fg == INVALCOLOR && bg == INVALCOLOR)
2427 {
2428 // If both GUI colors are not set fall back to the cterm colors. Helps
2429 // if the GUI only has an attribute, such as undercurl.
2430 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2431 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2432 }
2433 else
2434 {
2435 at_en.ae_u.cterm.fg_rgb = fg;
2436 at_en.ae_u.cterm.bg_rgb = bg;
2437 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002438 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002439 return get_attr_entry(&cterm_attr_table, &at_en);
2440}
2441#endif
2442
2443#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2444/*
2445 * Get an attribute index for a cterm entry.
2446 * Uses an existing entry when possible or adds one when needed.
2447 */
2448 int
2449get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2450{
2451 attrentry_T at_en;
2452
Bram Moolenaara80faa82020-04-12 19:37:17 +02002453 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002454 at_en.ae_attr = attr;
2455 at_en.ae_u.gui.fg_color = fg;
2456 at_en.ae_u.gui.bg_color = bg;
2457 return get_attr_entry(&gui_attr_table, &at_en);
2458}
2459#endif
2460
2461/*
2462 * Clear all highlight tables.
2463 */
2464 void
2465clear_hl_tables(void)
2466{
2467 int i;
2468 attrentry_T *taep;
2469
2470#ifdef FEAT_GUI
2471 ga_clear(&gui_attr_table);
2472#endif
2473 for (i = 0; i < term_attr_table.ga_len; ++i)
2474 {
2475 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2476 vim_free(taep->ae_u.term.start);
2477 vim_free(taep->ae_u.term.stop);
2478 }
2479 ga_clear(&term_attr_table);
2480 ga_clear(&cterm_attr_table);
2481}
2482
2483/*
2484 * Combine special attributes (e.g., for spelling) with other attributes
2485 * (e.g., for syntax highlighting).
2486 * "prim_attr" overrules "char_attr".
2487 * This creates a new group when required.
2488 * Since we expect there to be few spelling mistakes we don't cache the
2489 * result.
2490 * Return the resulting attributes.
2491 */
2492 int
2493hl_combine_attr(int char_attr, int prim_attr)
2494{
2495 attrentry_T *char_aep = NULL;
2496 attrentry_T *spell_aep;
2497 attrentry_T new_en;
2498
2499 if (char_attr == 0)
2500 return prim_attr;
2501 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2502 return ATTR_COMBINE(char_attr, prim_attr);
2503#ifdef FEAT_GUI
2504 if (gui.in_use)
2505 {
2506 if (char_attr > HL_ALL)
2507 char_aep = syn_gui_attr2entry(char_attr);
2508 if (char_aep != NULL)
2509 new_en = *char_aep;
2510 else
2511 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002512 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002513 new_en.ae_u.gui.fg_color = INVALCOLOR;
2514 new_en.ae_u.gui.bg_color = INVALCOLOR;
2515 new_en.ae_u.gui.sp_color = INVALCOLOR;
2516 if (char_attr <= HL_ALL)
2517 new_en.ae_attr = char_attr;
2518 }
2519
2520 if (prim_attr <= HL_ALL)
2521 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2522 else
2523 {
2524 spell_aep = syn_gui_attr2entry(prim_attr);
2525 if (spell_aep != NULL)
2526 {
2527 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2528 spell_aep->ae_attr);
2529 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2530 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2531 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2532 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2533 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2534 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2535 if (spell_aep->ae_u.gui.font != NOFONT)
2536 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2537# ifdef FEAT_XFONTSET
2538 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2539 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2540# endif
2541 }
2542 }
2543 return get_attr_entry(&gui_attr_table, &new_en);
2544 }
2545#endif
2546
2547 if (IS_CTERM)
2548 {
2549 if (char_attr > HL_ALL)
2550 char_aep = syn_cterm_attr2entry(char_attr);
2551 if (char_aep != NULL)
2552 new_en = *char_aep;
2553 else
2554 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002555 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002556#ifdef FEAT_TERMGUICOLORS
2557 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2558 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002559 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002560#endif
2561 if (char_attr <= HL_ALL)
2562 new_en.ae_attr = char_attr;
2563 }
2564
2565 if (prim_attr <= HL_ALL)
2566 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2567 else
2568 {
2569 spell_aep = syn_cterm_attr2entry(prim_attr);
2570 if (spell_aep != NULL)
2571 {
2572 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2573 spell_aep->ae_attr);
2574 if (spell_aep->ae_u.cterm.fg_color > 0)
2575 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2576 if (spell_aep->ae_u.cterm.bg_color > 0)
2577 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002578 if (spell_aep->ae_u.cterm.ul_color > 0)
2579 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002580#ifdef FEAT_TERMGUICOLORS
2581 // If both fg and bg are not set fall back to cterm colors.
2582 // Helps for SpellBad which uses undercurl in the GUI.
2583 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2584 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2585 {
2586 if (spell_aep->ae_u.cterm.fg_color > 0)
2587 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2588 if (spell_aep->ae_u.cterm.bg_color > 0)
2589 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2590 }
2591 else
2592 {
2593 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2594 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2595 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2596 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2597 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002598 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2599 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002600#endif
2601 }
2602 }
2603 return get_attr_entry(&cterm_attr_table, &new_en);
2604 }
2605
2606 if (char_attr > HL_ALL)
2607 char_aep = syn_term_attr2entry(char_attr);
2608 if (char_aep != NULL)
2609 new_en = *char_aep;
2610 else
2611 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002612 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002613 if (char_attr <= HL_ALL)
2614 new_en.ae_attr = char_attr;
2615 }
2616
2617 if (prim_attr <= HL_ALL)
2618 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2619 else
2620 {
2621 spell_aep = syn_term_attr2entry(prim_attr);
2622 if (spell_aep != NULL)
2623 {
2624 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2625 if (spell_aep->ae_u.term.start != NULL)
2626 {
2627 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2628 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2629 }
2630 }
2631 }
2632 return get_attr_entry(&term_attr_table, &new_en);
2633}
2634
2635#ifdef FEAT_GUI
2636 attrentry_T *
2637syn_gui_attr2entry(int attr)
2638{
2639 attr -= ATTR_OFF;
2640 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2641 return NULL;
2642 return &(GUI_ATTR_ENTRY(attr));
2643}
2644#endif
2645
2646/*
2647 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2648 * Only to be used when "attr" > HL_ALL.
2649 */
2650 int
2651syn_attr2attr(int attr)
2652{
2653 attrentry_T *aep;
2654
2655#ifdef FEAT_GUI
2656 if (gui.in_use)
2657 aep = syn_gui_attr2entry(attr);
2658 else
2659#endif
2660 if (IS_CTERM)
2661 aep = syn_cterm_attr2entry(attr);
2662 else
2663 aep = syn_term_attr2entry(attr);
2664
2665 if (aep == NULL) // highlighting not set
2666 return 0;
2667 return aep->ae_attr;
2668}
2669
2670
2671 attrentry_T *
2672syn_term_attr2entry(int attr)
2673{
2674 attr -= ATTR_OFF;
2675 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2676 return NULL;
2677 return &(TERM_ATTR_ENTRY(attr));
2678}
2679
2680 attrentry_T *
2681syn_cterm_attr2entry(int attr)
2682{
2683 attr -= ATTR_OFF;
2684 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2685 return NULL;
2686 return &(CTERM_ATTR_ENTRY(attr));
2687}
2688
2689#define LIST_ATTR 1
2690#define LIST_STRING 2
2691#define LIST_INT 3
2692
2693 static void
2694highlight_list_one(int id)
2695{
2696 hl_group_T *sgp;
2697 int didh = FALSE;
2698
2699 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2700
2701 if (message_filtered(sgp->sg_name))
2702 return;
2703
2704 didh = highlight_list_arg(id, didh, LIST_ATTR,
2705 sgp->sg_term, NULL, "term");
2706 didh = highlight_list_arg(id, didh, LIST_STRING,
2707 0, sgp->sg_start, "start");
2708 didh = highlight_list_arg(id, didh, LIST_STRING,
2709 0, sgp->sg_stop, "stop");
2710
2711 didh = highlight_list_arg(id, didh, LIST_ATTR,
2712 sgp->sg_cterm, NULL, "cterm");
2713 didh = highlight_list_arg(id, didh, LIST_INT,
2714 sgp->sg_cterm_fg, NULL, "ctermfg");
2715 didh = highlight_list_arg(id, didh, LIST_INT,
2716 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002717 didh = highlight_list_arg(id, didh, LIST_INT,
2718 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002719
2720#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2721 didh = highlight_list_arg(id, didh, LIST_ATTR,
2722 sgp->sg_gui, NULL, "gui");
2723 didh = highlight_list_arg(id, didh, LIST_STRING,
2724 0, sgp->sg_gui_fg_name, "guifg");
2725 didh = highlight_list_arg(id, didh, LIST_STRING,
2726 0, sgp->sg_gui_bg_name, "guibg");
2727 didh = highlight_list_arg(id, didh, LIST_STRING,
2728 0, sgp->sg_gui_sp_name, "guisp");
2729#endif
2730#ifdef FEAT_GUI
2731 didh = highlight_list_arg(id, didh, LIST_STRING,
2732 0, sgp->sg_font_name, "font");
2733#endif
2734
2735 if (sgp->sg_link && !got_int)
2736 {
2737 (void)syn_list_header(didh, 9999, id);
2738 didh = TRUE;
2739 msg_puts_attr("links to", HL_ATTR(HLF_D));
2740 msg_putchar(' ');
2741 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2742 }
2743
2744 if (!didh)
2745 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2746#ifdef FEAT_EVAL
2747 if (p_verbose > 0)
2748 last_set_msg(sgp->sg_script_ctx);
2749#endif
2750}
2751
2752 static int
2753highlight_list_arg(
2754 int id,
2755 int didh,
2756 int type,
2757 int iarg,
2758 char_u *sarg,
2759 char *name)
2760{
2761 char_u buf[100];
2762 char_u *ts;
2763 int i;
2764
2765 if (got_int)
2766 return FALSE;
2767 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2768 {
2769 ts = buf;
2770 if (type == LIST_INT)
2771 sprintf((char *)buf, "%d", iarg - 1);
2772 else if (type == LIST_STRING)
2773 ts = sarg;
2774 else // type == LIST_ATTR
2775 {
2776 buf[0] = NUL;
2777 for (i = 0; hl_attr_table[i] != 0; ++i)
2778 {
2779 if (iarg & hl_attr_table[i])
2780 {
2781 if (buf[0] != NUL)
2782 vim_strcat(buf, (char_u *)",", 100);
2783 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2784 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2785 }
2786 }
2787 }
2788
2789 (void)syn_list_header(didh,
2790 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2791 didh = TRUE;
2792 if (!got_int)
2793 {
2794 if (*name != NUL)
2795 {
2796 msg_puts_attr(name, HL_ATTR(HLF_D));
2797 msg_puts_attr("=", HL_ATTR(HLF_D));
2798 }
2799 msg_outtrans(ts);
2800 }
2801 }
2802 return didh;
2803}
2804
2805#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2806/*
2807 * Return "1" if highlight group "id" has attribute "flag".
2808 * Return NULL otherwise.
2809 */
2810 char_u *
2811highlight_has_attr(
2812 int id,
2813 int flag,
2814 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2815{
2816 int attr;
2817
2818 if (id <= 0 || id > highlight_ga.ga_len)
2819 return NULL;
2820
2821#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2822 if (modec == 'g')
2823 attr = HL_TABLE()[id - 1].sg_gui;
2824 else
2825#endif
Yegappan Lakshmanand43d8e22021-10-19 13:44:52 +01002826 {
2827 if (modec == 'c')
2828 attr = HL_TABLE()[id - 1].sg_cterm;
2829 else
2830 attr = HL_TABLE()[id - 1].sg_term;
2831 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002832
2833 if (attr & flag)
2834 return (char_u *)"1";
2835 return NULL;
2836}
2837#endif
2838
2839#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2840/*
2841 * Return color name of highlight group "id".
2842 */
2843 char_u *
2844highlight_color(
2845 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02002846 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002847 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2848{
2849 static char_u name[20];
2850 int n;
2851 int fg = FALSE;
2852 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02002853 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002854 int font = FALSE;
2855
2856 if (id <= 0 || id > highlight_ga.ga_len)
2857 return NULL;
2858
2859 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2860 fg = TRUE;
2861 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2862 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2863 font = TRUE;
2864 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2865 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02002866 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
2867 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002868 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2869 return NULL;
2870 if (modec == 'g')
2871 {
2872# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2873# ifdef FEAT_GUI
2874 // return font name
2875 if (font)
2876 return HL_TABLE()[id - 1].sg_font_name;
2877# endif
2878
2879 // return #RRGGBB form (only possible when GUI is running)
2880 if ((USE_24BIT) && what[2] == '#')
2881 {
2882 guicolor_T color;
2883 long_u rgb;
2884 static char_u buf[10];
2885
2886 if (fg)
2887 color = HL_TABLE()[id - 1].sg_gui_fg;
2888 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002889 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002890 else
2891 color = HL_TABLE()[id - 1].sg_gui_bg;
2892 if (color == INVALCOLOR)
2893 return NULL;
2894 rgb = (long_u)GUI_MCH_GET_RGB(color);
2895 sprintf((char *)buf, "#%02x%02x%02x",
2896 (unsigned)(rgb >> 16),
2897 (unsigned)(rgb >> 8) & 255,
2898 (unsigned)rgb & 255);
2899 return buf;
2900 }
2901# endif
2902 if (fg)
2903 return (HL_TABLE()[id - 1].sg_gui_fg_name);
2904 if (sp)
2905 return (HL_TABLE()[id - 1].sg_gui_sp_name);
2906 return (HL_TABLE()[id - 1].sg_gui_bg_name);
2907 }
2908 if (font || sp)
2909 return NULL;
2910 if (modec == 'c')
2911 {
2912 if (fg)
2913 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02002914 else if (ul)
2915 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002916 else
2917 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2918 if (n < 0)
2919 return NULL;
2920 sprintf((char *)name, "%d", n);
2921 return name;
2922 }
2923 // term doesn't have color
2924 return NULL;
2925}
2926#endif
2927
2928#if (defined(FEAT_SYN_HL) \
2929 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2930 && defined(FEAT_PRINTER)) || defined(PROTO)
2931/*
2932 * Return color name of highlight group "id" as RGB value.
2933 */
2934 long_u
2935highlight_gui_color_rgb(
2936 int id,
2937 int fg) // TRUE = fg, FALSE = bg
2938{
2939 guicolor_T color;
2940
2941 if (id <= 0 || id > highlight_ga.ga_len)
2942 return 0L;
2943
2944 if (fg)
2945 color = HL_TABLE()[id - 1].sg_gui_fg;
2946 else
2947 color = HL_TABLE()[id - 1].sg_gui_bg;
2948
2949 if (color == INVALCOLOR)
2950 return 0L;
2951
2952 return GUI_MCH_GET_RGB(color);
2953}
2954#endif
2955
2956/*
2957 * Output the syntax list header.
2958 * Return TRUE when started a new line.
2959 */
2960 int
2961syn_list_header(
2962 int did_header, // did header already
2963 int outlen, // length of string that comes
2964 int id) // highlight group id
2965{
2966 int endcol = 19;
2967 int newline = TRUE;
2968 int name_col = 0;
2969
2970 if (!did_header)
2971 {
2972 msg_putchar('\n');
2973 if (got_int)
2974 return TRUE;
2975 msg_outtrans(HL_TABLE()[id - 1].sg_name);
2976 name_col = msg_col;
2977 endcol = 15;
2978 }
2979 else if (msg_col + outlen + 1 >= Columns)
2980 {
2981 msg_putchar('\n');
2982 if (got_int)
2983 return TRUE;
2984 }
2985 else
2986 {
2987 if (msg_col >= endcol) // wrap around is like starting a new line
2988 newline = FALSE;
2989 }
2990
2991 if (msg_col >= endcol) // output at least one space
2992 endcol = msg_col + 1;
2993 if (Columns <= endcol) // avoid hang for tiny window
2994 endcol = Columns - 1;
2995
2996 msg_advance(endcol);
2997
2998 // Show "xxx" with the attributes.
2999 if (!did_header)
3000 {
3001 if (endcol == Columns - 1 && endcol <= name_col)
3002 msg_putchar(' ');
3003 msg_puts_attr("xxx", syn_id2attr(id));
3004 msg_putchar(' ');
3005 }
3006
3007 return newline;
3008}
3009
3010/*
3011 * Set the attribute numbers for a highlight group.
3012 * Called after one of the attributes has changed.
3013 */
3014 static void
3015set_hl_attr(
3016 int idx) // index in array
3017{
3018 attrentry_T at_en;
3019 hl_group_T *sgp = HL_TABLE() + idx;
3020
3021 // The "Normal" group doesn't need an attribute number
3022 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
3023 return;
3024
3025#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003026 // For the GUI mode: If there are other than "normal" highlighting
3027 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003028 if (sgp->sg_gui_fg == INVALCOLOR
3029 && sgp->sg_gui_bg == INVALCOLOR
3030 && sgp->sg_gui_sp == INVALCOLOR
3031 && sgp->sg_font == NOFONT
3032# ifdef FEAT_XFONTSET
3033 && sgp->sg_fontset == NOFONTSET
3034# endif
3035 )
3036 {
3037 sgp->sg_gui_attr = sgp->sg_gui;
3038 }
3039 else
3040 {
3041 at_en.ae_attr = sgp->sg_gui;
3042 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
3043 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
3044 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
3045 at_en.ae_u.gui.font = sgp->sg_font;
3046# ifdef FEAT_XFONTSET
3047 at_en.ae_u.gui.fontset = sgp->sg_fontset;
3048# endif
3049 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
3050 }
3051#endif
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003052 // For the term mode: If there are other than "normal" highlighting
3053 // attributes, need to allocate an attr number.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003054 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
3055 sgp->sg_term_attr = sgp->sg_term;
3056 else
3057 {
3058 at_en.ae_attr = sgp->sg_term;
3059 at_en.ae_u.term.start = sgp->sg_start;
3060 at_en.ae_u.term.stop = sgp->sg_stop;
3061 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
3062 }
3063
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003064 // For the color term mode: If there are other than "normal"
3065 // highlighting attributes, need to allocate an attr number.
Bram Moolenaare023e882020-05-31 16:42:30 +02003066 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003067# ifdef FEAT_TERMGUICOLORS
3068 && sgp->sg_gui_fg == INVALCOLOR
3069 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02003070 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003071# endif
3072 )
3073 sgp->sg_cterm_attr = sgp->sg_cterm;
3074 else
3075 {
3076 at_en.ae_attr = sgp->sg_cterm;
3077 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
3078 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02003079 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003080# ifdef FEAT_TERMGUICOLORS
3081# ifdef MSWIN
3082# ifdef VIMDLL
3083 // Only when not using the GUI.
3084 if (!gui.in_use && !gui.starting)
3085# endif
3086 {
3087 int id;
3088 guicolor_T fg, bg;
3089
3090 id = syn_name2id((char_u *)"Normal");
3091 if (id > 0)
3092 {
3093 syn_id2colors(id, &fg, &bg);
3094 if (sgp->sg_gui_fg == INVALCOLOR)
3095 sgp->sg_gui_fg = fg;
3096 if (sgp->sg_gui_bg == INVALCOLOR)
3097 sgp->sg_gui_bg = bg;
3098 }
3099
3100 }
3101# endif
3102 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
3103 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02003104 // Only use the underline/undercurl color when used, it may clear the
3105 // background color if not supported.
3106 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
3107 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
3108 else
3109 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003110 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
3111 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
3112 {
3113 // If both fg and bg are invalid fall back to the cterm colors.
3114 // Helps when the GUI only uses an attribute, e.g. undercurl.
3115 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
3116 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
3117 }
3118# endif
3119 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
3120 }
3121}
3122
3123/*
3124 * Lookup a highlight group name and return its ID.
3125 * If it is not found, 0 is returned.
3126 */
3127 int
3128syn_name2id(char_u *name)
3129{
3130 int i;
3131 char_u name_u[200];
3132
3133 // Avoid using stricmp() too much, it's slow on some systems
3134 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
3135 // don't deserve to be found!
3136 vim_strncpy(name_u, name, 199);
3137 vim_strup(name_u);
3138 for (i = highlight_ga.ga_len; --i >= 0; )
3139 if (HL_TABLE()[i].sg_name_u != NULL
3140 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
3141 break;
3142 return i + 1;
3143}
3144
3145/*
3146 * Lookup a highlight group name and return its attributes.
3147 * Return zero if not found.
3148 */
3149 int
3150syn_name2attr(char_u *name)
3151{
3152 int id = syn_name2id(name);
3153
3154 if (id != 0)
3155 return syn_id2attr(id);
3156 return 0;
3157}
3158
3159#if defined(FEAT_EVAL) || defined(PROTO)
3160/*
3161 * Return TRUE if highlight group "name" exists.
3162 */
3163 int
3164highlight_exists(char_u *name)
3165{
3166 return (syn_name2id(name) > 0);
3167}
3168
3169# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3170/*
3171 * Return the name of highlight group "id".
3172 * When not a valid ID return an empty string.
3173 */
3174 char_u *
3175syn_id2name(int id)
3176{
3177 if (id <= 0 || id > highlight_ga.ga_len)
3178 return (char_u *)"";
3179 return HL_TABLE()[id - 1].sg_name;
3180}
3181# endif
3182#endif
3183
3184/*
3185 * Like syn_name2id(), but take a pointer + length argument.
3186 */
3187 int
3188syn_namen2id(char_u *linep, int len)
3189{
3190 char_u *name;
3191 int id = 0;
3192
3193 name = vim_strnsave(linep, len);
3194 if (name != NULL)
3195 {
3196 id = syn_name2id(name);
3197 vim_free(name);
3198 }
3199 return id;
3200}
3201
3202/*
3203 * Find highlight group name in the table and return its ID.
3204 * The argument is a pointer to the name and the length of the name.
3205 * If it doesn't exist yet, a new entry is created.
3206 * Return 0 for failure.
3207 */
3208 int
3209syn_check_group(char_u *pp, int len)
3210{
3211 int id;
3212 char_u *name;
3213
3214 name = vim_strnsave(pp, len);
3215 if (name == NULL)
3216 return 0;
3217
3218 id = syn_name2id(name);
3219 if (id == 0) // doesn't exist yet
3220 id = syn_add_group(name);
3221 else
3222 vim_free(name);
3223 return id;
3224}
3225
3226/*
3227 * Add new highlight group and return its ID.
3228 * "name" must be an allocated string, it will be consumed.
3229 * Return 0 for failure.
3230 */
3231 static int
3232syn_add_group(char_u *name)
3233{
3234 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003235 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003236
3237 // Check that the name is ASCII letters, digits and underscore.
3238 for (p = name; *p != NUL; ++p)
3239 {
3240 if (!vim_isprintc(*p))
3241 {
3242 emsg(_("E669: Unprintable character in group name"));
3243 vim_free(name);
3244 return 0;
3245 }
3246 else if (!ASCII_ISALNUM(*p) && *p != '_')
3247 {
3248 // This is an error, but since there previously was no check only
3249 // give a warning.
3250 msg_source(HL_ATTR(HLF_W));
3251 msg(_("W18: Invalid character in group name"));
3252 break;
3253 }
3254 }
3255
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003256 // First call for this growarray: init growing array.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003257 if (highlight_ga.ga_data == NULL)
3258 {
3259 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3260 highlight_ga.ga_growsize = 10;
3261 }
3262
3263 if (highlight_ga.ga_len >= MAX_HL_ID)
3264 {
3265 emsg(_("E849: Too many highlight and syntax groups"));
3266 vim_free(name);
3267 return 0;
3268 }
3269
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003270 // Make room for at least one other syntax_highlight entry.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003271 if (ga_grow(&highlight_ga, 1) == FAIL)
3272 {
3273 vim_free(name);
3274 return 0;
3275 }
3276
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003277 name_up = vim_strsave_up(name);
3278 if (name_up == NULL)
3279 {
3280 vim_free(name);
3281 return 0;
3282 }
3283
Bram Moolenaara80faa82020-04-12 19:37:17 +02003284 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003285 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003286 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003287#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3288 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3289 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003290 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003291#endif
3292 ++highlight_ga.ga_len;
3293
3294 return highlight_ga.ga_len; // ID is index plus one
3295}
3296
3297/*
3298 * When, just after calling syn_add_group(), an error is discovered, this
3299 * function deletes the new name.
3300 */
3301 static void
3302syn_unadd_group(void)
3303{
3304 --highlight_ga.ga_len;
3305 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3306 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3307}
3308
3309/*
3310 * Translate a group ID to highlight attributes.
3311 */
3312 int
3313syn_id2attr(int hl_id)
3314{
3315 int attr;
3316 hl_group_T *sgp;
3317
3318 hl_id = syn_get_final_id(hl_id);
3319 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3320
3321#ifdef FEAT_GUI
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003322 // Only use GUI attr when the GUI is being used.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003323 if (gui.in_use)
3324 attr = sgp->sg_gui_attr;
3325 else
3326#endif
3327 if (IS_CTERM)
3328 attr = sgp->sg_cterm_attr;
3329 else
3330 attr = sgp->sg_term_attr;
3331
3332 return attr;
3333}
3334
3335#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3336/*
3337 * Get the GUI colors and attributes for a group ID.
3338 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3339 */
3340 int
3341syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3342{
3343 hl_group_T *sgp;
3344
3345 hl_id = syn_get_final_id(hl_id);
3346 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3347
3348 *fgp = sgp->sg_gui_fg;
3349 *bgp = sgp->sg_gui_bg;
3350 return sgp->sg_gui;
3351}
3352#endif
3353
3354#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003355 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3356 && defined(FEAT_TERMGUICOLORS)) \
3357 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003358 void
3359syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3360{
3361 hl_group_T *sgp;
3362
3363 hl_id = syn_get_final_id(hl_id);
3364 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3365 *fgp = sgp->sg_cterm_fg - 1;
3366 *bgp = sgp->sg_cterm_bg - 1;
3367}
3368#endif
3369
3370/*
3371 * Translate a group ID to the final group ID (following links).
3372 */
3373 int
3374syn_get_final_id(int hl_id)
3375{
3376 int count;
3377 hl_group_T *sgp;
3378
3379 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3380 return 0; // Can be called from eval!!
3381
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003382 // Follow links until there is no more.
3383 // Look out for loops! Break after 100 links.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003384 for (count = 100; --count >= 0; )
3385 {
3386 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3387 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3388 break;
3389 hl_id = sgp->sg_link;
3390 }
3391
3392 return hl_id;
3393}
3394
3395#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3396/*
3397 * Call this function just after the GUI has started.
3398 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3399 * It finds the font and color handles for the highlighting groups.
3400 */
3401 void
3402highlight_gui_started(void)
3403{
3404 int idx;
3405
3406 // First get the colors from the "Normal" and "Menu" group, if set
3407 if (USE_24BIT)
3408 set_normal_colors();
3409
3410 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3411 gui_do_one_color(idx, FALSE, FALSE);
3412
3413 highlight_changed();
3414}
3415
3416 static void
3417gui_do_one_color(
3418 int idx,
3419 int do_menu UNUSED, // TRUE: might set the menu font
3420 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3421{
3422 int didit = FALSE;
3423
3424# ifdef FEAT_GUI
3425# ifdef FEAT_TERMGUICOLORS
3426 if (gui.in_use)
3427# endif
3428 if (HL_TABLE()[idx].sg_font_name != NULL)
3429 {
3430 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3431 do_tooltip, TRUE);
3432 didit = TRUE;
3433 }
3434# endif
3435 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3436 {
3437 HL_TABLE()[idx].sg_gui_fg =
3438 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3439 didit = TRUE;
3440 }
3441 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3442 {
3443 HL_TABLE()[idx].sg_gui_bg =
3444 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3445 didit = TRUE;
3446 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003447 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3448 {
3449 HL_TABLE()[idx].sg_gui_sp =
3450 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3451 didit = TRUE;
3452 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003453 if (didit) // need to get a new attr number
3454 set_hl_attr(idx);
3455}
3456#endif
3457
3458#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3459/*
3460 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3461 */
3462 static void
3463combine_stl_hlt(
3464 int id,
3465 int id_S,
3466 int id_alt,
3467 int hlcnt,
3468 int i,
3469 int hlf,
3470 int *table)
3471{
3472 hl_group_T *hlt = HL_TABLE();
3473
3474 if (id_alt == 0)
3475 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003476 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003477 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3478 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3479# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3480 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3481# endif
3482 }
3483 else
3484 mch_memmove(&hlt[hlcnt + i],
3485 &hlt[id_alt - 1],
3486 sizeof(hl_group_T));
3487 hlt[hlcnt + i].sg_link = 0;
3488
3489 hlt[hlcnt + i].sg_term ^=
3490 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3491 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3492 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3493 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3494 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3495 hlt[hlcnt + i].sg_cterm ^=
3496 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3497 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3498 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3499 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3500 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3501# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3502 hlt[hlcnt + i].sg_gui ^=
3503 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3504# endif
3505# ifdef FEAT_GUI
3506 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3507 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3508 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3509 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3510 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3511 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3512 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3513 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3514# ifdef FEAT_XFONTSET
3515 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3516 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3517# endif
3518# endif
3519 highlight_ga.ga_len = hlcnt + i + 1;
3520 set_hl_attr(hlcnt + i); // At long last we can apply
3521 table[i] = syn_id2attr(hlcnt + i + 1);
3522}
3523#endif
3524
3525/*
3526 * Translate the 'highlight' option into attributes in highlight_attr[] and
3527 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3528 * corresponding highlights to use on top of HLF_SNC is computed.
3529 * Called only when the 'highlight' option has been changed and upon first
3530 * screen redraw after any :highlight command.
3531 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3532 */
3533 int
3534highlight_changed(void)
3535{
3536 int hlf;
3537 int i;
3538 char_u *p;
3539 int attr;
3540 char_u *end;
3541 int id;
3542#ifdef USER_HIGHLIGHT
3543 char_u userhl[30]; // use 30 to avoid compiler warning
3544# ifdef FEAT_STL_OPT
3545 int id_S = -1;
3546 int id_SNC = 0;
3547# ifdef FEAT_TERMINAL
3548 int id_ST = 0;
3549 int id_STNC = 0;
3550# endif
3551 int hlcnt;
3552# endif
3553#endif
3554 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3555
3556 need_highlight_changed = FALSE;
3557
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003558 // Clear all attributes.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003559 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3560 highlight_attr[hlf] = 0;
3561
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003562 // First set all attributes to their default value.
3563 // Then use the attributes from the 'highlight' option.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003564 for (i = 0; i < 2; ++i)
3565 {
3566 if (i)
3567 p = p_hl;
3568 else
3569 p = get_highlight_default();
3570 if (p == NULL) // just in case
3571 continue;
3572
3573 while (*p)
3574 {
3575 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3576 if (hl_flags[hlf] == *p)
3577 break;
3578 ++p;
3579 if (hlf == (int)HLF_COUNT || *p == NUL)
3580 return FAIL;
3581
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003582 // Allow several hl_flags to be combined, like "bu" for
3583 // bold-underlined.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003584 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003585 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003586 {
3587 if (VIM_ISWHITE(*p)) // ignore white space
3588 continue;
3589
3590 if (attr > HL_ALL) // Combination with ':' is not allowed.
3591 return FAIL;
3592
3593 switch (*p)
3594 {
3595 case 'b': attr |= HL_BOLD;
3596 break;
3597 case 'i': attr |= HL_ITALIC;
3598 break;
3599 case '-':
3600 case 'n': // no highlighting
3601 break;
3602 case 'r': attr |= HL_INVERSE;
3603 break;
3604 case 's': attr |= HL_STANDOUT;
3605 break;
3606 case 'u': attr |= HL_UNDERLINE;
3607 break;
3608 case 'c': attr |= HL_UNDERCURL;
3609 break;
3610 case 't': attr |= HL_STRIKETHROUGH;
3611 break;
3612 case ':': ++p; // highlight group name
3613 if (attr || *p == NUL) // no combinations
3614 return FAIL;
3615 end = vim_strchr(p, ',');
3616 if (end == NULL)
3617 end = p + STRLEN(p);
3618 id = syn_check_group(p, (int)(end - p));
3619 if (id == 0)
3620 return FAIL;
3621 attr = syn_id2attr(id);
3622 p = end - 1;
3623#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3624 if (hlf == (int)HLF_SNC)
3625 id_SNC = syn_get_final_id(id);
3626# ifdef FEAT_TERMINAL
3627 else if (hlf == (int)HLF_ST)
3628 id_ST = syn_get_final_id(id);
3629 else if (hlf == (int)HLF_STNC)
3630 id_STNC = syn_get_final_id(id);
3631# endif
3632 else if (hlf == (int)HLF_S)
3633 id_S = syn_get_final_id(id);
3634#endif
3635 break;
3636 default: return FAIL;
3637 }
3638 }
3639 highlight_attr[hlf] = attr;
3640
3641 p = skip_to_option_part(p); // skip comma and spaces
3642 }
3643 }
3644
3645#ifdef USER_HIGHLIGHT
Yegappan Lakshmananad6b90c2021-10-18 22:13:57 +01003646 // Setup the user highlights
3647 //
3648 // Temporarily utilize 28 more hl entries:
3649 // 9 for User1-User9 combined with StatusLineNC
3650 // 9 for User1-User9 combined with StatusLineTerm
3651 // 9 for User1-User9 combined with StatusLineTermNC
3652 // 1 for StatusLine default
3653 // Have to be in there simultaneously in case of table overflows in
3654 // get_attr_entry()
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003655# ifdef FEAT_STL_OPT
3656 if (ga_grow(&highlight_ga, 28) == FAIL)
3657 return FAIL;
3658 hlcnt = highlight_ga.ga_len;
3659 if (id_S == -1)
3660 {
3661 // Make sure id_S is always valid to simplify code below. Use the last
3662 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003663 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003664 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3665 id_S = hlcnt + 19;
3666 }
3667# endif
3668 for (i = 0; i < 9; i++)
3669 {
3670 sprintf((char *)userhl, "User%d", i + 1);
3671 id = syn_name2id(userhl);
3672 if (id == 0)
3673 {
3674 highlight_user[i] = 0;
3675# ifdef FEAT_STL_OPT
3676 highlight_stlnc[i] = 0;
3677# ifdef FEAT_TERMINAL
3678 highlight_stlterm[i] = 0;
3679 highlight_stltermnc[i] = 0;
3680# endif
3681# endif
3682 }
3683 else
3684 {
3685 highlight_user[i] = syn_id2attr(id);
3686# ifdef FEAT_STL_OPT
3687 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3688 HLF_SNC, highlight_stlnc);
3689# ifdef FEAT_TERMINAL
3690 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3691 HLF_ST, highlight_stlterm);
3692 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3693 HLF_STNC, highlight_stltermnc);
3694# endif
3695# endif
3696 }
3697 }
3698# ifdef FEAT_STL_OPT
3699 highlight_ga.ga_len = hlcnt;
3700# endif
3701
3702#endif // USER_HIGHLIGHT
3703
3704 return OK;
3705}
3706
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003707static void highlight_list(void);
3708static void highlight_list_two(int cnt, int attr);
3709
3710/*
3711 * Handle command line completion for :highlight command.
3712 */
3713 void
3714set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3715{
3716 char_u *p;
3717
3718 // Default: expand group names
3719 xp->xp_context = EXPAND_HIGHLIGHT;
3720 xp->xp_pattern = arg;
3721 include_link = 2;
3722 include_default = 1;
3723
3724 // (part of) subcommand already typed
3725 if (*arg != NUL)
3726 {
3727 p = skiptowhite(arg);
3728 if (*p != NUL) // past "default" or group name
3729 {
3730 include_default = 0;
3731 if (STRNCMP("default", arg, p - arg) == 0)
3732 {
3733 arg = skipwhite(p);
3734 xp->xp_pattern = arg;
3735 p = skiptowhite(arg);
3736 }
3737 if (*p != NUL) // past group name
3738 {
3739 include_link = 0;
3740 if (arg[1] == 'i' && arg[0] == 'N')
3741 highlight_list();
3742 if (STRNCMP("link", arg, p - arg) == 0
3743 || STRNCMP("clear", arg, p - arg) == 0)
3744 {
3745 xp->xp_pattern = skipwhite(p);
3746 p = skiptowhite(xp->xp_pattern);
3747 if (*p != NUL) // past first group name
3748 {
3749 xp->xp_pattern = skipwhite(p);
3750 p = skiptowhite(xp->xp_pattern);
3751 }
3752 }
3753 if (*p != NUL) // past group name(s)
3754 xp->xp_context = EXPAND_NOTHING;
3755 }
3756 }
3757 }
3758}
3759
3760/*
3761 * List highlighting matches in a nice way.
3762 */
3763 static void
3764highlight_list(void)
3765{
3766 int i;
3767
3768 for (i = 10; --i >= 0; )
3769 highlight_list_two(i, HL_ATTR(HLF_D));
3770 for (i = 40; --i >= 0; )
3771 highlight_list_two(99, 0);
3772}
3773
3774 static void
3775highlight_list_two(int cnt, int attr)
3776{
3777 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3778 msg_clr_eos();
3779 out_flush();
3780 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3781}
3782
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003783/*
3784 * Function given to ExpandGeneric() to obtain the list of group names.
3785 */
3786 char_u *
3787get_highlight_name(expand_T *xp UNUSED, int idx)
3788{
3789 return get_highlight_name_ext(xp, idx, TRUE);
3790}
3791
3792/*
3793 * Obtain a highlight group name.
3794 * When "skip_cleared" is TRUE don't return a cleared entry.
3795 */
3796 char_u *
3797get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3798{
3799 if (idx < 0)
3800 return NULL;
3801
3802 // Items are never removed from the table, skip the ones that were
3803 // cleared.
3804 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3805 return (char_u *)"";
3806
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003807 if (idx == highlight_ga.ga_len && include_none != 0)
3808 return (char_u *)"none";
3809 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3810 return (char_u *)"default";
3811 if (idx == highlight_ga.ga_len + include_none + include_default
3812 && include_link != 0)
3813 return (char_u *)"link";
3814 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3815 && include_link != 0)
3816 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003817 if (idx >= highlight_ga.ga_len)
3818 return NULL;
3819 return HL_TABLE()[idx].sg_name;
3820}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003821
3822#if defined(FEAT_GUI) || defined(PROTO)
3823/*
3824 * Free all the highlight group fonts.
3825 * Used when quitting for systems which need it.
3826 */
3827 void
3828free_highlight_fonts(void)
3829{
3830 int idx;
3831
3832 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3833 {
3834 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3835 HL_TABLE()[idx].sg_font = NOFONT;
3836# ifdef FEAT_XFONTSET
3837 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3838 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3839# endif
3840 }
3841
3842 gui_mch_free_font(gui.norm_font);
3843# ifdef FEAT_XFONTSET
3844 gui_mch_free_fontset(gui.fontset);
3845# endif
3846# ifndef FEAT_GUI_GTK
3847 gui_mch_free_font(gui.bold_font);
3848 gui_mch_free_font(gui.ital_font);
3849 gui_mch_free_font(gui.boldital_font);
3850# endif
3851}
3852#endif