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