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