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