blob: a08d1f1d2314774d173894e163395efaaf8a6430 [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
380 /*
381 * Try finding the color scheme file. Used when a color file was loaded
382 * and 'background' or 't_Co' is changed.
383 */
384 p = get_var_value((char_u *)"g:colors_name");
385 if (p != NULL)
386 {
387 // The value of g:colors_name could be freed when sourcing the script,
388 // making "p" invalid, so copy it.
389 char_u *copy_p = vim_strsave(p);
390 int r;
391
392 if (copy_p != NULL)
393 {
394 r = load_colors(copy_p);
395 vim_free(copy_p);
396 if (r == OK)
397 return;
398 }
399 }
400
401#endif
402
403 /*
404 * Didn't use a color file, use the compiled-in colors.
405 */
406 if (both)
407 {
408 had_both = TRUE;
409 pp = highlight_init_both;
410 for (i = 0; pp[i] != NULL; ++i)
411 do_highlight((char_u *)pp[i], reset, TRUE);
412 }
413 else if (!had_both)
414 // Don't do anything before the call with both == TRUE from main().
415 // Not everything has been setup then, and that call will overrule
416 // everything anyway.
417 return;
418
419 if (*p_bg == 'l')
420 pp = highlight_init_light;
421 else
422 pp = highlight_init_dark;
423 for (i = 0; pp[i] != NULL; ++i)
424 do_highlight((char_u *)pp[i], reset, TRUE);
425
426 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
427 // depend on the number of colors available.
428 // With 8 colors brown is equal to yellow, need to use black for Search fg
429 // to avoid Statement highlighted text disappears.
430 // Clear the attributes, needed when changing the t_Co value.
431 if (t_colors > 8)
432 do_highlight((char_u *)(*p_bg == 'l'
433 ? "Visual cterm=NONE ctermbg=LightGrey"
434 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
435 else
436 {
437 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
438 FALSE, TRUE);
439 if (*p_bg == 'l')
440 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
441 }
442
443#ifdef FEAT_SYN_HL
444 /*
445 * If syntax highlighting is enabled load the highlighting for it.
446 */
447 if (get_var_value((char_u *)"g:syntax_on") != NULL)
448 {
449 static int recursive = 0;
450
451 if (recursive >= 5)
452 emsg(_("E679: recursive loop loading syncolor.vim"));
453 else
454 {
455 ++recursive;
456 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
457 --recursive;
458 }
459 }
460#endif
461}
462
463/*
464 * Load color file "name".
465 * Return OK for success, FAIL for failure.
466 */
467 int
468load_colors(char_u *name)
469{
470 char_u *buf;
471 int retval = FAIL;
472 static int recursive = FALSE;
473
474 // When being called recursively, this is probably because setting
475 // 'background' caused the highlighting to be reloaded. This means it is
476 // working, thus we should return OK.
477 if (recursive)
478 return OK;
479
480 recursive = TRUE;
481 buf = alloc(STRLEN(name) + 12);
482 if (buf != NULL)
483 {
484 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
485 curbuf->b_fname, FALSE, curbuf);
486 sprintf((char *)buf, "colors/%s.vim", name);
487 retval = source_runtime(buf, DIP_START + DIP_OPT);
488 vim_free(buf);
489 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
490 }
491 recursive = FALSE;
492
493 return retval;
494}
495
496static char *(color_names[28]) = {
497 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
498 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
499 "Gray", "Grey", "LightGray", "LightGrey",
500 "DarkGray", "DarkGrey",
501 "Blue", "LightBlue", "Green", "LightGreen",
502 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
503 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
504 // indices:
505 // 0, 1, 2, 3,
506 // 4, 5, 6, 7,
507 // 8, 9, 10, 11,
508 // 12, 13,
509 // 14, 15, 16, 17,
510 // 18, 19, 20, 21, 22,
511 // 23, 24, 25, 26, 27
512static int color_numbers_16[28] = {0, 1, 2, 3,
513 4, 5, 6, 6,
514 7, 7, 7, 7,
515 8, 8,
516 9, 9, 10, 10,
517 11, 11, 12, 12, 13,
518 13, 14, 14, 15, -1};
519// for xterm with 88 colors...
520static int color_numbers_88[28] = {0, 4, 2, 6,
521 1, 5, 32, 72,
522 84, 84, 7, 7,
523 82, 82,
524 12, 43, 10, 61,
525 14, 63, 9, 74, 13,
526 75, 11, 78, 15, -1};
527// for xterm with 256 colors...
528static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200529 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200530 248, 248, 7, 7,
531 242, 242,
532 12, 81, 10, 121,
533 14, 159, 9, 224, 13,
534 225, 11, 229, 15, -1};
535// for terminals with less than 16 colors...
536static int color_numbers_8[28] = {0, 4, 2, 6,
537 1, 5, 3, 3,
538 7, 7, 7, 7,
539 0+8, 0+8,
540 4+8, 4+8, 2+8, 2+8,
541 6+8, 6+8, 1+8, 1+8, 5+8,
542 5+8, 3+8, 3+8, 7+8, -1};
543
544/*
545 * Lookup the "cterm" value to be used for color with index "idx" in
546 * color_names[].
547 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
548 * colors, otherwise it will be unchanged.
549 */
550 int
551lookup_color(int idx, int foreground, int *boldp)
552{
553 int color = color_numbers_16[idx];
554 char_u *p;
555
556 // Use the _16 table to check if it's a valid color name.
557 if (color < 0)
558 return -1;
559
560 if (t_colors == 8)
561 {
562 // t_Co is 8: use the 8 colors table
563#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100564 // On qnx, the 8 & 16 color arrays are the same
565 if (STRNCMP(T_NAME, "qansi", 5) == 0)
566 color = color_numbers_16[idx];
567 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200568#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100569 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200570 if (foreground)
571 {
572 // set/reset bold attribute to get light foreground
573 // colors (on some terminals, e.g. "linux")
574 if (color & 8)
575 *boldp = TRUE;
576 else
577 *boldp = FALSE;
578 }
579 color &= 7; // truncate to 8 colors
580 }
581 else if (t_colors == 16 || t_colors == 88
582 || t_colors >= 256)
583 {
584 /*
585 * Guess: if the termcap entry ends in 'm', it is
586 * probably an xterm-like terminal. Use the changed
587 * order for colors.
588 */
589 if (*T_CAF != NUL)
590 p = T_CAF;
591 else
592 p = T_CSF;
593 if (*p != NUL && (t_colors > 256
594 || *(p + STRLEN(p) - 1) == 'm'))
595 {
596 if (t_colors == 88)
597 color = color_numbers_88[idx];
598 else if (t_colors >= 256)
599 color = color_numbers_256[idx];
600 else
601 color = color_numbers_8[idx];
602 }
603#ifdef FEAT_TERMRESPONSE
604 if (t_colors >= 256 && color == 15 && is_mac_terminal)
605 // Terminal.app has a bug: 15 is light grey. Use white
606 // from the color cube instead.
607 color = 231;
608#endif
609 }
610 return color;
611}
612
613/*
614 * Handle the ":highlight .." command.
615 * When using ":hi clear" this is called recursively for each group with
616 * "forceit" and "init" both TRUE.
617 */
618 void
619do_highlight(
620 char_u *line,
621 int forceit,
622 int init) // TRUE when called for initializing
623{
624 char_u *name_end;
625 char_u *p;
626 char_u *linep;
627 char_u *key_start;
628 char_u *arg_start;
629 char_u *key = NULL, *arg = NULL;
630 long i;
631 int off;
632 int len;
633 int attr;
634 int id;
635 int idx;
636 hl_group_T item_before;
637 int did_change = FALSE;
638 int dodefault = FALSE;
639 int doclear = FALSE;
640 int dolink = FALSE;
641 int error = FALSE;
642 int color;
643 int is_normal_group = FALSE; // "Normal" group
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200644#ifdef FEAT_GUI_X11
645 int is_menu_group = FALSE; // "Menu" group
646 int is_scrollbar_group = FALSE; // "Scrollbar" group
647 int is_tooltip_group = FALSE; // "Tooltip" group
648 int do_colors = FALSE; // need to update colors?
649#else
650# define is_menu_group 0
651# define is_tooltip_group 0
652#endif
653#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
654 int did_highlight_changed = FALSE;
655#endif
656
657 /*
658 * If no argument, list current highlighting.
659 */
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +0200660 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200661 {
662 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
663 // TODO: only call when the group has attributes set
664 highlight_list_one((int)i);
665 return;
666 }
667
668 /*
669 * Isolate the name.
670 */
671 name_end = skiptowhite(line);
672 linep = skipwhite(name_end);
673
674 /*
675 * Check for "default" argument.
676 */
677 if (STRNCMP(line, "default", name_end - line) == 0)
678 {
679 dodefault = TRUE;
680 line = linep;
681 name_end = skiptowhite(line);
682 linep = skipwhite(name_end);
683 }
684
685 /*
686 * Check for "clear" or "link" argument.
687 */
688 if (STRNCMP(line, "clear", name_end - line) == 0)
689 doclear = TRUE;
690 if (STRNCMP(line, "link", name_end - line) == 0)
691 dolink = TRUE;
692
693 /*
694 * ":highlight {group-name}": list highlighting for one group.
695 */
Bram Moolenaar1966c242020-04-20 22:42:32 +0200696 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200697 {
698 id = syn_namen2id(line, (int)(name_end - line));
699 if (id == 0)
700 semsg(_("E411: highlight group not found: %s"), line);
701 else
702 highlight_list_one(id);
703 return;
704 }
705
706 /*
707 * Handle ":highlight link {from} {to}" command.
708 */
709 if (dolink)
710 {
711 char_u *from_start = linep;
712 char_u *from_end;
713 char_u *to_start;
714 char_u *to_end;
715 int from_id;
716 int to_id;
Bram Moolenaar213da552020-09-17 19:59:26 +0200717 hl_group_T *hlgroup = NULL;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200718
719 from_end = skiptowhite(from_start);
720 to_start = skipwhite(from_end);
721 to_end = skiptowhite(to_start);
722
Bram Moolenaar1966c242020-04-20 22:42:32 +0200723 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200724 {
725 semsg(_("E412: Not enough arguments: \":highlight link %s\""),
726 from_start);
727 return;
728 }
729
Bram Moolenaar1966c242020-04-20 22:42:32 +0200730 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200731 {
Bram Moolenaar05eb5b92020-09-16 15:43:21 +0200732 semsg(_("E413: Too many arguments: \":highlight link %s\""),
733 from_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200734 return;
735 }
736
737 from_id = syn_check_group(from_start, (int)(from_end - from_start));
738 if (STRNCMP(to_start, "NONE", 4) == 0)
739 to_id = 0;
740 else
741 to_id = syn_check_group(to_start, (int)(to_end - to_start));
742
Bram Moolenaar213da552020-09-17 19:59:26 +0200743 if (from_id > 0)
744 {
745 hlgroup = &HL_TABLE()[from_id - 1];
746 if (dodefault && (forceit || hlgroup->sg_deflink == 0))
Bram Moolenaare8df0102020-09-18 19:40:45 +0200747 {
Bram Moolenaar213da552020-09-17 19:59:26 +0200748 hlgroup->sg_deflink = to_id;
Bram Moolenaare8df0102020-09-18 19:40:45 +0200749#ifdef FEAT_EVAL
750 hlgroup->sg_deflink_sctx = current_sctx;
751 hlgroup->sg_deflink_sctx.sc_lnum += SOURCING_LNUM;
752#endif
753 }
Bram Moolenaar213da552020-09-17 19:59:26 +0200754 }
755
756 if (from_id > 0 && (!init || hlgroup->sg_set == 0))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200757 {
758 /*
759 * Don't allow a link when there already is some highlighting
760 * for the group, unless '!' is used
761 */
762 if (to_id > 0 && !forceit && !init
763 && hl_has_settings(from_id - 1, dodefault))
764 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100765 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200766 emsg(_("E414: group has settings, highlight link ignored"));
767 }
Bram Moolenaar213da552020-09-17 19:59:26 +0200768 else if (hlgroup->sg_link != to_id
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200769#ifdef FEAT_EVAL
Bram Moolenaar213da552020-09-17 19:59:26 +0200770 || hlgroup->sg_script_ctx.sc_sid != current_sctx.sc_sid
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200771#endif
Bram Moolenaar213da552020-09-17 19:59:26 +0200772 || hlgroup->sg_cleared)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200773 {
774 if (!init)
Bram Moolenaar213da552020-09-17 19:59:26 +0200775 hlgroup->sg_set |= SG_LINK;
776 hlgroup->sg_link = to_id;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200777#ifdef FEAT_EVAL
Bram Moolenaar213da552020-09-17 19:59:26 +0200778 hlgroup->sg_script_ctx = current_sctx;
779 hlgroup->sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200780#endif
Bram Moolenaar213da552020-09-17 19:59:26 +0200781 hlgroup->sg_cleared = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200782 redraw_all_later(SOME_VALID);
783
784 // Only call highlight_changed() once after multiple changes.
785 need_highlight_changed = TRUE;
786 }
787 }
788
789 return;
790 }
791
792 if (doclear)
793 {
794 /*
795 * ":highlight clear [group]" command.
796 */
Bram Moolenaar1966c242020-04-20 22:42:32 +0200797 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200798 {
799#ifdef FEAT_GUI
800 // First, we do not destroy the old values, but allocate the new
801 // ones and update the display. THEN we destroy the old values.
802 // If we destroy the old values first, then the old values
803 // (such as GuiFont's or GuiFontset's) will still be displayed but
804 // invalid because they were free'd.
805 if (gui.in_use)
806 {
807# ifdef FEAT_BEVAL_TIP
808 gui_init_tooltip_font();
809# endif
810# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
811 gui_init_menu_font();
812# endif
813 }
814# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
815 gui_mch_def_colors();
816# endif
817# ifdef FEAT_GUI_X11
818# ifdef FEAT_MENU
819
820 // This only needs to be done when there is no Menu highlight
821 // group defined by default, which IS currently the case.
822 gui_mch_new_menu_colors();
823# endif
824 if (gui.in_use)
825 {
826 gui_new_scrollbar_colors();
827# ifdef FEAT_BEVAL_GUI
828 gui_mch_new_tooltip_colors();
829# endif
830# ifdef FEAT_MENU
831 gui_mch_new_menu_font();
832# endif
833 }
834# endif
835
836 // Ok, we're done allocating the new default graphics items.
837 // The screen should already be refreshed at this point.
838 // It is now Ok to clear out the old data.
839#endif
840#ifdef FEAT_EVAL
Bram Moolenaar1966c242020-04-20 22:42:32 +0200841 do_unlet((char_u *)"g:colors_name", TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200842#endif
843 restore_cterm_colors();
844
845 /*
846 * Clear all default highlight groups and load the defaults.
847 */
848 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
849 highlight_clear(idx);
850 init_highlight(TRUE, TRUE);
851#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
852 if (USE_24BIT)
853 highlight_gui_started();
854 else
855#endif
856 highlight_changed();
857 redraw_later_clear();
858 return;
859 }
Bram Moolenaar1966c242020-04-20 22:42:32 +0200860 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200861 name_end = skiptowhite(line);
862 linep = skipwhite(name_end);
863 }
864
865 /*
866 * Find the group name in the table. If it does not exist yet, add it.
867 */
868 id = syn_check_group(line, (int)(name_end - line));
869 if (id == 0) // failed (out of memory)
870 return;
871 idx = id - 1; // index is ID minus one
872
873 // Return if "default" was used and the group already has settings.
874 if (dodefault && hl_has_settings(idx, TRUE))
875 return;
876
877 // Make a copy so we can check if any attribute actually changed.
878 item_before = HL_TABLE()[idx];
879
880 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
881 is_normal_group = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200882#ifdef FEAT_GUI_X11
883 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
884 is_menu_group = TRUE;
885 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
886 is_scrollbar_group = TRUE;
887 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
888 is_tooltip_group = TRUE;
889#endif
890
891 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
892 if (doclear || (forceit && init))
893 {
894 highlight_clear(idx);
895 if (!doclear)
896 HL_TABLE()[idx].sg_set = 0;
897 }
898
899 if (!doclear)
Bram Moolenaar1966c242020-04-20 22:42:32 +0200900 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200901 {
902 key_start = linep;
903 if (*linep == '=')
904 {
905 semsg(_("E415: unexpected equal sign: %s"), key_start);
906 error = TRUE;
907 break;
908 }
909
910 /*
911 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
912 * "guibg").
913 */
914 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
915 ++linep;
916 vim_free(key);
Bram Moolenaardf44a272020-06-07 20:49:05 +0200917 key = vim_strnsave_up(key_start, linep - key_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200918 if (key == NULL)
919 {
920 error = TRUE;
921 break;
922 }
923 linep = skipwhite(linep);
924
925 if (STRCMP(key, "NONE") == 0)
926 {
927 if (!init || HL_TABLE()[idx].sg_set == 0)
928 {
929 if (!init)
930 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
931 highlight_clear(idx);
932 }
933 continue;
934 }
935
936 /*
937 * Check for the equal sign.
938 */
939 if (*linep != '=')
940 {
941 semsg(_("E416: missing equal sign: %s"), key_start);
942 error = TRUE;
943 break;
944 }
945 ++linep;
946
947 /*
948 * Isolate the argument.
949 */
950 linep = skipwhite(linep);
951 if (*linep == '\'') // guifg='color name'
952 {
953 arg_start = ++linep;
954 linep = vim_strchr(linep, '\'');
955 if (linep == NULL)
956 {
957 semsg(_(e_invarg2), key_start);
958 error = TRUE;
959 break;
960 }
961 }
962 else
963 {
964 arg_start = linep;
965 linep = skiptowhite(linep);
966 }
967 if (linep == arg_start)
968 {
969 semsg(_("E417: missing argument: %s"), key_start);
970 error = TRUE;
971 break;
972 }
973 vim_free(arg);
Bram Moolenaar71ccd032020-06-12 22:59:11 +0200974 arg = vim_strnsave(arg_start, linep - arg_start);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200975 if (arg == NULL)
976 {
977 error = TRUE;
978 break;
979 }
980 if (*linep == '\'')
981 ++linep;
982
983 /*
984 * Store the argument.
985 */
986 if ( STRCMP(key, "TERM") == 0
987 || STRCMP(key, "CTERM") == 0
988 || STRCMP(key, "GUI") == 0)
989 {
990 attr = 0;
991 off = 0;
992 while (arg[off] != NUL)
993 {
K.Takataeeec2542021-06-02 13:28:16 +0200994 for (i = ARRAY_LENGTH(hl_attr_table); --i >= 0; )
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200995 {
996 len = (int)STRLEN(hl_name_table[i]);
997 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
998 {
999 attr |= hl_attr_table[i];
1000 off += len;
1001 break;
1002 }
1003 }
1004 if (i < 0)
1005 {
1006 semsg(_("E418: Illegal value: %s"), arg);
1007 error = TRUE;
1008 break;
1009 }
1010 if (arg[off] == ',') // another one follows
1011 ++off;
1012 }
1013 if (error)
1014 break;
1015 if (*key == 'T')
1016 {
1017 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
1018 {
1019 if (!init)
1020 HL_TABLE()[idx].sg_set |= SG_TERM;
1021 HL_TABLE()[idx].sg_term = attr;
1022 }
1023 }
1024 else if (*key == 'C')
1025 {
1026 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1027 {
1028 if (!init)
1029 HL_TABLE()[idx].sg_set |= SG_CTERM;
1030 HL_TABLE()[idx].sg_cterm = attr;
1031 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1032 }
1033 }
1034#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1035 else
1036 {
1037 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1038 {
1039 if (!init)
1040 HL_TABLE()[idx].sg_set |= SG_GUI;
1041 HL_TABLE()[idx].sg_gui = attr;
1042 }
1043 }
1044#endif
1045 }
1046 else if (STRCMP(key, "FONT") == 0)
1047 {
1048 // in non-GUI fonts are simply ignored
1049#ifdef FEAT_GUI
1050 if (HL_TABLE()[idx].sg_font_name != NULL
1051 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
1052 {
1053 // Font name didn't change, ignore.
1054 }
1055 else if (!gui.shell_created)
1056 {
1057 // GUI not started yet, always accept the name.
1058 vim_free(HL_TABLE()[idx].sg_font_name);
1059 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1060 did_change = TRUE;
1061 }
1062 else
1063 {
1064 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
1065# ifdef FEAT_XFONTSET
1066 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
1067# endif
1068 // First, save the current font/fontset.
1069 // Then try to allocate the font/fontset.
1070 // If the allocation fails, HL_TABLE()[idx].sg_font OR
1071 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
1072
1073 HL_TABLE()[idx].sg_font = NOFONT;
1074# ifdef FEAT_XFONTSET
1075 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1076# endif
1077 hl_do_font(idx, arg, is_normal_group, is_menu_group,
1078 is_tooltip_group, FALSE);
1079
1080# ifdef FEAT_XFONTSET
1081 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1082 {
1083 // New fontset was accepted. Free the old one, if there
1084 // was one.
1085 gui_mch_free_fontset(temp_sg_fontset);
1086 vim_free(HL_TABLE()[idx].sg_font_name);
1087 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1088 did_change = TRUE;
1089 }
1090 else
1091 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
1092# endif
1093 if (HL_TABLE()[idx].sg_font != NOFONT)
1094 {
1095 // New font was accepted. Free the old one, if there was
1096 // one.
1097 gui_mch_free_font(temp_sg_font);
1098 vim_free(HL_TABLE()[idx].sg_font_name);
1099 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1100 did_change = TRUE;
1101 }
1102 else
1103 HL_TABLE()[idx].sg_font = temp_sg_font;
1104 }
1105#endif
1106 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001107 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0
1108 || STRCMP(key, "CTERMUL") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001109 {
1110 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1111 {
1112 if (!init)
1113 HL_TABLE()[idx].sg_set |= SG_CTERM;
1114
1115 // When setting the foreground color, and previously the "bold"
1116 // flag was set for a light color, reset it now
1117 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1118 {
1119 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1120 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1121 }
1122
1123 if (VIM_ISDIGIT(*arg))
1124 color = atoi((char *)arg);
1125 else if (STRICMP(arg, "fg") == 0)
1126 {
1127 if (cterm_normal_fg_color)
1128 color = cterm_normal_fg_color - 1;
1129 else
1130 {
1131 emsg(_("E419: FG color unknown"));
1132 error = TRUE;
1133 break;
1134 }
1135 }
1136 else if (STRICMP(arg, "bg") == 0)
1137 {
1138 if (cterm_normal_bg_color > 0)
1139 color = cterm_normal_bg_color - 1;
1140 else
1141 {
1142 emsg(_("E420: BG color unknown"));
1143 error = TRUE;
1144 break;
1145 }
1146 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001147 else if (STRICMP(arg, "ul") == 0)
1148 {
1149 if (cterm_normal_ul_color > 0)
1150 color = cterm_normal_ul_color - 1;
1151 else
1152 {
1153 emsg(_("E453: UL color unknown"));
1154 error = TRUE;
1155 break;
1156 }
1157 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001158 else
1159 {
1160 int bold = MAYBE;
1161
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001162 // reduce calls to STRICMP a bit, it can be slow
1163 off = TOUPPER_ASC(*arg);
K.Takataeeec2542021-06-02 13:28:16 +02001164 for (i = ARRAY_LENGTH(color_names); --i >= 0; )
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001165 if (off == color_names[i][0]
1166 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1167 break;
1168 if (i < 0)
1169 {
1170 semsg(_("E421: Color name or number not recognized: %s"), key_start);
1171 error = TRUE;
1172 break;
1173 }
1174
1175 color = lookup_color(i, key[5] == 'F', &bold);
1176
1177 // set/reset bold attribute to get light foreground
1178 // colors (on some terminals, e.g. "linux")
1179 if (bold == TRUE)
1180 {
1181 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1182 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1183 }
1184 else if (bold == FALSE)
1185 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1186 }
1187
1188 // Add one to the argument, to avoid zero. Zero is used for
1189 // "NONE", then "color" is -1.
1190 if (key[5] == 'F')
1191 {
1192 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1193 if (is_normal_group)
1194 {
1195 cterm_normal_fg_color = color + 1;
1196 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
1197#ifdef FEAT_GUI
1198 // Don't do this if the GUI is used.
1199 if (!gui.in_use && !gui.starting)
1200#endif
1201 {
1202 must_redraw = CLEAR;
1203 if (termcap_active && color >= 0)
1204 term_fg_color(color);
1205 }
1206 }
1207 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001208 else if (key[5] == 'B')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001209 {
1210 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1211 if (is_normal_group)
1212 {
1213 cterm_normal_bg_color = color + 1;
1214#ifdef FEAT_GUI
1215 // Don't mess with 'background' if the GUI is used.
1216 if (!gui.in_use && !gui.starting)
1217#endif
1218 {
1219 must_redraw = CLEAR;
1220 if (color >= 0)
1221 {
1222 int dark = -1;
1223
1224 if (termcap_active)
1225 term_bg_color(color);
1226 if (t_colors < 16)
1227 dark = (color == 0 || color == 4);
1228 // Limit the heuristic to the standard 16 colors
1229 else if (color < 16)
1230 dark = (color < 7 || color == 8);
1231 // Set the 'background' option if the value is
1232 // wrong.
1233 if (dark != -1
1234 && dark != (*p_bg == 'd')
1235 && !option_was_set((char_u *)"bg"))
1236 {
1237 set_option_value((char_u *)"bg", 0L,
1238 (char_u *)(dark ? "dark" : "light"), 0);
1239 reset_option_was_set((char_u *)"bg");
1240 }
1241 }
1242 }
1243 }
1244 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001245 else // ctermul
1246 {
1247 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1248 if (is_normal_group)
1249 {
1250 cterm_normal_ul_color = color + 1;
1251#ifdef FEAT_GUI
1252 // Don't do this if the GUI is used.
1253 if (!gui.in_use && !gui.starting)
1254#endif
1255 {
1256 must_redraw = CLEAR;
1257 if (termcap_active && color >= 0)
1258 term_ul_color(color);
1259 }
1260 }
1261 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001262 }
1263 }
1264 else if (STRCMP(key, "GUIFG") == 0)
1265 {
1266#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1267 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
1268
1269 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1270 {
1271 if (!init)
1272 HL_TABLE()[idx].sg_set |= SG_GUI;
1273
1274# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1275 // In GUI guifg colors are only used when recognized
1276 i = color_name2handle(arg);
1277 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1278 {
1279 HL_TABLE()[idx].sg_gui_fg = i;
1280# endif
1281 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1282 {
1283 vim_free(*namep);
1284 if (STRCMP(arg, "NONE") != 0)
1285 *namep = vim_strsave(arg);
1286 else
1287 *namep = NULL;
1288 did_change = TRUE;
1289 }
1290# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1291# ifdef FEAT_GUI_X11
1292 if (is_menu_group && gui.menu_fg_pixel != i)
1293 {
1294 gui.menu_fg_pixel = i;
1295 do_colors = TRUE;
1296 }
1297 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1298 {
1299 gui.scroll_fg_pixel = i;
1300 do_colors = TRUE;
1301 }
1302# ifdef FEAT_BEVAL_GUI
1303 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1304 {
1305 gui.tooltip_fg_pixel = i;
1306 do_colors = TRUE;
1307 }
1308# endif
1309# endif
1310 }
1311# endif
1312 }
1313#endif
1314 }
1315 else if (STRCMP(key, "GUIBG") == 0)
1316 {
1317#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1318 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
1319
1320 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1321 {
1322 if (!init)
1323 HL_TABLE()[idx].sg_set |= SG_GUI;
1324
1325# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1326 // In GUI guifg colors are only used when recognized
1327 i = color_name2handle(arg);
1328 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1329 {
1330 HL_TABLE()[idx].sg_gui_bg = i;
1331# endif
1332 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1333 {
1334 vim_free(*namep);
1335 if (STRCMP(arg, "NONE") != 0)
1336 *namep = vim_strsave(arg);
1337 else
1338 *namep = NULL;
1339 did_change = TRUE;
1340 }
1341# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1342# ifdef FEAT_GUI_X11
1343 if (is_menu_group && gui.menu_bg_pixel != i)
1344 {
1345 gui.menu_bg_pixel = i;
1346 do_colors = TRUE;
1347 }
1348 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1349 {
1350 gui.scroll_bg_pixel = i;
1351 do_colors = TRUE;
1352 }
1353# ifdef FEAT_BEVAL_GUI
1354 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1355 {
1356 gui.tooltip_bg_pixel = i;
1357 do_colors = TRUE;
1358 }
1359# endif
1360# endif
1361 }
1362# endif
1363 }
1364#endif
1365 }
1366 else if (STRCMP(key, "GUISP") == 0)
1367 {
1368#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1369 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
1370
1371 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1372 {
1373 if (!init)
1374 HL_TABLE()[idx].sg_set |= SG_GUI;
1375
Bram Moolenaare023e882020-05-31 16:42:30 +02001376# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1377 // In GUI guisp colors are only used when recognized
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001378 i = color_name2handle(arg);
Bram Moolenaare023e882020-05-31 16:42:30 +02001379 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001380 {
1381 HL_TABLE()[idx].sg_gui_sp = i;
1382# endif
1383 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1384 {
1385 vim_free(*namep);
1386 if (STRCMP(arg, "NONE") != 0)
1387 *namep = vim_strsave(arg);
1388 else
1389 *namep = NULL;
1390 did_change = TRUE;
1391 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001392# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001393 }
1394# endif
1395 }
1396#endif
1397 }
1398 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1399 {
1400 char_u buf[100];
1401 char_u *tname;
1402
1403 if (!init)
1404 HL_TABLE()[idx].sg_set |= SG_TERM;
1405
1406 /*
1407 * The "start" and "stop" arguments can be a literal escape
1408 * sequence, or a comma separated list of terminal codes.
1409 */
1410 if (STRNCMP(arg, "t_", 2) == 0)
1411 {
1412 off = 0;
1413 buf[0] = 0;
1414 while (arg[off] != NUL)
1415 {
1416 // Isolate one termcap name
1417 for (len = 0; arg[off + len] &&
1418 arg[off + len] != ','; ++len)
1419 ;
1420 tname = vim_strnsave(arg + off, len);
1421 if (tname == NULL) // out of memory
1422 {
1423 error = TRUE;
1424 break;
1425 }
1426 // lookup the escape sequence for the item
1427 p = get_term_code(tname);
1428 vim_free(tname);
1429 if (p == NULL) // ignore non-existing things
1430 p = (char_u *)"";
1431
1432 // Append it to the already found stuff
1433 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1434 {
1435 semsg(_("E422: terminal code too long: %s"), arg);
1436 error = TRUE;
1437 break;
1438 }
1439 STRCAT(buf, p);
1440
1441 // Advance to the next item
1442 off += len;
1443 if (arg[off] == ',') // another one follows
1444 ++off;
1445 }
1446 }
1447 else
1448 {
1449 /*
1450 * Copy characters from arg[] to buf[], translating <> codes.
1451 */
1452 for (p = arg, off = 0; off < 100 - 6 && *p; )
1453 {
Bram Moolenaarebe9d342020-05-30 21:52:54 +02001454 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001455 if (len > 0) // recognized special char
1456 off += len;
1457 else // copy as normal char
1458 buf[off++] = *p++;
1459 }
1460 buf[off] = NUL;
1461 }
1462 if (error)
1463 break;
1464
1465 if (STRCMP(buf, "NONE") == 0) // resetting the value
1466 p = NULL;
1467 else
1468 p = vim_strsave(buf);
1469 if (key[2] == 'A')
1470 {
1471 vim_free(HL_TABLE()[idx].sg_start);
1472 HL_TABLE()[idx].sg_start = p;
1473 }
1474 else
1475 {
1476 vim_free(HL_TABLE()[idx].sg_stop);
1477 HL_TABLE()[idx].sg_stop = p;
1478 }
1479 }
1480 else
1481 {
1482 semsg(_("E423: Illegal argument: %s"), key_start);
1483 error = TRUE;
1484 break;
1485 }
1486 HL_TABLE()[idx].sg_cleared = FALSE;
1487
1488 /*
1489 * When highlighting has been given for a group, don't link it.
1490 */
1491 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1492 HL_TABLE()[idx].sg_link = 0;
1493
1494 /*
1495 * Continue with next argument.
1496 */
1497 linep = skipwhite(linep);
1498 }
1499
1500 /*
1501 * If there is an error, and it's a new entry, remove it from the table.
1502 */
1503 if (error && idx == highlight_ga.ga_len)
1504 syn_unadd_group();
1505 else
1506 {
1507 if (is_normal_group)
1508 {
1509 HL_TABLE()[idx].sg_term_attr = 0;
1510 HL_TABLE()[idx].sg_cterm_attr = 0;
1511#ifdef FEAT_GUI
1512 HL_TABLE()[idx].sg_gui_attr = 0;
1513 /*
1514 * Need to update all groups, because they might be using "bg"
1515 * and/or "fg", which have been changed now.
1516 */
1517#endif
1518#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1519 if (USE_24BIT)
1520 {
1521 highlight_gui_started();
1522 did_highlight_changed = TRUE;
1523 redraw_all_later(NOT_VALID);
1524 }
1525#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001526#ifdef FEAT_VTP
1527 control_console_color_rgb();
1528#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001529 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001530#ifdef FEAT_GUI_X11
1531# ifdef FEAT_MENU
1532 else if (is_menu_group)
1533 {
1534 if (gui.in_use && do_colors)
1535 gui_mch_new_menu_colors();
1536 }
1537# endif
1538 else if (is_scrollbar_group)
1539 {
1540 if (gui.in_use && do_colors)
1541 gui_new_scrollbar_colors();
1542 else
1543 set_hl_attr(idx);
1544 }
1545# ifdef FEAT_BEVAL_GUI
1546 else if (is_tooltip_group)
1547 {
1548 if (gui.in_use && do_colors)
1549 gui_mch_new_tooltip_colors();
1550 }
1551# endif
1552#endif
1553 else
1554 set_hl_attr(idx);
1555#ifdef FEAT_EVAL
1556 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001557 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001558#endif
1559 }
1560
1561 vim_free(key);
1562 vim_free(arg);
1563
1564 // Only call highlight_changed() once, after a sequence of highlight
1565 // commands, and only if an attribute actually changed.
1566 if ((did_change
1567 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1568#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1569 && !did_highlight_changed
1570#endif
1571 )
1572 {
1573 // Do not trigger a redraw when highlighting is changed while
1574 // redrawing. This may happen when evaluating 'statusline' changes the
1575 // StatusLine group.
1576 if (!updating_screen)
1577 redraw_all_later(NOT_VALID);
1578 need_highlight_changed = TRUE;
1579 }
1580}
1581
1582#if defined(EXITFREE) || defined(PROTO)
1583 void
1584free_highlight(void)
1585{
1586 int i;
1587
1588 for (i = 0; i < highlight_ga.ga_len; ++i)
1589 {
1590 highlight_clear(i);
1591 vim_free(HL_TABLE()[i].sg_name);
1592 vim_free(HL_TABLE()[i].sg_name_u);
1593 }
1594 ga_clear(&highlight_ga);
1595}
1596#endif
1597
1598/*
1599 * Reset the cterm colors to what they were before Vim was started, if
1600 * possible. Otherwise reset them to zero.
1601 */
1602 void
1603restore_cterm_colors(void)
1604{
1605#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1606 // Since t_me has been set, this probably means that the user
1607 // wants to use this as default colors. Need to reset default
1608 // background/foreground colors.
1609 mch_set_normal_colors();
1610#else
1611# ifdef VIMDLL
1612 if (!gui.in_use)
1613 {
1614 mch_set_normal_colors();
1615 return;
1616 }
1617# endif
1618 cterm_normal_fg_color = 0;
1619 cterm_normal_fg_bold = 0;
1620 cterm_normal_bg_color = 0;
1621# ifdef FEAT_TERMGUICOLORS
1622 cterm_normal_fg_gui_color = INVALCOLOR;
1623 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001624 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001625# endif
1626#endif
1627}
1628
1629/*
1630 * Return TRUE if highlight group "idx" has any settings.
1631 * When "check_link" is TRUE also check for an existing link.
1632 */
1633 static int
1634hl_has_settings(int idx, int check_link)
1635{
Bram Moolenaar05eb5b92020-09-16 15:43:21 +02001636 return HL_TABLE()[idx].sg_cleared == 0
1637 && ( HL_TABLE()[idx].sg_term_attr != 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001638 || HL_TABLE()[idx].sg_cterm_attr != 0
1639 || HL_TABLE()[idx].sg_cterm_fg != 0
1640 || HL_TABLE()[idx].sg_cterm_bg != 0
1641#ifdef FEAT_GUI
1642 || HL_TABLE()[idx].sg_gui_attr != 0
1643 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1644 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1645 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1646 || HL_TABLE()[idx].sg_font_name != NULL
1647#endif
1648 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1649}
1650
1651/*
1652 * Clear highlighting for one group.
1653 */
1654 static void
1655highlight_clear(int idx)
1656{
1657 HL_TABLE()[idx].sg_cleared = TRUE;
1658
1659 HL_TABLE()[idx].sg_term = 0;
1660 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1661 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1662 HL_TABLE()[idx].sg_term_attr = 0;
1663 HL_TABLE()[idx].sg_cterm = 0;
1664 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1665 HL_TABLE()[idx].sg_cterm_fg = 0;
1666 HL_TABLE()[idx].sg_cterm_bg = 0;
1667 HL_TABLE()[idx].sg_cterm_attr = 0;
1668#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1669 HL_TABLE()[idx].sg_gui = 0;
1670 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1671 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1672 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1673#endif
1674#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1675 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1676 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001677 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001678#endif
1679#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001680 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1681 HL_TABLE()[idx].sg_font = NOFONT;
1682# ifdef FEAT_XFONTSET
1683 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1684 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1685# endif
1686 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1687 HL_TABLE()[idx].sg_gui_attr = 0;
1688#endif
Bram Moolenaare8df0102020-09-18 19:40:45 +02001689 // Restore default link and context if they exist. Otherwise clears.
Bram Moolenaar213da552020-09-17 19:59:26 +02001690 HL_TABLE()[idx].sg_link = HL_TABLE()[idx].sg_deflink;
Bram Moolenaare8df0102020-09-18 19:40:45 +02001691#ifdef FEAT_EVAL
1692 // Since we set the default link, set the location to where the default
1693 // link was set.
1694 HL_TABLE()[idx].sg_script_ctx = HL_TABLE()[idx].sg_deflink_sctx;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001695#endif
1696}
1697
1698#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1699/*
1700 * Set the normal foreground and background colors according to the "Normal"
1701 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1702 * "Tooltip" colors.
1703 */
1704 void
1705set_normal_colors(void)
1706{
1707# ifdef FEAT_GUI
1708# ifdef FEAT_TERMGUICOLORS
1709 if (gui.in_use)
1710# endif
1711 {
1712 if (set_group_colors((char_u *)"Normal",
1713 &gui.norm_pixel, &gui.back_pixel,
1714 FALSE, TRUE, FALSE))
1715 {
1716 gui_mch_new_colors();
1717 must_redraw = CLEAR;
1718 }
1719# ifdef FEAT_GUI_X11
1720 if (set_group_colors((char_u *)"Menu",
1721 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1722 TRUE, FALSE, FALSE))
1723 {
1724# ifdef FEAT_MENU
1725 gui_mch_new_menu_colors();
1726# endif
1727 must_redraw = CLEAR;
1728 }
1729# ifdef FEAT_BEVAL_GUI
1730 if (set_group_colors((char_u *)"Tooltip",
1731 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1732 FALSE, FALSE, TRUE))
1733 {
1734# ifdef FEAT_TOOLBAR
1735 gui_mch_new_tooltip_colors();
1736# endif
1737 must_redraw = CLEAR;
1738 }
1739# endif
1740 if (set_group_colors((char_u *)"Scrollbar",
1741 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1742 FALSE, FALSE, FALSE))
1743 {
1744 gui_new_scrollbar_colors();
1745 must_redraw = CLEAR;
1746 }
1747# endif
1748 }
1749# endif
1750# ifdef FEAT_TERMGUICOLORS
1751# ifdef FEAT_GUI
1752 else
1753# endif
1754 {
1755 int idx;
1756
1757 idx = syn_name2id((char_u *)"Normal") - 1;
1758 if (idx >= 0)
1759 {
1760 gui_do_one_color(idx, FALSE, FALSE);
1761
1762 // If the normal fg or bg color changed a complete redraw is
1763 // required.
1764 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1765 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1766 {
1767 // if the GUI color is INVALCOLOR then we use the default cterm
1768 // color
1769 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1770 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1771 must_redraw = CLEAR;
1772 }
1773 }
1774 }
1775# endif
1776}
1777#endif
1778
1779#if defined(FEAT_GUI) || defined(PROTO)
1780/*
1781 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1782 */
1783 static int
1784set_group_colors(
1785 char_u *name,
1786 guicolor_T *fgp,
1787 guicolor_T *bgp,
1788 int do_menu,
1789 int use_norm,
1790 int do_tooltip)
1791{
1792 int idx;
1793
1794 idx = syn_name2id(name) - 1;
1795 if (idx >= 0)
1796 {
1797 gui_do_one_color(idx, do_menu, do_tooltip);
1798
1799 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1800 *fgp = HL_TABLE()[idx].sg_gui_fg;
1801 else if (use_norm)
1802 *fgp = gui.def_norm_pixel;
1803 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1804 *bgp = HL_TABLE()[idx].sg_gui_bg;
1805 else if (use_norm)
1806 *bgp = gui.def_back_pixel;
1807 return TRUE;
1808 }
1809 return FALSE;
1810}
1811
1812/*
1813 * Get the font of the "Normal" group.
1814 * Returns "" when it's not found or not set.
1815 */
1816 char_u *
1817hl_get_font_name(void)
1818{
1819 int id;
1820 char_u *s;
1821
1822 id = syn_name2id((char_u *)"Normal");
1823 if (id > 0)
1824 {
1825 s = HL_TABLE()[id - 1].sg_font_name;
1826 if (s != NULL)
1827 return s;
1828 }
1829 return (char_u *)"";
1830}
1831
1832/*
1833 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
1834 * actually chosen to be used.
1835 */
1836 void
1837hl_set_font_name(char_u *font_name)
1838{
1839 int id;
1840
1841 id = syn_name2id((char_u *)"Normal");
1842 if (id > 0)
1843 {
1844 vim_free(HL_TABLE()[id - 1].sg_font_name);
1845 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
1846 }
1847}
1848
1849/*
1850 * Set background color for "Normal" group. Called by gui_set_bg_color()
1851 * when the color is known.
1852 */
1853 void
1854hl_set_bg_color_name(
1855 char_u *name) // must have been allocated
1856{
1857 int id;
1858
1859 if (name != NULL)
1860 {
1861 id = syn_name2id((char_u *)"Normal");
1862 if (id > 0)
1863 {
1864 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
1865 HL_TABLE()[id - 1].sg_gui_bg_name = name;
1866 }
1867 }
1868}
1869
1870/*
1871 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
1872 * when the color is known.
1873 */
1874 void
1875hl_set_fg_color_name(
1876 char_u *name) // must have been allocated
1877{
1878 int id;
1879
1880 if (name != NULL)
1881 {
1882 id = syn_name2id((char_u *)"Normal");
1883 if (id > 0)
1884 {
1885 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
1886 HL_TABLE()[id - 1].sg_gui_fg_name = name;
1887 }
1888 }
1889}
1890
1891/*
1892 * Return the handle for a font name.
1893 * Returns NOFONT when failed.
1894 */
1895 static GuiFont
1896font_name2handle(char_u *name)
1897{
1898 if (STRCMP(name, "NONE") == 0)
1899 return NOFONT;
1900
1901 return gui_mch_get_font(name, TRUE);
1902}
1903
1904# ifdef FEAT_XFONTSET
1905/*
1906 * Return the handle for a fontset name.
1907 * Returns NOFONTSET when failed.
1908 */
1909 static GuiFontset
1910fontset_name2handle(char_u *name, int fixed_width)
1911{
1912 if (STRCMP(name, "NONE") == 0)
1913 return NOFONTSET;
1914
1915 return gui_mch_get_fontset(name, TRUE, fixed_width);
1916}
1917# endif
1918
1919/*
1920 * Get the font or fontset for one highlight group.
1921 */
1922 static void
1923hl_do_font(
1924 int idx,
1925 char_u *arg,
1926 int do_normal, // set normal font
1927 int do_menu UNUSED, // set menu font
1928 int do_tooltip UNUSED, // set tooltip font
1929 int free_font) // free current font/fontset
1930{
1931# ifdef FEAT_XFONTSET
1932 // If 'guifontset' is not empty, first try using the name as a
1933 // fontset. If that doesn't work, use it as a font name.
1934 if (*p_guifontset != NUL
1935# ifdef FONTSET_ALWAYS
1936 || do_menu
1937# endif
1938# ifdef FEAT_BEVAL_TIP
1939 // In Athena & Motif, the Tooltip highlight group is always a fontset
1940 || do_tooltip
1941# endif
1942 )
1943 {
1944 if (free_font)
1945 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1946 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
1947# ifdef FONTSET_ALWAYS
1948 || do_menu
1949# endif
1950# ifdef FEAT_BEVAL_TIP
1951 || do_tooltip
1952# endif
1953 );
1954 }
1955 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1956 {
1957 // If it worked and it's the Normal group, use it as the normal
1958 // fontset. Same for the Menu group.
1959 if (do_normal)
1960 gui_init_font(arg, TRUE);
1961# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1962 if (do_menu)
1963 {
1964# ifdef FONTSET_ALWAYS
1965 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
1966# else
1967 // YIKES! This is a bug waiting to crash the program
1968 gui.menu_font = HL_TABLE()[idx].sg_fontset;
1969# endif
1970 gui_mch_new_menu_font();
1971 }
1972# ifdef FEAT_BEVAL_GUI
1973 if (do_tooltip)
1974 {
1975 // The Athena widget set cannot currently handle switching between
1976 // displaying a single font and a fontset.
1977 // If the XtNinternational resource is set to True at widget
1978 // creation, then a fontset is always used, otherwise an
1979 // XFontStruct is used.
1980 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
1981 gui_mch_new_tooltip_font();
1982 }
1983# endif
1984# endif
1985 }
1986 else
1987# endif
1988 {
1989 if (free_font)
1990 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1991 HL_TABLE()[idx].sg_font = font_name2handle(arg);
1992 // If it worked and it's the Normal group, use it as the
1993 // normal font. Same for the Menu group.
1994 if (HL_TABLE()[idx].sg_font != NOFONT)
1995 {
1996 if (do_normal)
1997 gui_init_font(arg, FALSE);
1998#ifndef FONTSET_ALWAYS
1999# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
2000 if (do_menu)
2001 {
2002 gui.menu_font = HL_TABLE()[idx].sg_font;
2003 gui_mch_new_menu_font();
2004 }
2005# endif
2006#endif
2007 }
2008 }
2009}
2010
2011#endif // FEAT_GUI
2012
2013#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2014/*
2015 * Return the handle for a color name.
2016 * Returns INVALCOLOR when failed.
2017 */
2018 guicolor_T
2019color_name2handle(char_u *name)
2020{
2021 if (STRCMP(name, "NONE") == 0)
2022 return INVALCOLOR;
2023
2024 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2025 {
2026#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2027 if (gui.in_use)
2028#endif
2029#ifdef FEAT_GUI
2030 return gui.norm_pixel;
2031#endif
2032#ifdef FEAT_TERMGUICOLORS
2033 if (cterm_normal_fg_gui_color != INVALCOLOR)
2034 return cterm_normal_fg_gui_color;
2035 // Guess that the foreground is black or white.
2036 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2037#endif
2038 }
2039 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2040 {
2041#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2042 if (gui.in_use)
2043#endif
2044#ifdef FEAT_GUI
2045 return gui.back_pixel;
2046#endif
2047#ifdef FEAT_TERMGUICOLORS
2048 if (cterm_normal_bg_gui_color != INVALCOLOR)
2049 return cterm_normal_bg_gui_color;
2050 // Guess that the background is white or black.
2051 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2052#endif
2053 }
2054
2055 return GUI_GET_COLOR(name);
2056}
2057#endif
2058
2059/*
2060 * Table with the specifications for an attribute number.
2061 * Note that this table is used by ALL buffers. This is required because the
2062 * GUI can redraw at any time for any buffer.
2063 */
2064static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2065
2066#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2067
2068static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2069
2070#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2071
2072#ifdef FEAT_GUI
2073static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2074
2075#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2076#endif
2077
2078/*
2079 * Return the attr number for a set of colors and font.
2080 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2081 * if the combination is new.
2082 * Return 0 for error (no more room).
2083 */
2084 static int
2085get_attr_entry(garray_T *table, attrentry_T *aep)
2086{
2087 int i;
2088 attrentry_T *taep;
2089 static int recursive = FALSE;
2090
2091 /*
2092 * Init the table, in case it wasn't done yet.
2093 */
2094 table->ga_itemsize = sizeof(attrentry_T);
2095 table->ga_growsize = 7;
2096
2097 /*
2098 * Try to find an entry with the same specifications.
2099 */
2100 for (i = 0; i < table->ga_len; ++i)
2101 {
2102 taep = &(((attrentry_T *)table->ga_data)[i]);
2103 if ( aep->ae_attr == taep->ae_attr
2104 && (
2105#ifdef FEAT_GUI
2106 (table == &gui_attr_table
2107 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2108 && aep->ae_u.gui.bg_color
2109 == taep->ae_u.gui.bg_color
2110 && aep->ae_u.gui.sp_color
2111 == taep->ae_u.gui.sp_color
2112 && aep->ae_u.gui.font == taep->ae_u.gui.font
2113# ifdef FEAT_XFONTSET
2114 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2115# endif
2116 ))
2117 ||
2118#endif
2119 (table == &term_attr_table
2120 && (aep->ae_u.term.start == NULL)
2121 == (taep->ae_u.term.start == NULL)
2122 && (aep->ae_u.term.start == NULL
2123 || STRCMP(aep->ae_u.term.start,
2124 taep->ae_u.term.start) == 0)
2125 && (aep->ae_u.term.stop == NULL)
2126 == (taep->ae_u.term.stop == NULL)
2127 && (aep->ae_u.term.stop == NULL
2128 || STRCMP(aep->ae_u.term.stop,
2129 taep->ae_u.term.stop) == 0))
2130 || (table == &cterm_attr_table
2131 && aep->ae_u.cterm.fg_color
2132 == taep->ae_u.cterm.fg_color
2133 && aep->ae_u.cterm.bg_color
2134 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002135 && aep->ae_u.cterm.ul_color
2136 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002137#ifdef FEAT_TERMGUICOLORS
2138 && aep->ae_u.cterm.fg_rgb
2139 == taep->ae_u.cterm.fg_rgb
2140 && aep->ae_u.cterm.bg_rgb
2141 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002142 && aep->ae_u.cterm.ul_rgb
2143 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002144#endif
2145 )))
2146
2147 return i + ATTR_OFF;
2148 }
2149
2150 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2151 {
2152 /*
2153 * Running out of attribute entries! remove all attributes, and
2154 * compute new ones for all groups.
2155 * When called recursively, we are really out of numbers.
2156 */
2157 if (recursive)
2158 {
2159 emsg(_("E424: Too many different highlighting attributes in use"));
2160 return 0;
2161 }
2162 recursive = TRUE;
2163
2164 clear_hl_tables();
2165
2166 must_redraw = CLEAR;
2167
2168 for (i = 0; i < highlight_ga.ga_len; ++i)
2169 set_hl_attr(i);
2170
2171 recursive = FALSE;
2172 }
2173
2174 /*
2175 * This is a new combination of colors and font, add an entry.
2176 */
2177 if (ga_grow(table, 1) == FAIL)
2178 return 0;
2179
2180 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002181 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002182 taep->ae_attr = aep->ae_attr;
2183#ifdef FEAT_GUI
2184 if (table == &gui_attr_table)
2185 {
2186 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2187 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2188 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2189 taep->ae_u.gui.font = aep->ae_u.gui.font;
2190# ifdef FEAT_XFONTSET
2191 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2192# endif
2193 }
2194#endif
2195 if (table == &term_attr_table)
2196 {
2197 if (aep->ae_u.term.start == NULL)
2198 taep->ae_u.term.start = NULL;
2199 else
2200 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2201 if (aep->ae_u.term.stop == NULL)
2202 taep->ae_u.term.stop = NULL;
2203 else
2204 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2205 }
2206 else if (table == &cterm_attr_table)
2207 {
2208 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2209 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002210 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002211#ifdef FEAT_TERMGUICOLORS
2212 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2213 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002214 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002215#endif
2216 }
2217 ++table->ga_len;
2218 return (table->ga_len - 1 + ATTR_OFF);
2219}
2220
2221#if defined(FEAT_TERMINAL) || defined(PROTO)
2222/*
2223 * Get an attribute index for a cterm entry.
2224 * Uses an existing entry when possible or adds one when needed.
2225 */
2226 int
2227get_cterm_attr_idx(int attr, int fg, int bg)
2228{
2229 attrentry_T at_en;
2230
Bram Moolenaara80faa82020-04-12 19:37:17 +02002231 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002232#ifdef FEAT_TERMGUICOLORS
2233 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2234 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002235 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002236#endif
2237 at_en.ae_attr = attr;
2238 at_en.ae_u.cterm.fg_color = fg;
2239 at_en.ae_u.cterm.bg_color = bg;
Bram Moolenaarcc836552020-06-03 10:04:49 +02002240 at_en.ae_u.cterm.ul_color = 0;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002241 return get_attr_entry(&cterm_attr_table, &at_en);
2242}
2243#endif
2244
2245#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2246/*
2247 * Get an attribute index for a 'termguicolors' entry.
2248 * Uses an existing entry when possible or adds one when needed.
2249 */
2250 int
2251get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2252{
2253 attrentry_T at_en;
2254
Bram Moolenaara80faa82020-04-12 19:37:17 +02002255 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002256 at_en.ae_attr = attr;
2257 if (fg == INVALCOLOR && bg == INVALCOLOR)
2258 {
2259 // If both GUI colors are not set fall back to the cterm colors. Helps
2260 // if the GUI only has an attribute, such as undercurl.
2261 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2262 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2263 }
2264 else
2265 {
2266 at_en.ae_u.cterm.fg_rgb = fg;
2267 at_en.ae_u.cterm.bg_rgb = bg;
2268 }
Bram Moolenaar1e5f8f62020-06-02 23:18:24 +02002269 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002270 return get_attr_entry(&cterm_attr_table, &at_en);
2271}
2272#endif
2273
2274#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2275/*
2276 * Get an attribute index for a cterm entry.
2277 * Uses an existing entry when possible or adds one when needed.
2278 */
2279 int
2280get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2281{
2282 attrentry_T at_en;
2283
Bram Moolenaara80faa82020-04-12 19:37:17 +02002284 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002285 at_en.ae_attr = attr;
2286 at_en.ae_u.gui.fg_color = fg;
2287 at_en.ae_u.gui.bg_color = bg;
2288 return get_attr_entry(&gui_attr_table, &at_en);
2289}
2290#endif
2291
2292/*
2293 * Clear all highlight tables.
2294 */
2295 void
2296clear_hl_tables(void)
2297{
2298 int i;
2299 attrentry_T *taep;
2300
2301#ifdef FEAT_GUI
2302 ga_clear(&gui_attr_table);
2303#endif
2304 for (i = 0; i < term_attr_table.ga_len; ++i)
2305 {
2306 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2307 vim_free(taep->ae_u.term.start);
2308 vim_free(taep->ae_u.term.stop);
2309 }
2310 ga_clear(&term_attr_table);
2311 ga_clear(&cterm_attr_table);
2312}
2313
2314/*
2315 * Combine special attributes (e.g., for spelling) with other attributes
2316 * (e.g., for syntax highlighting).
2317 * "prim_attr" overrules "char_attr".
2318 * This creates a new group when required.
2319 * Since we expect there to be few spelling mistakes we don't cache the
2320 * result.
2321 * Return the resulting attributes.
2322 */
2323 int
2324hl_combine_attr(int char_attr, int prim_attr)
2325{
2326 attrentry_T *char_aep = NULL;
2327 attrentry_T *spell_aep;
2328 attrentry_T new_en;
2329
2330 if (char_attr == 0)
2331 return prim_attr;
2332 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2333 return ATTR_COMBINE(char_attr, prim_attr);
2334#ifdef FEAT_GUI
2335 if (gui.in_use)
2336 {
2337 if (char_attr > HL_ALL)
2338 char_aep = syn_gui_attr2entry(char_attr);
2339 if (char_aep != NULL)
2340 new_en = *char_aep;
2341 else
2342 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002343 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002344 new_en.ae_u.gui.fg_color = INVALCOLOR;
2345 new_en.ae_u.gui.bg_color = INVALCOLOR;
2346 new_en.ae_u.gui.sp_color = INVALCOLOR;
2347 if (char_attr <= HL_ALL)
2348 new_en.ae_attr = char_attr;
2349 }
2350
2351 if (prim_attr <= HL_ALL)
2352 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2353 else
2354 {
2355 spell_aep = syn_gui_attr2entry(prim_attr);
2356 if (spell_aep != NULL)
2357 {
2358 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2359 spell_aep->ae_attr);
2360 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2361 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2362 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2363 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2364 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2365 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2366 if (spell_aep->ae_u.gui.font != NOFONT)
2367 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2368# ifdef FEAT_XFONTSET
2369 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2370 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2371# endif
2372 }
2373 }
2374 return get_attr_entry(&gui_attr_table, &new_en);
2375 }
2376#endif
2377
2378 if (IS_CTERM)
2379 {
2380 if (char_attr > HL_ALL)
2381 char_aep = syn_cterm_attr2entry(char_attr);
2382 if (char_aep != NULL)
2383 new_en = *char_aep;
2384 else
2385 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002386 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002387#ifdef FEAT_TERMGUICOLORS
2388 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2389 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002390 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002391#endif
2392 if (char_attr <= HL_ALL)
2393 new_en.ae_attr = char_attr;
2394 }
2395
2396 if (prim_attr <= HL_ALL)
2397 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2398 else
2399 {
2400 spell_aep = syn_cterm_attr2entry(prim_attr);
2401 if (spell_aep != NULL)
2402 {
2403 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2404 spell_aep->ae_attr);
2405 if (spell_aep->ae_u.cterm.fg_color > 0)
2406 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2407 if (spell_aep->ae_u.cterm.bg_color > 0)
2408 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002409 if (spell_aep->ae_u.cterm.ul_color > 0)
2410 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002411#ifdef FEAT_TERMGUICOLORS
2412 // If both fg and bg are not set fall back to cterm colors.
2413 // Helps for SpellBad which uses undercurl in the GUI.
2414 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2415 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2416 {
2417 if (spell_aep->ae_u.cterm.fg_color > 0)
2418 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2419 if (spell_aep->ae_u.cterm.bg_color > 0)
2420 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2421 }
2422 else
2423 {
2424 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2425 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2426 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2427 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2428 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002429 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2430 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002431#endif
2432 }
2433 }
2434 return get_attr_entry(&cterm_attr_table, &new_en);
2435 }
2436
2437 if (char_attr > HL_ALL)
2438 char_aep = syn_term_attr2entry(char_attr);
2439 if (char_aep != NULL)
2440 new_en = *char_aep;
2441 else
2442 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002443 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002444 if (char_attr <= HL_ALL)
2445 new_en.ae_attr = char_attr;
2446 }
2447
2448 if (prim_attr <= HL_ALL)
2449 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2450 else
2451 {
2452 spell_aep = syn_term_attr2entry(prim_attr);
2453 if (spell_aep != NULL)
2454 {
2455 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2456 if (spell_aep->ae_u.term.start != NULL)
2457 {
2458 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2459 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2460 }
2461 }
2462 }
2463 return get_attr_entry(&term_attr_table, &new_en);
2464}
2465
2466#ifdef FEAT_GUI
2467 attrentry_T *
2468syn_gui_attr2entry(int attr)
2469{
2470 attr -= ATTR_OFF;
2471 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2472 return NULL;
2473 return &(GUI_ATTR_ENTRY(attr));
2474}
2475#endif
2476
2477/*
2478 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2479 * Only to be used when "attr" > HL_ALL.
2480 */
2481 int
2482syn_attr2attr(int attr)
2483{
2484 attrentry_T *aep;
2485
2486#ifdef FEAT_GUI
2487 if (gui.in_use)
2488 aep = syn_gui_attr2entry(attr);
2489 else
2490#endif
2491 if (IS_CTERM)
2492 aep = syn_cterm_attr2entry(attr);
2493 else
2494 aep = syn_term_attr2entry(attr);
2495
2496 if (aep == NULL) // highlighting not set
2497 return 0;
2498 return aep->ae_attr;
2499}
2500
2501
2502 attrentry_T *
2503syn_term_attr2entry(int attr)
2504{
2505 attr -= ATTR_OFF;
2506 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2507 return NULL;
2508 return &(TERM_ATTR_ENTRY(attr));
2509}
2510
2511 attrentry_T *
2512syn_cterm_attr2entry(int attr)
2513{
2514 attr -= ATTR_OFF;
2515 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2516 return NULL;
2517 return &(CTERM_ATTR_ENTRY(attr));
2518}
2519
2520#define LIST_ATTR 1
2521#define LIST_STRING 2
2522#define LIST_INT 3
2523
2524 static void
2525highlight_list_one(int id)
2526{
2527 hl_group_T *sgp;
2528 int didh = FALSE;
2529
2530 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2531
2532 if (message_filtered(sgp->sg_name))
2533 return;
2534
2535 didh = highlight_list_arg(id, didh, LIST_ATTR,
2536 sgp->sg_term, NULL, "term");
2537 didh = highlight_list_arg(id, didh, LIST_STRING,
2538 0, sgp->sg_start, "start");
2539 didh = highlight_list_arg(id, didh, LIST_STRING,
2540 0, sgp->sg_stop, "stop");
2541
2542 didh = highlight_list_arg(id, didh, LIST_ATTR,
2543 sgp->sg_cterm, NULL, "cterm");
2544 didh = highlight_list_arg(id, didh, LIST_INT,
2545 sgp->sg_cterm_fg, NULL, "ctermfg");
2546 didh = highlight_list_arg(id, didh, LIST_INT,
2547 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002548 didh = highlight_list_arg(id, didh, LIST_INT,
2549 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002550
2551#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2552 didh = highlight_list_arg(id, didh, LIST_ATTR,
2553 sgp->sg_gui, NULL, "gui");
2554 didh = highlight_list_arg(id, didh, LIST_STRING,
2555 0, sgp->sg_gui_fg_name, "guifg");
2556 didh = highlight_list_arg(id, didh, LIST_STRING,
2557 0, sgp->sg_gui_bg_name, "guibg");
2558 didh = highlight_list_arg(id, didh, LIST_STRING,
2559 0, sgp->sg_gui_sp_name, "guisp");
2560#endif
2561#ifdef FEAT_GUI
2562 didh = highlight_list_arg(id, didh, LIST_STRING,
2563 0, sgp->sg_font_name, "font");
2564#endif
2565
2566 if (sgp->sg_link && !got_int)
2567 {
2568 (void)syn_list_header(didh, 9999, id);
2569 didh = TRUE;
2570 msg_puts_attr("links to", HL_ATTR(HLF_D));
2571 msg_putchar(' ');
2572 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2573 }
2574
2575 if (!didh)
2576 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2577#ifdef FEAT_EVAL
2578 if (p_verbose > 0)
2579 last_set_msg(sgp->sg_script_ctx);
2580#endif
2581}
2582
2583 static int
2584highlight_list_arg(
2585 int id,
2586 int didh,
2587 int type,
2588 int iarg,
2589 char_u *sarg,
2590 char *name)
2591{
2592 char_u buf[100];
2593 char_u *ts;
2594 int i;
2595
2596 if (got_int)
2597 return FALSE;
2598 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2599 {
2600 ts = buf;
2601 if (type == LIST_INT)
2602 sprintf((char *)buf, "%d", iarg - 1);
2603 else if (type == LIST_STRING)
2604 ts = sarg;
2605 else // type == LIST_ATTR
2606 {
2607 buf[0] = NUL;
2608 for (i = 0; hl_attr_table[i] != 0; ++i)
2609 {
2610 if (iarg & hl_attr_table[i])
2611 {
2612 if (buf[0] != NUL)
2613 vim_strcat(buf, (char_u *)",", 100);
2614 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2615 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2616 }
2617 }
2618 }
2619
2620 (void)syn_list_header(didh,
2621 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2622 didh = TRUE;
2623 if (!got_int)
2624 {
2625 if (*name != NUL)
2626 {
2627 msg_puts_attr(name, HL_ATTR(HLF_D));
2628 msg_puts_attr("=", HL_ATTR(HLF_D));
2629 }
2630 msg_outtrans(ts);
2631 }
2632 }
2633 return didh;
2634}
2635
2636#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2637/*
2638 * Return "1" if highlight group "id" has attribute "flag".
2639 * Return NULL otherwise.
2640 */
2641 char_u *
2642highlight_has_attr(
2643 int id,
2644 int flag,
2645 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2646{
2647 int attr;
2648
2649 if (id <= 0 || id > highlight_ga.ga_len)
2650 return NULL;
2651
2652#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2653 if (modec == 'g')
2654 attr = HL_TABLE()[id - 1].sg_gui;
2655 else
2656#endif
2657 if (modec == 'c')
2658 attr = HL_TABLE()[id - 1].sg_cterm;
2659 else
2660 attr = HL_TABLE()[id - 1].sg_term;
2661
2662 if (attr & flag)
2663 return (char_u *)"1";
2664 return NULL;
2665}
2666#endif
2667
2668#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2669/*
2670 * Return color name of highlight group "id".
2671 */
2672 char_u *
2673highlight_color(
2674 int id,
Bram Moolenaar391c3622020-09-29 20:59:17 +02002675 char_u *what, // "font", "fg", "bg", "sp", "ul", "fg#", "bg#" or "sp#"
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002676 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2677{
2678 static char_u name[20];
2679 int n;
2680 int fg = FALSE;
2681 int sp = FALSE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02002682 int ul = FALSE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002683 int font = FALSE;
2684
2685 if (id <= 0 || id > highlight_ga.ga_len)
2686 return NULL;
2687
2688 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2689 fg = TRUE;
2690 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2691 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2692 font = TRUE;
2693 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2694 sp = TRUE;
Bram Moolenaar391c3622020-09-29 20:59:17 +02002695 else if (TOLOWER_ASC(what[0]) == 'u' && TOLOWER_ASC(what[1]) == 'l')
2696 ul = TRUE;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002697 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2698 return NULL;
2699 if (modec == 'g')
2700 {
2701# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2702# ifdef FEAT_GUI
2703 // return font name
2704 if (font)
2705 return HL_TABLE()[id - 1].sg_font_name;
2706# endif
2707
2708 // return #RRGGBB form (only possible when GUI is running)
2709 if ((USE_24BIT) && what[2] == '#')
2710 {
2711 guicolor_T color;
2712 long_u rgb;
2713 static char_u buf[10];
2714
2715 if (fg)
2716 color = HL_TABLE()[id - 1].sg_gui_fg;
2717 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002718 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002719 else
2720 color = HL_TABLE()[id - 1].sg_gui_bg;
2721 if (color == INVALCOLOR)
2722 return NULL;
2723 rgb = (long_u)GUI_MCH_GET_RGB(color);
2724 sprintf((char *)buf, "#%02x%02x%02x",
2725 (unsigned)(rgb >> 16),
2726 (unsigned)(rgb >> 8) & 255,
2727 (unsigned)rgb & 255);
2728 return buf;
2729 }
2730# endif
2731 if (fg)
2732 return (HL_TABLE()[id - 1].sg_gui_fg_name);
2733 if (sp)
2734 return (HL_TABLE()[id - 1].sg_gui_sp_name);
2735 return (HL_TABLE()[id - 1].sg_gui_bg_name);
2736 }
2737 if (font || sp)
2738 return NULL;
2739 if (modec == 'c')
2740 {
2741 if (fg)
2742 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
Bram Moolenaar391c3622020-09-29 20:59:17 +02002743 else if (ul)
2744 n = HL_TABLE()[id - 1].sg_cterm_ul - 1;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002745 else
2746 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2747 if (n < 0)
2748 return NULL;
2749 sprintf((char *)name, "%d", n);
2750 return name;
2751 }
2752 // term doesn't have color
2753 return NULL;
2754}
2755#endif
2756
2757#if (defined(FEAT_SYN_HL) \
2758 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2759 && defined(FEAT_PRINTER)) || defined(PROTO)
2760/*
2761 * Return color name of highlight group "id" as RGB value.
2762 */
2763 long_u
2764highlight_gui_color_rgb(
2765 int id,
2766 int fg) // TRUE = fg, FALSE = bg
2767{
2768 guicolor_T color;
2769
2770 if (id <= 0 || id > highlight_ga.ga_len)
2771 return 0L;
2772
2773 if (fg)
2774 color = HL_TABLE()[id - 1].sg_gui_fg;
2775 else
2776 color = HL_TABLE()[id - 1].sg_gui_bg;
2777
2778 if (color == INVALCOLOR)
2779 return 0L;
2780
2781 return GUI_MCH_GET_RGB(color);
2782}
2783#endif
2784
2785/*
2786 * Output the syntax list header.
2787 * Return TRUE when started a new line.
2788 */
2789 int
2790syn_list_header(
2791 int did_header, // did header already
2792 int outlen, // length of string that comes
2793 int id) // highlight group id
2794{
2795 int endcol = 19;
2796 int newline = TRUE;
2797 int name_col = 0;
2798
2799 if (!did_header)
2800 {
2801 msg_putchar('\n');
2802 if (got_int)
2803 return TRUE;
2804 msg_outtrans(HL_TABLE()[id - 1].sg_name);
2805 name_col = msg_col;
2806 endcol = 15;
2807 }
2808 else if (msg_col + outlen + 1 >= Columns)
2809 {
2810 msg_putchar('\n');
2811 if (got_int)
2812 return TRUE;
2813 }
2814 else
2815 {
2816 if (msg_col >= endcol) // wrap around is like starting a new line
2817 newline = FALSE;
2818 }
2819
2820 if (msg_col >= endcol) // output at least one space
2821 endcol = msg_col + 1;
2822 if (Columns <= endcol) // avoid hang for tiny window
2823 endcol = Columns - 1;
2824
2825 msg_advance(endcol);
2826
2827 // Show "xxx" with the attributes.
2828 if (!did_header)
2829 {
2830 if (endcol == Columns - 1 && endcol <= name_col)
2831 msg_putchar(' ');
2832 msg_puts_attr("xxx", syn_id2attr(id));
2833 msg_putchar(' ');
2834 }
2835
2836 return newline;
2837}
2838
2839/*
2840 * Set the attribute numbers for a highlight group.
2841 * Called after one of the attributes has changed.
2842 */
2843 static void
2844set_hl_attr(
2845 int idx) // index in array
2846{
2847 attrentry_T at_en;
2848 hl_group_T *sgp = HL_TABLE() + idx;
2849
2850 // The "Normal" group doesn't need an attribute number
2851 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
2852 return;
2853
2854#ifdef FEAT_GUI
2855 /*
2856 * For the GUI mode: If there are other than "normal" highlighting
2857 * attributes, need to allocate an attr number.
2858 */
2859 if (sgp->sg_gui_fg == INVALCOLOR
2860 && sgp->sg_gui_bg == INVALCOLOR
2861 && sgp->sg_gui_sp == INVALCOLOR
2862 && sgp->sg_font == NOFONT
2863# ifdef FEAT_XFONTSET
2864 && sgp->sg_fontset == NOFONTSET
2865# endif
2866 )
2867 {
2868 sgp->sg_gui_attr = sgp->sg_gui;
2869 }
2870 else
2871 {
2872 at_en.ae_attr = sgp->sg_gui;
2873 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
2874 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
2875 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
2876 at_en.ae_u.gui.font = sgp->sg_font;
2877# ifdef FEAT_XFONTSET
2878 at_en.ae_u.gui.fontset = sgp->sg_fontset;
2879# endif
2880 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
2881 }
2882#endif
2883 /*
2884 * For the term mode: If there are other than "normal" highlighting
2885 * attributes, need to allocate an attr number.
2886 */
2887 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
2888 sgp->sg_term_attr = sgp->sg_term;
2889 else
2890 {
2891 at_en.ae_attr = sgp->sg_term;
2892 at_en.ae_u.term.start = sgp->sg_start;
2893 at_en.ae_u.term.stop = sgp->sg_stop;
2894 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
2895 }
2896
2897 /*
2898 * For the color term mode: If there are other than "normal"
2899 * highlighting attributes, need to allocate an attr number.
2900 */
Bram Moolenaare023e882020-05-31 16:42:30 +02002901 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002902# ifdef FEAT_TERMGUICOLORS
2903 && sgp->sg_gui_fg == INVALCOLOR
2904 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02002905 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002906# endif
2907 )
2908 sgp->sg_cterm_attr = sgp->sg_cterm;
2909 else
2910 {
2911 at_en.ae_attr = sgp->sg_cterm;
2912 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
2913 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02002914 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002915# ifdef FEAT_TERMGUICOLORS
2916# ifdef MSWIN
2917# ifdef VIMDLL
2918 // Only when not using the GUI.
2919 if (!gui.in_use && !gui.starting)
2920# endif
2921 {
2922 int id;
2923 guicolor_T fg, bg;
2924
2925 id = syn_name2id((char_u *)"Normal");
2926 if (id > 0)
2927 {
2928 syn_id2colors(id, &fg, &bg);
2929 if (sgp->sg_gui_fg == INVALCOLOR)
2930 sgp->sg_gui_fg = fg;
2931 if (sgp->sg_gui_bg == INVALCOLOR)
2932 sgp->sg_gui_bg = bg;
2933 }
2934
2935 }
2936# endif
2937 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
2938 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaarea563cc2020-06-05 19:36:57 +02002939 // Only use the underline/undercurl color when used, it may clear the
2940 // background color if not supported.
2941 if (sgp->sg_cterm & (HL_UNDERLINE | HL_UNDERCURL))
2942 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
2943 else
2944 at_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002945 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2946 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2947 {
2948 // If both fg and bg are invalid fall back to the cterm colors.
2949 // Helps when the GUI only uses an attribute, e.g. undercurl.
2950 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2951 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2952 }
2953# endif
2954 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2955 }
2956}
2957
2958/*
2959 * Lookup a highlight group name and return its ID.
2960 * If it is not found, 0 is returned.
2961 */
2962 int
2963syn_name2id(char_u *name)
2964{
2965 int i;
2966 char_u name_u[200];
2967
2968 // Avoid using stricmp() too much, it's slow on some systems
2969 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
2970 // don't deserve to be found!
2971 vim_strncpy(name_u, name, 199);
2972 vim_strup(name_u);
2973 for (i = highlight_ga.ga_len; --i >= 0; )
2974 if (HL_TABLE()[i].sg_name_u != NULL
2975 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2976 break;
2977 return i + 1;
2978}
2979
2980/*
2981 * Lookup a highlight group name and return its attributes.
2982 * Return zero if not found.
2983 */
2984 int
2985syn_name2attr(char_u *name)
2986{
2987 int id = syn_name2id(name);
2988
2989 if (id != 0)
2990 return syn_id2attr(id);
2991 return 0;
2992}
2993
2994#if defined(FEAT_EVAL) || defined(PROTO)
2995/*
2996 * Return TRUE if highlight group "name" exists.
2997 */
2998 int
2999highlight_exists(char_u *name)
3000{
3001 return (syn_name2id(name) > 0);
3002}
3003
3004# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
3005/*
3006 * Return the name of highlight group "id".
3007 * When not a valid ID return an empty string.
3008 */
3009 char_u *
3010syn_id2name(int id)
3011{
3012 if (id <= 0 || id > highlight_ga.ga_len)
3013 return (char_u *)"";
3014 return HL_TABLE()[id - 1].sg_name;
3015}
3016# endif
3017#endif
3018
3019/*
3020 * Like syn_name2id(), but take a pointer + length argument.
3021 */
3022 int
3023syn_namen2id(char_u *linep, int len)
3024{
3025 char_u *name;
3026 int id = 0;
3027
3028 name = vim_strnsave(linep, len);
3029 if (name != NULL)
3030 {
3031 id = syn_name2id(name);
3032 vim_free(name);
3033 }
3034 return id;
3035}
3036
3037/*
3038 * Find highlight group name in the table and return its ID.
3039 * The argument is a pointer to the name and the length of the name.
3040 * If it doesn't exist yet, a new entry is created.
3041 * Return 0 for failure.
3042 */
3043 int
3044syn_check_group(char_u *pp, int len)
3045{
3046 int id;
3047 char_u *name;
3048
3049 name = vim_strnsave(pp, len);
3050 if (name == NULL)
3051 return 0;
3052
3053 id = syn_name2id(name);
3054 if (id == 0) // doesn't exist yet
3055 id = syn_add_group(name);
3056 else
3057 vim_free(name);
3058 return id;
3059}
3060
3061/*
3062 * Add new highlight group and return its ID.
3063 * "name" must be an allocated string, it will be consumed.
3064 * Return 0 for failure.
3065 */
3066 static int
3067syn_add_group(char_u *name)
3068{
3069 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003070 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003071
3072 // Check that the name is ASCII letters, digits and underscore.
3073 for (p = name; *p != NUL; ++p)
3074 {
3075 if (!vim_isprintc(*p))
3076 {
3077 emsg(_("E669: Unprintable character in group name"));
3078 vim_free(name);
3079 return 0;
3080 }
3081 else if (!ASCII_ISALNUM(*p) && *p != '_')
3082 {
3083 // This is an error, but since there previously was no check only
3084 // give a warning.
3085 msg_source(HL_ATTR(HLF_W));
3086 msg(_("W18: Invalid character in group name"));
3087 break;
3088 }
3089 }
3090
3091 /*
3092 * First call for this growarray: init growing array.
3093 */
3094 if (highlight_ga.ga_data == NULL)
3095 {
3096 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3097 highlight_ga.ga_growsize = 10;
3098 }
3099
3100 if (highlight_ga.ga_len >= MAX_HL_ID)
3101 {
3102 emsg(_("E849: Too many highlight and syntax groups"));
3103 vim_free(name);
3104 return 0;
3105 }
3106
3107 /*
3108 * Make room for at least one other syntax_highlight entry.
3109 */
3110 if (ga_grow(&highlight_ga, 1) == FAIL)
3111 {
3112 vim_free(name);
3113 return 0;
3114 }
3115
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003116 name_up = vim_strsave_up(name);
3117 if (name_up == NULL)
3118 {
3119 vim_free(name);
3120 return 0;
3121 }
3122
Bram Moolenaara80faa82020-04-12 19:37:17 +02003123 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003124 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003125 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003126#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3127 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3128 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003129 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003130#endif
3131 ++highlight_ga.ga_len;
3132
3133 return highlight_ga.ga_len; // ID is index plus one
3134}
3135
3136/*
3137 * When, just after calling syn_add_group(), an error is discovered, this
3138 * function deletes the new name.
3139 */
3140 static void
3141syn_unadd_group(void)
3142{
3143 --highlight_ga.ga_len;
3144 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3145 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3146}
3147
3148/*
3149 * Translate a group ID to highlight attributes.
3150 */
3151 int
3152syn_id2attr(int hl_id)
3153{
3154 int attr;
3155 hl_group_T *sgp;
3156
3157 hl_id = syn_get_final_id(hl_id);
3158 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3159
3160#ifdef FEAT_GUI
3161 /*
3162 * Only use GUI attr when the GUI is being used.
3163 */
3164 if (gui.in_use)
3165 attr = sgp->sg_gui_attr;
3166 else
3167#endif
3168 if (IS_CTERM)
3169 attr = sgp->sg_cterm_attr;
3170 else
3171 attr = sgp->sg_term_attr;
3172
3173 return attr;
3174}
3175
3176#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3177/*
3178 * Get the GUI colors and attributes for a group ID.
3179 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3180 */
3181 int
3182syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3183{
3184 hl_group_T *sgp;
3185
3186 hl_id = syn_get_final_id(hl_id);
3187 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3188
3189 *fgp = sgp->sg_gui_fg;
3190 *bgp = sgp->sg_gui_bg;
3191 return sgp->sg_gui;
3192}
3193#endif
3194
3195#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003196 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3197 && defined(FEAT_TERMGUICOLORS)) \
3198 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003199 void
3200syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3201{
3202 hl_group_T *sgp;
3203
3204 hl_id = syn_get_final_id(hl_id);
3205 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3206 *fgp = sgp->sg_cterm_fg - 1;
3207 *bgp = sgp->sg_cterm_bg - 1;
3208}
3209#endif
3210
3211/*
3212 * Translate a group ID to the final group ID (following links).
3213 */
3214 int
3215syn_get_final_id(int hl_id)
3216{
3217 int count;
3218 hl_group_T *sgp;
3219
3220 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3221 return 0; // Can be called from eval!!
3222
3223 /*
3224 * Follow links until there is no more.
3225 * Look out for loops! Break after 100 links.
3226 */
3227 for (count = 100; --count >= 0; )
3228 {
3229 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3230 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3231 break;
3232 hl_id = sgp->sg_link;
3233 }
3234
3235 return hl_id;
3236}
3237
3238#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3239/*
3240 * Call this function just after the GUI has started.
3241 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3242 * It finds the font and color handles for the highlighting groups.
3243 */
3244 void
3245highlight_gui_started(void)
3246{
3247 int idx;
3248
3249 // First get the colors from the "Normal" and "Menu" group, if set
3250 if (USE_24BIT)
3251 set_normal_colors();
3252
3253 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3254 gui_do_one_color(idx, FALSE, FALSE);
3255
3256 highlight_changed();
3257}
3258
3259 static void
3260gui_do_one_color(
3261 int idx,
3262 int do_menu UNUSED, // TRUE: might set the menu font
3263 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3264{
3265 int didit = FALSE;
3266
3267# ifdef FEAT_GUI
3268# ifdef FEAT_TERMGUICOLORS
3269 if (gui.in_use)
3270# endif
3271 if (HL_TABLE()[idx].sg_font_name != NULL)
3272 {
3273 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3274 do_tooltip, TRUE);
3275 didit = TRUE;
3276 }
3277# endif
3278 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3279 {
3280 HL_TABLE()[idx].sg_gui_fg =
3281 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3282 didit = TRUE;
3283 }
3284 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3285 {
3286 HL_TABLE()[idx].sg_gui_bg =
3287 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3288 didit = TRUE;
3289 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003290 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3291 {
3292 HL_TABLE()[idx].sg_gui_sp =
3293 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3294 didit = TRUE;
3295 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003296 if (didit) // need to get a new attr number
3297 set_hl_attr(idx);
3298}
3299#endif
3300
3301#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3302/*
3303 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3304 */
3305 static void
3306combine_stl_hlt(
3307 int id,
3308 int id_S,
3309 int id_alt,
3310 int hlcnt,
3311 int i,
3312 int hlf,
3313 int *table)
3314{
3315 hl_group_T *hlt = HL_TABLE();
3316
3317 if (id_alt == 0)
3318 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003319 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003320 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3321 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3322# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3323 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3324# endif
3325 }
3326 else
3327 mch_memmove(&hlt[hlcnt + i],
3328 &hlt[id_alt - 1],
3329 sizeof(hl_group_T));
3330 hlt[hlcnt + i].sg_link = 0;
3331
3332 hlt[hlcnt + i].sg_term ^=
3333 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3334 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3335 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3336 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3337 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3338 hlt[hlcnt + i].sg_cterm ^=
3339 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3340 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3341 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3342 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3343 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3344# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3345 hlt[hlcnt + i].sg_gui ^=
3346 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3347# endif
3348# ifdef FEAT_GUI
3349 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3350 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3351 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3352 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3353 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3354 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3355 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3356 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3357# ifdef FEAT_XFONTSET
3358 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3359 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3360# endif
3361# endif
3362 highlight_ga.ga_len = hlcnt + i + 1;
3363 set_hl_attr(hlcnt + i); // At long last we can apply
3364 table[i] = syn_id2attr(hlcnt + i + 1);
3365}
3366#endif
3367
3368/*
3369 * Translate the 'highlight' option into attributes in highlight_attr[] and
3370 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3371 * corresponding highlights to use on top of HLF_SNC is computed.
3372 * Called only when the 'highlight' option has been changed and upon first
3373 * screen redraw after any :highlight command.
3374 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3375 */
3376 int
3377highlight_changed(void)
3378{
3379 int hlf;
3380 int i;
3381 char_u *p;
3382 int attr;
3383 char_u *end;
3384 int id;
3385#ifdef USER_HIGHLIGHT
3386 char_u userhl[30]; // use 30 to avoid compiler warning
3387# ifdef FEAT_STL_OPT
3388 int id_S = -1;
3389 int id_SNC = 0;
3390# ifdef FEAT_TERMINAL
3391 int id_ST = 0;
3392 int id_STNC = 0;
3393# endif
3394 int hlcnt;
3395# endif
3396#endif
3397 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3398
3399 need_highlight_changed = FALSE;
3400
3401 /*
3402 * Clear all attributes.
3403 */
3404 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3405 highlight_attr[hlf] = 0;
3406
3407 /*
3408 * First set all attributes to their default value.
3409 * Then use the attributes from the 'highlight' option.
3410 */
3411 for (i = 0; i < 2; ++i)
3412 {
3413 if (i)
3414 p = p_hl;
3415 else
3416 p = get_highlight_default();
3417 if (p == NULL) // just in case
3418 continue;
3419
3420 while (*p)
3421 {
3422 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3423 if (hl_flags[hlf] == *p)
3424 break;
3425 ++p;
3426 if (hlf == (int)HLF_COUNT || *p == NUL)
3427 return FAIL;
3428
3429 /*
3430 * Allow several hl_flags to be combined, like "bu" for
3431 * bold-underlined.
3432 */
3433 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003434 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003435 {
3436 if (VIM_ISWHITE(*p)) // ignore white space
3437 continue;
3438
3439 if (attr > HL_ALL) // Combination with ':' is not allowed.
3440 return FAIL;
3441
3442 switch (*p)
3443 {
3444 case 'b': attr |= HL_BOLD;
3445 break;
3446 case 'i': attr |= HL_ITALIC;
3447 break;
3448 case '-':
3449 case 'n': // no highlighting
3450 break;
3451 case 'r': attr |= HL_INVERSE;
3452 break;
3453 case 's': attr |= HL_STANDOUT;
3454 break;
3455 case 'u': attr |= HL_UNDERLINE;
3456 break;
3457 case 'c': attr |= HL_UNDERCURL;
3458 break;
3459 case 't': attr |= HL_STRIKETHROUGH;
3460 break;
3461 case ':': ++p; // highlight group name
3462 if (attr || *p == NUL) // no combinations
3463 return FAIL;
3464 end = vim_strchr(p, ',');
3465 if (end == NULL)
3466 end = p + STRLEN(p);
3467 id = syn_check_group(p, (int)(end - p));
3468 if (id == 0)
3469 return FAIL;
3470 attr = syn_id2attr(id);
3471 p = end - 1;
3472#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3473 if (hlf == (int)HLF_SNC)
3474 id_SNC = syn_get_final_id(id);
3475# ifdef FEAT_TERMINAL
3476 else if (hlf == (int)HLF_ST)
3477 id_ST = syn_get_final_id(id);
3478 else if (hlf == (int)HLF_STNC)
3479 id_STNC = syn_get_final_id(id);
3480# endif
3481 else if (hlf == (int)HLF_S)
3482 id_S = syn_get_final_id(id);
3483#endif
3484 break;
3485 default: return FAIL;
3486 }
3487 }
3488 highlight_attr[hlf] = attr;
3489
3490 p = skip_to_option_part(p); // skip comma and spaces
3491 }
3492 }
3493
3494#ifdef USER_HIGHLIGHT
3495 /*
3496 * Setup the user highlights
3497 *
3498 * Temporarily utilize 28 more hl entries:
3499 * 9 for User1-User9 combined with StatusLineNC
3500 * 9 for User1-User9 combined with StatusLineTerm
3501 * 9 for User1-User9 combined with StatusLineTermNC
3502 * 1 for StatusLine default
3503 * Have to be in there simultaneously in case of table overflows in
3504 * get_attr_entry()
3505 */
3506# ifdef FEAT_STL_OPT
3507 if (ga_grow(&highlight_ga, 28) == FAIL)
3508 return FAIL;
3509 hlcnt = highlight_ga.ga_len;
3510 if (id_S == -1)
3511 {
3512 // Make sure id_S is always valid to simplify code below. Use the last
3513 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003514 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003515 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3516 id_S = hlcnt + 19;
3517 }
3518# endif
3519 for (i = 0; i < 9; i++)
3520 {
3521 sprintf((char *)userhl, "User%d", i + 1);
3522 id = syn_name2id(userhl);
3523 if (id == 0)
3524 {
3525 highlight_user[i] = 0;
3526# ifdef FEAT_STL_OPT
3527 highlight_stlnc[i] = 0;
3528# ifdef FEAT_TERMINAL
3529 highlight_stlterm[i] = 0;
3530 highlight_stltermnc[i] = 0;
3531# endif
3532# endif
3533 }
3534 else
3535 {
3536 highlight_user[i] = syn_id2attr(id);
3537# ifdef FEAT_STL_OPT
3538 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3539 HLF_SNC, highlight_stlnc);
3540# ifdef FEAT_TERMINAL
3541 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3542 HLF_ST, highlight_stlterm);
3543 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3544 HLF_STNC, highlight_stltermnc);
3545# endif
3546# endif
3547 }
3548 }
3549# ifdef FEAT_STL_OPT
3550 highlight_ga.ga_len = hlcnt;
3551# endif
3552
3553#endif // USER_HIGHLIGHT
3554
3555 return OK;
3556}
3557
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003558static void highlight_list(void);
3559static void highlight_list_two(int cnt, int attr);
3560
3561/*
3562 * Handle command line completion for :highlight command.
3563 */
3564 void
3565set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3566{
3567 char_u *p;
3568
3569 // Default: expand group names
3570 xp->xp_context = EXPAND_HIGHLIGHT;
3571 xp->xp_pattern = arg;
3572 include_link = 2;
3573 include_default = 1;
3574
3575 // (part of) subcommand already typed
3576 if (*arg != NUL)
3577 {
3578 p = skiptowhite(arg);
3579 if (*p != NUL) // past "default" or group name
3580 {
3581 include_default = 0;
3582 if (STRNCMP("default", arg, p - arg) == 0)
3583 {
3584 arg = skipwhite(p);
3585 xp->xp_pattern = arg;
3586 p = skiptowhite(arg);
3587 }
3588 if (*p != NUL) // past group name
3589 {
3590 include_link = 0;
3591 if (arg[1] == 'i' && arg[0] == 'N')
3592 highlight_list();
3593 if (STRNCMP("link", arg, p - arg) == 0
3594 || STRNCMP("clear", arg, p - arg) == 0)
3595 {
3596 xp->xp_pattern = skipwhite(p);
3597 p = skiptowhite(xp->xp_pattern);
3598 if (*p != NUL) // past first group name
3599 {
3600 xp->xp_pattern = skipwhite(p);
3601 p = skiptowhite(xp->xp_pattern);
3602 }
3603 }
3604 if (*p != NUL) // past group name(s)
3605 xp->xp_context = EXPAND_NOTHING;
3606 }
3607 }
3608 }
3609}
3610
3611/*
3612 * List highlighting matches in a nice way.
3613 */
3614 static void
3615highlight_list(void)
3616{
3617 int i;
3618
3619 for (i = 10; --i >= 0; )
3620 highlight_list_two(i, HL_ATTR(HLF_D));
3621 for (i = 40; --i >= 0; )
3622 highlight_list_two(99, 0);
3623}
3624
3625 static void
3626highlight_list_two(int cnt, int attr)
3627{
3628 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3629 msg_clr_eos();
3630 out_flush();
3631 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3632}
3633
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003634/*
3635 * Function given to ExpandGeneric() to obtain the list of group names.
3636 */
3637 char_u *
3638get_highlight_name(expand_T *xp UNUSED, int idx)
3639{
3640 return get_highlight_name_ext(xp, idx, TRUE);
3641}
3642
3643/*
3644 * Obtain a highlight group name.
3645 * When "skip_cleared" is TRUE don't return a cleared entry.
3646 */
3647 char_u *
3648get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3649{
3650 if (idx < 0)
3651 return NULL;
3652
3653 // Items are never removed from the table, skip the ones that were
3654 // cleared.
3655 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3656 return (char_u *)"";
3657
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003658 if (idx == highlight_ga.ga_len && include_none != 0)
3659 return (char_u *)"none";
3660 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3661 return (char_u *)"default";
3662 if (idx == highlight_ga.ga_len + include_none + include_default
3663 && include_link != 0)
3664 return (char_u *)"link";
3665 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3666 && include_link != 0)
3667 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003668 if (idx >= highlight_ga.ga_len)
3669 return NULL;
3670 return HL_TABLE()[idx].sg_name;
3671}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003672
3673#if defined(FEAT_GUI) || defined(PROTO)
3674/*
3675 * Free all the highlight group fonts.
3676 * Used when quitting for systems which need it.
3677 */
3678 void
3679free_highlight_fonts(void)
3680{
3681 int idx;
3682
3683 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3684 {
3685 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3686 HL_TABLE()[idx].sg_font = NOFONT;
3687# ifdef FEAT_XFONTSET
3688 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3689 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3690# endif
3691 }
3692
3693 gui_mch_free_font(gui.norm_font);
3694# ifdef FEAT_XFONTSET
3695 gui_mch_free_fontset(gui.fontset);
3696# endif
3697# ifndef FEAT_GUI_GTK
3698 gui_mch_free_font(gui.bold_font);
3699 gui_mch_free_font(gui.ital_font);
3700 gui_mch_free_font(gui.boldital_font);
3701# endif
3702}
3703#endif