blob: f32081d8ab2e8f7c844332894adf417619d7689b [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;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003019 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003020
3021 // Check that the name is ASCII letters, digits and underscore.
3022 for (p = name; *p != NUL; ++p)
3023 {
3024 if (!vim_isprintc(*p))
3025 {
3026 emsg(_("E669: Unprintable character in group name"));
3027 vim_free(name);
3028 return 0;
3029 }
3030 else if (!ASCII_ISALNUM(*p) && *p != '_')
3031 {
3032 // This is an error, but since there previously was no check only
3033 // give a warning.
3034 msg_source(HL_ATTR(HLF_W));
3035 msg(_("W18: Invalid character in group name"));
3036 break;
3037 }
3038 }
3039
3040 /*
3041 * First call for this growarray: init growing array.
3042 */
3043 if (highlight_ga.ga_data == NULL)
3044 {
3045 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3046 highlight_ga.ga_growsize = 10;
3047 }
3048
3049 if (highlight_ga.ga_len >= MAX_HL_ID)
3050 {
3051 emsg(_("E849: Too many highlight and syntax groups"));
3052 vim_free(name);
3053 return 0;
3054 }
3055
3056 /*
3057 * Make room for at least one other syntax_highlight entry.
3058 */
3059 if (ga_grow(&highlight_ga, 1) == FAIL)
3060 {
3061 vim_free(name);
3062 return 0;
3063 }
3064
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003065 name_up = vim_strsave_up(name);
3066 if (name_up == NULL)
3067 {
3068 vim_free(name);
3069 return 0;
3070 }
3071
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003072 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(hl_group_T));
3073 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003074 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003075#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3076 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3077 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
3078# ifdef FEAT_GUI
3079 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
3080# endif
3081#endif
3082 ++highlight_ga.ga_len;
3083
3084 return highlight_ga.ga_len; // ID is index plus one
3085}
3086
3087/*
3088 * When, just after calling syn_add_group(), an error is discovered, this
3089 * function deletes the new name.
3090 */
3091 static void
3092syn_unadd_group(void)
3093{
3094 --highlight_ga.ga_len;
3095 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3096 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3097}
3098
3099/*
3100 * Translate a group ID to highlight attributes.
3101 */
3102 int
3103syn_id2attr(int hl_id)
3104{
3105 int attr;
3106 hl_group_T *sgp;
3107
3108 hl_id = syn_get_final_id(hl_id);
3109 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3110
3111#ifdef FEAT_GUI
3112 /*
3113 * Only use GUI attr when the GUI is being used.
3114 */
3115 if (gui.in_use)
3116 attr = sgp->sg_gui_attr;
3117 else
3118#endif
3119 if (IS_CTERM)
3120 attr = sgp->sg_cterm_attr;
3121 else
3122 attr = sgp->sg_term_attr;
3123
3124 return attr;
3125}
3126
3127#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3128/*
3129 * Get the GUI colors and attributes for a group ID.
3130 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3131 */
3132 int
3133syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3134{
3135 hl_group_T *sgp;
3136
3137 hl_id = syn_get_final_id(hl_id);
3138 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3139
3140 *fgp = sgp->sg_gui_fg;
3141 *bgp = sgp->sg_gui_bg;
3142 return sgp->sg_gui;
3143}
3144#endif
3145
3146#if (defined(MSWIN) \
3147 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3148 && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
3149 void
3150syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3151{
3152 hl_group_T *sgp;
3153
3154 hl_id = syn_get_final_id(hl_id);
3155 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3156 *fgp = sgp->sg_cterm_fg - 1;
3157 *bgp = sgp->sg_cterm_bg - 1;
3158}
3159#endif
3160
3161/*
3162 * Translate a group ID to the final group ID (following links).
3163 */
3164 int
3165syn_get_final_id(int hl_id)
3166{
3167 int count;
3168 hl_group_T *sgp;
3169
3170 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3171 return 0; // Can be called from eval!!
3172
3173 /*
3174 * Follow links until there is no more.
3175 * Look out for loops! Break after 100 links.
3176 */
3177 for (count = 100; --count >= 0; )
3178 {
3179 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3180 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3181 break;
3182 hl_id = sgp->sg_link;
3183 }
3184
3185 return hl_id;
3186}
3187
3188#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3189/*
3190 * Call this function just after the GUI has started.
3191 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3192 * It finds the font and color handles for the highlighting groups.
3193 */
3194 void
3195highlight_gui_started(void)
3196{
3197 int idx;
3198
3199 // First get the colors from the "Normal" and "Menu" group, if set
3200 if (USE_24BIT)
3201 set_normal_colors();
3202
3203 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3204 gui_do_one_color(idx, FALSE, FALSE);
3205
3206 highlight_changed();
3207}
3208
3209 static void
3210gui_do_one_color(
3211 int idx,
3212 int do_menu UNUSED, // TRUE: might set the menu font
3213 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3214{
3215 int didit = FALSE;
3216
3217# ifdef FEAT_GUI
3218# ifdef FEAT_TERMGUICOLORS
3219 if (gui.in_use)
3220# endif
3221 if (HL_TABLE()[idx].sg_font_name != NULL)
3222 {
3223 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3224 do_tooltip, TRUE);
3225 didit = TRUE;
3226 }
3227# endif
3228 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3229 {
3230 HL_TABLE()[idx].sg_gui_fg =
3231 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3232 didit = TRUE;
3233 }
3234 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3235 {
3236 HL_TABLE()[idx].sg_gui_bg =
3237 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3238 didit = TRUE;
3239 }
3240# ifdef FEAT_GUI
3241 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3242 {
3243 HL_TABLE()[idx].sg_gui_sp =
3244 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3245 didit = TRUE;
3246 }
3247# endif
3248 if (didit) // need to get a new attr number
3249 set_hl_attr(idx);
3250}
3251#endif
3252
3253#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3254/*
3255 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3256 */
3257 static void
3258combine_stl_hlt(
3259 int id,
3260 int id_S,
3261 int id_alt,
3262 int hlcnt,
3263 int i,
3264 int hlf,
3265 int *table)
3266{
3267 hl_group_T *hlt = HL_TABLE();
3268
3269 if (id_alt == 0)
3270 {
3271 vim_memset(&hlt[hlcnt + i], 0, sizeof(hl_group_T));
3272 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3273 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3274# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3275 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3276# endif
3277 }
3278 else
3279 mch_memmove(&hlt[hlcnt + i],
3280 &hlt[id_alt - 1],
3281 sizeof(hl_group_T));
3282 hlt[hlcnt + i].sg_link = 0;
3283
3284 hlt[hlcnt + i].sg_term ^=
3285 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3286 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3287 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3288 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3289 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3290 hlt[hlcnt + i].sg_cterm ^=
3291 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3292 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3293 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3294 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3295 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3296# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3297 hlt[hlcnt + i].sg_gui ^=
3298 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3299# endif
3300# ifdef FEAT_GUI
3301 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3302 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3303 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3304 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3305 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3306 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3307 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3308 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3309# ifdef FEAT_XFONTSET
3310 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3311 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3312# endif
3313# endif
3314 highlight_ga.ga_len = hlcnt + i + 1;
3315 set_hl_attr(hlcnt + i); // At long last we can apply
3316 table[i] = syn_id2attr(hlcnt + i + 1);
3317}
3318#endif
3319
3320/*
3321 * Translate the 'highlight' option into attributes in highlight_attr[] and
3322 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3323 * corresponding highlights to use on top of HLF_SNC is computed.
3324 * Called only when the 'highlight' option has been changed and upon first
3325 * screen redraw after any :highlight command.
3326 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3327 */
3328 int
3329highlight_changed(void)
3330{
3331 int hlf;
3332 int i;
3333 char_u *p;
3334 int attr;
3335 char_u *end;
3336 int id;
3337#ifdef USER_HIGHLIGHT
3338 char_u userhl[30]; // use 30 to avoid compiler warning
3339# ifdef FEAT_STL_OPT
3340 int id_S = -1;
3341 int id_SNC = 0;
3342# ifdef FEAT_TERMINAL
3343 int id_ST = 0;
3344 int id_STNC = 0;
3345# endif
3346 int hlcnt;
3347# endif
3348#endif
3349 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3350
3351 need_highlight_changed = FALSE;
3352
3353 /*
3354 * Clear all attributes.
3355 */
3356 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3357 highlight_attr[hlf] = 0;
3358
3359 /*
3360 * First set all attributes to their default value.
3361 * Then use the attributes from the 'highlight' option.
3362 */
3363 for (i = 0; i < 2; ++i)
3364 {
3365 if (i)
3366 p = p_hl;
3367 else
3368 p = get_highlight_default();
3369 if (p == NULL) // just in case
3370 continue;
3371
3372 while (*p)
3373 {
3374 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3375 if (hl_flags[hlf] == *p)
3376 break;
3377 ++p;
3378 if (hlf == (int)HLF_COUNT || *p == NUL)
3379 return FAIL;
3380
3381 /*
3382 * Allow several hl_flags to be combined, like "bu" for
3383 * bold-underlined.
3384 */
3385 attr = 0;
3386 for ( ; *p && *p != ','; ++p) // parse upto comma
3387 {
3388 if (VIM_ISWHITE(*p)) // ignore white space
3389 continue;
3390
3391 if (attr > HL_ALL) // Combination with ':' is not allowed.
3392 return FAIL;
3393
3394 switch (*p)
3395 {
3396 case 'b': attr |= HL_BOLD;
3397 break;
3398 case 'i': attr |= HL_ITALIC;
3399 break;
3400 case '-':
3401 case 'n': // no highlighting
3402 break;
3403 case 'r': attr |= HL_INVERSE;
3404 break;
3405 case 's': attr |= HL_STANDOUT;
3406 break;
3407 case 'u': attr |= HL_UNDERLINE;
3408 break;
3409 case 'c': attr |= HL_UNDERCURL;
3410 break;
3411 case 't': attr |= HL_STRIKETHROUGH;
3412 break;
3413 case ':': ++p; // highlight group name
3414 if (attr || *p == NUL) // no combinations
3415 return FAIL;
3416 end = vim_strchr(p, ',');
3417 if (end == NULL)
3418 end = p + STRLEN(p);
3419 id = syn_check_group(p, (int)(end - p));
3420 if (id == 0)
3421 return FAIL;
3422 attr = syn_id2attr(id);
3423 p = end - 1;
3424#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3425 if (hlf == (int)HLF_SNC)
3426 id_SNC = syn_get_final_id(id);
3427# ifdef FEAT_TERMINAL
3428 else if (hlf == (int)HLF_ST)
3429 id_ST = syn_get_final_id(id);
3430 else if (hlf == (int)HLF_STNC)
3431 id_STNC = syn_get_final_id(id);
3432# endif
3433 else if (hlf == (int)HLF_S)
3434 id_S = syn_get_final_id(id);
3435#endif
3436 break;
3437 default: return FAIL;
3438 }
3439 }
3440 highlight_attr[hlf] = attr;
3441
3442 p = skip_to_option_part(p); // skip comma and spaces
3443 }
3444 }
3445
3446#ifdef USER_HIGHLIGHT
3447 /*
3448 * Setup the user highlights
3449 *
3450 * Temporarily utilize 28 more hl entries:
3451 * 9 for User1-User9 combined with StatusLineNC
3452 * 9 for User1-User9 combined with StatusLineTerm
3453 * 9 for User1-User9 combined with StatusLineTermNC
3454 * 1 for StatusLine default
3455 * Have to be in there simultaneously in case of table overflows in
3456 * get_attr_entry()
3457 */
3458# ifdef FEAT_STL_OPT
3459 if (ga_grow(&highlight_ga, 28) == FAIL)
3460 return FAIL;
3461 hlcnt = highlight_ga.ga_len;
3462 if (id_S == -1)
3463 {
3464 // Make sure id_S is always valid to simplify code below. Use the last
3465 // entry.
3466 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(hl_group_T));
3467 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3468 id_S = hlcnt + 19;
3469 }
3470# endif
3471 for (i = 0; i < 9; i++)
3472 {
3473 sprintf((char *)userhl, "User%d", i + 1);
3474 id = syn_name2id(userhl);
3475 if (id == 0)
3476 {
3477 highlight_user[i] = 0;
3478# ifdef FEAT_STL_OPT
3479 highlight_stlnc[i] = 0;
3480# ifdef FEAT_TERMINAL
3481 highlight_stlterm[i] = 0;
3482 highlight_stltermnc[i] = 0;
3483# endif
3484# endif
3485 }
3486 else
3487 {
3488 highlight_user[i] = syn_id2attr(id);
3489# ifdef FEAT_STL_OPT
3490 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3491 HLF_SNC, highlight_stlnc);
3492# ifdef FEAT_TERMINAL
3493 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3494 HLF_ST, highlight_stlterm);
3495 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3496 HLF_STNC, highlight_stltermnc);
3497# endif
3498# endif
3499 }
3500 }
3501# ifdef FEAT_STL_OPT
3502 highlight_ga.ga_len = hlcnt;
3503# endif
3504
3505#endif // USER_HIGHLIGHT
3506
3507 return OK;
3508}
3509
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003510static void highlight_list(void);
3511static void highlight_list_two(int cnt, int attr);
3512
3513/*
3514 * Handle command line completion for :highlight command.
3515 */
3516 void
3517set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3518{
3519 char_u *p;
3520
3521 // Default: expand group names
3522 xp->xp_context = EXPAND_HIGHLIGHT;
3523 xp->xp_pattern = arg;
3524 include_link = 2;
3525 include_default = 1;
3526
3527 // (part of) subcommand already typed
3528 if (*arg != NUL)
3529 {
3530 p = skiptowhite(arg);
3531 if (*p != NUL) // past "default" or group name
3532 {
3533 include_default = 0;
3534 if (STRNCMP("default", arg, p - arg) == 0)
3535 {
3536 arg = skipwhite(p);
3537 xp->xp_pattern = arg;
3538 p = skiptowhite(arg);
3539 }
3540 if (*p != NUL) // past group name
3541 {
3542 include_link = 0;
3543 if (arg[1] == 'i' && arg[0] == 'N')
3544 highlight_list();
3545 if (STRNCMP("link", arg, p - arg) == 0
3546 || STRNCMP("clear", arg, p - arg) == 0)
3547 {
3548 xp->xp_pattern = skipwhite(p);
3549 p = skiptowhite(xp->xp_pattern);
3550 if (*p != NUL) // past first group name
3551 {
3552 xp->xp_pattern = skipwhite(p);
3553 p = skiptowhite(xp->xp_pattern);
3554 }
3555 }
3556 if (*p != NUL) // past group name(s)
3557 xp->xp_context = EXPAND_NOTHING;
3558 }
3559 }
3560 }
3561}
3562
3563/*
3564 * List highlighting matches in a nice way.
3565 */
3566 static void
3567highlight_list(void)
3568{
3569 int i;
3570
3571 for (i = 10; --i >= 0; )
3572 highlight_list_two(i, HL_ATTR(HLF_D));
3573 for (i = 40; --i >= 0; )
3574 highlight_list_two(99, 0);
3575}
3576
3577 static void
3578highlight_list_two(int cnt, int attr)
3579{
3580 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3581 msg_clr_eos();
3582 out_flush();
3583 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3584}
3585
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003586/*
3587 * Function given to ExpandGeneric() to obtain the list of group names.
3588 */
3589 char_u *
3590get_highlight_name(expand_T *xp UNUSED, int idx)
3591{
3592 return get_highlight_name_ext(xp, idx, TRUE);
3593}
3594
3595/*
3596 * Obtain a highlight group name.
3597 * When "skip_cleared" is TRUE don't return a cleared entry.
3598 */
3599 char_u *
3600get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3601{
3602 if (idx < 0)
3603 return NULL;
3604
3605 // Items are never removed from the table, skip the ones that were
3606 // cleared.
3607 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3608 return (char_u *)"";
3609
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003610 if (idx == highlight_ga.ga_len && include_none != 0)
3611 return (char_u *)"none";
3612 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3613 return (char_u *)"default";
3614 if (idx == highlight_ga.ga_len + include_none + include_default
3615 && include_link != 0)
3616 return (char_u *)"link";
3617 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3618 && include_link != 0)
3619 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003620 if (idx >= highlight_ga.ga_len)
3621 return NULL;
3622 return HL_TABLE()[idx].sg_name;
3623}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003624
3625#if defined(FEAT_GUI) || defined(PROTO)
3626/*
3627 * Free all the highlight group fonts.
3628 * Used when quitting for systems which need it.
3629 */
3630 void
3631free_highlight_fonts(void)
3632{
3633 int idx;
3634
3635 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3636 {
3637 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3638 HL_TABLE()[idx].sg_font = NOFONT;
3639# ifdef FEAT_XFONTSET
3640 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3641 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3642# endif
3643 }
3644
3645 gui_mch_free_font(gui.norm_font);
3646# ifdef FEAT_XFONTSET
3647 gui_mch_free_fontset(gui.fontset);
3648# endif
3649# ifndef FEAT_GUI_GTK
3650 gui_mch_free_font(gui.bold_font);
3651 gui_mch_free_font(gui.ital_font);
3652 gui_mch_free_font(gui.boldital_font);
3653# endif
3654}
3655#endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003656
3657
3658#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003659
3660# define SEARCH_HL_PRIORITY 0
3661
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003662/*
3663 * Add match to the match list of window 'wp'. The pattern 'pat' will be
3664 * highlighted with the group 'grp' with priority 'prio'.
3665 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3666 * If no particular ID is desired, -1 must be specified for 'id'.
3667 * Return ID of added match, -1 on failure.
3668 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003669 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003670match_add(
3671 win_T *wp,
3672 char_u *grp,
3673 char_u *pat,
3674 int prio,
3675 int id,
3676 list_T *pos_list,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003677 char_u *conceal_char UNUSED) // pointer to conceal replacement char
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003678{
3679 matchitem_T *cur;
3680 matchitem_T *prev;
3681 matchitem_T *m;
3682 int hlg_id;
3683 regprog_T *regprog = NULL;
3684 int rtype = SOME_VALID;
3685
3686 if (*grp == NUL || (pat != NULL && *pat == NUL))
3687 return -1;
3688 if (id < -1 || id == 0)
3689 {
3690 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"), id);
3691 return -1;
3692 }
3693 if (id != -1)
3694 {
3695 cur = wp->w_match_head;
3696 while (cur != NULL)
3697 {
3698 if (cur->id == id)
3699 {
3700 semsg(_("E801: ID already taken: %d"), id);
3701 return -1;
3702 }
3703 cur = cur->next;
3704 }
3705 }
3706 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3707 {
3708 semsg(_(e_nogroup), grp);
3709 return -1;
3710 }
3711 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3712 {
3713 semsg(_(e_invarg2), pat);
3714 return -1;
3715 }
3716
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003717 // Find available match ID.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003718 while (id == -1)
3719 {
3720 cur = wp->w_match_head;
3721 while (cur != NULL && cur->id != wp->w_next_match_id)
3722 cur = cur->next;
3723 if (cur == NULL)
3724 id = wp->w_next_match_id;
3725 wp->w_next_match_id++;
3726 }
3727
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003728 // Build new match.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003729 m = ALLOC_CLEAR_ONE(matchitem_T);
3730 m->id = id;
3731 m->priority = prio;
3732 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3733 m->hlg_id = hlg_id;
3734 m->match.regprog = regprog;
3735 m->match.rmm_ic = FALSE;
3736 m->match.rmm_maxcol = 0;
3737# if defined(FEAT_CONCEAL)
3738 m->conceal_char = 0;
3739 if (conceal_char != NULL)
3740 m->conceal_char = (*mb_ptr2char)(conceal_char);
3741# endif
3742
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003743 // Set up position matches
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003744 if (pos_list != NULL)
3745 {
3746 linenr_T toplnum = 0;
3747 linenr_T botlnum = 0;
3748 listitem_T *li;
3749 int i;
3750
3751 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3752 i++, li = li->li_next)
3753 {
3754 linenr_T lnum = 0;
3755 colnr_T col = 0;
3756 int len = 1;
3757 list_T *subl;
3758 listitem_T *subli;
3759 int error = FALSE;
3760
3761 if (li->li_tv.v_type == VAR_LIST)
3762 {
3763 subl = li->li_tv.vval.v_list;
3764 if (subl == NULL)
3765 goto fail;
3766 subli = subl->lv_first;
3767 if (subli == NULL)
3768 goto fail;
3769 lnum = tv_get_number_chk(&subli->li_tv, &error);
3770 if (error == TRUE)
3771 goto fail;
3772 if (lnum == 0)
3773 {
3774 --i;
3775 continue;
3776 }
3777 m->pos.pos[i].lnum = lnum;
3778 subli = subli->li_next;
3779 if (subli != NULL)
3780 {
3781 col = tv_get_number_chk(&subli->li_tv, &error);
3782 if (error == TRUE)
3783 goto fail;
3784 subli = subli->li_next;
3785 if (subli != NULL)
3786 {
3787 len = tv_get_number_chk(&subli->li_tv, &error);
3788 if (error == TRUE)
3789 goto fail;
3790 }
3791 }
3792 m->pos.pos[i].col = col;
3793 m->pos.pos[i].len = len;
3794 }
3795 else if (li->li_tv.v_type == VAR_NUMBER)
3796 {
3797 if (li->li_tv.vval.v_number == 0)
3798 {
3799 --i;
3800 continue;
3801 }
3802 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3803 m->pos.pos[i].col = 0;
3804 m->pos.pos[i].len = 0;
3805 }
3806 else
3807 {
3808 emsg(_("List or number required"));
3809 goto fail;
3810 }
3811 if (toplnum == 0 || lnum < toplnum)
3812 toplnum = lnum;
3813 if (botlnum == 0 || lnum >= botlnum)
3814 botlnum = lnum + 1;
3815 }
3816
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003817 // Calculate top and bottom lines for redrawing area
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003818 if (toplnum != 0)
3819 {
3820 if (wp->w_buffer->b_mod_set)
3821 {
3822 if (wp->w_buffer->b_mod_top > toplnum)
3823 wp->w_buffer->b_mod_top = toplnum;
3824 if (wp->w_buffer->b_mod_bot < botlnum)
3825 wp->w_buffer->b_mod_bot = botlnum;
3826 }
3827 else
3828 {
3829 wp->w_buffer->b_mod_set = TRUE;
3830 wp->w_buffer->b_mod_top = toplnum;
3831 wp->w_buffer->b_mod_bot = botlnum;
3832 wp->w_buffer->b_mod_xlines = 0;
3833 }
3834 m->pos.toplnum = toplnum;
3835 m->pos.botlnum = botlnum;
3836 rtype = VALID;
3837 }
3838 }
3839
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003840 // Insert new match. The match list is in ascending order with regard to
3841 // the match priorities.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003842 cur = wp->w_match_head;
3843 prev = cur;
3844 while (cur != NULL && prio >= cur->priority)
3845 {
3846 prev = cur;
3847 cur = cur->next;
3848 }
3849 if (cur == prev)
3850 wp->w_match_head = m;
3851 else
3852 prev->next = m;
3853 m->next = cur;
3854
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003855 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003856 return id;
3857
3858fail:
3859 vim_free(m);
3860 return -1;
3861}
3862
3863/*
3864 * Delete match with ID 'id' in the match list of window 'wp'.
3865 * Print error messages if 'perr' is TRUE.
3866 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003867 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003868match_delete(win_T *wp, int id, int perr)
3869{
3870 matchitem_T *cur = wp->w_match_head;
3871 matchitem_T *prev = cur;
3872 int rtype = SOME_VALID;
3873
3874 if (id < 1)
3875 {
3876 if (perr == TRUE)
3877 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3878 id);
3879 return -1;
3880 }
3881 while (cur != NULL && cur->id != id)
3882 {
3883 prev = cur;
3884 cur = cur->next;
3885 }
3886 if (cur == NULL)
3887 {
3888 if (perr == TRUE)
3889 semsg(_("E803: ID not found: %d"), id);
3890 return -1;
3891 }
3892 if (cur == prev)
3893 wp->w_match_head = cur->next;
3894 else
3895 prev->next = cur->next;
3896 vim_regfree(cur->match.regprog);
3897 vim_free(cur->pattern);
3898 if (cur->pos.toplnum != 0)
3899 {
3900 if (wp->w_buffer->b_mod_set)
3901 {
3902 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3903 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3904 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3905 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3906 }
3907 else
3908 {
3909 wp->w_buffer->b_mod_set = TRUE;
3910 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3911 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3912 wp->w_buffer->b_mod_xlines = 0;
3913 }
3914 rtype = VALID;
3915 }
3916 vim_free(cur);
Bram Moolenaar06029a82019-07-24 14:25:26 +02003917 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003918 return 0;
3919}
3920
3921/*
3922 * Delete all matches in the match list of window 'wp'.
3923 */
3924 void
3925clear_matches(win_T *wp)
3926{
3927 matchitem_T *m;
3928
3929 while (wp->w_match_head != NULL)
3930 {
3931 m = wp->w_match_head->next;
3932 vim_regfree(wp->w_match_head->match.regprog);
3933 vim_free(wp->w_match_head->pattern);
3934 vim_free(wp->w_match_head);
3935 wp->w_match_head = m;
3936 }
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003937 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003938}
3939
3940/*
3941 * Get match from ID 'id' in window 'wp'.
3942 * Return NULL if match not found.
3943 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003944 static matchitem_T *
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003945get_match(win_T *wp, int id)
3946{
3947 matchitem_T *cur = wp->w_match_head;
3948
3949 while (cur != NULL && cur->id != id)
3950 cur = cur->next;
3951 return cur;
3952}
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003953
3954/*
3955 * Init for calling prepare_search_hl().
3956 */
3957 void
3958init_search_hl(win_T *wp, match_T *search_hl)
3959{
3960 matchitem_T *cur;
3961
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003962 // Setup for match and 'hlsearch' highlighting. Disable any previous
3963 // match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003964 cur = wp->w_match_head;
3965 while (cur != NULL)
3966 {
3967 cur->hl.rm = cur->match;
3968 if (cur->hlg_id == 0)
3969 cur->hl.attr = 0;
3970 else
3971 cur->hl.attr = syn_id2attr(cur->hlg_id);
3972 cur->hl.buf = wp->w_buffer;
3973 cur->hl.lnum = 0;
3974 cur->hl.first_lnum = 0;
3975# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003976 // Set the time limit to 'redrawtime'.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003977 profile_setlimit(p_rdt, &(cur->hl.tm));
3978# endif
3979 cur = cur->next;
3980 }
3981 search_hl->buf = wp->w_buffer;
3982 search_hl->lnum = 0;
3983 search_hl->first_lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003984 // time limit is set at the toplevel, for all windows
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003985}
3986
3987/*
3988 * If there is a match fill "shl" and return one.
3989 * Return zero otherwise.
3990 */
3991 static int
3992next_search_hl_pos(
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003993 match_T *shl, // points to a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003994 linenr_T lnum,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003995 posmatch_T *posmatch, // match positions
3996 colnr_T mincol) // minimal column for a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003997{
3998 int i;
3999 int found = -1;
4000
4001 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
4002 {
4003 llpos_T *pos = &posmatch->pos[i];
4004
4005 if (pos->lnum == 0)
4006 break;
4007 if (pos->len == 0 && pos->col < mincol)
4008 continue;
4009 if (pos->lnum == lnum)
4010 {
4011 if (found >= 0)
4012 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004013 // if this match comes before the one at "found" then swap
4014 // them
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004015 if (pos->col < posmatch->pos[found].col)
4016 {
4017 llpos_T tmp = *pos;
4018
4019 *pos = posmatch->pos[found];
4020 posmatch->pos[found] = tmp;
4021 }
4022 }
4023 else
4024 found = i;
4025 }
4026 }
4027 posmatch->cur = 0;
4028 if (found >= 0)
4029 {
4030 colnr_T start = posmatch->pos[found].col == 0
4031 ? 0 : posmatch->pos[found].col - 1;
4032 colnr_T end = posmatch->pos[found].col == 0
4033 ? MAXCOL : start + posmatch->pos[found].len;
4034
4035 shl->lnum = lnum;
4036 shl->rm.startpos[0].lnum = 0;
4037 shl->rm.startpos[0].col = start;
4038 shl->rm.endpos[0].lnum = 0;
4039 shl->rm.endpos[0].col = end;
4040 shl->is_addpos = TRUE;
4041 posmatch->cur = found + 1;
4042 return 1;
4043 }
4044 return 0;
4045}
4046
4047/*
4048 * Search for a next 'hlsearch' or match.
4049 * Uses shl->buf.
4050 * Sets shl->lnum and shl->rm contents.
4051 * Note: Assumes a previous match is always before "lnum", unless
4052 * shl->lnum is zero.
4053 * Careful: Any pointers for buffer lines will become invalid.
4054 */
4055 static void
4056next_search_hl(
4057 win_T *win,
4058 match_T *search_hl,
4059 match_T *shl, // points to search_hl or a match
4060 linenr_T lnum,
4061 colnr_T mincol, // minimal column for a match
4062 matchitem_T *cur) // to retrieve match positions if any
4063{
4064 linenr_T l;
4065 colnr_T matchcol;
4066 long nmatched;
4067 int save_called_emsg = called_emsg;
4068
4069 // for :{range}s/pat only highlight inside the range
4070 if (lnum < search_first_line || lnum > search_last_line)
4071 {
4072 shl->lnum = 0;
4073 return;
4074 }
4075
4076 if (shl->lnum != 0)
4077 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004078 // Check for three situations:
4079 // 1. If the "lnum" is below a previous match, start a new search.
4080 // 2. If the previous match includes "mincol", use it.
4081 // 3. Continue after the previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004082 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4083 if (lnum > l)
4084 shl->lnum = 0;
4085 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4086 return;
4087 }
4088
4089 /*
4090 * Repeat searching for a match until one is found that includes "mincol"
4091 * or none is found in this line.
4092 */
4093 called_emsg = FALSE;
4094 for (;;)
4095 {
4096# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004097 // Stop searching after passing the time limit.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004098 if (profile_passed_limit(&(shl->tm)))
4099 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004100 shl->lnum = 0; // no match found in time
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004101 break;
4102 }
4103# endif
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004104 // Three situations:
4105 // 1. No useful previous match: search from start of line.
4106 // 2. Not Vi compatible or empty match: continue at next character.
4107 // Break the loop if this is beyond the end of the line.
4108 // 3. Vi compatible searching: continue at end of previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004109 if (shl->lnum == 0)
4110 matchcol = 0;
4111 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4112 || (shl->rm.endpos[0].lnum == 0
4113 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4114 {
4115 char_u *ml;
4116
4117 matchcol = shl->rm.startpos[0].col;
4118 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4119 if (*ml == NUL)
4120 {
4121 ++matchcol;
4122 shl->lnum = 0;
4123 break;
4124 }
4125 if (has_mbyte)
4126 matchcol += mb_ptr2len(ml);
4127 else
4128 ++matchcol;
4129 }
4130 else
4131 matchcol = shl->rm.endpos[0].col;
4132
4133 shl->lnum = lnum;
4134 if (shl->rm.regprog != NULL)
4135 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004136 // Remember whether shl->rm is using a copy of the regprog in
4137 // cur->match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004138 int regprog_is_copy = (shl != search_hl && cur != NULL
4139 && shl == &cur->hl
4140 && cur->match.regprog == cur->hl.rm.regprog);
4141 int timed_out = FALSE;
4142
4143 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4144 matchcol,
4145#ifdef FEAT_RELTIME
4146 &(shl->tm), &timed_out
4147#else
4148 NULL, NULL
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004149#endif
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004150 );
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004151 // Copy the regprog, in case it got freed and recompiled.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004152 if (regprog_is_copy)
4153 cur->match.regprog = cur->hl.rm.regprog;
4154
4155 if (called_emsg || got_int || timed_out)
4156 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004157 // Error while handling regexp: stop using this regexp.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004158 if (shl == search_hl)
4159 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004160 // don't free regprog in the match list, it's a copy
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004161 vim_regfree(shl->rm.regprog);
4162 set_no_hlsearch(TRUE);
4163 }
4164 shl->rm.regprog = NULL;
4165 shl->lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004166 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004167 break;
4168 }
4169 }
4170 else if (cur != NULL)
4171 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4172 else
4173 nmatched = 0;
4174 if (nmatched == 0)
4175 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004176 shl->lnum = 0; // no match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004177 break;
4178 }
4179 if (shl->rm.startpos[0].lnum > 0
4180 || shl->rm.startpos[0].col >= mincol
4181 || nmatched > 1
4182 || shl->rm.endpos[0].col > mincol)
4183 {
4184 shl->lnum += shl->rm.startpos[0].lnum;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004185 break; // useful match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004186 }
4187 }
4188
4189 // Restore called_emsg for assert_fails().
4190 called_emsg = save_called_emsg;
4191}
4192
4193/*
4194 * Advance to the match in window "wp" line "lnum" or past it.
4195 */
4196 void
4197prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4198{
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004199 matchitem_T *cur; // points to the match list
4200 match_T *shl; // points to search_hl or a match
4201 int shl_flag; // flag to indicate whether search_hl
4202 // has been processed or not
4203 int pos_inprogress; // marks that position match search is
4204 // in progress
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004205 int n;
4206
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004207 // When using a multi-line pattern, start searching at the top
4208 // of the window or just after a closed fold.
4209 // Do this both for search_hl and the match list.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004210 cur = wp->w_match_head;
4211 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4212 while (cur != NULL || shl_flag == FALSE)
4213 {
4214 if (shl_flag == FALSE)
4215 {
4216 shl = search_hl;
4217 shl_flag = TRUE;
4218 }
4219 else
4220 shl = &cur->hl;
4221 if (shl->rm.regprog != NULL
4222 && shl->lnum == 0
4223 && re_multiline(shl->rm.regprog))
4224 {
4225 if (shl->first_lnum == 0)
4226 {
4227# ifdef FEAT_FOLDING
4228 for (shl->first_lnum = lnum;
4229 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4230 if (hasFoldingWin(wp, shl->first_lnum - 1,
4231 NULL, NULL, TRUE, NULL))
4232 break;
4233# else
4234 shl->first_lnum = wp->w_topline;
4235# endif
4236 }
4237 if (cur != NULL)
4238 cur->pos.cur = 0;
4239 pos_inprogress = TRUE;
4240 n = 0;
4241 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4242 || (cur != NULL && pos_inprogress)))
4243 {
4244 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4245 shl == search_hl ? NULL : cur);
4246 pos_inprogress = cur == NULL || cur->pos.cur == 0
4247 ? FALSE : TRUE;
4248 if (shl->lnum != 0)
4249 {
4250 shl->first_lnum = shl->lnum
4251 + shl->rm.endpos[0].lnum
4252 - shl->rm.startpos[0].lnum;
4253 n = shl->rm.endpos[0].col;
4254 }
4255 else
4256 {
4257 ++shl->first_lnum;
4258 n = 0;
4259 }
4260 }
4261 }
4262 if (shl != search_hl && cur != NULL)
4263 cur = cur->next;
4264 }
4265}
4266
4267/*
4268 * Prepare for 'hlsearch' and match highlighting in one window line.
4269 * Return TRUE if there is such highlighting and set "search_attr" to the
4270 * current highlight attribute.
4271 */
4272 int
4273prepare_search_hl_line(
4274 win_T *wp,
4275 linenr_T lnum,
4276 colnr_T mincol,
4277 char_u **line,
4278 match_T *search_hl,
4279 int *search_attr)
4280{
4281 matchitem_T *cur; // points to the match list
4282 match_T *shl; // points to search_hl or a match
4283 int shl_flag; // flag to indicate whether search_hl
4284 // has been processed or not
4285 int area_highlighting = FALSE;
4286
4287 /*
4288 * Handle highlighting the last used search pattern and matches.
4289 * Do this for both search_hl and the match list.
4290 * Do not use search_hl in a popup window.
4291 */
4292 cur = wp->w_match_head;
4293 shl_flag = WIN_IS_POPUP(wp);
4294 while (cur != NULL || shl_flag == FALSE)
4295 {
4296 if (shl_flag == FALSE)
4297 {
4298 shl = search_hl;
4299 shl_flag = TRUE;
4300 }
4301 else
4302 shl = &cur->hl;
4303 shl->startcol = MAXCOL;
4304 shl->endcol = MAXCOL;
4305 shl->attr_cur = 0;
4306 shl->is_addpos = FALSE;
4307 if (cur != NULL)
4308 cur->pos.cur = 0;
4309 next_search_hl(wp, search_hl, shl, lnum, mincol,
4310 shl == search_hl ? NULL : cur);
4311
4312 // Need to get the line again, a multi-line regexp may have made it
4313 // invalid.
4314 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4315
4316 if (shl->lnum != 0 && shl->lnum <= lnum)
4317 {
4318 if (shl->lnum == lnum)
4319 shl->startcol = shl->rm.startpos[0].col;
4320 else
4321 shl->startcol = 0;
4322 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4323 - shl->rm.startpos[0].lnum)
4324 shl->endcol = shl->rm.endpos[0].col;
4325 else
4326 shl->endcol = MAXCOL;
4327 // Highlight one character for an empty match.
4328 if (shl->startcol == shl->endcol)
4329 {
4330 if (has_mbyte && (*line)[shl->endcol] != NUL)
4331 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4332 else
4333 ++shl->endcol;
4334 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004335 if ((long)shl->startcol < mincol) // match at leftcol
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004336 {
4337 shl->attr_cur = shl->attr;
4338 *search_attr = shl->attr;
4339 }
4340 area_highlighting = TRUE;
4341 }
4342 if (shl != search_hl && cur != NULL)
4343 cur = cur->next;
4344 }
4345 return area_highlighting;
4346}
4347
4348/*
4349 * For a position in a line: Check for start/end of 'hlsearch' and other
4350 * matches.
4351 * After end, check for start/end of next match.
4352 * When another match, have to check for start again.
4353 * Watch out for matching an empty string!
4354 * Return the udpated search_attr.
4355 */
4356 int
4357update_search_hl(
4358 win_T *wp,
4359 linenr_T lnum,
4360 colnr_T col,
4361 char_u **line,
4362 match_T *search_hl,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004363 int *has_match_conc UNUSED,
4364 int *match_conc UNUSED,
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004365 int did_line_attr,
4366 int lcs_eol_one)
4367{
4368 matchitem_T *cur; // points to the match list
4369 match_T *shl; // points to search_hl or a match
4370 int shl_flag; // flag to indicate whether search_hl
4371 // has been processed or not
4372 int pos_inprogress; // marks that position match search is in
4373 // progress
4374 int search_attr = 0;
4375
4376
4377 // Do this for 'search_hl' and the match list (ordered by priority).
4378 cur = wp->w_match_head;
4379 shl_flag = WIN_IS_POPUP(wp);
4380 while (cur != NULL || shl_flag == FALSE)
4381 {
4382 if (shl_flag == FALSE
4383 && ((cur != NULL
4384 && cur->priority > SEARCH_HL_PRIORITY)
4385 || cur == NULL))
4386 {
4387 shl = search_hl;
4388 shl_flag = TRUE;
4389 }
4390 else
4391 shl = &cur->hl;
4392 if (cur != NULL)
4393 cur->pos.cur = 0;
4394 pos_inprogress = TRUE;
4395 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4396 {
4397 if (shl->startcol != MAXCOL
4398 && col >= shl->startcol
4399 && col < shl->endcol)
4400 {
4401 int next_col = col + MB_PTR2LEN(*line + col);
4402
4403 if (shl->endcol < next_col)
4404 shl->endcol = next_col;
4405 shl->attr_cur = shl->attr;
4406# ifdef FEAT_CONCEAL
4407 // Match with the "Conceal" group results in hiding
4408 // the match.
4409 if (cur != NULL
4410 && shl != search_hl
4411 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4412 {
4413 *has_match_conc = col == shl->startcol ? 2 : 1;
4414 *match_conc = cur->conceal_char;
4415 }
4416 else
4417 *has_match_conc = *match_conc = 0;
4418# endif
4419 }
4420 else if (col == shl->endcol)
4421 {
4422 shl->attr_cur = 0;
4423 next_search_hl(wp, search_hl, shl, lnum, col,
4424 shl == search_hl ? NULL : cur);
4425 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4426
4427 // Need to get the line again, a multi-line regexp may have
4428 // made it invalid.
4429 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4430
4431 if (shl->lnum == lnum)
4432 {
4433 shl->startcol = shl->rm.startpos[0].col;
4434 if (shl->rm.endpos[0].lnum == 0)
4435 shl->endcol = shl->rm.endpos[0].col;
4436 else
4437 shl->endcol = MAXCOL;
4438
4439 if (shl->startcol == shl->endcol)
4440 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004441 // highlight empty match, try again after
4442 // it
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004443 if (has_mbyte)
4444 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4445 else
4446 ++shl->endcol;
4447 }
4448
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004449 // Loop to check if the match starts at the
4450 // current position
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004451 continue;
4452 }
4453 }
4454 break;
4455 }
4456 if (shl != search_hl && cur != NULL)
4457 cur = cur->next;
4458 }
4459
4460 // Use attributes from match with highest priority among 'search_hl' and
4461 // the match list.
4462 cur = wp->w_match_head;
4463 shl_flag = WIN_IS_POPUP(wp);
4464 while (cur != NULL || shl_flag == FALSE)
4465 {
4466 if (shl_flag == FALSE
4467 && ((cur != NULL
4468 && cur->priority > SEARCH_HL_PRIORITY)
4469 || cur == NULL))
4470 {
4471 shl = search_hl;
4472 shl_flag = TRUE;
4473 }
4474 else
4475 shl = &cur->hl;
4476 if (shl->attr_cur != 0)
4477 search_attr = shl->attr_cur;
4478 if (shl != search_hl && cur != NULL)
4479 cur = cur->next;
4480 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004481 // Only highlight one character after the last column.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004482 if (*(*line + col) == NUL && (did_line_attr >= 1
4483 || (wp->w_p_list && lcs_eol_one == -1)))
4484 search_attr = 0;
4485 return search_attr;
4486}
4487
4488 int
4489get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4490{
4491 long prevcol = curcol;
4492 int prevcol_hl_flag = FALSE;
4493 matchitem_T *cur; // points to the match list
4494
4495 // we're not really at that column when skipping some text
4496 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4497 ++prevcol;
4498
4499 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4500 prevcol_hl_flag = TRUE;
4501 else
4502 {
4503 cur = wp->w_match_head;
4504 while (cur != NULL)
4505 {
4506 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4507 {
4508 prevcol_hl_flag = TRUE;
4509 break;
4510 }
4511 cur = cur->next;
4512 }
4513 }
4514 return prevcol_hl_flag;
4515}
4516
4517/*
4518 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4519 * or match highlighting.
4520 */
4521 void
4522get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4523{
4524 matchitem_T *cur; // points to the match list
4525 match_T *shl; // points to search_hl or a match
4526 int shl_flag; // flag to indicate whether search_hl
4527 // has been processed or not
4528
4529 cur = wp->w_match_head;
4530 shl_flag = WIN_IS_POPUP(wp);
4531 while (cur != NULL || shl_flag == FALSE)
4532 {
4533 if (shl_flag == FALSE
4534 && ((cur != NULL
4535 && cur->priority > SEARCH_HL_PRIORITY)
4536 || cur == NULL))
4537 {
4538 shl = search_hl;
4539 shl_flag = TRUE;
4540 }
4541 else
4542 shl = &cur->hl;
4543 if (col - 1 == (long)shl->startcol
4544 && (shl == search_hl || !shl->is_addpos))
4545 *char_attr = shl->attr;
4546 if (shl != search_hl && cur != NULL)
4547 cur = cur->next;
4548 }
4549}
4550
4551#endif // FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004552
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004553#if defined(FEAT_EVAL) || defined(PROTO)
4554# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004555 static int
4556matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4557{
4558 dictitem_T *di;
4559
4560 if (tv->v_type != VAR_DICT)
4561 {
4562 emsg(_(e_dictreq));
4563 return FAIL;
4564 }
4565
4566 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4567 *conceal_char = dict_get_string(tv->vval.v_dict,
4568 (char_u *)"conceal", FALSE);
4569
4570 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4571 {
4572 *win = find_win_by_nr_or_id(&di->di_tv);
4573 if (*win == NULL)
4574 {
4575 emsg(_(e_invalwindow));
4576 return FAIL;
4577 }
4578 }
4579
4580 return OK;
4581}
4582#endif
4583
4584/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004585 * "clearmatches()" function
4586 */
4587 void
4588f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4589{
4590#ifdef FEAT_SEARCH_EXTRA
4591 win_T *win = get_optional_window(argvars, 0);
4592
4593 if (win != NULL)
4594 clear_matches(win);
4595#endif
4596}
4597
4598/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004599 * "getmatches()" function
4600 */
4601 void
4602f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4603{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004604# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004605 dict_T *dict;
4606 matchitem_T *cur;
4607 int i;
4608 win_T *win = get_optional_window(argvars, 0);
4609
4610 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4611 return;
4612
4613 cur = win->w_match_head;
4614 while (cur != NULL)
4615 {
4616 dict = dict_alloc();
4617 if (dict == NULL)
4618 return;
4619 if (cur->match.regprog == NULL)
4620 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004621 // match added with matchaddpos()
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004622 for (i = 0; i < MAXPOSMATCH; ++i)
4623 {
4624 llpos_T *llpos;
4625 char buf[30]; // use 30 to avoid compiler warning
4626 list_T *l;
4627
4628 llpos = &cur->pos.pos[i];
4629 if (llpos->lnum == 0)
4630 break;
4631 l = list_alloc();
4632 if (l == NULL)
4633 break;
4634 list_append_number(l, (varnumber_T)llpos->lnum);
4635 if (llpos->col > 0)
4636 {
4637 list_append_number(l, (varnumber_T)llpos->col);
4638 list_append_number(l, (varnumber_T)llpos->len);
4639 }
4640 sprintf(buf, "pos%d", i + 1);
4641 dict_add_list(dict, buf, l);
4642 }
4643 }
4644 else
4645 {
4646 dict_add_string(dict, "pattern", cur->pattern);
4647 }
4648 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4649 dict_add_number(dict, "priority", (long)cur->priority);
4650 dict_add_number(dict, "id", (long)cur->id);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004651# if defined(FEAT_CONCEAL)
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004652 if (cur->conceal_char)
4653 {
4654 char_u buf[MB_MAXBYTES + 1];
4655
4656 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4657 dict_add_string(dict, "conceal", (char_u *)&buf);
4658 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004659# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004660 list_append_dict(rettv->vval.v_list, dict);
4661 cur = cur->next;
4662 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004663# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004664}
4665
4666/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004667 * "setmatches()" function
4668 */
4669 void
4670f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4671{
4672#ifdef FEAT_SEARCH_EXTRA
4673 list_T *l;
4674 listitem_T *li;
4675 dict_T *d;
4676 list_T *s = NULL;
4677 win_T *win = get_optional_window(argvars, 1);
4678
4679 rettv->vval.v_number = -1;
4680 if (argvars[0].v_type != VAR_LIST)
4681 {
4682 emsg(_(e_listreq));
4683 return;
4684 }
4685 if (win == NULL)
4686 return;
4687
4688 if ((l = argvars[0].vval.v_list) != NULL)
4689 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004690 // To some extent make sure that we are dealing with a list from
4691 // "getmatches()".
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004692 li = l->lv_first;
4693 while (li != NULL)
4694 {
4695 if (li->li_tv.v_type != VAR_DICT
4696 || (d = li->li_tv.vval.v_dict) == NULL)
4697 {
4698 emsg(_(e_invarg));
4699 return;
4700 }
4701 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4702 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4703 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4704 && dict_find(d, (char_u *)"priority", -1) != NULL
4705 && dict_find(d, (char_u *)"id", -1) != NULL))
4706 {
4707 emsg(_(e_invarg));
4708 return;
4709 }
4710 li = li->li_next;
4711 }
4712
4713 clear_matches(win);
4714 li = l->lv_first;
4715 while (li != NULL)
4716 {
4717 int i = 0;
4718 char buf[30]; // use 30 to avoid compiler warning
4719 dictitem_T *di;
4720 char_u *group;
4721 int priority;
4722 int id;
4723 char_u *conceal;
4724
4725 d = li->li_tv.vval.v_dict;
4726 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4727 {
4728 if (s == NULL)
4729 {
4730 s = list_alloc();
4731 if (s == NULL)
4732 return;
4733 }
4734
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004735 // match from matchaddpos()
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004736 for (i = 1; i < 9; i++)
4737 {
4738 sprintf((char *)buf, (char *)"pos%d", i);
4739 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4740 {
4741 if (di->di_tv.v_type != VAR_LIST)
4742 return;
4743
4744 list_append_tv(s, &di->di_tv);
4745 s->lv_refcount++;
4746 }
4747 else
4748 break;
4749 }
4750 }
4751
4752 group = dict_get_string(d, (char_u *)"group", TRUE);
4753 priority = (int)dict_get_number(d, (char_u *)"priority");
4754 id = (int)dict_get_number(d, (char_u *)"id");
4755 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4756 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4757 : NULL;
4758 if (i == 0)
4759 {
4760 match_add(win, group,
4761 dict_get_string(d, (char_u *)"pattern", FALSE),
4762 priority, id, NULL, conceal);
4763 }
4764 else
4765 {
4766 match_add(win, group, NULL, priority, id, s, conceal);
4767 list_unref(s);
4768 s = NULL;
4769 }
4770 vim_free(group);
4771 vim_free(conceal);
4772
4773 li = li->li_next;
4774 }
4775 rettv->vval.v_number = 0;
4776 }
4777#endif
4778}
4779
4780/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004781 * "matchadd()" function
4782 */
4783 void
4784f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4785{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004786# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004787 char_u buf[NUMBUFLEN];
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004788 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4789 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4790 int prio = 10; // default priority
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004791 int id = -1;
4792 int error = FALSE;
4793 char_u *conceal_char = NULL;
4794 win_T *win = curwin;
4795
4796 rettv->vval.v_number = -1;
4797
4798 if (grp == NULL || pat == NULL)
4799 return;
4800 if (argvars[2].v_type != VAR_UNKNOWN)
4801 {
4802 prio = (int)tv_get_number_chk(&argvars[2], &error);
4803 if (argvars[3].v_type != VAR_UNKNOWN)
4804 {
4805 id = (int)tv_get_number_chk(&argvars[3], &error);
4806 if (argvars[4].v_type != VAR_UNKNOWN
4807 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4808 return;
4809 }
4810 }
4811 if (error == TRUE)
4812 return;
4813 if (id >= 1 && id <= 3)
4814 {
4815 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4816 return;
4817 }
4818
4819 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4820 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004821# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004822}
4823
4824/*
4825 * "matchaddpos()" function
4826 */
4827 void
4828f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4829{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004830# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004831 char_u buf[NUMBUFLEN];
4832 char_u *group;
4833 int prio = 10;
4834 int id = -1;
4835 int error = FALSE;
4836 list_T *l;
4837 char_u *conceal_char = NULL;
4838 win_T *win = curwin;
4839
4840 rettv->vval.v_number = -1;
4841
4842 group = tv_get_string_buf_chk(&argvars[0], buf);
4843 if (group == NULL)
4844 return;
4845
4846 if (argvars[1].v_type != VAR_LIST)
4847 {
4848 semsg(_(e_listarg), "matchaddpos()");
4849 return;
4850 }
4851 l = argvars[1].vval.v_list;
4852 if (l == NULL)
4853 return;
4854
4855 if (argvars[2].v_type != VAR_UNKNOWN)
4856 {
4857 prio = (int)tv_get_number_chk(&argvars[2], &error);
4858 if (argvars[3].v_type != VAR_UNKNOWN)
4859 {
4860 id = (int)tv_get_number_chk(&argvars[3], &error);
4861
4862 if (argvars[4].v_type != VAR_UNKNOWN
4863 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4864 return;
4865 }
4866 }
4867 if (error == TRUE)
4868 return;
4869
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004870 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004871 if (id == 1 || id == 2)
4872 {
4873 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4874 return;
4875 }
4876
4877 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4878 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004879# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004880}
4881
4882/*
4883 * "matcharg()" function
4884 */
4885 void
4886f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4887{
4888 if (rettv_list_alloc(rettv) == OK)
4889 {
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004890# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004891 int id = (int)tv_get_number(&argvars[0]);
4892 matchitem_T *m;
4893
4894 if (id >= 1 && id <= 3)
4895 {
4896 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4897 {
4898 list_append_string(rettv->vval.v_list,
4899 syn_id2name(m->hlg_id), -1);
4900 list_append_string(rettv->vval.v_list, m->pattern, -1);
4901 }
4902 else
4903 {
4904 list_append_string(rettv->vval.v_list, NULL, -1);
4905 list_append_string(rettv->vval.v_list, NULL, -1);
4906 }
4907 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004908# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004909 }
4910}
4911
4912/*
4913 * "matchdelete()" function
4914 */
4915 void
4916f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4917{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004918# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004919 win_T *win = get_optional_window(argvars, 1);
4920
4921 if (win == NULL)
4922 rettv->vval.v_number = -1;
4923 else
4924 rettv->vval.v_number = match_delete(win,
4925 (int)tv_get_number(&argvars[0]), TRUE);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004926# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004927}
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004928#endif
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004929
4930#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4931/*
4932 * ":[N]match {group} {pattern}"
4933 * Sets nextcmd to the start of the next command, if any. Also called when
4934 * skipping commands to find the next command.
4935 */
4936 void
4937ex_match(exarg_T *eap)
4938{
4939 char_u *p;
4940 char_u *g = NULL;
4941 char_u *end;
4942 int c;
4943 int id;
4944
4945 if (eap->line2 <= 3)
4946 id = eap->line2;
4947 else
4948 {
4949 emsg(_(e_invcmd));
4950 return;
4951 }
4952
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004953 // First clear any old pattern.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004954 if (!eap->skip)
4955 match_delete(curwin, id, FALSE);
4956
4957 if (ends_excmd(*eap->arg))
4958 end = eap->arg;
4959 else if ((STRNICMP(eap->arg, "none", 4) == 0
4960 && (VIM_ISWHITE(eap->arg[4]) || ends_excmd(eap->arg[4]))))
4961 end = eap->arg + 4;
4962 else
4963 {
4964 p = skiptowhite(eap->arg);
4965 if (!eap->skip)
4966 g = vim_strnsave(eap->arg, (int)(p - eap->arg));
4967 p = skipwhite(p);
4968 if (*p == NUL)
4969 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004970 // There must be two arguments.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004971 vim_free(g);
4972 semsg(_(e_invarg2), eap->arg);
4973 return;
4974 }
4975 end = skip_regexp(p + 1, *p, TRUE, NULL);
4976 if (!eap->skip)
4977 {
4978 if (*end != NUL && !ends_excmd(*skipwhite(end + 1)))
4979 {
4980 vim_free(g);
4981 eap->errmsg = e_trailing;
4982 return;
4983 }
4984 if (*end != *p)
4985 {
4986 vim_free(g);
4987 semsg(_(e_invarg2), p);
4988 return;
4989 }
4990
4991 c = *end;
4992 *end = NUL;
4993 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
4994 vim_free(g);
4995 *end = c;
4996 }
4997 }
4998 eap->nextcmd = find_nextcmd(end);
4999}
5000#endif