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