blob: db65ffca71f0e74d87ad409fd3850d6533d26d10 [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
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200160// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200161static char *(highlight_init_light[]) = {
162 CENT("Directory term=bold ctermfg=DarkBlue",
163 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
164 CENT("LineNr term=underline ctermfg=Brown",
165 "LineNr term=underline ctermfg=Brown guifg=Brown"),
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
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200253// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200254static 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
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003502static void highlight_list(void);
3503static void highlight_list_two(int cnt, int attr);
3504
3505/*
3506 * Handle command line completion for :highlight command.
3507 */
3508 void
3509set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3510{
3511 char_u *p;
3512
3513 // Default: expand group names
3514 xp->xp_context = EXPAND_HIGHLIGHT;
3515 xp->xp_pattern = arg;
3516 include_link = 2;
3517 include_default = 1;
3518
3519 // (part of) subcommand already typed
3520 if (*arg != NUL)
3521 {
3522 p = skiptowhite(arg);
3523 if (*p != NUL) // past "default" or group name
3524 {
3525 include_default = 0;
3526 if (STRNCMP("default", arg, p - arg) == 0)
3527 {
3528 arg = skipwhite(p);
3529 xp->xp_pattern = arg;
3530 p = skiptowhite(arg);
3531 }
3532 if (*p != NUL) // past group name
3533 {
3534 include_link = 0;
3535 if (arg[1] == 'i' && arg[0] == 'N')
3536 highlight_list();
3537 if (STRNCMP("link", arg, p - arg) == 0
3538 || STRNCMP("clear", arg, p - arg) == 0)
3539 {
3540 xp->xp_pattern = skipwhite(p);
3541 p = skiptowhite(xp->xp_pattern);
3542 if (*p != NUL) // past first group name
3543 {
3544 xp->xp_pattern = skipwhite(p);
3545 p = skiptowhite(xp->xp_pattern);
3546 }
3547 }
3548 if (*p != NUL) // past group name(s)
3549 xp->xp_context = EXPAND_NOTHING;
3550 }
3551 }
3552 }
3553}
3554
3555/*
3556 * List highlighting matches in a nice way.
3557 */
3558 static void
3559highlight_list(void)
3560{
3561 int i;
3562
3563 for (i = 10; --i >= 0; )
3564 highlight_list_two(i, HL_ATTR(HLF_D));
3565 for (i = 40; --i >= 0; )
3566 highlight_list_two(99, 0);
3567}
3568
3569 static void
3570highlight_list_two(int cnt, int attr)
3571{
3572 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3573 msg_clr_eos();
3574 out_flush();
3575 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3576}
3577
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003578/*
3579 * Function given to ExpandGeneric() to obtain the list of group names.
3580 */
3581 char_u *
3582get_highlight_name(expand_T *xp UNUSED, int idx)
3583{
3584 return get_highlight_name_ext(xp, idx, TRUE);
3585}
3586
3587/*
3588 * Obtain a highlight group name.
3589 * When "skip_cleared" is TRUE don't return a cleared entry.
3590 */
3591 char_u *
3592get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3593{
3594 if (idx < 0)
3595 return NULL;
3596
3597 // Items are never removed from the table, skip the ones that were
3598 // cleared.
3599 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3600 return (char_u *)"";
3601
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003602 if (idx == highlight_ga.ga_len && include_none != 0)
3603 return (char_u *)"none";
3604 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3605 return (char_u *)"default";
3606 if (idx == highlight_ga.ga_len + include_none + include_default
3607 && include_link != 0)
3608 return (char_u *)"link";
3609 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3610 && include_link != 0)
3611 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003612 if (idx >= highlight_ga.ga_len)
3613 return NULL;
3614 return HL_TABLE()[idx].sg_name;
3615}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003616
3617#if defined(FEAT_GUI) || defined(PROTO)
3618/*
3619 * Free all the highlight group fonts.
3620 * Used when quitting for systems which need it.
3621 */
3622 void
3623free_highlight_fonts(void)
3624{
3625 int idx;
3626
3627 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3628 {
3629 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3630 HL_TABLE()[idx].sg_font = NOFONT;
3631# ifdef FEAT_XFONTSET
3632 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3633 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3634# endif
3635 }
3636
3637 gui_mch_free_font(gui.norm_font);
3638# ifdef FEAT_XFONTSET
3639 gui_mch_free_fontset(gui.fontset);
3640# endif
3641# ifndef FEAT_GUI_GTK
3642 gui_mch_free_font(gui.bold_font);
3643 gui_mch_free_font(gui.ital_font);
3644 gui_mch_free_font(gui.boldital_font);
3645# endif
3646}
3647#endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003648
3649
3650#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003651
3652# define SEARCH_HL_PRIORITY 0
3653
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003654/*
3655 * Add match to the match list of window 'wp'. The pattern 'pat' will be
3656 * highlighted with the group 'grp' with priority 'prio'.
3657 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3658 * If no particular ID is desired, -1 must be specified for 'id'.
3659 * Return ID of added match, -1 on failure.
3660 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003661 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003662match_add(
3663 win_T *wp,
3664 char_u *grp,
3665 char_u *pat,
3666 int prio,
3667 int id,
3668 list_T *pos_list,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003669 char_u *conceal_char UNUSED) // pointer to conceal replacement char
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003670{
3671 matchitem_T *cur;
3672 matchitem_T *prev;
3673 matchitem_T *m;
3674 int hlg_id;
3675 regprog_T *regprog = NULL;
3676 int rtype = SOME_VALID;
3677
3678 if (*grp == NUL || (pat != NULL && *pat == NUL))
3679 return -1;
3680 if (id < -1 || id == 0)
3681 {
3682 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"), id);
3683 return -1;
3684 }
3685 if (id != -1)
3686 {
3687 cur = wp->w_match_head;
3688 while (cur != NULL)
3689 {
3690 if (cur->id == id)
3691 {
3692 semsg(_("E801: ID already taken: %d"), id);
3693 return -1;
3694 }
3695 cur = cur->next;
3696 }
3697 }
3698 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3699 {
3700 semsg(_(e_nogroup), grp);
3701 return -1;
3702 }
3703 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3704 {
3705 semsg(_(e_invarg2), pat);
3706 return -1;
3707 }
3708
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003709 // Find available match ID.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003710 while (id == -1)
3711 {
3712 cur = wp->w_match_head;
3713 while (cur != NULL && cur->id != wp->w_next_match_id)
3714 cur = cur->next;
3715 if (cur == NULL)
3716 id = wp->w_next_match_id;
3717 wp->w_next_match_id++;
3718 }
3719
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003720 // Build new match.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003721 m = ALLOC_CLEAR_ONE(matchitem_T);
3722 m->id = id;
3723 m->priority = prio;
3724 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3725 m->hlg_id = hlg_id;
3726 m->match.regprog = regprog;
3727 m->match.rmm_ic = FALSE;
3728 m->match.rmm_maxcol = 0;
3729# if defined(FEAT_CONCEAL)
3730 m->conceal_char = 0;
3731 if (conceal_char != NULL)
3732 m->conceal_char = (*mb_ptr2char)(conceal_char);
3733# endif
3734
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003735 // Set up position matches
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003736 if (pos_list != NULL)
3737 {
3738 linenr_T toplnum = 0;
3739 linenr_T botlnum = 0;
3740 listitem_T *li;
3741 int i;
3742
3743 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3744 i++, li = li->li_next)
3745 {
3746 linenr_T lnum = 0;
3747 colnr_T col = 0;
3748 int len = 1;
3749 list_T *subl;
3750 listitem_T *subli;
3751 int error = FALSE;
3752
3753 if (li->li_tv.v_type == VAR_LIST)
3754 {
3755 subl = li->li_tv.vval.v_list;
3756 if (subl == NULL)
3757 goto fail;
3758 subli = subl->lv_first;
3759 if (subli == NULL)
3760 goto fail;
3761 lnum = tv_get_number_chk(&subli->li_tv, &error);
3762 if (error == TRUE)
3763 goto fail;
3764 if (lnum == 0)
3765 {
3766 --i;
3767 continue;
3768 }
3769 m->pos.pos[i].lnum = lnum;
3770 subli = subli->li_next;
3771 if (subli != NULL)
3772 {
3773 col = tv_get_number_chk(&subli->li_tv, &error);
3774 if (error == TRUE)
3775 goto fail;
3776 subli = subli->li_next;
3777 if (subli != NULL)
3778 {
3779 len = tv_get_number_chk(&subli->li_tv, &error);
3780 if (error == TRUE)
3781 goto fail;
3782 }
3783 }
3784 m->pos.pos[i].col = col;
3785 m->pos.pos[i].len = len;
3786 }
3787 else if (li->li_tv.v_type == VAR_NUMBER)
3788 {
3789 if (li->li_tv.vval.v_number == 0)
3790 {
3791 --i;
3792 continue;
3793 }
3794 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3795 m->pos.pos[i].col = 0;
3796 m->pos.pos[i].len = 0;
3797 }
3798 else
3799 {
3800 emsg(_("List or number required"));
3801 goto fail;
3802 }
3803 if (toplnum == 0 || lnum < toplnum)
3804 toplnum = lnum;
3805 if (botlnum == 0 || lnum >= botlnum)
3806 botlnum = lnum + 1;
3807 }
3808
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003809 // Calculate top and bottom lines for redrawing area
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003810 if (toplnum != 0)
3811 {
3812 if (wp->w_buffer->b_mod_set)
3813 {
3814 if (wp->w_buffer->b_mod_top > toplnum)
3815 wp->w_buffer->b_mod_top = toplnum;
3816 if (wp->w_buffer->b_mod_bot < botlnum)
3817 wp->w_buffer->b_mod_bot = botlnum;
3818 }
3819 else
3820 {
3821 wp->w_buffer->b_mod_set = TRUE;
3822 wp->w_buffer->b_mod_top = toplnum;
3823 wp->w_buffer->b_mod_bot = botlnum;
3824 wp->w_buffer->b_mod_xlines = 0;
3825 }
3826 m->pos.toplnum = toplnum;
3827 m->pos.botlnum = botlnum;
3828 rtype = VALID;
3829 }
3830 }
3831
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003832 // Insert new match. The match list is in ascending order with regard to
3833 // the match priorities.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003834 cur = wp->w_match_head;
3835 prev = cur;
3836 while (cur != NULL && prio >= cur->priority)
3837 {
3838 prev = cur;
3839 cur = cur->next;
3840 }
3841 if (cur == prev)
3842 wp->w_match_head = m;
3843 else
3844 prev->next = m;
3845 m->next = cur;
3846
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003847 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003848 return id;
3849
3850fail:
3851 vim_free(m);
3852 return -1;
3853}
3854
3855/*
3856 * Delete match with ID 'id' in the match list of window 'wp'.
3857 * Print error messages if 'perr' is TRUE.
3858 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003859 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003860match_delete(win_T *wp, int id, int perr)
3861{
3862 matchitem_T *cur = wp->w_match_head;
3863 matchitem_T *prev = cur;
3864 int rtype = SOME_VALID;
3865
3866 if (id < 1)
3867 {
3868 if (perr == TRUE)
3869 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3870 id);
3871 return -1;
3872 }
3873 while (cur != NULL && cur->id != id)
3874 {
3875 prev = cur;
3876 cur = cur->next;
3877 }
3878 if (cur == NULL)
3879 {
3880 if (perr == TRUE)
3881 semsg(_("E803: ID not found: %d"), id);
3882 return -1;
3883 }
3884 if (cur == prev)
3885 wp->w_match_head = cur->next;
3886 else
3887 prev->next = cur->next;
3888 vim_regfree(cur->match.regprog);
3889 vim_free(cur->pattern);
3890 if (cur->pos.toplnum != 0)
3891 {
3892 if (wp->w_buffer->b_mod_set)
3893 {
3894 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3895 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3896 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3897 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3898 }
3899 else
3900 {
3901 wp->w_buffer->b_mod_set = TRUE;
3902 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3903 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3904 wp->w_buffer->b_mod_xlines = 0;
3905 }
3906 rtype = VALID;
3907 }
3908 vim_free(cur);
Bram Moolenaar06029a82019-07-24 14:25:26 +02003909 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003910 return 0;
3911}
3912
3913/*
3914 * Delete all matches in the match list of window 'wp'.
3915 */
3916 void
3917clear_matches(win_T *wp)
3918{
3919 matchitem_T *m;
3920
3921 while (wp->w_match_head != NULL)
3922 {
3923 m = wp->w_match_head->next;
3924 vim_regfree(wp->w_match_head->match.regprog);
3925 vim_free(wp->w_match_head->pattern);
3926 vim_free(wp->w_match_head);
3927 wp->w_match_head = m;
3928 }
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003929 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003930}
3931
3932/*
3933 * Get match from ID 'id' in window 'wp'.
3934 * Return NULL if match not found.
3935 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003936 static matchitem_T *
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003937get_match(win_T *wp, int id)
3938{
3939 matchitem_T *cur = wp->w_match_head;
3940
3941 while (cur != NULL && cur->id != id)
3942 cur = cur->next;
3943 return cur;
3944}
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003945
3946/*
3947 * Init for calling prepare_search_hl().
3948 */
3949 void
3950init_search_hl(win_T *wp, match_T *search_hl)
3951{
3952 matchitem_T *cur;
3953
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003954 // Setup for match and 'hlsearch' highlighting. Disable any previous
3955 // match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003956 cur = wp->w_match_head;
3957 while (cur != NULL)
3958 {
3959 cur->hl.rm = cur->match;
3960 if (cur->hlg_id == 0)
3961 cur->hl.attr = 0;
3962 else
3963 cur->hl.attr = syn_id2attr(cur->hlg_id);
3964 cur->hl.buf = wp->w_buffer;
3965 cur->hl.lnum = 0;
3966 cur->hl.first_lnum = 0;
3967# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003968 // Set the time limit to 'redrawtime'.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003969 profile_setlimit(p_rdt, &(cur->hl.tm));
3970# endif
3971 cur = cur->next;
3972 }
3973 search_hl->buf = wp->w_buffer;
3974 search_hl->lnum = 0;
3975 search_hl->first_lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003976 // time limit is set at the toplevel, for all windows
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003977}
3978
3979/*
3980 * If there is a match fill "shl" and return one.
3981 * Return zero otherwise.
3982 */
3983 static int
3984next_search_hl_pos(
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003985 match_T *shl, // points to a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003986 linenr_T lnum,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003987 posmatch_T *posmatch, // match positions
3988 colnr_T mincol) // minimal column for a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003989{
3990 int i;
3991 int found = -1;
3992
3993 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
3994 {
3995 llpos_T *pos = &posmatch->pos[i];
3996
3997 if (pos->lnum == 0)
3998 break;
3999 if (pos->len == 0 && pos->col < mincol)
4000 continue;
4001 if (pos->lnum == lnum)
4002 {
4003 if (found >= 0)
4004 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004005 // if this match comes before the one at "found" then swap
4006 // them
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004007 if (pos->col < posmatch->pos[found].col)
4008 {
4009 llpos_T tmp = *pos;
4010
4011 *pos = posmatch->pos[found];
4012 posmatch->pos[found] = tmp;
4013 }
4014 }
4015 else
4016 found = i;
4017 }
4018 }
4019 posmatch->cur = 0;
4020 if (found >= 0)
4021 {
4022 colnr_T start = posmatch->pos[found].col == 0
4023 ? 0 : posmatch->pos[found].col - 1;
4024 colnr_T end = posmatch->pos[found].col == 0
4025 ? MAXCOL : start + posmatch->pos[found].len;
4026
4027 shl->lnum = lnum;
4028 shl->rm.startpos[0].lnum = 0;
4029 shl->rm.startpos[0].col = start;
4030 shl->rm.endpos[0].lnum = 0;
4031 shl->rm.endpos[0].col = end;
4032 shl->is_addpos = TRUE;
4033 posmatch->cur = found + 1;
4034 return 1;
4035 }
4036 return 0;
4037}
4038
4039/*
4040 * Search for a next 'hlsearch' or match.
4041 * Uses shl->buf.
4042 * Sets shl->lnum and shl->rm contents.
4043 * Note: Assumes a previous match is always before "lnum", unless
4044 * shl->lnum is zero.
4045 * Careful: Any pointers for buffer lines will become invalid.
4046 */
4047 static void
4048next_search_hl(
4049 win_T *win,
4050 match_T *search_hl,
4051 match_T *shl, // points to search_hl or a match
4052 linenr_T lnum,
4053 colnr_T mincol, // minimal column for a match
4054 matchitem_T *cur) // to retrieve match positions if any
4055{
4056 linenr_T l;
4057 colnr_T matchcol;
4058 long nmatched;
4059 int save_called_emsg = called_emsg;
4060
4061 // for :{range}s/pat only highlight inside the range
4062 if (lnum < search_first_line || lnum > search_last_line)
4063 {
4064 shl->lnum = 0;
4065 return;
4066 }
4067
4068 if (shl->lnum != 0)
4069 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004070 // Check for three situations:
4071 // 1. If the "lnum" is below a previous match, start a new search.
4072 // 2. If the previous match includes "mincol", use it.
4073 // 3. Continue after the previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004074 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4075 if (lnum > l)
4076 shl->lnum = 0;
4077 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4078 return;
4079 }
4080
4081 /*
4082 * Repeat searching for a match until one is found that includes "mincol"
4083 * or none is found in this line.
4084 */
4085 called_emsg = FALSE;
4086 for (;;)
4087 {
4088# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004089 // Stop searching after passing the time limit.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004090 if (profile_passed_limit(&(shl->tm)))
4091 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004092 shl->lnum = 0; // no match found in time
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004093 break;
4094 }
4095# endif
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004096 // Three situations:
4097 // 1. No useful previous match: search from start of line.
4098 // 2. Not Vi compatible or empty match: continue at next character.
4099 // Break the loop if this is beyond the end of the line.
4100 // 3. Vi compatible searching: continue at end of previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004101 if (shl->lnum == 0)
4102 matchcol = 0;
4103 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4104 || (shl->rm.endpos[0].lnum == 0
4105 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4106 {
4107 char_u *ml;
4108
4109 matchcol = shl->rm.startpos[0].col;
4110 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4111 if (*ml == NUL)
4112 {
4113 ++matchcol;
4114 shl->lnum = 0;
4115 break;
4116 }
4117 if (has_mbyte)
4118 matchcol += mb_ptr2len(ml);
4119 else
4120 ++matchcol;
4121 }
4122 else
4123 matchcol = shl->rm.endpos[0].col;
4124
4125 shl->lnum = lnum;
4126 if (shl->rm.regprog != NULL)
4127 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004128 // Remember whether shl->rm is using a copy of the regprog in
4129 // cur->match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004130 int regprog_is_copy = (shl != search_hl && cur != NULL
4131 && shl == &cur->hl
4132 && cur->match.regprog == cur->hl.rm.regprog);
4133 int timed_out = FALSE;
4134
4135 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4136 matchcol,
4137#ifdef FEAT_RELTIME
4138 &(shl->tm), &timed_out
4139#else
4140 NULL, NULL
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004141#endif
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004142 );
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004143 // Copy the regprog, in case it got freed and recompiled.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004144 if (regprog_is_copy)
4145 cur->match.regprog = cur->hl.rm.regprog;
4146
4147 if (called_emsg || got_int || timed_out)
4148 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004149 // Error while handling regexp: stop using this regexp.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004150 if (shl == search_hl)
4151 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004152 // don't free regprog in the match list, it's a copy
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004153 vim_regfree(shl->rm.regprog);
4154 set_no_hlsearch(TRUE);
4155 }
4156 shl->rm.regprog = NULL;
4157 shl->lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004158 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004159 break;
4160 }
4161 }
4162 else if (cur != NULL)
4163 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4164 else
4165 nmatched = 0;
4166 if (nmatched == 0)
4167 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004168 shl->lnum = 0; // no match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004169 break;
4170 }
4171 if (shl->rm.startpos[0].lnum > 0
4172 || shl->rm.startpos[0].col >= mincol
4173 || nmatched > 1
4174 || shl->rm.endpos[0].col > mincol)
4175 {
4176 shl->lnum += shl->rm.startpos[0].lnum;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004177 break; // useful match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004178 }
4179 }
4180
4181 // Restore called_emsg for assert_fails().
4182 called_emsg = save_called_emsg;
4183}
4184
4185/*
4186 * Advance to the match in window "wp" line "lnum" or past it.
4187 */
4188 void
4189prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4190{
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004191 matchitem_T *cur; // points to the match list
4192 match_T *shl; // points to search_hl or a match
4193 int shl_flag; // flag to indicate whether search_hl
4194 // has been processed or not
4195 int pos_inprogress; // marks that position match search is
4196 // in progress
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004197 int n;
4198
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004199 // When using a multi-line pattern, start searching at the top
4200 // of the window or just after a closed fold.
4201 // Do this both for search_hl and the match list.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004202 cur = wp->w_match_head;
4203 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4204 while (cur != NULL || shl_flag == FALSE)
4205 {
4206 if (shl_flag == FALSE)
4207 {
4208 shl = search_hl;
4209 shl_flag = TRUE;
4210 }
4211 else
4212 shl = &cur->hl;
4213 if (shl->rm.regprog != NULL
4214 && shl->lnum == 0
4215 && re_multiline(shl->rm.regprog))
4216 {
4217 if (shl->first_lnum == 0)
4218 {
4219# ifdef FEAT_FOLDING
4220 for (shl->first_lnum = lnum;
4221 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4222 if (hasFoldingWin(wp, shl->first_lnum - 1,
4223 NULL, NULL, TRUE, NULL))
4224 break;
4225# else
4226 shl->first_lnum = wp->w_topline;
4227# endif
4228 }
4229 if (cur != NULL)
4230 cur->pos.cur = 0;
4231 pos_inprogress = TRUE;
4232 n = 0;
4233 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4234 || (cur != NULL && pos_inprogress)))
4235 {
4236 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4237 shl == search_hl ? NULL : cur);
4238 pos_inprogress = cur == NULL || cur->pos.cur == 0
4239 ? FALSE : TRUE;
4240 if (shl->lnum != 0)
4241 {
4242 shl->first_lnum = shl->lnum
4243 + shl->rm.endpos[0].lnum
4244 - shl->rm.startpos[0].lnum;
4245 n = shl->rm.endpos[0].col;
4246 }
4247 else
4248 {
4249 ++shl->first_lnum;
4250 n = 0;
4251 }
4252 }
4253 }
4254 if (shl != search_hl && cur != NULL)
4255 cur = cur->next;
4256 }
4257}
4258
4259/*
4260 * Prepare for 'hlsearch' and match highlighting in one window line.
4261 * Return TRUE if there is such highlighting and set "search_attr" to the
4262 * current highlight attribute.
4263 */
4264 int
4265prepare_search_hl_line(
4266 win_T *wp,
4267 linenr_T lnum,
4268 colnr_T mincol,
4269 char_u **line,
4270 match_T *search_hl,
4271 int *search_attr)
4272{
4273 matchitem_T *cur; // points to the match list
4274 match_T *shl; // points to search_hl or a match
4275 int shl_flag; // flag to indicate whether search_hl
4276 // has been processed or not
4277 int area_highlighting = FALSE;
4278
4279 /*
4280 * Handle highlighting the last used search pattern and matches.
4281 * Do this for both search_hl and the match list.
4282 * Do not use search_hl in a popup window.
4283 */
4284 cur = wp->w_match_head;
4285 shl_flag = WIN_IS_POPUP(wp);
4286 while (cur != NULL || shl_flag == FALSE)
4287 {
4288 if (shl_flag == FALSE)
4289 {
4290 shl = search_hl;
4291 shl_flag = TRUE;
4292 }
4293 else
4294 shl = &cur->hl;
4295 shl->startcol = MAXCOL;
4296 shl->endcol = MAXCOL;
4297 shl->attr_cur = 0;
4298 shl->is_addpos = FALSE;
4299 if (cur != NULL)
4300 cur->pos.cur = 0;
4301 next_search_hl(wp, search_hl, shl, lnum, mincol,
4302 shl == search_hl ? NULL : cur);
4303
4304 // Need to get the line again, a multi-line regexp may have made it
4305 // invalid.
4306 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4307
4308 if (shl->lnum != 0 && shl->lnum <= lnum)
4309 {
4310 if (shl->lnum == lnum)
4311 shl->startcol = shl->rm.startpos[0].col;
4312 else
4313 shl->startcol = 0;
4314 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4315 - shl->rm.startpos[0].lnum)
4316 shl->endcol = shl->rm.endpos[0].col;
4317 else
4318 shl->endcol = MAXCOL;
4319 // Highlight one character for an empty match.
4320 if (shl->startcol == shl->endcol)
4321 {
4322 if (has_mbyte && (*line)[shl->endcol] != NUL)
4323 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4324 else
4325 ++shl->endcol;
4326 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004327 if ((long)shl->startcol < mincol) // match at leftcol
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004328 {
4329 shl->attr_cur = shl->attr;
4330 *search_attr = shl->attr;
4331 }
4332 area_highlighting = TRUE;
4333 }
4334 if (shl != search_hl && cur != NULL)
4335 cur = cur->next;
4336 }
4337 return area_highlighting;
4338}
4339
4340/*
4341 * For a position in a line: Check for start/end of 'hlsearch' and other
4342 * matches.
4343 * After end, check for start/end of next match.
4344 * When another match, have to check for start again.
4345 * Watch out for matching an empty string!
4346 * Return the udpated search_attr.
4347 */
4348 int
4349update_search_hl(
4350 win_T *wp,
4351 linenr_T lnum,
4352 colnr_T col,
4353 char_u **line,
4354 match_T *search_hl,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004355 int *has_match_conc UNUSED,
4356 int *match_conc UNUSED,
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004357 int did_line_attr,
4358 int lcs_eol_one)
4359{
4360 matchitem_T *cur; // points to the match list
4361 match_T *shl; // points to search_hl or a match
4362 int shl_flag; // flag to indicate whether search_hl
4363 // has been processed or not
4364 int pos_inprogress; // marks that position match search is in
4365 // progress
4366 int search_attr = 0;
4367
4368
4369 // Do this for 'search_hl' and the match list (ordered by priority).
4370 cur = wp->w_match_head;
4371 shl_flag = WIN_IS_POPUP(wp);
4372 while (cur != NULL || shl_flag == FALSE)
4373 {
4374 if (shl_flag == FALSE
4375 && ((cur != NULL
4376 && cur->priority > SEARCH_HL_PRIORITY)
4377 || cur == NULL))
4378 {
4379 shl = search_hl;
4380 shl_flag = TRUE;
4381 }
4382 else
4383 shl = &cur->hl;
4384 if (cur != NULL)
4385 cur->pos.cur = 0;
4386 pos_inprogress = TRUE;
4387 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4388 {
4389 if (shl->startcol != MAXCOL
4390 && col >= shl->startcol
4391 && col < shl->endcol)
4392 {
4393 int next_col = col + MB_PTR2LEN(*line + col);
4394
4395 if (shl->endcol < next_col)
4396 shl->endcol = next_col;
4397 shl->attr_cur = shl->attr;
4398# ifdef FEAT_CONCEAL
4399 // Match with the "Conceal" group results in hiding
4400 // the match.
4401 if (cur != NULL
4402 && shl != search_hl
4403 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4404 {
4405 *has_match_conc = col == shl->startcol ? 2 : 1;
4406 *match_conc = cur->conceal_char;
4407 }
4408 else
4409 *has_match_conc = *match_conc = 0;
4410# endif
4411 }
4412 else if (col == shl->endcol)
4413 {
4414 shl->attr_cur = 0;
4415 next_search_hl(wp, search_hl, shl, lnum, col,
4416 shl == search_hl ? NULL : cur);
4417 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4418
4419 // Need to get the line again, a multi-line regexp may have
4420 // made it invalid.
4421 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4422
4423 if (shl->lnum == lnum)
4424 {
4425 shl->startcol = shl->rm.startpos[0].col;
4426 if (shl->rm.endpos[0].lnum == 0)
4427 shl->endcol = shl->rm.endpos[0].col;
4428 else
4429 shl->endcol = MAXCOL;
4430
4431 if (shl->startcol == shl->endcol)
4432 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004433 // highlight empty match, try again after
4434 // it
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004435 if (has_mbyte)
4436 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4437 else
4438 ++shl->endcol;
4439 }
4440
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004441 // Loop to check if the match starts at the
4442 // current position
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004443 continue;
4444 }
4445 }
4446 break;
4447 }
4448 if (shl != search_hl && cur != NULL)
4449 cur = cur->next;
4450 }
4451
4452 // Use attributes from match with highest priority among 'search_hl' and
4453 // the match list.
4454 cur = wp->w_match_head;
4455 shl_flag = WIN_IS_POPUP(wp);
4456 while (cur != NULL || shl_flag == FALSE)
4457 {
4458 if (shl_flag == FALSE
4459 && ((cur != NULL
4460 && cur->priority > SEARCH_HL_PRIORITY)
4461 || cur == NULL))
4462 {
4463 shl = search_hl;
4464 shl_flag = TRUE;
4465 }
4466 else
4467 shl = &cur->hl;
4468 if (shl->attr_cur != 0)
4469 search_attr = shl->attr_cur;
4470 if (shl != search_hl && cur != NULL)
4471 cur = cur->next;
4472 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004473 // Only highlight one character after the last column.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004474 if (*(*line + col) == NUL && (did_line_attr >= 1
4475 || (wp->w_p_list && lcs_eol_one == -1)))
4476 search_attr = 0;
4477 return search_attr;
4478}
4479
4480 int
4481get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4482{
4483 long prevcol = curcol;
4484 int prevcol_hl_flag = FALSE;
4485 matchitem_T *cur; // points to the match list
4486
4487 // we're not really at that column when skipping some text
4488 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4489 ++prevcol;
4490
4491 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4492 prevcol_hl_flag = TRUE;
4493 else
4494 {
4495 cur = wp->w_match_head;
4496 while (cur != NULL)
4497 {
4498 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4499 {
4500 prevcol_hl_flag = TRUE;
4501 break;
4502 }
4503 cur = cur->next;
4504 }
4505 }
4506 return prevcol_hl_flag;
4507}
4508
4509/*
4510 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4511 * or match highlighting.
4512 */
4513 void
4514get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4515{
4516 matchitem_T *cur; // points to the match list
4517 match_T *shl; // points to search_hl or a match
4518 int shl_flag; // flag to indicate whether search_hl
4519 // has been processed or not
4520
4521 cur = wp->w_match_head;
4522 shl_flag = WIN_IS_POPUP(wp);
4523 while (cur != NULL || shl_flag == FALSE)
4524 {
4525 if (shl_flag == FALSE
4526 && ((cur != NULL
4527 && cur->priority > SEARCH_HL_PRIORITY)
4528 || cur == NULL))
4529 {
4530 shl = search_hl;
4531 shl_flag = TRUE;
4532 }
4533 else
4534 shl = &cur->hl;
4535 if (col - 1 == (long)shl->startcol
4536 && (shl == search_hl || !shl->is_addpos))
4537 *char_attr = shl->attr;
4538 if (shl != search_hl && cur != NULL)
4539 cur = cur->next;
4540 }
4541}
4542
4543#endif // FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004544
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004545#if defined(FEAT_EVAL) || defined(PROTO)
4546# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004547 static int
4548matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4549{
4550 dictitem_T *di;
4551
4552 if (tv->v_type != VAR_DICT)
4553 {
4554 emsg(_(e_dictreq));
4555 return FAIL;
4556 }
4557
4558 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4559 *conceal_char = dict_get_string(tv->vval.v_dict,
4560 (char_u *)"conceal", FALSE);
4561
4562 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4563 {
4564 *win = find_win_by_nr_or_id(&di->di_tv);
4565 if (*win == NULL)
4566 {
4567 emsg(_(e_invalwindow));
4568 return FAIL;
4569 }
4570 }
4571
4572 return OK;
4573}
4574#endif
4575
4576/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004577 * "clearmatches()" function
4578 */
4579 void
4580f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4581{
4582#ifdef FEAT_SEARCH_EXTRA
4583 win_T *win = get_optional_window(argvars, 0);
4584
4585 if (win != NULL)
4586 clear_matches(win);
4587#endif
4588}
4589
4590/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004591 * "getmatches()" function
4592 */
4593 void
4594f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4595{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004596# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004597 dict_T *dict;
4598 matchitem_T *cur;
4599 int i;
4600 win_T *win = get_optional_window(argvars, 0);
4601
4602 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4603 return;
4604
4605 cur = win->w_match_head;
4606 while (cur != NULL)
4607 {
4608 dict = dict_alloc();
4609 if (dict == NULL)
4610 return;
4611 if (cur->match.regprog == NULL)
4612 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004613 // match added with matchaddpos()
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004614 for (i = 0; i < MAXPOSMATCH; ++i)
4615 {
4616 llpos_T *llpos;
4617 char buf[30]; // use 30 to avoid compiler warning
4618 list_T *l;
4619
4620 llpos = &cur->pos.pos[i];
4621 if (llpos->lnum == 0)
4622 break;
4623 l = list_alloc();
4624 if (l == NULL)
4625 break;
4626 list_append_number(l, (varnumber_T)llpos->lnum);
4627 if (llpos->col > 0)
4628 {
4629 list_append_number(l, (varnumber_T)llpos->col);
4630 list_append_number(l, (varnumber_T)llpos->len);
4631 }
4632 sprintf(buf, "pos%d", i + 1);
4633 dict_add_list(dict, buf, l);
4634 }
4635 }
4636 else
4637 {
4638 dict_add_string(dict, "pattern", cur->pattern);
4639 }
4640 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4641 dict_add_number(dict, "priority", (long)cur->priority);
4642 dict_add_number(dict, "id", (long)cur->id);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004643# if defined(FEAT_CONCEAL)
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004644 if (cur->conceal_char)
4645 {
4646 char_u buf[MB_MAXBYTES + 1];
4647
4648 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4649 dict_add_string(dict, "conceal", (char_u *)&buf);
4650 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004651# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004652 list_append_dict(rettv->vval.v_list, dict);
4653 cur = cur->next;
4654 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004655# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004656}
4657
4658/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004659 * "setmatches()" function
4660 */
4661 void
4662f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4663{
4664#ifdef FEAT_SEARCH_EXTRA
4665 list_T *l;
4666 listitem_T *li;
4667 dict_T *d;
4668 list_T *s = NULL;
4669 win_T *win = get_optional_window(argvars, 1);
4670
4671 rettv->vval.v_number = -1;
4672 if (argvars[0].v_type != VAR_LIST)
4673 {
4674 emsg(_(e_listreq));
4675 return;
4676 }
4677 if (win == NULL)
4678 return;
4679
4680 if ((l = argvars[0].vval.v_list) != NULL)
4681 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004682 // To some extent make sure that we are dealing with a list from
4683 // "getmatches()".
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004684 li = l->lv_first;
4685 while (li != NULL)
4686 {
4687 if (li->li_tv.v_type != VAR_DICT
4688 || (d = li->li_tv.vval.v_dict) == NULL)
4689 {
4690 emsg(_(e_invarg));
4691 return;
4692 }
4693 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4694 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4695 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4696 && dict_find(d, (char_u *)"priority", -1) != NULL
4697 && dict_find(d, (char_u *)"id", -1) != NULL))
4698 {
4699 emsg(_(e_invarg));
4700 return;
4701 }
4702 li = li->li_next;
4703 }
4704
4705 clear_matches(win);
4706 li = l->lv_first;
4707 while (li != NULL)
4708 {
4709 int i = 0;
4710 char buf[30]; // use 30 to avoid compiler warning
4711 dictitem_T *di;
4712 char_u *group;
4713 int priority;
4714 int id;
4715 char_u *conceal;
4716
4717 d = li->li_tv.vval.v_dict;
4718 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4719 {
4720 if (s == NULL)
4721 {
4722 s = list_alloc();
4723 if (s == NULL)
4724 return;
4725 }
4726
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004727 // match from matchaddpos()
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004728 for (i = 1; i < 9; i++)
4729 {
4730 sprintf((char *)buf, (char *)"pos%d", i);
4731 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4732 {
4733 if (di->di_tv.v_type != VAR_LIST)
4734 return;
4735
4736 list_append_tv(s, &di->di_tv);
4737 s->lv_refcount++;
4738 }
4739 else
4740 break;
4741 }
4742 }
4743
4744 group = dict_get_string(d, (char_u *)"group", TRUE);
4745 priority = (int)dict_get_number(d, (char_u *)"priority");
4746 id = (int)dict_get_number(d, (char_u *)"id");
4747 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4748 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4749 : NULL;
4750 if (i == 0)
4751 {
4752 match_add(win, group,
4753 dict_get_string(d, (char_u *)"pattern", FALSE),
4754 priority, id, NULL, conceal);
4755 }
4756 else
4757 {
4758 match_add(win, group, NULL, priority, id, s, conceal);
4759 list_unref(s);
4760 s = NULL;
4761 }
4762 vim_free(group);
4763 vim_free(conceal);
4764
4765 li = li->li_next;
4766 }
4767 rettv->vval.v_number = 0;
4768 }
4769#endif
4770}
4771
4772/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004773 * "matchadd()" function
4774 */
4775 void
4776f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4777{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004778# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004779 char_u buf[NUMBUFLEN];
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004780 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4781 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4782 int prio = 10; // default priority
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004783 int id = -1;
4784 int error = FALSE;
4785 char_u *conceal_char = NULL;
4786 win_T *win = curwin;
4787
4788 rettv->vval.v_number = -1;
4789
4790 if (grp == NULL || pat == NULL)
4791 return;
4792 if (argvars[2].v_type != VAR_UNKNOWN)
4793 {
4794 prio = (int)tv_get_number_chk(&argvars[2], &error);
4795 if (argvars[3].v_type != VAR_UNKNOWN)
4796 {
4797 id = (int)tv_get_number_chk(&argvars[3], &error);
4798 if (argvars[4].v_type != VAR_UNKNOWN
4799 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4800 return;
4801 }
4802 }
4803 if (error == TRUE)
4804 return;
4805 if (id >= 1 && id <= 3)
4806 {
4807 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4808 return;
4809 }
4810
4811 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4812 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004813# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004814}
4815
4816/*
4817 * "matchaddpos()" function
4818 */
4819 void
4820f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4821{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004822# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004823 char_u buf[NUMBUFLEN];
4824 char_u *group;
4825 int prio = 10;
4826 int id = -1;
4827 int error = FALSE;
4828 list_T *l;
4829 char_u *conceal_char = NULL;
4830 win_T *win = curwin;
4831
4832 rettv->vval.v_number = -1;
4833
4834 group = tv_get_string_buf_chk(&argvars[0], buf);
4835 if (group == NULL)
4836 return;
4837
4838 if (argvars[1].v_type != VAR_LIST)
4839 {
4840 semsg(_(e_listarg), "matchaddpos()");
4841 return;
4842 }
4843 l = argvars[1].vval.v_list;
4844 if (l == NULL)
4845 return;
4846
4847 if (argvars[2].v_type != VAR_UNKNOWN)
4848 {
4849 prio = (int)tv_get_number_chk(&argvars[2], &error);
4850 if (argvars[3].v_type != VAR_UNKNOWN)
4851 {
4852 id = (int)tv_get_number_chk(&argvars[3], &error);
4853
4854 if (argvars[4].v_type != VAR_UNKNOWN
4855 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4856 return;
4857 }
4858 }
4859 if (error == TRUE)
4860 return;
4861
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004862 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004863 if (id == 1 || id == 2)
4864 {
4865 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4866 return;
4867 }
4868
4869 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4870 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004871# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004872}
4873
4874/*
4875 * "matcharg()" function
4876 */
4877 void
4878f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4879{
4880 if (rettv_list_alloc(rettv) == OK)
4881 {
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004882# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004883 int id = (int)tv_get_number(&argvars[0]);
4884 matchitem_T *m;
4885
4886 if (id >= 1 && id <= 3)
4887 {
4888 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4889 {
4890 list_append_string(rettv->vval.v_list,
4891 syn_id2name(m->hlg_id), -1);
4892 list_append_string(rettv->vval.v_list, m->pattern, -1);
4893 }
4894 else
4895 {
4896 list_append_string(rettv->vval.v_list, NULL, -1);
4897 list_append_string(rettv->vval.v_list, NULL, -1);
4898 }
4899 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004900# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004901 }
4902}
4903
4904/*
4905 * "matchdelete()" function
4906 */
4907 void
4908f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4909{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004910# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004911 win_T *win = get_optional_window(argvars, 1);
4912
4913 if (win == NULL)
4914 rettv->vval.v_number = -1;
4915 else
4916 rettv->vval.v_number = match_delete(win,
4917 (int)tv_get_number(&argvars[0]), TRUE);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004918# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004919}
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004920#endif
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004921
4922#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4923/*
4924 * ":[N]match {group} {pattern}"
4925 * Sets nextcmd to the start of the next command, if any. Also called when
4926 * skipping commands to find the next command.
4927 */
4928 void
4929ex_match(exarg_T *eap)
4930{
4931 char_u *p;
4932 char_u *g = NULL;
4933 char_u *end;
4934 int c;
4935 int id;
4936
4937 if (eap->line2 <= 3)
4938 id = eap->line2;
4939 else
4940 {
4941 emsg(_(e_invcmd));
4942 return;
4943 }
4944
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004945 // First clear any old pattern.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004946 if (!eap->skip)
4947 match_delete(curwin, id, FALSE);
4948
4949 if (ends_excmd(*eap->arg))
4950 end = eap->arg;
4951 else if ((STRNICMP(eap->arg, "none", 4) == 0
4952 && (VIM_ISWHITE(eap->arg[4]) || ends_excmd(eap->arg[4]))))
4953 end = eap->arg + 4;
4954 else
4955 {
4956 p = skiptowhite(eap->arg);
4957 if (!eap->skip)
4958 g = vim_strnsave(eap->arg, (int)(p - eap->arg));
4959 p = skipwhite(p);
4960 if (*p == NUL)
4961 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004962 // There must be two arguments.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004963 vim_free(g);
4964 semsg(_(e_invarg2), eap->arg);
4965 return;
4966 }
4967 end = skip_regexp(p + 1, *p, TRUE, NULL);
4968 if (!eap->skip)
4969 {
4970 if (*end != NUL && !ends_excmd(*skipwhite(end + 1)))
4971 {
4972 vim_free(g);
4973 eap->errmsg = e_trailing;
4974 return;
4975 }
4976 if (*end != *p)
4977 {
4978 vim_free(g);
4979 semsg(_(e_invarg2), p);
4980 return;
4981 }
4982
4983 c = *end;
4984 *end = NUL;
4985 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
4986 vim_free(g);
4987 *end = c;
4988 }
4989 }
4990 eap->nextcmd = find_nextcmd(end);
4991}
4992#endif