blob: e44696675204f7aff2d60b919136c2cb268d04d4 [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
Bram Moolenaare023e882020-05-31 16:42:30 +020053 int sg_cterm_ul; // terminal ul color number + 1
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020054 int sg_cterm_attr; // Screen attr for color term mode
55// for when using the GUI
56#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
57 guicolor_T sg_gui_fg; // GUI foreground color handle
58 guicolor_T sg_gui_bg; // GUI background color handle
Bram Moolenaare023e882020-05-31 16:42:30 +020059 guicolor_T sg_gui_sp; // GUI special color handle
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020060#endif
61#ifdef FEAT_GUI
Bram Moolenaar2ac6e822019-07-15 22:40:22 +020062 GuiFont sg_font; // GUI font handle
63#ifdef FEAT_XFONTSET
64 GuiFontset sg_fontset; // GUI fontset handle
65#endif
66 char_u *sg_font_name; // GUI font or fontset name
67 int sg_gui_attr; // Screen attr for GUI mode
68#endif
69#if defined(FEAT_GUI) || defined(FEAT_EVAL)
70// Store the sp color name for the GUI or synIDattr()
71 int sg_gui; // "gui=" highlighting attributes
72 char_u *sg_gui_fg_name;// GUI foreground color name
73 char_u *sg_gui_bg_name;// GUI background color name
74 char_u *sg_gui_sp_name;// GUI special color name
75#endif
76 int sg_link; // link to this highlight group ID
77 int sg_set; // combination of SG_* flags
78#ifdef FEAT_EVAL
79 sctx_T sg_script_ctx; // script in which the group was last set
80#endif
81} hl_group_T;
82
83// highlight groups for 'highlight' option
84static garray_T highlight_ga;
85#define HL_TABLE() ((hl_group_T *)((highlight_ga.ga_data)))
86
87/*
88 * An attribute number is the index in attr_table plus ATTR_OFF.
89 */
90#define ATTR_OFF (HL_ALL + 1)
91
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +020092static void syn_unadd_group(void);
93static void set_hl_attr(int idx);
94static void highlight_list_one(int id);
95static int highlight_list_arg(int id, int didh, int type, int iarg, char_u *sarg, char *name);
96static int syn_add_group(char_u *name);
97static int hl_has_settings(int idx, int check_link);
98static void highlight_clear(int idx);
99
100#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
101static void gui_do_one_color(int idx, int do_menu, int do_tooltip);
102#endif
103#ifdef FEAT_GUI
104static int set_group_colors(char_u *name, guicolor_T *fgp, guicolor_T *bgp, int do_menu, int use_norm, int do_tooltip);
105static void hl_do_font(int idx, char_u *arg, int do_normal, int do_menu, int do_tooltip, int free_font);
106#endif
107
108/*
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200109 * The default highlight groups. These are compiled-in for fast startup and
110 * they still work when the runtime files can't be found.
111 * When making changes here, also change runtime/colors/default.vim!
112 * The #ifdefs are needed to reduce the amount of static data. Helps to make
113 * the 16 bit DOS (museum) version compile.
114 */
115#if defined(FEAT_GUI) || defined(FEAT_EVAL)
116# define CENT(a, b) b
117#else
118# define CENT(a, b) a
119#endif
120static char *(highlight_init_both[]) = {
121 CENT("ErrorMsg term=standout ctermbg=DarkRed ctermfg=White",
122 "ErrorMsg term=standout ctermbg=DarkRed ctermfg=White guibg=Red guifg=White"),
123 CENT("IncSearch term=reverse cterm=reverse",
124 "IncSearch term=reverse cterm=reverse gui=reverse"),
125 CENT("ModeMsg term=bold cterm=bold",
126 "ModeMsg term=bold cterm=bold gui=bold"),
127 CENT("NonText term=bold ctermfg=Blue",
128 "NonText term=bold ctermfg=Blue gui=bold guifg=Blue"),
129 CENT("StatusLine term=reverse,bold cterm=reverse,bold",
130 "StatusLine term=reverse,bold cterm=reverse,bold gui=reverse,bold"),
131 CENT("StatusLineNC term=reverse cterm=reverse",
132 "StatusLineNC term=reverse cterm=reverse gui=reverse"),
133 "default link EndOfBuffer NonText",
134 CENT("VertSplit term=reverse cterm=reverse",
135 "VertSplit term=reverse cterm=reverse gui=reverse"),
136#ifdef FEAT_CLIPBOARD
137 CENT("VisualNOS term=underline,bold cterm=underline,bold",
138 "VisualNOS term=underline,bold cterm=underline,bold gui=underline,bold"),
139#endif
140#ifdef FEAT_DIFF
141 CENT("DiffText term=reverse cterm=bold ctermbg=Red",
142 "DiffText term=reverse cterm=bold ctermbg=Red gui=bold guibg=Red"),
143#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200144 CENT("PmenuSbar ctermbg=Grey",
145 "PmenuSbar ctermbg=Grey guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200146 CENT("TabLineSel term=bold cterm=bold",
147 "TabLineSel term=bold cterm=bold gui=bold"),
148 CENT("TabLineFill term=reverse cterm=reverse",
149 "TabLineFill term=reverse cterm=reverse gui=reverse"),
150#ifdef FEAT_GUI
151 "Cursor guibg=fg guifg=bg",
152 "lCursor guibg=fg guifg=bg", // should be different, but what?
153#endif
154 "default link QuickFixLine Search",
155 CENT("Normal cterm=NONE", "Normal gui=NONE"),
156 NULL
157};
158
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200159// Default colors only used with a light background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200160static char *(highlight_init_light[]) = {
161 CENT("Directory term=bold ctermfg=DarkBlue",
162 "Directory term=bold ctermfg=DarkBlue guifg=Blue"),
163 CENT("LineNr term=underline ctermfg=Brown",
164 "LineNr term=underline ctermfg=Brown guifg=Brown"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200165 CENT("CursorLineNr term=bold cterm=underline ctermfg=Brown",
166 "CursorLineNr term=bold cterm=underline ctermfg=Brown gui=bold guifg=Brown"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200167 CENT("MoreMsg term=bold ctermfg=DarkGreen",
168 "MoreMsg term=bold ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
169 CENT("Question term=standout ctermfg=DarkGreen",
170 "Question term=standout ctermfg=DarkGreen gui=bold guifg=SeaGreen"),
171 CENT("Search term=reverse ctermbg=Yellow ctermfg=NONE",
172 "Search term=reverse ctermbg=Yellow ctermfg=NONE guibg=Yellow guifg=NONE"),
173#ifdef FEAT_SPELL
174 CENT("SpellBad term=reverse ctermbg=LightRed",
175 "SpellBad term=reverse ctermbg=LightRed guisp=Red gui=undercurl"),
176 CENT("SpellCap term=reverse ctermbg=LightBlue",
177 "SpellCap term=reverse ctermbg=LightBlue guisp=Blue gui=undercurl"),
178 CENT("SpellRare term=reverse ctermbg=LightMagenta",
179 "SpellRare term=reverse ctermbg=LightMagenta guisp=Magenta gui=undercurl"),
180 CENT("SpellLocal term=underline ctermbg=Cyan",
181 "SpellLocal term=underline ctermbg=Cyan guisp=DarkCyan gui=undercurl"),
182#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200183 CENT("PmenuThumb ctermbg=Black",
184 "PmenuThumb ctermbg=Black guibg=Black"),
185 CENT("Pmenu ctermbg=LightMagenta ctermfg=Black",
186 "Pmenu ctermbg=LightMagenta ctermfg=Black guibg=LightMagenta"),
187 CENT("PmenuSel ctermbg=LightGrey ctermfg=Black",
188 "PmenuSel ctermbg=LightGrey ctermfg=Black guibg=Grey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200189 CENT("SpecialKey term=bold ctermfg=DarkBlue",
190 "SpecialKey term=bold ctermfg=DarkBlue guifg=Blue"),
191 CENT("Title term=bold ctermfg=DarkMagenta",
192 "Title term=bold ctermfg=DarkMagenta gui=bold guifg=Magenta"),
193 CENT("WarningMsg term=standout ctermfg=DarkRed",
194 "WarningMsg term=standout ctermfg=DarkRed guifg=Red"),
195#ifdef FEAT_WILDMENU
196 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
197 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
198#endif
199#ifdef FEAT_FOLDING
200 CENT("Folded term=standout ctermbg=Grey ctermfg=DarkBlue",
201 "Folded term=standout ctermbg=Grey ctermfg=DarkBlue guibg=LightGrey guifg=DarkBlue"),
202 CENT("FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
203 "FoldColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
204#endif
205#ifdef FEAT_SIGNS
206 CENT("SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue",
207 "SignColumn term=standout ctermbg=Grey ctermfg=DarkBlue guibg=Grey guifg=DarkBlue"),
208#endif
209 CENT("Visual term=reverse",
210 "Visual term=reverse guibg=LightGrey"),
211#ifdef FEAT_DIFF
212 CENT("DiffAdd term=bold ctermbg=LightBlue",
213 "DiffAdd term=bold ctermbg=LightBlue guibg=LightBlue"),
214 CENT("DiffChange term=bold ctermbg=LightMagenta",
215 "DiffChange term=bold ctermbg=LightMagenta guibg=LightMagenta"),
216 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan",
217 "DiffDelete term=bold ctermfg=Blue ctermbg=LightCyan gui=bold guifg=Blue guibg=LightCyan"),
218#endif
219 CENT("TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey",
220 "TabLine term=underline cterm=underline ctermfg=black ctermbg=LightGrey gui=underline guibg=LightGrey"),
221#ifdef FEAT_SYN_HL
222 CENT("CursorColumn term=reverse ctermbg=LightGrey",
223 "CursorColumn term=reverse ctermbg=LightGrey guibg=Grey90"),
224 CENT("CursorLine term=underline cterm=underline",
225 "CursorLine term=underline cterm=underline guibg=Grey90"),
226 CENT("ColorColumn term=reverse ctermbg=LightRed",
227 "ColorColumn term=reverse ctermbg=LightRed guibg=LightRed"),
228#endif
229#ifdef FEAT_CONCEAL
230 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
231 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
232#endif
233 CENT("MatchParen term=reverse ctermbg=Cyan",
234 "MatchParen term=reverse ctermbg=Cyan guibg=Cyan"),
235#ifdef FEAT_TERMINAL
236 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen",
237 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=White ctermbg=DarkGreen gui=bold guifg=bg guibg=DarkGreen"),
238 CENT("StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen",
239 "StatusLineTermNC term=reverse ctermfg=White ctermbg=DarkGreen guifg=bg guibg=DarkGreen"),
240#endif
241#ifdef FEAT_MENU
242 CENT("ToolbarLine term=underline ctermbg=LightGrey",
243 "ToolbarLine term=underline ctermbg=LightGrey guibg=LightGrey"),
244 CENT("ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey",
245 "ToolbarButton cterm=bold ctermfg=White ctermbg=DarkGrey gui=bold guifg=White guibg=Grey40"),
246#endif
247 NULL
248};
249
Bram Moolenaar1f164b12019-07-24 19:00:36 +0200250// Default colors only used with a dark background.
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200251static char *(highlight_init_dark[]) = {
252 CENT("Directory term=bold ctermfg=LightCyan",
253 "Directory term=bold ctermfg=LightCyan guifg=Cyan"),
254 CENT("LineNr term=underline ctermfg=Yellow",
255 "LineNr term=underline ctermfg=Yellow guifg=Yellow"),
Bram Moolenaar017ba072019-09-14 21:01:23 +0200256 CENT("CursorLineNr term=bold cterm=underline ctermfg=Yellow",
257 "CursorLineNr term=bold cterm=underline ctermfg=Yellow gui=bold guifg=Yellow"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200258 CENT("MoreMsg term=bold ctermfg=LightGreen",
259 "MoreMsg term=bold ctermfg=LightGreen gui=bold guifg=SeaGreen"),
260 CENT("Question term=standout ctermfg=LightGreen",
261 "Question term=standout ctermfg=LightGreen gui=bold guifg=Green"),
262 CENT("Search term=reverse ctermbg=Yellow ctermfg=Black",
263 "Search term=reverse ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
264 CENT("SpecialKey term=bold ctermfg=LightBlue",
265 "SpecialKey term=bold ctermfg=LightBlue guifg=Cyan"),
266#ifdef FEAT_SPELL
267 CENT("SpellBad term=reverse ctermbg=Red",
268 "SpellBad term=reverse ctermbg=Red guisp=Red gui=undercurl"),
269 CENT("SpellCap term=reverse ctermbg=Blue",
270 "SpellCap term=reverse ctermbg=Blue guisp=Blue gui=undercurl"),
271 CENT("SpellRare term=reverse ctermbg=Magenta",
272 "SpellRare term=reverse ctermbg=Magenta guisp=Magenta gui=undercurl"),
273 CENT("SpellLocal term=underline ctermbg=Cyan",
274 "SpellLocal term=underline ctermbg=Cyan guisp=Cyan gui=undercurl"),
275#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200276 CENT("PmenuThumb ctermbg=White",
277 "PmenuThumb ctermbg=White guibg=White"),
278 CENT("Pmenu ctermbg=Magenta ctermfg=Black",
279 "Pmenu ctermbg=Magenta ctermfg=Black guibg=Magenta"),
280 CENT("PmenuSel ctermbg=Black ctermfg=DarkGrey",
281 "PmenuSel ctermbg=Black ctermfg=DarkGrey guibg=DarkGrey"),
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200282 CENT("Title term=bold ctermfg=LightMagenta",
283 "Title term=bold ctermfg=LightMagenta gui=bold guifg=Magenta"),
284 CENT("WarningMsg term=standout ctermfg=LightRed",
285 "WarningMsg term=standout ctermfg=LightRed guifg=Red"),
286#ifdef FEAT_WILDMENU
287 CENT("WildMenu term=standout ctermbg=Yellow ctermfg=Black",
288 "WildMenu term=standout ctermbg=Yellow ctermfg=Black guibg=Yellow guifg=Black"),
289#endif
290#ifdef FEAT_FOLDING
291 CENT("Folded term=standout ctermbg=DarkGrey ctermfg=Cyan",
292 "Folded term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=DarkGrey guifg=Cyan"),
293 CENT("FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
294 "FoldColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
295#endif
296#ifdef FEAT_SIGNS
297 CENT("SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan",
298 "SignColumn term=standout ctermbg=DarkGrey ctermfg=Cyan guibg=Grey guifg=Cyan"),
299#endif
300 CENT("Visual term=reverse",
301 "Visual term=reverse guibg=DarkGrey"),
302#ifdef FEAT_DIFF
303 CENT("DiffAdd term=bold ctermbg=DarkBlue",
304 "DiffAdd term=bold ctermbg=DarkBlue guibg=DarkBlue"),
305 CENT("DiffChange term=bold ctermbg=DarkMagenta",
306 "DiffChange term=bold ctermbg=DarkMagenta guibg=DarkMagenta"),
307 CENT("DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan",
308 "DiffDelete term=bold ctermfg=Blue ctermbg=DarkCyan gui=bold guifg=Blue guibg=DarkCyan"),
309#endif
310 CENT("TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey",
311 "TabLine term=underline cterm=underline ctermfg=white ctermbg=DarkGrey gui=underline guibg=DarkGrey"),
312#ifdef FEAT_SYN_HL
313 CENT("CursorColumn term=reverse ctermbg=DarkGrey",
314 "CursorColumn term=reverse ctermbg=DarkGrey guibg=Grey40"),
315 CENT("CursorLine term=underline cterm=underline",
316 "CursorLine term=underline cterm=underline guibg=Grey40"),
317 CENT("ColorColumn term=reverse ctermbg=DarkRed",
318 "ColorColumn term=reverse ctermbg=DarkRed guibg=DarkRed"),
319#endif
320 CENT("MatchParen term=reverse ctermbg=DarkCyan",
321 "MatchParen term=reverse ctermbg=DarkCyan guibg=DarkCyan"),
322#ifdef FEAT_CONCEAL
323 CENT("Conceal ctermbg=DarkGrey ctermfg=LightGrey",
324 "Conceal ctermbg=DarkGrey ctermfg=LightGrey guibg=DarkGrey guifg=LightGrey"),
325#endif
326#ifdef FEAT_TERMINAL
327 CENT("StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen",
328 "StatusLineTerm term=reverse,bold cterm=bold ctermfg=Black ctermbg=LightGreen gui=bold guifg=bg guibg=LightGreen"),
329 CENT("StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen",
330 "StatusLineTermNC term=reverse ctermfg=Black ctermbg=LightGreen guifg=bg guibg=LightGreen"),
331#endif
332#ifdef FEAT_MENU
333 CENT("ToolbarLine term=underline ctermbg=DarkGrey",
334 "ToolbarLine term=underline ctermbg=DarkGrey guibg=Grey50"),
335 CENT("ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey",
336 "ToolbarButton cterm=bold ctermfg=Black ctermbg=LightGrey gui=bold guifg=Black guibg=LightGrey"),
337#endif
338 NULL
339};
340
Bram Moolenaar2ac6e822019-07-15 22:40:22 +0200341/*
342 * Returns the number of highlight groups.
343 */
344 int
345highlight_num_groups(void)
346{
347 return highlight_ga.ga_len;
348}
349
350/*
351 * Returns the name of a highlight group.
352 */
353 char_u *
354highlight_group_name(int id)
355{
356 return HL_TABLE()[id].sg_name;
357}
358
359/*
360 * Returns the ID of the link to a highlight group.
361 */
362 int
363highlight_link_id(int id)
364{
365 return HL_TABLE()[id].sg_link;
366}
367
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200368 void
369init_highlight(
370 int both, // include groups where 'bg' doesn't matter
371 int reset) // clear group first
372{
373 int i;
374 char **pp;
375 static int had_both = FALSE;
376#ifdef FEAT_EVAL
377 char_u *p;
378
379 /*
380 * Try finding the color scheme file. Used when a color file was loaded
381 * and 'background' or 't_Co' is changed.
382 */
383 p = get_var_value((char_u *)"g:colors_name");
384 if (p != NULL)
385 {
386 // The value of g:colors_name could be freed when sourcing the script,
387 // making "p" invalid, so copy it.
388 char_u *copy_p = vim_strsave(p);
389 int r;
390
391 if (copy_p != NULL)
392 {
393 r = load_colors(copy_p);
394 vim_free(copy_p);
395 if (r == OK)
396 return;
397 }
398 }
399
400#endif
401
402 /*
403 * Didn't use a color file, use the compiled-in colors.
404 */
405 if (both)
406 {
407 had_both = TRUE;
408 pp = highlight_init_both;
409 for (i = 0; pp[i] != NULL; ++i)
410 do_highlight((char_u *)pp[i], reset, TRUE);
411 }
412 else if (!had_both)
413 // Don't do anything before the call with both == TRUE from main().
414 // Not everything has been setup then, and that call will overrule
415 // everything anyway.
416 return;
417
418 if (*p_bg == 'l')
419 pp = highlight_init_light;
420 else
421 pp = highlight_init_dark;
422 for (i = 0; pp[i] != NULL; ++i)
423 do_highlight((char_u *)pp[i], reset, TRUE);
424
425 // Reverse looks ugly, but grey may not work for 8 colors. Thus let it
426 // depend on the number of colors available.
427 // With 8 colors brown is equal to yellow, need to use black for Search fg
428 // to avoid Statement highlighted text disappears.
429 // Clear the attributes, needed when changing the t_Co value.
430 if (t_colors > 8)
431 do_highlight((char_u *)(*p_bg == 'l'
432 ? "Visual cterm=NONE ctermbg=LightGrey"
433 : "Visual cterm=NONE ctermbg=DarkGrey"), FALSE, TRUE);
434 else
435 {
436 do_highlight((char_u *)"Visual cterm=reverse ctermbg=NONE",
437 FALSE, TRUE);
438 if (*p_bg == 'l')
439 do_highlight((char_u *)"Search ctermfg=black", FALSE, TRUE);
440 }
441
442#ifdef FEAT_SYN_HL
443 /*
444 * If syntax highlighting is enabled load the highlighting for it.
445 */
446 if (get_var_value((char_u *)"g:syntax_on") != NULL)
447 {
448 static int recursive = 0;
449
450 if (recursive >= 5)
451 emsg(_("E679: recursive loop loading syncolor.vim"));
452 else
453 {
454 ++recursive;
455 (void)source_runtime((char_u *)"syntax/syncolor.vim", DIP_ALL);
456 --recursive;
457 }
458 }
459#endif
460}
461
462/*
463 * Load color file "name".
464 * Return OK for success, FAIL for failure.
465 */
466 int
467load_colors(char_u *name)
468{
469 char_u *buf;
470 int retval = FAIL;
471 static int recursive = FALSE;
472
473 // When being called recursively, this is probably because setting
474 // 'background' caused the highlighting to be reloaded. This means it is
475 // working, thus we should return OK.
476 if (recursive)
477 return OK;
478
479 recursive = TRUE;
480 buf = alloc(STRLEN(name) + 12);
481 if (buf != NULL)
482 {
483 apply_autocmds(EVENT_COLORSCHEMEPRE, name,
484 curbuf->b_fname, FALSE, curbuf);
485 sprintf((char *)buf, "colors/%s.vim", name);
486 retval = source_runtime(buf, DIP_START + DIP_OPT);
487 vim_free(buf);
488 apply_autocmds(EVENT_COLORSCHEME, name, curbuf->b_fname, FALSE, curbuf);
489 }
490 recursive = FALSE;
491
492 return retval;
493}
494
495static char *(color_names[28]) = {
496 "Black", "DarkBlue", "DarkGreen", "DarkCyan",
497 "DarkRed", "DarkMagenta", "Brown", "DarkYellow",
498 "Gray", "Grey", "LightGray", "LightGrey",
499 "DarkGray", "DarkGrey",
500 "Blue", "LightBlue", "Green", "LightGreen",
501 "Cyan", "LightCyan", "Red", "LightRed", "Magenta",
502 "LightMagenta", "Yellow", "LightYellow", "White", "NONE"};
503 // indices:
504 // 0, 1, 2, 3,
505 // 4, 5, 6, 7,
506 // 8, 9, 10, 11,
507 // 12, 13,
508 // 14, 15, 16, 17,
509 // 18, 19, 20, 21, 22,
510 // 23, 24, 25, 26, 27
511static int color_numbers_16[28] = {0, 1, 2, 3,
512 4, 5, 6, 6,
513 7, 7, 7, 7,
514 8, 8,
515 9, 9, 10, 10,
516 11, 11, 12, 12, 13,
517 13, 14, 14, 15, -1};
518// for xterm with 88 colors...
519static int color_numbers_88[28] = {0, 4, 2, 6,
520 1, 5, 32, 72,
521 84, 84, 7, 7,
522 82, 82,
523 12, 43, 10, 61,
524 14, 63, 9, 74, 13,
525 75, 11, 78, 15, -1};
526// for xterm with 256 colors...
527static int color_numbers_256[28] = {0, 4, 2, 6,
Bram Moolenaare93c9682020-04-25 15:35:32 +0200528 1, 5, 130, 3,
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200529 248, 248, 7, 7,
530 242, 242,
531 12, 81, 10, 121,
532 14, 159, 9, 224, 13,
533 225, 11, 229, 15, -1};
534// for terminals with less than 16 colors...
535static int color_numbers_8[28] = {0, 4, 2, 6,
536 1, 5, 3, 3,
537 7, 7, 7, 7,
538 0+8, 0+8,
539 4+8, 4+8, 2+8, 2+8,
540 6+8, 6+8, 1+8, 1+8, 5+8,
541 5+8, 3+8, 3+8, 7+8, -1};
542
543/*
544 * Lookup the "cterm" value to be used for color with index "idx" in
545 * color_names[].
546 * "boldp" will be set to TRUE or FALSE for a foreground color when using 8
547 * colors, otherwise it will be unchanged.
548 */
549 int
550lookup_color(int idx, int foreground, int *boldp)
551{
552 int color = color_numbers_16[idx];
553 char_u *p;
554
555 // Use the _16 table to check if it's a valid color name.
556 if (color < 0)
557 return -1;
558
559 if (t_colors == 8)
560 {
561 // t_Co is 8: use the 8 colors table
562#if defined(__QNXNTO__)
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100563 // On qnx, the 8 & 16 color arrays are the same
564 if (STRNCMP(T_NAME, "qansi", 5) == 0)
565 color = color_numbers_16[idx];
566 else
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200567#endif
Bram Moolenaarc95e8d62019-12-05 18:35:44 +0100568 color = color_numbers_8[idx];
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200569 if (foreground)
570 {
571 // set/reset bold attribute to get light foreground
572 // colors (on some terminals, e.g. "linux")
573 if (color & 8)
574 *boldp = TRUE;
575 else
576 *boldp = FALSE;
577 }
578 color &= 7; // truncate to 8 colors
579 }
580 else if (t_colors == 16 || t_colors == 88
581 || t_colors >= 256)
582 {
583 /*
584 * Guess: if the termcap entry ends in 'm', it is
585 * probably an xterm-like terminal. Use the changed
586 * order for colors.
587 */
588 if (*T_CAF != NUL)
589 p = T_CAF;
590 else
591 p = T_CSF;
592 if (*p != NUL && (t_colors > 256
593 || *(p + STRLEN(p) - 1) == 'm'))
594 {
595 if (t_colors == 88)
596 color = color_numbers_88[idx];
597 else if (t_colors >= 256)
598 color = color_numbers_256[idx];
599 else
600 color = color_numbers_8[idx];
601 }
602#ifdef FEAT_TERMRESPONSE
603 if (t_colors >= 256 && color == 15 && is_mac_terminal)
604 // Terminal.app has a bug: 15 is light grey. Use white
605 // from the color cube instead.
606 color = 231;
607#endif
608 }
609 return color;
610}
611
612/*
613 * Handle the ":highlight .." command.
614 * When using ":hi clear" this is called recursively for each group with
615 * "forceit" and "init" both TRUE.
616 */
617 void
618do_highlight(
619 char_u *line,
620 int forceit,
621 int init) // TRUE when called for initializing
622{
623 char_u *name_end;
624 char_u *p;
625 char_u *linep;
626 char_u *key_start;
627 char_u *arg_start;
628 char_u *key = NULL, *arg = NULL;
629 long i;
630 int off;
631 int len;
632 int attr;
633 int id;
634 int idx;
635 hl_group_T item_before;
636 int did_change = FALSE;
637 int dodefault = FALSE;
638 int doclear = FALSE;
639 int dolink = FALSE;
640 int error = FALSE;
641 int color;
642 int is_normal_group = FALSE; // "Normal" group
643#ifdef FEAT_TERMINAL
644 int is_terminal_group = FALSE; // "Terminal" group
645#endif
646#ifdef FEAT_GUI_X11
647 int is_menu_group = FALSE; // "Menu" group
648 int is_scrollbar_group = FALSE; // "Scrollbar" group
649 int is_tooltip_group = FALSE; // "Tooltip" group
650 int do_colors = FALSE; // need to update colors?
651#else
652# define is_menu_group 0
653# define is_tooltip_group 0
654#endif
655#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
656 int did_highlight_changed = FALSE;
657#endif
658
659 /*
660 * If no argument, list current highlighting.
661 */
Bram Moolenaar2c5ed4e2020-04-20 19:42:10 +0200662 if (!init && ends_excmd2(line - 1, line))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200663 {
664 for (i = 1; i <= highlight_ga.ga_len && !got_int; ++i)
665 // TODO: only call when the group has attributes set
666 highlight_list_one((int)i);
667 return;
668 }
669
670 /*
671 * Isolate the name.
672 */
673 name_end = skiptowhite(line);
674 linep = skipwhite(name_end);
675
676 /*
677 * Check for "default" argument.
678 */
679 if (STRNCMP(line, "default", name_end - line) == 0)
680 {
681 dodefault = TRUE;
682 line = linep;
683 name_end = skiptowhite(line);
684 linep = skipwhite(name_end);
685 }
686
687 /*
688 * Check for "clear" or "link" argument.
689 */
690 if (STRNCMP(line, "clear", name_end - line) == 0)
691 doclear = TRUE;
692 if (STRNCMP(line, "link", name_end - line) == 0)
693 dolink = TRUE;
694
695 /*
696 * ":highlight {group-name}": list highlighting for one group.
697 */
Bram Moolenaar1966c242020-04-20 22:42:32 +0200698 if (!doclear && !dolink && ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200699 {
700 id = syn_namen2id(line, (int)(name_end - line));
701 if (id == 0)
702 semsg(_("E411: highlight group not found: %s"), line);
703 else
704 highlight_list_one(id);
705 return;
706 }
707
708 /*
709 * Handle ":highlight link {from} {to}" command.
710 */
711 if (dolink)
712 {
713 char_u *from_start = linep;
714 char_u *from_end;
715 char_u *to_start;
716 char_u *to_end;
717 int from_id;
718 int to_id;
719
720 from_end = skiptowhite(from_start);
721 to_start = skipwhite(from_end);
722 to_end = skiptowhite(to_start);
723
Bram Moolenaar1966c242020-04-20 22:42:32 +0200724 if (ends_excmd2(line, from_start) || ends_excmd2(line, to_start))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200725 {
726 semsg(_("E412: Not enough arguments: \":highlight link %s\""),
727 from_start);
728 return;
729 }
730
Bram Moolenaar1966c242020-04-20 22:42:32 +0200731 if (!ends_excmd2(line, skipwhite(to_end)))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200732 {
733 semsg(_("E413: Too many arguments: \":highlight link %s\""), from_start);
734 return;
735 }
736
737 from_id = syn_check_group(from_start, (int)(from_end - from_start));
738 if (STRNCMP(to_start, "NONE", 4) == 0)
739 to_id = 0;
740 else
741 to_id = syn_check_group(to_start, (int)(to_end - to_start));
742
743 if (from_id > 0 && (!init || HL_TABLE()[from_id - 1].sg_set == 0))
744 {
745 /*
746 * Don't allow a link when there already is some highlighting
747 * for the group, unless '!' is used
748 */
749 if (to_id > 0 && !forceit && !init
750 && hl_has_settings(from_id - 1, dodefault))
751 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100752 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200753 emsg(_("E414: group has settings, highlight link ignored"));
754 }
755 else if (HL_TABLE()[from_id - 1].sg_link != to_id
756#ifdef FEAT_EVAL
757 || HL_TABLE()[from_id - 1].sg_script_ctx.sc_sid
758 != current_sctx.sc_sid
759#endif
760 || HL_TABLE()[from_id - 1].sg_cleared)
761 {
762 if (!init)
763 HL_TABLE()[from_id - 1].sg_set |= SG_LINK;
764 HL_TABLE()[from_id - 1].sg_link = to_id;
765#ifdef FEAT_EVAL
766 HL_TABLE()[from_id - 1].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100767 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200768#endif
769 HL_TABLE()[from_id - 1].sg_cleared = FALSE;
770 redraw_all_later(SOME_VALID);
771
772 // Only call highlight_changed() once after multiple changes.
773 need_highlight_changed = TRUE;
774 }
775 }
776
777 return;
778 }
779
780 if (doclear)
781 {
782 /*
783 * ":highlight clear [group]" command.
784 */
Bram Moolenaar1966c242020-04-20 22:42:32 +0200785 if (ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200786 {
787#ifdef FEAT_GUI
788 // First, we do not destroy the old values, but allocate the new
789 // ones and update the display. THEN we destroy the old values.
790 // If we destroy the old values first, then the old values
791 // (such as GuiFont's or GuiFontset's) will still be displayed but
792 // invalid because they were free'd.
793 if (gui.in_use)
794 {
795# ifdef FEAT_BEVAL_TIP
796 gui_init_tooltip_font();
797# endif
798# if defined(FEAT_MENU) && (defined(FEAT_GUI_ATHENA) || defined(FEAT_GUI_MOTIF))
799 gui_init_menu_font();
800# endif
801 }
802# if defined(FEAT_GUI_MSWIN) || defined(FEAT_GUI_X11)
803 gui_mch_def_colors();
804# endif
805# ifdef FEAT_GUI_X11
806# ifdef FEAT_MENU
807
808 // This only needs to be done when there is no Menu highlight
809 // group defined by default, which IS currently the case.
810 gui_mch_new_menu_colors();
811# endif
812 if (gui.in_use)
813 {
814 gui_new_scrollbar_colors();
815# ifdef FEAT_BEVAL_GUI
816 gui_mch_new_tooltip_colors();
817# endif
818# ifdef FEAT_MENU
819 gui_mch_new_menu_font();
820# endif
821 }
822# endif
823
824 // Ok, we're done allocating the new default graphics items.
825 // The screen should already be refreshed at this point.
826 // It is now Ok to clear out the old data.
827#endif
828#ifdef FEAT_EVAL
Bram Moolenaar1966c242020-04-20 22:42:32 +0200829 do_unlet((char_u *)"g:colors_name", TRUE);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200830#endif
831 restore_cterm_colors();
832
833 /*
834 * Clear all default highlight groups and load the defaults.
835 */
836 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
837 highlight_clear(idx);
838 init_highlight(TRUE, TRUE);
839#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
840 if (USE_24BIT)
841 highlight_gui_started();
842 else
843#endif
844 highlight_changed();
845 redraw_later_clear();
846 return;
847 }
Bram Moolenaar1966c242020-04-20 22:42:32 +0200848 line = linep;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200849 name_end = skiptowhite(line);
850 linep = skipwhite(name_end);
851 }
852
853 /*
854 * Find the group name in the table. If it does not exist yet, add it.
855 */
856 id = syn_check_group(line, (int)(name_end - line));
857 if (id == 0) // failed (out of memory)
858 return;
859 idx = id - 1; // index is ID minus one
860
861 // Return if "default" was used and the group already has settings.
862 if (dodefault && hl_has_settings(idx, TRUE))
863 return;
864
865 // Make a copy so we can check if any attribute actually changed.
866 item_before = HL_TABLE()[idx];
867
868 if (STRCMP(HL_TABLE()[idx].sg_name_u, "NORMAL") == 0)
869 is_normal_group = TRUE;
870#ifdef FEAT_TERMINAL
871 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TERMINAL") == 0)
872 is_terminal_group = TRUE;
873#endif
874#ifdef FEAT_GUI_X11
875 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "MENU") == 0)
876 is_menu_group = TRUE;
877 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "SCROLLBAR") == 0)
878 is_scrollbar_group = TRUE;
879 else if (STRCMP(HL_TABLE()[idx].sg_name_u, "TOOLTIP") == 0)
880 is_tooltip_group = TRUE;
881#endif
882
883 // Clear the highlighting for ":hi clear {group}" and ":hi clear".
884 if (doclear || (forceit && init))
885 {
886 highlight_clear(idx);
887 if (!doclear)
888 HL_TABLE()[idx].sg_set = 0;
889 }
890
891 if (!doclear)
Bram Moolenaar1966c242020-04-20 22:42:32 +0200892 while (!ends_excmd2(line, linep))
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200893 {
894 key_start = linep;
895 if (*linep == '=')
896 {
897 semsg(_("E415: unexpected equal sign: %s"), key_start);
898 error = TRUE;
899 break;
900 }
901
902 /*
903 * Isolate the key ("term", "ctermfg", "ctermbg", "font", "guifg" or
904 * "guibg").
905 */
906 while (*linep && !VIM_ISWHITE(*linep) && *linep != '=')
907 ++linep;
908 vim_free(key);
909 key = vim_strnsave_up(key_start, (int)(linep - key_start));
910 if (key == NULL)
911 {
912 error = TRUE;
913 break;
914 }
915 linep = skipwhite(linep);
916
917 if (STRCMP(key, "NONE") == 0)
918 {
919 if (!init || HL_TABLE()[idx].sg_set == 0)
920 {
921 if (!init)
922 HL_TABLE()[idx].sg_set |= SG_TERM+SG_CTERM+SG_GUI;
923 highlight_clear(idx);
924 }
925 continue;
926 }
927
928 /*
929 * Check for the equal sign.
930 */
931 if (*linep != '=')
932 {
933 semsg(_("E416: missing equal sign: %s"), key_start);
934 error = TRUE;
935 break;
936 }
937 ++linep;
938
939 /*
940 * Isolate the argument.
941 */
942 linep = skipwhite(linep);
943 if (*linep == '\'') // guifg='color name'
944 {
945 arg_start = ++linep;
946 linep = vim_strchr(linep, '\'');
947 if (linep == NULL)
948 {
949 semsg(_(e_invarg2), key_start);
950 error = TRUE;
951 break;
952 }
953 }
954 else
955 {
956 arg_start = linep;
957 linep = skiptowhite(linep);
958 }
959 if (linep == arg_start)
960 {
961 semsg(_("E417: missing argument: %s"), key_start);
962 error = TRUE;
963 break;
964 }
965 vim_free(arg);
966 arg = vim_strnsave(arg_start, (int)(linep - arg_start));
967 if (arg == NULL)
968 {
969 error = TRUE;
970 break;
971 }
972 if (*linep == '\'')
973 ++linep;
974
975 /*
976 * Store the argument.
977 */
978 if ( STRCMP(key, "TERM") == 0
979 || STRCMP(key, "CTERM") == 0
980 || STRCMP(key, "GUI") == 0)
981 {
982 attr = 0;
983 off = 0;
984 while (arg[off] != NUL)
985 {
986 for (i = sizeof(hl_attr_table) / sizeof(int); --i >= 0; )
987 {
988 len = (int)STRLEN(hl_name_table[i]);
989 if (STRNICMP(arg + off, hl_name_table[i], len) == 0)
990 {
991 attr |= hl_attr_table[i];
992 off += len;
993 break;
994 }
995 }
996 if (i < 0)
997 {
998 semsg(_("E418: Illegal value: %s"), arg);
999 error = TRUE;
1000 break;
1001 }
1002 if (arg[off] == ',') // another one follows
1003 ++off;
1004 }
1005 if (error)
1006 break;
1007 if (*key == 'T')
1008 {
1009 if (!init || !(HL_TABLE()[idx].sg_set & SG_TERM))
1010 {
1011 if (!init)
1012 HL_TABLE()[idx].sg_set |= SG_TERM;
1013 HL_TABLE()[idx].sg_term = attr;
1014 }
1015 }
1016 else if (*key == 'C')
1017 {
1018 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1019 {
1020 if (!init)
1021 HL_TABLE()[idx].sg_set |= SG_CTERM;
1022 HL_TABLE()[idx].sg_cterm = attr;
1023 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1024 }
1025 }
1026#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1027 else
1028 {
1029 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1030 {
1031 if (!init)
1032 HL_TABLE()[idx].sg_set |= SG_GUI;
1033 HL_TABLE()[idx].sg_gui = attr;
1034 }
1035 }
1036#endif
1037 }
1038 else if (STRCMP(key, "FONT") == 0)
1039 {
1040 // in non-GUI fonts are simply ignored
1041#ifdef FEAT_GUI
1042 if (HL_TABLE()[idx].sg_font_name != NULL
1043 && STRCMP(HL_TABLE()[idx].sg_font_name, arg) == 0)
1044 {
1045 // Font name didn't change, ignore.
1046 }
1047 else if (!gui.shell_created)
1048 {
1049 // GUI not started yet, always accept the name.
1050 vim_free(HL_TABLE()[idx].sg_font_name);
1051 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1052 did_change = TRUE;
1053 }
1054 else
1055 {
1056 GuiFont temp_sg_font = HL_TABLE()[idx].sg_font;
1057# ifdef FEAT_XFONTSET
1058 GuiFontset temp_sg_fontset = HL_TABLE()[idx].sg_fontset;
1059# endif
1060 // First, save the current font/fontset.
1061 // Then try to allocate the font/fontset.
1062 // If the allocation fails, HL_TABLE()[idx].sg_font OR
1063 // sg_fontset will be set to NOFONT or NOFONTSET respectively.
1064
1065 HL_TABLE()[idx].sg_font = NOFONT;
1066# ifdef FEAT_XFONTSET
1067 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1068# endif
1069 hl_do_font(idx, arg, is_normal_group, is_menu_group,
1070 is_tooltip_group, FALSE);
1071
1072# ifdef FEAT_XFONTSET
1073 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1074 {
1075 // New fontset was accepted. Free the old one, if there
1076 // was one.
1077 gui_mch_free_fontset(temp_sg_fontset);
1078 vim_free(HL_TABLE()[idx].sg_font_name);
1079 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1080 did_change = TRUE;
1081 }
1082 else
1083 HL_TABLE()[idx].sg_fontset = temp_sg_fontset;
1084# endif
1085 if (HL_TABLE()[idx].sg_font != NOFONT)
1086 {
1087 // New font was accepted. Free the old one, if there was
1088 // one.
1089 gui_mch_free_font(temp_sg_font);
1090 vim_free(HL_TABLE()[idx].sg_font_name);
1091 HL_TABLE()[idx].sg_font_name = vim_strsave(arg);
1092 did_change = TRUE;
1093 }
1094 else
1095 HL_TABLE()[idx].sg_font = temp_sg_font;
1096 }
1097#endif
1098 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001099 else if (STRCMP(key, "CTERMFG") == 0 || STRCMP(key, "CTERMBG") == 0
1100 || STRCMP(key, "CTERMUL") == 0)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001101 {
1102 if (!init || !(HL_TABLE()[idx].sg_set & SG_CTERM))
1103 {
1104 if (!init)
1105 HL_TABLE()[idx].sg_set |= SG_CTERM;
1106
1107 // When setting the foreground color, and previously the "bold"
1108 // flag was set for a light color, reset it now
1109 if (key[5] == 'F' && HL_TABLE()[idx].sg_cterm_bold)
1110 {
1111 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1112 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1113 }
1114
1115 if (VIM_ISDIGIT(*arg))
1116 color = atoi((char *)arg);
1117 else if (STRICMP(arg, "fg") == 0)
1118 {
1119 if (cterm_normal_fg_color)
1120 color = cterm_normal_fg_color - 1;
1121 else
1122 {
1123 emsg(_("E419: FG color unknown"));
1124 error = TRUE;
1125 break;
1126 }
1127 }
1128 else if (STRICMP(arg, "bg") == 0)
1129 {
1130 if (cterm_normal_bg_color > 0)
1131 color = cterm_normal_bg_color - 1;
1132 else
1133 {
1134 emsg(_("E420: BG color unknown"));
1135 error = TRUE;
1136 break;
1137 }
1138 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001139 else if (STRICMP(arg, "ul") == 0)
1140 {
1141 if (cterm_normal_ul_color > 0)
1142 color = cterm_normal_ul_color - 1;
1143 else
1144 {
1145 emsg(_("E453: UL color unknown"));
1146 error = TRUE;
1147 break;
1148 }
1149 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001150 else
1151 {
1152 int bold = MAYBE;
1153
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001154 // reduce calls to STRICMP a bit, it can be slow
1155 off = TOUPPER_ASC(*arg);
1156 for (i = (sizeof(color_names) / sizeof(char *)); --i >= 0; )
1157 if (off == color_names[i][0]
1158 && STRICMP(arg + 1, color_names[i] + 1) == 0)
1159 break;
1160 if (i < 0)
1161 {
1162 semsg(_("E421: Color name or number not recognized: %s"), key_start);
1163 error = TRUE;
1164 break;
1165 }
1166
1167 color = lookup_color(i, key[5] == 'F', &bold);
1168
1169 // set/reset bold attribute to get light foreground
1170 // colors (on some terminals, e.g. "linux")
1171 if (bold == TRUE)
1172 {
1173 HL_TABLE()[idx].sg_cterm |= HL_BOLD;
1174 HL_TABLE()[idx].sg_cterm_bold = TRUE;
1175 }
1176 else if (bold == FALSE)
1177 HL_TABLE()[idx].sg_cterm &= ~HL_BOLD;
1178 }
1179
1180 // Add one to the argument, to avoid zero. Zero is used for
1181 // "NONE", then "color" is -1.
1182 if (key[5] == 'F')
1183 {
1184 HL_TABLE()[idx].sg_cterm_fg = color + 1;
1185 if (is_normal_group)
1186 {
1187 cterm_normal_fg_color = color + 1;
1188 cterm_normal_fg_bold = (HL_TABLE()[idx].sg_cterm & HL_BOLD);
1189#ifdef FEAT_GUI
1190 // Don't do this if the GUI is used.
1191 if (!gui.in_use && !gui.starting)
1192#endif
1193 {
1194 must_redraw = CLEAR;
1195 if (termcap_active && color >= 0)
1196 term_fg_color(color);
1197 }
1198 }
1199 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001200 else if (key[5] == 'B')
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001201 {
1202 HL_TABLE()[idx].sg_cterm_bg = color + 1;
1203 if (is_normal_group)
1204 {
1205 cterm_normal_bg_color = color + 1;
1206#ifdef FEAT_GUI
1207 // Don't mess with 'background' if the GUI is used.
1208 if (!gui.in_use && !gui.starting)
1209#endif
1210 {
1211 must_redraw = CLEAR;
1212 if (color >= 0)
1213 {
1214 int dark = -1;
1215
1216 if (termcap_active)
1217 term_bg_color(color);
1218 if (t_colors < 16)
1219 dark = (color == 0 || color == 4);
1220 // Limit the heuristic to the standard 16 colors
1221 else if (color < 16)
1222 dark = (color < 7 || color == 8);
1223 // Set the 'background' option if the value is
1224 // wrong.
1225 if (dark != -1
1226 && dark != (*p_bg == 'd')
1227 && !option_was_set((char_u *)"bg"))
1228 {
1229 set_option_value((char_u *)"bg", 0L,
1230 (char_u *)(dark ? "dark" : "light"), 0);
1231 reset_option_was_set((char_u *)"bg");
1232 }
1233 }
1234 }
1235 }
1236 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001237 else // ctermul
1238 {
1239 HL_TABLE()[idx].sg_cterm_ul = color + 1;
1240 if (is_normal_group)
1241 {
1242 cterm_normal_ul_color = color + 1;
1243#ifdef FEAT_GUI
1244 // Don't do this if the GUI is used.
1245 if (!gui.in_use && !gui.starting)
1246#endif
1247 {
1248 must_redraw = CLEAR;
1249 if (termcap_active && color >= 0)
1250 term_ul_color(color);
1251 }
1252 }
1253 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001254 }
1255 }
1256 else if (STRCMP(key, "GUIFG") == 0)
1257 {
1258#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1259 char_u **namep = &HL_TABLE()[idx].sg_gui_fg_name;
1260
1261 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1262 {
1263 if (!init)
1264 HL_TABLE()[idx].sg_set |= SG_GUI;
1265
1266# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1267 // In GUI guifg colors are only used when recognized
1268 i = color_name2handle(arg);
1269 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1270 {
1271 HL_TABLE()[idx].sg_gui_fg = i;
1272# endif
1273 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1274 {
1275 vim_free(*namep);
1276 if (STRCMP(arg, "NONE") != 0)
1277 *namep = vim_strsave(arg);
1278 else
1279 *namep = NULL;
1280 did_change = TRUE;
1281 }
1282# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1283# ifdef FEAT_GUI_X11
1284 if (is_menu_group && gui.menu_fg_pixel != i)
1285 {
1286 gui.menu_fg_pixel = i;
1287 do_colors = TRUE;
1288 }
1289 if (is_scrollbar_group && gui.scroll_fg_pixel != i)
1290 {
1291 gui.scroll_fg_pixel = i;
1292 do_colors = TRUE;
1293 }
1294# ifdef FEAT_BEVAL_GUI
1295 if (is_tooltip_group && gui.tooltip_fg_pixel != i)
1296 {
1297 gui.tooltip_fg_pixel = i;
1298 do_colors = TRUE;
1299 }
1300# endif
1301# endif
1302 }
1303# endif
1304 }
1305#endif
1306 }
1307 else if (STRCMP(key, "GUIBG") == 0)
1308 {
1309#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1310 char_u **namep = &HL_TABLE()[idx].sg_gui_bg_name;
1311
1312 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1313 {
1314 if (!init)
1315 HL_TABLE()[idx].sg_set |= SG_GUI;
1316
1317# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1318 // In GUI guifg colors are only used when recognized
1319 i = color_name2handle(arg);
1320 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
1321 {
1322 HL_TABLE()[idx].sg_gui_bg = i;
1323# endif
1324 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1325 {
1326 vim_free(*namep);
1327 if (STRCMP(arg, "NONE") != 0)
1328 *namep = vim_strsave(arg);
1329 else
1330 *namep = NULL;
1331 did_change = TRUE;
1332 }
1333# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1334# ifdef FEAT_GUI_X11
1335 if (is_menu_group && gui.menu_bg_pixel != i)
1336 {
1337 gui.menu_bg_pixel = i;
1338 do_colors = TRUE;
1339 }
1340 if (is_scrollbar_group && gui.scroll_bg_pixel != i)
1341 {
1342 gui.scroll_bg_pixel = i;
1343 do_colors = TRUE;
1344 }
1345# ifdef FEAT_BEVAL_GUI
1346 if (is_tooltip_group && gui.tooltip_bg_pixel != i)
1347 {
1348 gui.tooltip_bg_pixel = i;
1349 do_colors = TRUE;
1350 }
1351# endif
1352# endif
1353 }
1354# endif
1355 }
1356#endif
1357 }
1358 else if (STRCMP(key, "GUISP") == 0)
1359 {
1360#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1361 char_u **namep = &HL_TABLE()[idx].sg_gui_sp_name;
1362
1363 if (!init || !(HL_TABLE()[idx].sg_set & SG_GUI))
1364 {
1365 if (!init)
1366 HL_TABLE()[idx].sg_set |= SG_GUI;
1367
Bram Moolenaare023e882020-05-31 16:42:30 +02001368# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1369 // In GUI guisp colors are only used when recognized
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001370 i = color_name2handle(arg);
Bram Moolenaare023e882020-05-31 16:42:30 +02001371 if (i != INVALCOLOR || STRCMP(arg, "NONE") == 0 || !USE_24BIT)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001372 {
1373 HL_TABLE()[idx].sg_gui_sp = i;
1374# endif
1375 if (*namep == NULL || STRCMP(*namep, arg) != 0)
1376 {
1377 vim_free(*namep);
1378 if (STRCMP(arg, "NONE") != 0)
1379 *namep = vim_strsave(arg);
1380 else
1381 *namep = NULL;
1382 did_change = TRUE;
1383 }
Bram Moolenaare023e882020-05-31 16:42:30 +02001384# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001385 }
1386# endif
1387 }
1388#endif
1389 }
1390 else if (STRCMP(key, "START") == 0 || STRCMP(key, "STOP") == 0)
1391 {
1392 char_u buf[100];
1393 char_u *tname;
1394
1395 if (!init)
1396 HL_TABLE()[idx].sg_set |= SG_TERM;
1397
1398 /*
1399 * The "start" and "stop" arguments can be a literal escape
1400 * sequence, or a comma separated list of terminal codes.
1401 */
1402 if (STRNCMP(arg, "t_", 2) == 0)
1403 {
1404 off = 0;
1405 buf[0] = 0;
1406 while (arg[off] != NUL)
1407 {
1408 // Isolate one termcap name
1409 for (len = 0; arg[off + len] &&
1410 arg[off + len] != ','; ++len)
1411 ;
1412 tname = vim_strnsave(arg + off, len);
1413 if (tname == NULL) // out of memory
1414 {
1415 error = TRUE;
1416 break;
1417 }
1418 // lookup the escape sequence for the item
1419 p = get_term_code(tname);
1420 vim_free(tname);
1421 if (p == NULL) // ignore non-existing things
1422 p = (char_u *)"";
1423
1424 // Append it to the already found stuff
1425 if ((int)(STRLEN(buf) + STRLEN(p)) >= 99)
1426 {
1427 semsg(_("E422: terminal code too long: %s"), arg);
1428 error = TRUE;
1429 break;
1430 }
1431 STRCAT(buf, p);
1432
1433 // Advance to the next item
1434 off += len;
1435 if (arg[off] == ',') // another one follows
1436 ++off;
1437 }
1438 }
1439 else
1440 {
1441 /*
1442 * Copy characters from arg[] to buf[], translating <> codes.
1443 */
1444 for (p = arg, off = 0; off < 100 - 6 && *p; )
1445 {
Bram Moolenaarebe9d342020-05-30 21:52:54 +02001446 len = trans_special(&p, buf + off, FSK_SIMPLIFY, NULL);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001447 if (len > 0) // recognized special char
1448 off += len;
1449 else // copy as normal char
1450 buf[off++] = *p++;
1451 }
1452 buf[off] = NUL;
1453 }
1454 if (error)
1455 break;
1456
1457 if (STRCMP(buf, "NONE") == 0) // resetting the value
1458 p = NULL;
1459 else
1460 p = vim_strsave(buf);
1461 if (key[2] == 'A')
1462 {
1463 vim_free(HL_TABLE()[idx].sg_start);
1464 HL_TABLE()[idx].sg_start = p;
1465 }
1466 else
1467 {
1468 vim_free(HL_TABLE()[idx].sg_stop);
1469 HL_TABLE()[idx].sg_stop = p;
1470 }
1471 }
1472 else
1473 {
1474 semsg(_("E423: Illegal argument: %s"), key_start);
1475 error = TRUE;
1476 break;
1477 }
1478 HL_TABLE()[idx].sg_cleared = FALSE;
1479
1480 /*
1481 * When highlighting has been given for a group, don't link it.
1482 */
1483 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1484 HL_TABLE()[idx].sg_link = 0;
1485
1486 /*
1487 * Continue with next argument.
1488 */
1489 linep = skipwhite(linep);
1490 }
1491
1492 /*
1493 * If there is an error, and it's a new entry, remove it from the table.
1494 */
1495 if (error && idx == highlight_ga.ga_len)
1496 syn_unadd_group();
1497 else
1498 {
1499 if (is_normal_group)
1500 {
1501 HL_TABLE()[idx].sg_term_attr = 0;
1502 HL_TABLE()[idx].sg_cterm_attr = 0;
1503#ifdef FEAT_GUI
1504 HL_TABLE()[idx].sg_gui_attr = 0;
1505 /*
1506 * Need to update all groups, because they might be using "bg"
1507 * and/or "fg", which have been changed now.
1508 */
1509#endif
1510#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1511 if (USE_24BIT)
1512 {
1513 highlight_gui_started();
1514 did_highlight_changed = TRUE;
1515 redraw_all_later(NOT_VALID);
1516 }
1517#endif
Bram Moolenaara8bd3492020-03-23 21:45:29 +01001518#ifdef FEAT_VTP
1519 control_console_color_rgb();
1520#endif
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001521 }
1522#ifdef FEAT_TERMINAL
1523 else if (is_terminal_group)
1524 set_terminal_default_colors(
1525 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
1526#endif
1527#ifdef FEAT_GUI_X11
1528# ifdef FEAT_MENU
1529 else if (is_menu_group)
1530 {
1531 if (gui.in_use && do_colors)
1532 gui_mch_new_menu_colors();
1533 }
1534# endif
1535 else if (is_scrollbar_group)
1536 {
1537 if (gui.in_use && do_colors)
1538 gui_new_scrollbar_colors();
1539 else
1540 set_hl_attr(idx);
1541 }
1542# ifdef FEAT_BEVAL_GUI
1543 else if (is_tooltip_group)
1544 {
1545 if (gui.in_use && do_colors)
1546 gui_mch_new_tooltip_colors();
1547 }
1548# endif
1549#endif
1550 else
1551 set_hl_attr(idx);
1552#ifdef FEAT_EVAL
1553 HL_TABLE()[idx].sg_script_ctx = current_sctx;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001554 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001555#endif
1556 }
1557
1558 vim_free(key);
1559 vim_free(arg);
1560
1561 // Only call highlight_changed() once, after a sequence of highlight
1562 // commands, and only if an attribute actually changed.
1563 if ((did_change
1564 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1565#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1566 && !did_highlight_changed
1567#endif
1568 )
1569 {
1570 // Do not trigger a redraw when highlighting is changed while
1571 // redrawing. This may happen when evaluating 'statusline' changes the
1572 // StatusLine group.
1573 if (!updating_screen)
1574 redraw_all_later(NOT_VALID);
1575 need_highlight_changed = TRUE;
1576 }
1577}
1578
1579#if defined(EXITFREE) || defined(PROTO)
1580 void
1581free_highlight(void)
1582{
1583 int i;
1584
1585 for (i = 0; i < highlight_ga.ga_len; ++i)
1586 {
1587 highlight_clear(i);
1588 vim_free(HL_TABLE()[i].sg_name);
1589 vim_free(HL_TABLE()[i].sg_name_u);
1590 }
1591 ga_clear(&highlight_ga);
1592}
1593#endif
1594
1595/*
1596 * Reset the cterm colors to what they were before Vim was started, if
1597 * possible. Otherwise reset them to zero.
1598 */
1599 void
1600restore_cterm_colors(void)
1601{
1602#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1603 // Since t_me has been set, this probably means that the user
1604 // wants to use this as default colors. Need to reset default
1605 // background/foreground colors.
1606 mch_set_normal_colors();
1607#else
1608# ifdef VIMDLL
1609 if (!gui.in_use)
1610 {
1611 mch_set_normal_colors();
1612 return;
1613 }
1614# endif
1615 cterm_normal_fg_color = 0;
1616 cterm_normal_fg_bold = 0;
1617 cterm_normal_bg_color = 0;
1618# ifdef FEAT_TERMGUICOLORS
1619 cterm_normal_fg_gui_color = INVALCOLOR;
1620 cterm_normal_bg_gui_color = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001621 cterm_normal_ul_gui_color = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001622# endif
1623#endif
1624}
1625
1626/*
1627 * Return TRUE if highlight group "idx" has any settings.
1628 * When "check_link" is TRUE also check for an existing link.
1629 */
1630 static int
1631hl_has_settings(int idx, int check_link)
1632{
1633 return ( HL_TABLE()[idx].sg_term_attr != 0
1634 || HL_TABLE()[idx].sg_cterm_attr != 0
1635 || HL_TABLE()[idx].sg_cterm_fg != 0
1636 || HL_TABLE()[idx].sg_cterm_bg != 0
1637#ifdef FEAT_GUI
1638 || HL_TABLE()[idx].sg_gui_attr != 0
1639 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1640 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1641 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1642 || HL_TABLE()[idx].sg_font_name != NULL
1643#endif
1644 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1645}
1646
1647/*
1648 * Clear highlighting for one group.
1649 */
1650 static void
1651highlight_clear(int idx)
1652{
1653 HL_TABLE()[idx].sg_cleared = TRUE;
1654
1655 HL_TABLE()[idx].sg_term = 0;
1656 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1657 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1658 HL_TABLE()[idx].sg_term_attr = 0;
1659 HL_TABLE()[idx].sg_cterm = 0;
1660 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1661 HL_TABLE()[idx].sg_cterm_fg = 0;
1662 HL_TABLE()[idx].sg_cterm_bg = 0;
1663 HL_TABLE()[idx].sg_cterm_attr = 0;
1664#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1665 HL_TABLE()[idx].sg_gui = 0;
1666 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1667 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1668 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1669#endif
1670#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1671 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1672 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02001673 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001674#endif
1675#ifdef FEAT_GUI
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001676 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1677 HL_TABLE()[idx].sg_font = NOFONT;
1678# ifdef FEAT_XFONTSET
1679 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1680 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1681# endif
1682 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1683 HL_TABLE()[idx].sg_gui_attr = 0;
1684#endif
1685#ifdef FEAT_EVAL
1686 // Clear the script ID only when there is no link, since that is not
1687 // cleared.
1688 if (HL_TABLE()[idx].sg_link == 0)
1689 {
1690 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
1691 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
1692 }
1693#endif
1694}
1695
1696#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1697/*
1698 * Set the normal foreground and background colors according to the "Normal"
1699 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1700 * "Tooltip" colors.
1701 */
1702 void
1703set_normal_colors(void)
1704{
1705# ifdef FEAT_GUI
1706# ifdef FEAT_TERMGUICOLORS
1707 if (gui.in_use)
1708# endif
1709 {
1710 if (set_group_colors((char_u *)"Normal",
1711 &gui.norm_pixel, &gui.back_pixel,
1712 FALSE, TRUE, FALSE))
1713 {
1714 gui_mch_new_colors();
1715 must_redraw = CLEAR;
1716 }
1717# ifdef FEAT_GUI_X11
1718 if (set_group_colors((char_u *)"Menu",
1719 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1720 TRUE, FALSE, FALSE))
1721 {
1722# ifdef FEAT_MENU
1723 gui_mch_new_menu_colors();
1724# endif
1725 must_redraw = CLEAR;
1726 }
1727# ifdef FEAT_BEVAL_GUI
1728 if (set_group_colors((char_u *)"Tooltip",
1729 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1730 FALSE, FALSE, TRUE))
1731 {
1732# ifdef FEAT_TOOLBAR
1733 gui_mch_new_tooltip_colors();
1734# endif
1735 must_redraw = CLEAR;
1736 }
1737# endif
1738 if (set_group_colors((char_u *)"Scrollbar",
1739 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1740 FALSE, FALSE, FALSE))
1741 {
1742 gui_new_scrollbar_colors();
1743 must_redraw = CLEAR;
1744 }
1745# endif
1746 }
1747# endif
1748# ifdef FEAT_TERMGUICOLORS
1749# ifdef FEAT_GUI
1750 else
1751# endif
1752 {
1753 int idx;
1754
1755 idx = syn_name2id((char_u *)"Normal") - 1;
1756 if (idx >= 0)
1757 {
1758 gui_do_one_color(idx, FALSE, FALSE);
1759
1760 // If the normal fg or bg color changed a complete redraw is
1761 // required.
1762 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1763 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1764 {
1765 // if the GUI color is INVALCOLOR then we use the default cterm
1766 // color
1767 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1768 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1769 must_redraw = CLEAR;
1770 }
1771 }
1772 }
1773# endif
1774}
1775#endif
1776
1777#if defined(FEAT_GUI) || defined(PROTO)
1778/*
1779 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1780 */
1781 static int
1782set_group_colors(
1783 char_u *name,
1784 guicolor_T *fgp,
1785 guicolor_T *bgp,
1786 int do_menu,
1787 int use_norm,
1788 int do_tooltip)
1789{
1790 int idx;
1791
1792 idx = syn_name2id(name) - 1;
1793 if (idx >= 0)
1794 {
1795 gui_do_one_color(idx, do_menu, do_tooltip);
1796
1797 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1798 *fgp = HL_TABLE()[idx].sg_gui_fg;
1799 else if (use_norm)
1800 *fgp = gui.def_norm_pixel;
1801 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1802 *bgp = HL_TABLE()[idx].sg_gui_bg;
1803 else if (use_norm)
1804 *bgp = gui.def_back_pixel;
1805 return TRUE;
1806 }
1807 return FALSE;
1808}
1809
1810/*
1811 * Get the font of the "Normal" group.
1812 * Returns "" when it's not found or not set.
1813 */
1814 char_u *
1815hl_get_font_name(void)
1816{
1817 int id;
1818 char_u *s;
1819
1820 id = syn_name2id((char_u *)"Normal");
1821 if (id > 0)
1822 {
1823 s = HL_TABLE()[id - 1].sg_font_name;
1824 if (s != NULL)
1825 return s;
1826 }
1827 return (char_u *)"";
1828}
1829
1830/*
1831 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
1832 * actually chosen to be used.
1833 */
1834 void
1835hl_set_font_name(char_u *font_name)
1836{
1837 int id;
1838
1839 id = syn_name2id((char_u *)"Normal");
1840 if (id > 0)
1841 {
1842 vim_free(HL_TABLE()[id - 1].sg_font_name);
1843 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
1844 }
1845}
1846
1847/*
1848 * Set background color for "Normal" group. Called by gui_set_bg_color()
1849 * when the color is known.
1850 */
1851 void
1852hl_set_bg_color_name(
1853 char_u *name) // must have been allocated
1854{
1855 int id;
1856
1857 if (name != NULL)
1858 {
1859 id = syn_name2id((char_u *)"Normal");
1860 if (id > 0)
1861 {
1862 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
1863 HL_TABLE()[id - 1].sg_gui_bg_name = name;
1864 }
1865 }
1866}
1867
1868/*
1869 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
1870 * when the color is known.
1871 */
1872 void
1873hl_set_fg_color_name(
1874 char_u *name) // must have been allocated
1875{
1876 int id;
1877
1878 if (name != NULL)
1879 {
1880 id = syn_name2id((char_u *)"Normal");
1881 if (id > 0)
1882 {
1883 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
1884 HL_TABLE()[id - 1].sg_gui_fg_name = name;
1885 }
1886 }
1887}
1888
1889/*
1890 * Return the handle for a font name.
1891 * Returns NOFONT when failed.
1892 */
1893 static GuiFont
1894font_name2handle(char_u *name)
1895{
1896 if (STRCMP(name, "NONE") == 0)
1897 return NOFONT;
1898
1899 return gui_mch_get_font(name, TRUE);
1900}
1901
1902# ifdef FEAT_XFONTSET
1903/*
1904 * Return the handle for a fontset name.
1905 * Returns NOFONTSET when failed.
1906 */
1907 static GuiFontset
1908fontset_name2handle(char_u *name, int fixed_width)
1909{
1910 if (STRCMP(name, "NONE") == 0)
1911 return NOFONTSET;
1912
1913 return gui_mch_get_fontset(name, TRUE, fixed_width);
1914}
1915# endif
1916
1917/*
1918 * Get the font or fontset for one highlight group.
1919 */
1920 static void
1921hl_do_font(
1922 int idx,
1923 char_u *arg,
1924 int do_normal, // set normal font
1925 int do_menu UNUSED, // set menu font
1926 int do_tooltip UNUSED, // set tooltip font
1927 int free_font) // free current font/fontset
1928{
1929# ifdef FEAT_XFONTSET
1930 // If 'guifontset' is not empty, first try using the name as a
1931 // fontset. If that doesn't work, use it as a font name.
1932 if (*p_guifontset != NUL
1933# ifdef FONTSET_ALWAYS
1934 || do_menu
1935# endif
1936# ifdef FEAT_BEVAL_TIP
1937 // In Athena & Motif, the Tooltip highlight group is always a fontset
1938 || do_tooltip
1939# endif
1940 )
1941 {
1942 if (free_font)
1943 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1944 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
1945# ifdef FONTSET_ALWAYS
1946 || do_menu
1947# endif
1948# ifdef FEAT_BEVAL_TIP
1949 || do_tooltip
1950# endif
1951 );
1952 }
1953 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1954 {
1955 // If it worked and it's the Normal group, use it as the normal
1956 // fontset. Same for the Menu group.
1957 if (do_normal)
1958 gui_init_font(arg, TRUE);
1959# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1960 if (do_menu)
1961 {
1962# ifdef FONTSET_ALWAYS
1963 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
1964# else
1965 // YIKES! This is a bug waiting to crash the program
1966 gui.menu_font = HL_TABLE()[idx].sg_fontset;
1967# endif
1968 gui_mch_new_menu_font();
1969 }
1970# ifdef FEAT_BEVAL_GUI
1971 if (do_tooltip)
1972 {
1973 // The Athena widget set cannot currently handle switching between
1974 // displaying a single font and a fontset.
1975 // If the XtNinternational resource is set to True at widget
1976 // creation, then a fontset is always used, otherwise an
1977 // XFontStruct is used.
1978 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
1979 gui_mch_new_tooltip_font();
1980 }
1981# endif
1982# endif
1983 }
1984 else
1985# endif
1986 {
1987 if (free_font)
1988 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1989 HL_TABLE()[idx].sg_font = font_name2handle(arg);
1990 // If it worked and it's the Normal group, use it as the
1991 // normal font. Same for the Menu group.
1992 if (HL_TABLE()[idx].sg_font != NOFONT)
1993 {
1994 if (do_normal)
1995 gui_init_font(arg, FALSE);
1996#ifndef FONTSET_ALWAYS
1997# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1998 if (do_menu)
1999 {
2000 gui.menu_font = HL_TABLE()[idx].sg_font;
2001 gui_mch_new_menu_font();
2002 }
2003# endif
2004#endif
2005 }
2006 }
2007}
2008
2009#endif // FEAT_GUI
2010
2011#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
2012/*
2013 * Return the handle for a color name.
2014 * Returns INVALCOLOR when failed.
2015 */
2016 guicolor_T
2017color_name2handle(char_u *name)
2018{
2019 if (STRCMP(name, "NONE") == 0)
2020 return INVALCOLOR;
2021
2022 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
2023 {
2024#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2025 if (gui.in_use)
2026#endif
2027#ifdef FEAT_GUI
2028 return gui.norm_pixel;
2029#endif
2030#ifdef FEAT_TERMGUICOLORS
2031 if (cterm_normal_fg_gui_color != INVALCOLOR)
2032 return cterm_normal_fg_gui_color;
2033 // Guess that the foreground is black or white.
2034 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2035#endif
2036 }
2037 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2038 {
2039#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2040 if (gui.in_use)
2041#endif
2042#ifdef FEAT_GUI
2043 return gui.back_pixel;
2044#endif
2045#ifdef FEAT_TERMGUICOLORS
2046 if (cterm_normal_bg_gui_color != INVALCOLOR)
2047 return cterm_normal_bg_gui_color;
2048 // Guess that the background is white or black.
2049 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2050#endif
2051 }
2052
2053 return GUI_GET_COLOR(name);
2054}
2055#endif
2056
2057/*
2058 * Table with the specifications for an attribute number.
2059 * Note that this table is used by ALL buffers. This is required because the
2060 * GUI can redraw at any time for any buffer.
2061 */
2062static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2063
2064#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2065
2066static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2067
2068#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2069
2070#ifdef FEAT_GUI
2071static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2072
2073#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2074#endif
2075
2076/*
2077 * Return the attr number for a set of colors and font.
2078 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2079 * if the combination is new.
2080 * Return 0 for error (no more room).
2081 */
2082 static int
2083get_attr_entry(garray_T *table, attrentry_T *aep)
2084{
2085 int i;
2086 attrentry_T *taep;
2087 static int recursive = FALSE;
2088
2089 /*
2090 * Init the table, in case it wasn't done yet.
2091 */
2092 table->ga_itemsize = sizeof(attrentry_T);
2093 table->ga_growsize = 7;
2094
2095 /*
2096 * Try to find an entry with the same specifications.
2097 */
2098 for (i = 0; i < table->ga_len; ++i)
2099 {
2100 taep = &(((attrentry_T *)table->ga_data)[i]);
2101 if ( aep->ae_attr == taep->ae_attr
2102 && (
2103#ifdef FEAT_GUI
2104 (table == &gui_attr_table
2105 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2106 && aep->ae_u.gui.bg_color
2107 == taep->ae_u.gui.bg_color
2108 && aep->ae_u.gui.sp_color
2109 == taep->ae_u.gui.sp_color
2110 && aep->ae_u.gui.font == taep->ae_u.gui.font
2111# ifdef FEAT_XFONTSET
2112 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2113# endif
2114 ))
2115 ||
2116#endif
2117 (table == &term_attr_table
2118 && (aep->ae_u.term.start == NULL)
2119 == (taep->ae_u.term.start == NULL)
2120 && (aep->ae_u.term.start == NULL
2121 || STRCMP(aep->ae_u.term.start,
2122 taep->ae_u.term.start) == 0)
2123 && (aep->ae_u.term.stop == NULL)
2124 == (taep->ae_u.term.stop == NULL)
2125 && (aep->ae_u.term.stop == NULL
2126 || STRCMP(aep->ae_u.term.stop,
2127 taep->ae_u.term.stop) == 0))
2128 || (table == &cterm_attr_table
2129 && aep->ae_u.cterm.fg_color
2130 == taep->ae_u.cterm.fg_color
2131 && aep->ae_u.cterm.bg_color
2132 == taep->ae_u.cterm.bg_color
Bram Moolenaare023e882020-05-31 16:42:30 +02002133 && aep->ae_u.cterm.ul_color
2134 == taep->ae_u.cterm.ul_color
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002135#ifdef FEAT_TERMGUICOLORS
2136 && aep->ae_u.cterm.fg_rgb
2137 == taep->ae_u.cterm.fg_rgb
2138 && aep->ae_u.cterm.bg_rgb
2139 == taep->ae_u.cterm.bg_rgb
Bram Moolenaare023e882020-05-31 16:42:30 +02002140 && aep->ae_u.cterm.ul_rgb
2141 == taep->ae_u.cterm.ul_rgb
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002142#endif
2143 )))
2144
2145 return i + ATTR_OFF;
2146 }
2147
2148 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2149 {
2150 /*
2151 * Running out of attribute entries! remove all attributes, and
2152 * compute new ones for all groups.
2153 * When called recursively, we are really out of numbers.
2154 */
2155 if (recursive)
2156 {
2157 emsg(_("E424: Too many different highlighting attributes in use"));
2158 return 0;
2159 }
2160 recursive = TRUE;
2161
2162 clear_hl_tables();
2163
2164 must_redraw = CLEAR;
2165
2166 for (i = 0; i < highlight_ga.ga_len; ++i)
2167 set_hl_attr(i);
2168
2169 recursive = FALSE;
2170 }
2171
2172 /*
2173 * This is a new combination of colors and font, add an entry.
2174 */
2175 if (ga_grow(table, 1) == FAIL)
2176 return 0;
2177
2178 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
Bram Moolenaara80faa82020-04-12 19:37:17 +02002179 CLEAR_POINTER(taep);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002180 taep->ae_attr = aep->ae_attr;
2181#ifdef FEAT_GUI
2182 if (table == &gui_attr_table)
2183 {
2184 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2185 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2186 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2187 taep->ae_u.gui.font = aep->ae_u.gui.font;
2188# ifdef FEAT_XFONTSET
2189 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2190# endif
2191 }
2192#endif
2193 if (table == &term_attr_table)
2194 {
2195 if (aep->ae_u.term.start == NULL)
2196 taep->ae_u.term.start = NULL;
2197 else
2198 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2199 if (aep->ae_u.term.stop == NULL)
2200 taep->ae_u.term.stop = NULL;
2201 else
2202 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2203 }
2204 else if (table == &cterm_attr_table)
2205 {
2206 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2207 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002208 taep->ae_u.cterm.ul_color = aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002209#ifdef FEAT_TERMGUICOLORS
2210 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2211 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
Bram Moolenaare023e882020-05-31 16:42:30 +02002212 taep->ae_u.cterm.ul_rgb = aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002213#endif
2214 }
2215 ++table->ga_len;
2216 return (table->ga_len - 1 + ATTR_OFF);
2217}
2218
2219#if defined(FEAT_TERMINAL) || defined(PROTO)
2220/*
2221 * Get an attribute index for a cterm entry.
2222 * Uses an existing entry when possible or adds one when needed.
2223 */
2224 int
2225get_cterm_attr_idx(int attr, int fg, int bg)
2226{
2227 attrentry_T at_en;
2228
Bram Moolenaara80faa82020-04-12 19:37:17 +02002229 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002230#ifdef FEAT_TERMGUICOLORS
2231 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2232 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2233#endif
2234 at_en.ae_attr = attr;
2235 at_en.ae_u.cterm.fg_color = fg;
2236 at_en.ae_u.cterm.bg_color = bg;
2237 return get_attr_entry(&cterm_attr_table, &at_en);
2238}
2239#endif
2240
2241#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2242/*
2243 * Get an attribute index for a 'termguicolors' entry.
2244 * Uses an existing entry when possible or adds one when needed.
2245 */
2246 int
2247get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2248{
2249 attrentry_T at_en;
2250
Bram Moolenaara80faa82020-04-12 19:37:17 +02002251 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002252 at_en.ae_attr = attr;
2253 if (fg == INVALCOLOR && bg == INVALCOLOR)
2254 {
2255 // If both GUI colors are not set fall back to the cterm colors. Helps
2256 // if the GUI only has an attribute, such as undercurl.
2257 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2258 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2259 }
2260 else
2261 {
2262 at_en.ae_u.cterm.fg_rgb = fg;
2263 at_en.ae_u.cterm.bg_rgb = bg;
2264 }
2265 return get_attr_entry(&cterm_attr_table, &at_en);
2266}
2267#endif
2268
2269#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2270/*
2271 * Get an attribute index for a cterm entry.
2272 * Uses an existing entry when possible or adds one when needed.
2273 */
2274 int
2275get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2276{
2277 attrentry_T at_en;
2278
Bram Moolenaara80faa82020-04-12 19:37:17 +02002279 CLEAR_FIELD(at_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002280 at_en.ae_attr = attr;
2281 at_en.ae_u.gui.fg_color = fg;
2282 at_en.ae_u.gui.bg_color = bg;
2283 return get_attr_entry(&gui_attr_table, &at_en);
2284}
2285#endif
2286
2287/*
2288 * Clear all highlight tables.
2289 */
2290 void
2291clear_hl_tables(void)
2292{
2293 int i;
2294 attrentry_T *taep;
2295
2296#ifdef FEAT_GUI
2297 ga_clear(&gui_attr_table);
2298#endif
2299 for (i = 0; i < term_attr_table.ga_len; ++i)
2300 {
2301 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2302 vim_free(taep->ae_u.term.start);
2303 vim_free(taep->ae_u.term.stop);
2304 }
2305 ga_clear(&term_attr_table);
2306 ga_clear(&cterm_attr_table);
2307}
2308
2309/*
2310 * Combine special attributes (e.g., for spelling) with other attributes
2311 * (e.g., for syntax highlighting).
2312 * "prim_attr" overrules "char_attr".
2313 * This creates a new group when required.
2314 * Since we expect there to be few spelling mistakes we don't cache the
2315 * result.
2316 * Return the resulting attributes.
2317 */
2318 int
2319hl_combine_attr(int char_attr, int prim_attr)
2320{
2321 attrentry_T *char_aep = NULL;
2322 attrentry_T *spell_aep;
2323 attrentry_T new_en;
2324
2325 if (char_attr == 0)
2326 return prim_attr;
2327 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2328 return ATTR_COMBINE(char_attr, prim_attr);
2329#ifdef FEAT_GUI
2330 if (gui.in_use)
2331 {
2332 if (char_attr > HL_ALL)
2333 char_aep = syn_gui_attr2entry(char_attr);
2334 if (char_aep != NULL)
2335 new_en = *char_aep;
2336 else
2337 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002338 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002339 new_en.ae_u.gui.fg_color = INVALCOLOR;
2340 new_en.ae_u.gui.bg_color = INVALCOLOR;
2341 new_en.ae_u.gui.sp_color = INVALCOLOR;
2342 if (char_attr <= HL_ALL)
2343 new_en.ae_attr = char_attr;
2344 }
2345
2346 if (prim_attr <= HL_ALL)
2347 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2348 else
2349 {
2350 spell_aep = syn_gui_attr2entry(prim_attr);
2351 if (spell_aep != NULL)
2352 {
2353 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2354 spell_aep->ae_attr);
2355 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2356 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2357 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2358 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2359 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2360 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2361 if (spell_aep->ae_u.gui.font != NOFONT)
2362 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2363# ifdef FEAT_XFONTSET
2364 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2365 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2366# endif
2367 }
2368 }
2369 return get_attr_entry(&gui_attr_table, &new_en);
2370 }
2371#endif
2372
2373 if (IS_CTERM)
2374 {
2375 if (char_attr > HL_ALL)
2376 char_aep = syn_cterm_attr2entry(char_attr);
2377 if (char_aep != NULL)
2378 new_en = *char_aep;
2379 else
2380 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002381 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002382#ifdef FEAT_TERMGUICOLORS
2383 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2384 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
Bram Moolenaare023e882020-05-31 16:42:30 +02002385 new_en.ae_u.cterm.ul_rgb = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002386#endif
2387 if (char_attr <= HL_ALL)
2388 new_en.ae_attr = char_attr;
2389 }
2390
2391 if (prim_attr <= HL_ALL)
2392 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2393 else
2394 {
2395 spell_aep = syn_cterm_attr2entry(prim_attr);
2396 if (spell_aep != NULL)
2397 {
2398 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2399 spell_aep->ae_attr);
2400 if (spell_aep->ae_u.cterm.fg_color > 0)
2401 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2402 if (spell_aep->ae_u.cterm.bg_color > 0)
2403 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
Bram Moolenaare023e882020-05-31 16:42:30 +02002404 if (spell_aep->ae_u.cterm.ul_color > 0)
2405 new_en.ae_u.cterm.ul_color = spell_aep->ae_u.cterm.ul_color;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002406#ifdef FEAT_TERMGUICOLORS
2407 // If both fg and bg are not set fall back to cterm colors.
2408 // Helps for SpellBad which uses undercurl in the GUI.
2409 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2410 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2411 {
2412 if (spell_aep->ae_u.cterm.fg_color > 0)
2413 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2414 if (spell_aep->ae_u.cterm.bg_color > 0)
2415 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2416 }
2417 else
2418 {
2419 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2420 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2421 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2422 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2423 }
Bram Moolenaare023e882020-05-31 16:42:30 +02002424 if (spell_aep->ae_u.cterm.ul_rgb != INVALCOLOR)
2425 new_en.ae_u.cterm.ul_rgb = spell_aep->ae_u.cterm.ul_rgb;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002426#endif
2427 }
2428 }
2429 return get_attr_entry(&cterm_attr_table, &new_en);
2430 }
2431
2432 if (char_attr > HL_ALL)
2433 char_aep = syn_term_attr2entry(char_attr);
2434 if (char_aep != NULL)
2435 new_en = *char_aep;
2436 else
2437 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02002438 CLEAR_FIELD(new_en);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002439 if (char_attr <= HL_ALL)
2440 new_en.ae_attr = char_attr;
2441 }
2442
2443 if (prim_attr <= HL_ALL)
2444 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2445 else
2446 {
2447 spell_aep = syn_term_attr2entry(prim_attr);
2448 if (spell_aep != NULL)
2449 {
2450 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2451 if (spell_aep->ae_u.term.start != NULL)
2452 {
2453 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2454 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2455 }
2456 }
2457 }
2458 return get_attr_entry(&term_attr_table, &new_en);
2459}
2460
2461#ifdef FEAT_GUI
2462 attrentry_T *
2463syn_gui_attr2entry(int attr)
2464{
2465 attr -= ATTR_OFF;
2466 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2467 return NULL;
2468 return &(GUI_ATTR_ENTRY(attr));
2469}
2470#endif
2471
2472/*
2473 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2474 * Only to be used when "attr" > HL_ALL.
2475 */
2476 int
2477syn_attr2attr(int attr)
2478{
2479 attrentry_T *aep;
2480
2481#ifdef FEAT_GUI
2482 if (gui.in_use)
2483 aep = syn_gui_attr2entry(attr);
2484 else
2485#endif
2486 if (IS_CTERM)
2487 aep = syn_cterm_attr2entry(attr);
2488 else
2489 aep = syn_term_attr2entry(attr);
2490
2491 if (aep == NULL) // highlighting not set
2492 return 0;
2493 return aep->ae_attr;
2494}
2495
2496
2497 attrentry_T *
2498syn_term_attr2entry(int attr)
2499{
2500 attr -= ATTR_OFF;
2501 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2502 return NULL;
2503 return &(TERM_ATTR_ENTRY(attr));
2504}
2505
2506 attrentry_T *
2507syn_cterm_attr2entry(int attr)
2508{
2509 attr -= ATTR_OFF;
2510 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2511 return NULL;
2512 return &(CTERM_ATTR_ENTRY(attr));
2513}
2514
2515#define LIST_ATTR 1
2516#define LIST_STRING 2
2517#define LIST_INT 3
2518
2519 static void
2520highlight_list_one(int id)
2521{
2522 hl_group_T *sgp;
2523 int didh = FALSE;
2524
2525 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2526
2527 if (message_filtered(sgp->sg_name))
2528 return;
2529
2530 didh = highlight_list_arg(id, didh, LIST_ATTR,
2531 sgp->sg_term, NULL, "term");
2532 didh = highlight_list_arg(id, didh, LIST_STRING,
2533 0, sgp->sg_start, "start");
2534 didh = highlight_list_arg(id, didh, LIST_STRING,
2535 0, sgp->sg_stop, "stop");
2536
2537 didh = highlight_list_arg(id, didh, LIST_ATTR,
2538 sgp->sg_cterm, NULL, "cterm");
2539 didh = highlight_list_arg(id, didh, LIST_INT,
2540 sgp->sg_cterm_fg, NULL, "ctermfg");
2541 didh = highlight_list_arg(id, didh, LIST_INT,
2542 sgp->sg_cterm_bg, NULL, "ctermbg");
Bram Moolenaare023e882020-05-31 16:42:30 +02002543 didh = highlight_list_arg(id, didh, LIST_INT,
2544 sgp->sg_cterm_ul, NULL, "ctermul");
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002545
2546#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2547 didh = highlight_list_arg(id, didh, LIST_ATTR,
2548 sgp->sg_gui, NULL, "gui");
2549 didh = highlight_list_arg(id, didh, LIST_STRING,
2550 0, sgp->sg_gui_fg_name, "guifg");
2551 didh = highlight_list_arg(id, didh, LIST_STRING,
2552 0, sgp->sg_gui_bg_name, "guibg");
2553 didh = highlight_list_arg(id, didh, LIST_STRING,
2554 0, sgp->sg_gui_sp_name, "guisp");
2555#endif
2556#ifdef FEAT_GUI
2557 didh = highlight_list_arg(id, didh, LIST_STRING,
2558 0, sgp->sg_font_name, "font");
2559#endif
2560
2561 if (sgp->sg_link && !got_int)
2562 {
2563 (void)syn_list_header(didh, 9999, id);
2564 didh = TRUE;
2565 msg_puts_attr("links to", HL_ATTR(HLF_D));
2566 msg_putchar(' ');
2567 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2568 }
2569
2570 if (!didh)
2571 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2572#ifdef FEAT_EVAL
2573 if (p_verbose > 0)
2574 last_set_msg(sgp->sg_script_ctx);
2575#endif
2576}
2577
2578 static int
2579highlight_list_arg(
2580 int id,
2581 int didh,
2582 int type,
2583 int iarg,
2584 char_u *sarg,
2585 char *name)
2586{
2587 char_u buf[100];
2588 char_u *ts;
2589 int i;
2590
2591 if (got_int)
2592 return FALSE;
2593 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2594 {
2595 ts = buf;
2596 if (type == LIST_INT)
2597 sprintf((char *)buf, "%d", iarg - 1);
2598 else if (type == LIST_STRING)
2599 ts = sarg;
2600 else // type == LIST_ATTR
2601 {
2602 buf[0] = NUL;
2603 for (i = 0; hl_attr_table[i] != 0; ++i)
2604 {
2605 if (iarg & hl_attr_table[i])
2606 {
2607 if (buf[0] != NUL)
2608 vim_strcat(buf, (char_u *)",", 100);
2609 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2610 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2611 }
2612 }
2613 }
2614
2615 (void)syn_list_header(didh,
2616 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2617 didh = TRUE;
2618 if (!got_int)
2619 {
2620 if (*name != NUL)
2621 {
2622 msg_puts_attr(name, HL_ATTR(HLF_D));
2623 msg_puts_attr("=", HL_ATTR(HLF_D));
2624 }
2625 msg_outtrans(ts);
2626 }
2627 }
2628 return didh;
2629}
2630
2631#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2632/*
2633 * Return "1" if highlight group "id" has attribute "flag".
2634 * Return NULL otherwise.
2635 */
2636 char_u *
2637highlight_has_attr(
2638 int id,
2639 int flag,
2640 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2641{
2642 int attr;
2643
2644 if (id <= 0 || id > highlight_ga.ga_len)
2645 return NULL;
2646
2647#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2648 if (modec == 'g')
2649 attr = HL_TABLE()[id - 1].sg_gui;
2650 else
2651#endif
2652 if (modec == 'c')
2653 attr = HL_TABLE()[id - 1].sg_cterm;
2654 else
2655 attr = HL_TABLE()[id - 1].sg_term;
2656
2657 if (attr & flag)
2658 return (char_u *)"1";
2659 return NULL;
2660}
2661#endif
2662
2663#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2664/*
2665 * Return color name of highlight group "id".
2666 */
2667 char_u *
2668highlight_color(
2669 int id,
2670 char_u *what, // "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#"
2671 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2672{
2673 static char_u name[20];
2674 int n;
2675 int fg = FALSE;
2676 int sp = FALSE;
2677 int font = FALSE;
2678
2679 if (id <= 0 || id > highlight_ga.ga_len)
2680 return NULL;
2681
2682 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2683 fg = TRUE;
2684 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2685 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2686 font = TRUE;
2687 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2688 sp = TRUE;
2689 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2690 return NULL;
2691 if (modec == 'g')
2692 {
2693# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2694# ifdef FEAT_GUI
2695 // return font name
2696 if (font)
2697 return HL_TABLE()[id - 1].sg_font_name;
2698# endif
2699
2700 // return #RRGGBB form (only possible when GUI is running)
2701 if ((USE_24BIT) && what[2] == '#')
2702 {
2703 guicolor_T color;
2704 long_u rgb;
2705 static char_u buf[10];
2706
2707 if (fg)
2708 color = HL_TABLE()[id - 1].sg_gui_fg;
2709 else if (sp)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002710 color = HL_TABLE()[id - 1].sg_gui_sp;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002711 else
2712 color = HL_TABLE()[id - 1].sg_gui_bg;
2713 if (color == INVALCOLOR)
2714 return NULL;
2715 rgb = (long_u)GUI_MCH_GET_RGB(color);
2716 sprintf((char *)buf, "#%02x%02x%02x",
2717 (unsigned)(rgb >> 16),
2718 (unsigned)(rgb >> 8) & 255,
2719 (unsigned)rgb & 255);
2720 return buf;
2721 }
2722# endif
2723 if (fg)
2724 return (HL_TABLE()[id - 1].sg_gui_fg_name);
2725 if (sp)
2726 return (HL_TABLE()[id - 1].sg_gui_sp_name);
2727 return (HL_TABLE()[id - 1].sg_gui_bg_name);
2728 }
2729 if (font || sp)
2730 return NULL;
2731 if (modec == 'c')
2732 {
2733 if (fg)
2734 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
2735 else
2736 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2737 if (n < 0)
2738 return NULL;
2739 sprintf((char *)name, "%d", n);
2740 return name;
2741 }
2742 // term doesn't have color
2743 return NULL;
2744}
2745#endif
2746
2747#if (defined(FEAT_SYN_HL) \
2748 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2749 && defined(FEAT_PRINTER)) || defined(PROTO)
2750/*
2751 * Return color name of highlight group "id" as RGB value.
2752 */
2753 long_u
2754highlight_gui_color_rgb(
2755 int id,
2756 int fg) // TRUE = fg, FALSE = bg
2757{
2758 guicolor_T color;
2759
2760 if (id <= 0 || id > highlight_ga.ga_len)
2761 return 0L;
2762
2763 if (fg)
2764 color = HL_TABLE()[id - 1].sg_gui_fg;
2765 else
2766 color = HL_TABLE()[id - 1].sg_gui_bg;
2767
2768 if (color == INVALCOLOR)
2769 return 0L;
2770
2771 return GUI_MCH_GET_RGB(color);
2772}
2773#endif
2774
2775/*
2776 * Output the syntax list header.
2777 * Return TRUE when started a new line.
2778 */
2779 int
2780syn_list_header(
2781 int did_header, // did header already
2782 int outlen, // length of string that comes
2783 int id) // highlight group id
2784{
2785 int endcol = 19;
2786 int newline = TRUE;
2787 int name_col = 0;
2788
2789 if (!did_header)
2790 {
2791 msg_putchar('\n');
2792 if (got_int)
2793 return TRUE;
2794 msg_outtrans(HL_TABLE()[id - 1].sg_name);
2795 name_col = msg_col;
2796 endcol = 15;
2797 }
2798 else if (msg_col + outlen + 1 >= Columns)
2799 {
2800 msg_putchar('\n');
2801 if (got_int)
2802 return TRUE;
2803 }
2804 else
2805 {
2806 if (msg_col >= endcol) // wrap around is like starting a new line
2807 newline = FALSE;
2808 }
2809
2810 if (msg_col >= endcol) // output at least one space
2811 endcol = msg_col + 1;
2812 if (Columns <= endcol) // avoid hang for tiny window
2813 endcol = Columns - 1;
2814
2815 msg_advance(endcol);
2816
2817 // Show "xxx" with the attributes.
2818 if (!did_header)
2819 {
2820 if (endcol == Columns - 1 && endcol <= name_col)
2821 msg_putchar(' ');
2822 msg_puts_attr("xxx", syn_id2attr(id));
2823 msg_putchar(' ');
2824 }
2825
2826 return newline;
2827}
2828
2829/*
2830 * Set the attribute numbers for a highlight group.
2831 * Called after one of the attributes has changed.
2832 */
2833 static void
2834set_hl_attr(
2835 int idx) // index in array
2836{
2837 attrentry_T at_en;
2838 hl_group_T *sgp = HL_TABLE() + idx;
2839
2840 // The "Normal" group doesn't need an attribute number
2841 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
2842 return;
2843
2844#ifdef FEAT_GUI
2845 /*
2846 * For the GUI mode: If there are other than "normal" highlighting
2847 * attributes, need to allocate an attr number.
2848 */
2849 if (sgp->sg_gui_fg == INVALCOLOR
2850 && sgp->sg_gui_bg == INVALCOLOR
2851 && sgp->sg_gui_sp == INVALCOLOR
2852 && sgp->sg_font == NOFONT
2853# ifdef FEAT_XFONTSET
2854 && sgp->sg_fontset == NOFONTSET
2855# endif
2856 )
2857 {
2858 sgp->sg_gui_attr = sgp->sg_gui;
2859 }
2860 else
2861 {
2862 at_en.ae_attr = sgp->sg_gui;
2863 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
2864 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
2865 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
2866 at_en.ae_u.gui.font = sgp->sg_font;
2867# ifdef FEAT_XFONTSET
2868 at_en.ae_u.gui.fontset = sgp->sg_fontset;
2869# endif
2870 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
2871 }
2872#endif
2873 /*
2874 * For the term mode: If there are other than "normal" highlighting
2875 * attributes, need to allocate an attr number.
2876 */
2877 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
2878 sgp->sg_term_attr = sgp->sg_term;
2879 else
2880 {
2881 at_en.ae_attr = sgp->sg_term;
2882 at_en.ae_u.term.start = sgp->sg_start;
2883 at_en.ae_u.term.stop = sgp->sg_stop;
2884 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
2885 }
2886
2887 /*
2888 * For the color term mode: If there are other than "normal"
2889 * highlighting attributes, need to allocate an attr number.
2890 */
Bram Moolenaare023e882020-05-31 16:42:30 +02002891 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0 && sgp->sg_cterm_ul == 0
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002892# ifdef FEAT_TERMGUICOLORS
2893 && sgp->sg_gui_fg == INVALCOLOR
2894 && sgp->sg_gui_bg == INVALCOLOR
Bram Moolenaare023e882020-05-31 16:42:30 +02002895 && sgp->sg_gui_sp == INVALCOLOR
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002896# endif
2897 )
2898 sgp->sg_cterm_attr = sgp->sg_cterm;
2899 else
2900 {
2901 at_en.ae_attr = sgp->sg_cterm;
2902 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
2903 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
Bram Moolenaare023e882020-05-31 16:42:30 +02002904 at_en.ae_u.cterm.ul_color = sgp->sg_cterm_ul;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002905# ifdef FEAT_TERMGUICOLORS
2906# ifdef MSWIN
2907# ifdef VIMDLL
2908 // Only when not using the GUI.
2909 if (!gui.in_use && !gui.starting)
2910# endif
2911 {
2912 int id;
2913 guicolor_T fg, bg;
2914
2915 id = syn_name2id((char_u *)"Normal");
2916 if (id > 0)
2917 {
2918 syn_id2colors(id, &fg, &bg);
2919 if (sgp->sg_gui_fg == INVALCOLOR)
2920 sgp->sg_gui_fg = fg;
2921 if (sgp->sg_gui_bg == INVALCOLOR)
2922 sgp->sg_gui_bg = bg;
2923 }
2924
2925 }
2926# endif
2927 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
2928 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
Bram Moolenaare023e882020-05-31 16:42:30 +02002929 at_en.ae_u.cterm.ul_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_sp);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02002930 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2931 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2932 {
2933 // If both fg and bg are invalid fall back to the cterm colors.
2934 // Helps when the GUI only uses an attribute, e.g. undercurl.
2935 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2936 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2937 }
2938# endif
2939 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2940 }
2941}
2942
2943/*
2944 * Lookup a highlight group name and return its ID.
2945 * If it is not found, 0 is returned.
2946 */
2947 int
2948syn_name2id(char_u *name)
2949{
2950 int i;
2951 char_u name_u[200];
2952
2953 // Avoid using stricmp() too much, it's slow on some systems
2954 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
2955 // don't deserve to be found!
2956 vim_strncpy(name_u, name, 199);
2957 vim_strup(name_u);
2958 for (i = highlight_ga.ga_len; --i >= 0; )
2959 if (HL_TABLE()[i].sg_name_u != NULL
2960 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2961 break;
2962 return i + 1;
2963}
2964
2965/*
2966 * Lookup a highlight group name and return its attributes.
2967 * Return zero if not found.
2968 */
2969 int
2970syn_name2attr(char_u *name)
2971{
2972 int id = syn_name2id(name);
2973
2974 if (id != 0)
2975 return syn_id2attr(id);
2976 return 0;
2977}
2978
2979#if defined(FEAT_EVAL) || defined(PROTO)
2980/*
2981 * Return TRUE if highlight group "name" exists.
2982 */
2983 int
2984highlight_exists(char_u *name)
2985{
2986 return (syn_name2id(name) > 0);
2987}
2988
2989# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
2990/*
2991 * Return the name of highlight group "id".
2992 * When not a valid ID return an empty string.
2993 */
2994 char_u *
2995syn_id2name(int id)
2996{
2997 if (id <= 0 || id > highlight_ga.ga_len)
2998 return (char_u *)"";
2999 return HL_TABLE()[id - 1].sg_name;
3000}
3001# endif
3002#endif
3003
3004/*
3005 * Like syn_name2id(), but take a pointer + length argument.
3006 */
3007 int
3008syn_namen2id(char_u *linep, int len)
3009{
3010 char_u *name;
3011 int id = 0;
3012
3013 name = vim_strnsave(linep, len);
3014 if (name != NULL)
3015 {
3016 id = syn_name2id(name);
3017 vim_free(name);
3018 }
3019 return id;
3020}
3021
3022/*
3023 * Find highlight group name in the table and return its ID.
3024 * The argument is a pointer to the name and the length of the name.
3025 * If it doesn't exist yet, a new entry is created.
3026 * Return 0 for failure.
3027 */
3028 int
3029syn_check_group(char_u *pp, int len)
3030{
3031 int id;
3032 char_u *name;
3033
3034 name = vim_strnsave(pp, len);
3035 if (name == NULL)
3036 return 0;
3037
3038 id = syn_name2id(name);
3039 if (id == 0) // doesn't exist yet
3040 id = syn_add_group(name);
3041 else
3042 vim_free(name);
3043 return id;
3044}
3045
3046/*
3047 * Add new highlight group and return its ID.
3048 * "name" must be an allocated string, it will be consumed.
3049 * Return 0 for failure.
3050 */
3051 static int
3052syn_add_group(char_u *name)
3053{
3054 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003055 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003056
3057 // Check that the name is ASCII letters, digits and underscore.
3058 for (p = name; *p != NUL; ++p)
3059 {
3060 if (!vim_isprintc(*p))
3061 {
3062 emsg(_("E669: Unprintable character in group name"));
3063 vim_free(name);
3064 return 0;
3065 }
3066 else if (!ASCII_ISALNUM(*p) && *p != '_')
3067 {
3068 // This is an error, but since there previously was no check only
3069 // give a warning.
3070 msg_source(HL_ATTR(HLF_W));
3071 msg(_("W18: Invalid character in group name"));
3072 break;
3073 }
3074 }
3075
3076 /*
3077 * First call for this growarray: init growing array.
3078 */
3079 if (highlight_ga.ga_data == NULL)
3080 {
3081 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3082 highlight_ga.ga_growsize = 10;
3083 }
3084
3085 if (highlight_ga.ga_len >= MAX_HL_ID)
3086 {
3087 emsg(_("E849: Too many highlight and syntax groups"));
3088 vim_free(name);
3089 return 0;
3090 }
3091
3092 /*
3093 * Make room for at least one other syntax_highlight entry.
3094 */
3095 if (ga_grow(&highlight_ga, 1) == FAIL)
3096 {
3097 vim_free(name);
3098 return 0;
3099 }
3100
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003101 name_up = vim_strsave_up(name);
3102 if (name_up == NULL)
3103 {
3104 vim_free(name);
3105 return 0;
3106 }
3107
Bram Moolenaara80faa82020-04-12 19:37:17 +02003108 CLEAR_POINTER(&(HL_TABLE()[highlight_ga.ga_len]));
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003109 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003110 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003111#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3112 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3113 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003114 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003115#endif
3116 ++highlight_ga.ga_len;
3117
3118 return highlight_ga.ga_len; // ID is index plus one
3119}
3120
3121/*
3122 * When, just after calling syn_add_group(), an error is discovered, this
3123 * function deletes the new name.
3124 */
3125 static void
3126syn_unadd_group(void)
3127{
3128 --highlight_ga.ga_len;
3129 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3130 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3131}
3132
3133/*
3134 * Translate a group ID to highlight attributes.
3135 */
3136 int
3137syn_id2attr(int hl_id)
3138{
3139 int attr;
3140 hl_group_T *sgp;
3141
3142 hl_id = syn_get_final_id(hl_id);
3143 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3144
3145#ifdef FEAT_GUI
3146 /*
3147 * Only use GUI attr when the GUI is being used.
3148 */
3149 if (gui.in_use)
3150 attr = sgp->sg_gui_attr;
3151 else
3152#endif
3153 if (IS_CTERM)
3154 attr = sgp->sg_cterm_attr;
3155 else
3156 attr = sgp->sg_term_attr;
3157
3158 return attr;
3159}
3160
3161#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3162/*
3163 * Get the GUI colors and attributes for a group ID.
3164 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3165 */
3166 int
3167syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3168{
3169 hl_group_T *sgp;
3170
3171 hl_id = syn_get_final_id(hl_id);
3172 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3173
3174 *fgp = sgp->sg_gui_fg;
3175 *bgp = sgp->sg_gui_bg;
3176 return sgp->sg_gui;
3177}
3178#endif
3179
3180#if (defined(MSWIN) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003181 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3182 && defined(FEAT_TERMGUICOLORS)) \
3183 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003184 void
3185syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3186{
3187 hl_group_T *sgp;
3188
3189 hl_id = syn_get_final_id(hl_id);
3190 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3191 *fgp = sgp->sg_cterm_fg - 1;
3192 *bgp = sgp->sg_cterm_bg - 1;
3193}
3194#endif
3195
3196/*
3197 * Translate a group ID to the final group ID (following links).
3198 */
3199 int
3200syn_get_final_id(int hl_id)
3201{
3202 int count;
3203 hl_group_T *sgp;
3204
3205 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3206 return 0; // Can be called from eval!!
3207
3208 /*
3209 * Follow links until there is no more.
3210 * Look out for loops! Break after 100 links.
3211 */
3212 for (count = 100; --count >= 0; )
3213 {
3214 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3215 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3216 break;
3217 hl_id = sgp->sg_link;
3218 }
3219
3220 return hl_id;
3221}
3222
3223#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3224/*
3225 * Call this function just after the GUI has started.
3226 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3227 * It finds the font and color handles for the highlighting groups.
3228 */
3229 void
3230highlight_gui_started(void)
3231{
3232 int idx;
3233
3234 // First get the colors from the "Normal" and "Menu" group, if set
3235 if (USE_24BIT)
3236 set_normal_colors();
3237
3238 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3239 gui_do_one_color(idx, FALSE, FALSE);
3240
3241 highlight_changed();
3242}
3243
3244 static void
3245gui_do_one_color(
3246 int idx,
3247 int do_menu UNUSED, // TRUE: might set the menu font
3248 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3249{
3250 int didit = FALSE;
3251
3252# ifdef FEAT_GUI
3253# ifdef FEAT_TERMGUICOLORS
3254 if (gui.in_use)
3255# endif
3256 if (HL_TABLE()[idx].sg_font_name != NULL)
3257 {
3258 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3259 do_tooltip, TRUE);
3260 didit = TRUE;
3261 }
3262# endif
3263 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3264 {
3265 HL_TABLE()[idx].sg_gui_fg =
3266 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3267 didit = TRUE;
3268 }
3269 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3270 {
3271 HL_TABLE()[idx].sg_gui_bg =
3272 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3273 didit = TRUE;
3274 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003275 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3276 {
3277 HL_TABLE()[idx].sg_gui_sp =
3278 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3279 didit = TRUE;
3280 }
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003281 if (didit) // need to get a new attr number
3282 set_hl_attr(idx);
3283}
3284#endif
3285
3286#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3287/*
3288 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3289 */
3290 static void
3291combine_stl_hlt(
3292 int id,
3293 int id_S,
3294 int id_alt,
3295 int hlcnt,
3296 int i,
3297 int hlf,
3298 int *table)
3299{
3300 hl_group_T *hlt = HL_TABLE();
3301
3302 if (id_alt == 0)
3303 {
Bram Moolenaara80faa82020-04-12 19:37:17 +02003304 CLEAR_POINTER(&hlt[hlcnt + i]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003305 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3306 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3307# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3308 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3309# endif
3310 }
3311 else
3312 mch_memmove(&hlt[hlcnt + i],
3313 &hlt[id_alt - 1],
3314 sizeof(hl_group_T));
3315 hlt[hlcnt + i].sg_link = 0;
3316
3317 hlt[hlcnt + i].sg_term ^=
3318 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3319 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3320 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3321 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3322 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3323 hlt[hlcnt + i].sg_cterm ^=
3324 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3325 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3326 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3327 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3328 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3329# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3330 hlt[hlcnt + i].sg_gui ^=
3331 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3332# endif
3333# ifdef FEAT_GUI
3334 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3335 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3336 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3337 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3338 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3339 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3340 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3341 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3342# ifdef FEAT_XFONTSET
3343 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3344 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3345# endif
3346# endif
3347 highlight_ga.ga_len = hlcnt + i + 1;
3348 set_hl_attr(hlcnt + i); // At long last we can apply
3349 table[i] = syn_id2attr(hlcnt + i + 1);
3350}
3351#endif
3352
3353/*
3354 * Translate the 'highlight' option into attributes in highlight_attr[] and
3355 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3356 * corresponding highlights to use on top of HLF_SNC is computed.
3357 * Called only when the 'highlight' option has been changed and upon first
3358 * screen redraw after any :highlight command.
3359 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3360 */
3361 int
3362highlight_changed(void)
3363{
3364 int hlf;
3365 int i;
3366 char_u *p;
3367 int attr;
3368 char_u *end;
3369 int id;
3370#ifdef USER_HIGHLIGHT
3371 char_u userhl[30]; // use 30 to avoid compiler warning
3372# ifdef FEAT_STL_OPT
3373 int id_S = -1;
3374 int id_SNC = 0;
3375# ifdef FEAT_TERMINAL
3376 int id_ST = 0;
3377 int id_STNC = 0;
3378# endif
3379 int hlcnt;
3380# endif
3381#endif
3382 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3383
3384 need_highlight_changed = FALSE;
3385
3386 /*
3387 * Clear all attributes.
3388 */
3389 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3390 highlight_attr[hlf] = 0;
3391
3392 /*
3393 * First set all attributes to their default value.
3394 * Then use the attributes from the 'highlight' option.
3395 */
3396 for (i = 0; i < 2; ++i)
3397 {
3398 if (i)
3399 p = p_hl;
3400 else
3401 p = get_highlight_default();
3402 if (p == NULL) // just in case
3403 continue;
3404
3405 while (*p)
3406 {
3407 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3408 if (hl_flags[hlf] == *p)
3409 break;
3410 ++p;
3411 if (hlf == (int)HLF_COUNT || *p == NUL)
3412 return FAIL;
3413
3414 /*
3415 * Allow several hl_flags to be combined, like "bu" for
3416 * bold-underlined.
3417 */
3418 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003419 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003420 {
3421 if (VIM_ISWHITE(*p)) // ignore white space
3422 continue;
3423
3424 if (attr > HL_ALL) // Combination with ':' is not allowed.
3425 return FAIL;
3426
3427 switch (*p)
3428 {
3429 case 'b': attr |= HL_BOLD;
3430 break;
3431 case 'i': attr |= HL_ITALIC;
3432 break;
3433 case '-':
3434 case 'n': // no highlighting
3435 break;
3436 case 'r': attr |= HL_INVERSE;
3437 break;
3438 case 's': attr |= HL_STANDOUT;
3439 break;
3440 case 'u': attr |= HL_UNDERLINE;
3441 break;
3442 case 'c': attr |= HL_UNDERCURL;
3443 break;
3444 case 't': attr |= HL_STRIKETHROUGH;
3445 break;
3446 case ':': ++p; // highlight group name
3447 if (attr || *p == NUL) // no combinations
3448 return FAIL;
3449 end = vim_strchr(p, ',');
3450 if (end == NULL)
3451 end = p + STRLEN(p);
3452 id = syn_check_group(p, (int)(end - p));
3453 if (id == 0)
3454 return FAIL;
3455 attr = syn_id2attr(id);
3456 p = end - 1;
3457#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3458 if (hlf == (int)HLF_SNC)
3459 id_SNC = syn_get_final_id(id);
3460# ifdef FEAT_TERMINAL
3461 else if (hlf == (int)HLF_ST)
3462 id_ST = syn_get_final_id(id);
3463 else if (hlf == (int)HLF_STNC)
3464 id_STNC = syn_get_final_id(id);
3465# endif
3466 else if (hlf == (int)HLF_S)
3467 id_S = syn_get_final_id(id);
3468#endif
3469 break;
3470 default: return FAIL;
3471 }
3472 }
3473 highlight_attr[hlf] = attr;
3474
3475 p = skip_to_option_part(p); // skip comma and spaces
3476 }
3477 }
3478
3479#ifdef USER_HIGHLIGHT
3480 /*
3481 * Setup the user highlights
3482 *
3483 * Temporarily utilize 28 more hl entries:
3484 * 9 for User1-User9 combined with StatusLineNC
3485 * 9 for User1-User9 combined with StatusLineTerm
3486 * 9 for User1-User9 combined with StatusLineTermNC
3487 * 1 for StatusLine default
3488 * Have to be in there simultaneously in case of table overflows in
3489 * get_attr_entry()
3490 */
3491# ifdef FEAT_STL_OPT
3492 if (ga_grow(&highlight_ga, 28) == FAIL)
3493 return FAIL;
3494 hlcnt = highlight_ga.ga_len;
3495 if (id_S == -1)
3496 {
3497 // Make sure id_S is always valid to simplify code below. Use the last
3498 // entry.
Bram Moolenaara80faa82020-04-12 19:37:17 +02003499 CLEAR_POINTER(&HL_TABLE()[hlcnt + 27]);
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003500 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3501 id_S = hlcnt + 19;
3502 }
3503# endif
3504 for (i = 0; i < 9; i++)
3505 {
3506 sprintf((char *)userhl, "User%d", i + 1);
3507 id = syn_name2id(userhl);
3508 if (id == 0)
3509 {
3510 highlight_user[i] = 0;
3511# ifdef FEAT_STL_OPT
3512 highlight_stlnc[i] = 0;
3513# ifdef FEAT_TERMINAL
3514 highlight_stlterm[i] = 0;
3515 highlight_stltermnc[i] = 0;
3516# endif
3517# endif
3518 }
3519 else
3520 {
3521 highlight_user[i] = syn_id2attr(id);
3522# ifdef FEAT_STL_OPT
3523 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3524 HLF_SNC, highlight_stlnc);
3525# ifdef FEAT_TERMINAL
3526 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3527 HLF_ST, highlight_stlterm);
3528 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3529 HLF_STNC, highlight_stltermnc);
3530# endif
3531# endif
3532 }
3533 }
3534# ifdef FEAT_STL_OPT
3535 highlight_ga.ga_len = hlcnt;
3536# endif
3537
3538#endif // USER_HIGHLIGHT
3539
3540 return OK;
3541}
3542
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003543static void highlight_list(void);
3544static void highlight_list_two(int cnt, int attr);
3545
3546/*
3547 * Handle command line completion for :highlight command.
3548 */
3549 void
3550set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3551{
3552 char_u *p;
3553
3554 // Default: expand group names
3555 xp->xp_context = EXPAND_HIGHLIGHT;
3556 xp->xp_pattern = arg;
3557 include_link = 2;
3558 include_default = 1;
3559
3560 // (part of) subcommand already typed
3561 if (*arg != NUL)
3562 {
3563 p = skiptowhite(arg);
3564 if (*p != NUL) // past "default" or group name
3565 {
3566 include_default = 0;
3567 if (STRNCMP("default", arg, p - arg) == 0)
3568 {
3569 arg = skipwhite(p);
3570 xp->xp_pattern = arg;
3571 p = skiptowhite(arg);
3572 }
3573 if (*p != NUL) // past group name
3574 {
3575 include_link = 0;
3576 if (arg[1] == 'i' && arg[0] == 'N')
3577 highlight_list();
3578 if (STRNCMP("link", arg, p - arg) == 0
3579 || STRNCMP("clear", arg, p - arg) == 0)
3580 {
3581 xp->xp_pattern = skipwhite(p);
3582 p = skiptowhite(xp->xp_pattern);
3583 if (*p != NUL) // past first group name
3584 {
3585 xp->xp_pattern = skipwhite(p);
3586 p = skiptowhite(xp->xp_pattern);
3587 }
3588 }
3589 if (*p != NUL) // past group name(s)
3590 xp->xp_context = EXPAND_NOTHING;
3591 }
3592 }
3593 }
3594}
3595
3596/*
3597 * List highlighting matches in a nice way.
3598 */
3599 static void
3600highlight_list(void)
3601{
3602 int i;
3603
3604 for (i = 10; --i >= 0; )
3605 highlight_list_two(i, HL_ATTR(HLF_D));
3606 for (i = 40; --i >= 0; )
3607 highlight_list_two(99, 0);
3608}
3609
3610 static void
3611highlight_list_two(int cnt, int attr)
3612{
3613 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3614 msg_clr_eos();
3615 out_flush();
3616 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3617}
3618
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003619/*
3620 * Function given to ExpandGeneric() to obtain the list of group names.
3621 */
3622 char_u *
3623get_highlight_name(expand_T *xp UNUSED, int idx)
3624{
3625 return get_highlight_name_ext(xp, idx, TRUE);
3626}
3627
3628/*
3629 * Obtain a highlight group name.
3630 * When "skip_cleared" is TRUE don't return a cleared entry.
3631 */
3632 char_u *
3633get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3634{
3635 if (idx < 0)
3636 return NULL;
3637
3638 // Items are never removed from the table, skip the ones that were
3639 // cleared.
3640 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3641 return (char_u *)"";
3642
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003643 if (idx == highlight_ga.ga_len && include_none != 0)
3644 return (char_u *)"none";
3645 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3646 return (char_u *)"default";
3647 if (idx == highlight_ga.ga_len + include_none + include_default
3648 && include_link != 0)
3649 return (char_u *)"link";
3650 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3651 && include_link != 0)
3652 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003653 if (idx >= highlight_ga.ga_len)
3654 return NULL;
3655 return HL_TABLE()[idx].sg_name;
3656}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003657
3658#if defined(FEAT_GUI) || defined(PROTO)
3659/*
3660 * Free all the highlight group fonts.
3661 * Used when quitting for systems which need it.
3662 */
3663 void
3664free_highlight_fonts(void)
3665{
3666 int idx;
3667
3668 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3669 {
3670 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3671 HL_TABLE()[idx].sg_font = NOFONT;
3672# ifdef FEAT_XFONTSET
3673 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3674 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3675# endif
3676 }
3677
3678 gui_mch_free_font(gui.norm_font);
3679# ifdef FEAT_XFONTSET
3680 gui_mch_free_fontset(gui.fontset);
3681# endif
3682# ifndef FEAT_GUI_GTK
3683 gui_mch_free_font(gui.bold_font);
3684 gui_mch_free_font(gui.ital_font);
3685 gui_mch_free_font(gui.boldital_font);
3686# endif
3687}
3688#endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003689
3690
3691#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003692
3693# define SEARCH_HL_PRIORITY 0
3694
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003695/*
3696 * Add match to the match list of window 'wp'. The pattern 'pat' will be
3697 * highlighted with the group 'grp' with priority 'prio'.
3698 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3699 * If no particular ID is desired, -1 must be specified for 'id'.
3700 * Return ID of added match, -1 on failure.
3701 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003702 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003703match_add(
3704 win_T *wp,
3705 char_u *grp,
3706 char_u *pat,
3707 int prio,
3708 int id,
3709 list_T *pos_list,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003710 char_u *conceal_char UNUSED) // pointer to conceal replacement char
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003711{
3712 matchitem_T *cur;
3713 matchitem_T *prev;
3714 matchitem_T *m;
3715 int hlg_id;
3716 regprog_T *regprog = NULL;
3717 int rtype = SOME_VALID;
3718
3719 if (*grp == NUL || (pat != NULL && *pat == NUL))
3720 return -1;
3721 if (id < -1 || id == 0)
3722 {
Bram Moolenaar32aa1022019-11-02 22:54:41 +01003723 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"),
3724 id);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003725 return -1;
3726 }
3727 if (id != -1)
3728 {
3729 cur = wp->w_match_head;
3730 while (cur != NULL)
3731 {
3732 if (cur->id == id)
3733 {
3734 semsg(_("E801: ID already taken: %d"), id);
3735 return -1;
3736 }
3737 cur = cur->next;
3738 }
3739 }
3740 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3741 {
3742 semsg(_(e_nogroup), grp);
3743 return -1;
3744 }
3745 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3746 {
3747 semsg(_(e_invarg2), pat);
3748 return -1;
3749 }
3750
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003751 // Find available match ID.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003752 while (id == -1)
3753 {
3754 cur = wp->w_match_head;
3755 while (cur != NULL && cur->id != wp->w_next_match_id)
3756 cur = cur->next;
3757 if (cur == NULL)
3758 id = wp->w_next_match_id;
3759 wp->w_next_match_id++;
3760 }
3761
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003762 // Build new match.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003763 m = ALLOC_CLEAR_ONE(matchitem_T);
3764 m->id = id;
3765 m->priority = prio;
3766 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3767 m->hlg_id = hlg_id;
3768 m->match.regprog = regprog;
3769 m->match.rmm_ic = FALSE;
3770 m->match.rmm_maxcol = 0;
3771# if defined(FEAT_CONCEAL)
3772 m->conceal_char = 0;
3773 if (conceal_char != NULL)
3774 m->conceal_char = (*mb_ptr2char)(conceal_char);
3775# endif
3776
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003777 // Set up position matches
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003778 if (pos_list != NULL)
3779 {
3780 linenr_T toplnum = 0;
3781 linenr_T botlnum = 0;
3782 listitem_T *li;
3783 int i;
3784
Bram Moolenaar7e9f3512020-05-13 22:44:22 +02003785 CHECK_LIST_MATERIALIZE(pos_list);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003786 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3787 i++, li = li->li_next)
3788 {
3789 linenr_T lnum = 0;
3790 colnr_T col = 0;
3791 int len = 1;
3792 list_T *subl;
3793 listitem_T *subli;
3794 int error = FALSE;
3795
3796 if (li->li_tv.v_type == VAR_LIST)
3797 {
3798 subl = li->li_tv.vval.v_list;
3799 if (subl == NULL)
3800 goto fail;
3801 subli = subl->lv_first;
3802 if (subli == NULL)
3803 goto fail;
3804 lnum = tv_get_number_chk(&subli->li_tv, &error);
3805 if (error == TRUE)
3806 goto fail;
3807 if (lnum == 0)
3808 {
3809 --i;
3810 continue;
3811 }
3812 m->pos.pos[i].lnum = lnum;
3813 subli = subli->li_next;
3814 if (subli != NULL)
3815 {
3816 col = tv_get_number_chk(&subli->li_tv, &error);
3817 if (error == TRUE)
3818 goto fail;
3819 subli = subli->li_next;
3820 if (subli != NULL)
3821 {
3822 len = tv_get_number_chk(&subli->li_tv, &error);
3823 if (error == TRUE)
3824 goto fail;
3825 }
3826 }
3827 m->pos.pos[i].col = col;
3828 m->pos.pos[i].len = len;
3829 }
3830 else if (li->li_tv.v_type == VAR_NUMBER)
3831 {
3832 if (li->li_tv.vval.v_number == 0)
3833 {
3834 --i;
3835 continue;
3836 }
3837 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3838 m->pos.pos[i].col = 0;
3839 m->pos.pos[i].len = 0;
3840 }
3841 else
3842 {
Bram Moolenaar1500a422019-12-24 15:38:21 +01003843 emsg(_("E290: List or number required"));
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003844 goto fail;
3845 }
3846 if (toplnum == 0 || lnum < toplnum)
3847 toplnum = lnum;
3848 if (botlnum == 0 || lnum >= botlnum)
3849 botlnum = lnum + 1;
3850 }
3851
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003852 // Calculate top and bottom lines for redrawing area
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003853 if (toplnum != 0)
3854 {
3855 if (wp->w_buffer->b_mod_set)
3856 {
3857 if (wp->w_buffer->b_mod_top > toplnum)
3858 wp->w_buffer->b_mod_top = toplnum;
3859 if (wp->w_buffer->b_mod_bot < botlnum)
3860 wp->w_buffer->b_mod_bot = botlnum;
3861 }
3862 else
3863 {
3864 wp->w_buffer->b_mod_set = TRUE;
3865 wp->w_buffer->b_mod_top = toplnum;
3866 wp->w_buffer->b_mod_bot = botlnum;
3867 wp->w_buffer->b_mod_xlines = 0;
3868 }
3869 m->pos.toplnum = toplnum;
3870 m->pos.botlnum = botlnum;
3871 rtype = VALID;
3872 }
3873 }
3874
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003875 // Insert new match. The match list is in ascending order with regard to
3876 // the match priorities.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003877 cur = wp->w_match_head;
3878 prev = cur;
3879 while (cur != NULL && prio >= cur->priority)
3880 {
3881 prev = cur;
3882 cur = cur->next;
3883 }
3884 if (cur == prev)
3885 wp->w_match_head = m;
3886 else
3887 prev->next = m;
3888 m->next = cur;
3889
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003890 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003891 return id;
3892
3893fail:
3894 vim_free(m);
3895 return -1;
3896}
3897
3898/*
3899 * Delete match with ID 'id' in the match list of window 'wp'.
3900 * Print error messages if 'perr' is TRUE.
3901 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003902 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003903match_delete(win_T *wp, int id, int perr)
3904{
3905 matchitem_T *cur = wp->w_match_head;
3906 matchitem_T *prev = cur;
3907 int rtype = SOME_VALID;
3908
3909 if (id < 1)
3910 {
3911 if (perr == TRUE)
3912 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3913 id);
3914 return -1;
3915 }
3916 while (cur != NULL && cur->id != id)
3917 {
3918 prev = cur;
3919 cur = cur->next;
3920 }
3921 if (cur == NULL)
3922 {
3923 if (perr == TRUE)
3924 semsg(_("E803: ID not found: %d"), id);
3925 return -1;
3926 }
3927 if (cur == prev)
3928 wp->w_match_head = cur->next;
3929 else
3930 prev->next = cur->next;
3931 vim_regfree(cur->match.regprog);
3932 vim_free(cur->pattern);
3933 if (cur->pos.toplnum != 0)
3934 {
3935 if (wp->w_buffer->b_mod_set)
3936 {
3937 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3938 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3939 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3940 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3941 }
3942 else
3943 {
3944 wp->w_buffer->b_mod_set = TRUE;
3945 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3946 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3947 wp->w_buffer->b_mod_xlines = 0;
3948 }
3949 rtype = VALID;
3950 }
3951 vim_free(cur);
Bram Moolenaar06029a82019-07-24 14:25:26 +02003952 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003953 return 0;
3954}
3955
3956/*
3957 * Delete all matches in the match list of window 'wp'.
3958 */
3959 void
3960clear_matches(win_T *wp)
3961{
3962 matchitem_T *m;
3963
3964 while (wp->w_match_head != NULL)
3965 {
3966 m = wp->w_match_head->next;
3967 vim_regfree(wp->w_match_head->match.regprog);
3968 vim_free(wp->w_match_head->pattern);
3969 vim_free(wp->w_match_head);
3970 wp->w_match_head = m;
3971 }
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003972 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003973}
3974
3975/*
3976 * Get match from ID 'id' in window 'wp'.
3977 * Return NULL if match not found.
3978 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003979 static matchitem_T *
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003980get_match(win_T *wp, int id)
3981{
3982 matchitem_T *cur = wp->w_match_head;
3983
3984 while (cur != NULL && cur->id != id)
3985 cur = cur->next;
3986 return cur;
3987}
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003988
3989/*
3990 * Init for calling prepare_search_hl().
3991 */
3992 void
3993init_search_hl(win_T *wp, match_T *search_hl)
3994{
3995 matchitem_T *cur;
3996
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003997 // Setup for match and 'hlsearch' highlighting. Disable any previous
3998 // match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003999 cur = wp->w_match_head;
4000 while (cur != NULL)
4001 {
4002 cur->hl.rm = cur->match;
4003 if (cur->hlg_id == 0)
4004 cur->hl.attr = 0;
4005 else
4006 cur->hl.attr = syn_id2attr(cur->hlg_id);
4007 cur->hl.buf = wp->w_buffer;
4008 cur->hl.lnum = 0;
4009 cur->hl.first_lnum = 0;
4010# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004011 // Set the time limit to 'redrawtime'.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004012 profile_setlimit(p_rdt, &(cur->hl.tm));
4013# endif
4014 cur = cur->next;
4015 }
4016 search_hl->buf = wp->w_buffer;
4017 search_hl->lnum = 0;
4018 search_hl->first_lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004019 // time limit is set at the toplevel, for all windows
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004020}
4021
4022/*
4023 * If there is a match fill "shl" and return one.
4024 * Return zero otherwise.
4025 */
4026 static int
4027next_search_hl_pos(
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004028 match_T *shl, // points to a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004029 linenr_T lnum,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004030 posmatch_T *posmatch, // match positions
4031 colnr_T mincol) // minimal column for a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004032{
4033 int i;
4034 int found = -1;
4035
4036 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
4037 {
4038 llpos_T *pos = &posmatch->pos[i];
4039
4040 if (pos->lnum == 0)
4041 break;
4042 if (pos->len == 0 && pos->col < mincol)
4043 continue;
4044 if (pos->lnum == lnum)
4045 {
4046 if (found >= 0)
4047 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004048 // if this match comes before the one at "found" then swap
4049 // them
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004050 if (pos->col < posmatch->pos[found].col)
4051 {
4052 llpos_T tmp = *pos;
4053
4054 *pos = posmatch->pos[found];
4055 posmatch->pos[found] = tmp;
4056 }
4057 }
4058 else
4059 found = i;
4060 }
4061 }
4062 posmatch->cur = 0;
4063 if (found >= 0)
4064 {
4065 colnr_T start = posmatch->pos[found].col == 0
4066 ? 0 : posmatch->pos[found].col - 1;
4067 colnr_T end = posmatch->pos[found].col == 0
4068 ? MAXCOL : start + posmatch->pos[found].len;
4069
4070 shl->lnum = lnum;
4071 shl->rm.startpos[0].lnum = 0;
4072 shl->rm.startpos[0].col = start;
4073 shl->rm.endpos[0].lnum = 0;
4074 shl->rm.endpos[0].col = end;
4075 shl->is_addpos = TRUE;
4076 posmatch->cur = found + 1;
4077 return 1;
4078 }
4079 return 0;
4080}
4081
4082/*
4083 * Search for a next 'hlsearch' or match.
4084 * Uses shl->buf.
4085 * Sets shl->lnum and shl->rm contents.
4086 * Note: Assumes a previous match is always before "lnum", unless
4087 * shl->lnum is zero.
4088 * Careful: Any pointers for buffer lines will become invalid.
4089 */
4090 static void
4091next_search_hl(
4092 win_T *win,
4093 match_T *search_hl,
4094 match_T *shl, // points to search_hl or a match
4095 linenr_T lnum,
4096 colnr_T mincol, // minimal column for a match
4097 matchitem_T *cur) // to retrieve match positions if any
4098{
4099 linenr_T l;
4100 colnr_T matchcol;
4101 long nmatched;
Bram Moolenaar53989552019-12-23 22:59:18 +01004102 int called_emsg_before = called_emsg;
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004103
4104 // for :{range}s/pat only highlight inside the range
4105 if (lnum < search_first_line || lnum > search_last_line)
4106 {
4107 shl->lnum = 0;
4108 return;
4109 }
4110
4111 if (shl->lnum != 0)
4112 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004113 // Check for three situations:
4114 // 1. If the "lnum" is below a previous match, start a new search.
4115 // 2. If the previous match includes "mincol", use it.
4116 // 3. Continue after the previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004117 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4118 if (lnum > l)
4119 shl->lnum = 0;
4120 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4121 return;
4122 }
4123
4124 /*
4125 * Repeat searching for a match until one is found that includes "mincol"
4126 * or none is found in this line.
4127 */
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004128 for (;;)
4129 {
4130# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004131 // Stop searching after passing the time limit.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004132 if (profile_passed_limit(&(shl->tm)))
4133 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004134 shl->lnum = 0; // no match found in time
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004135 break;
4136 }
4137# endif
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004138 // Three situations:
4139 // 1. No useful previous match: search from start of line.
4140 // 2. Not Vi compatible or empty match: continue at next character.
4141 // Break the loop if this is beyond the end of the line.
4142 // 3. Vi compatible searching: continue at end of previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004143 if (shl->lnum == 0)
4144 matchcol = 0;
4145 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4146 || (shl->rm.endpos[0].lnum == 0
4147 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4148 {
4149 char_u *ml;
4150
4151 matchcol = shl->rm.startpos[0].col;
4152 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4153 if (*ml == NUL)
4154 {
4155 ++matchcol;
4156 shl->lnum = 0;
4157 break;
4158 }
4159 if (has_mbyte)
4160 matchcol += mb_ptr2len(ml);
4161 else
4162 ++matchcol;
4163 }
4164 else
4165 matchcol = shl->rm.endpos[0].col;
4166
4167 shl->lnum = lnum;
4168 if (shl->rm.regprog != NULL)
4169 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004170 // Remember whether shl->rm is using a copy of the regprog in
4171 // cur->match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004172 int regprog_is_copy = (shl != search_hl && cur != NULL
4173 && shl == &cur->hl
4174 && cur->match.regprog == cur->hl.rm.regprog);
4175 int timed_out = FALSE;
4176
4177 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4178 matchcol,
4179#ifdef FEAT_RELTIME
4180 &(shl->tm), &timed_out
4181#else
4182 NULL, NULL
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004183#endif
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004184 );
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004185 // Copy the regprog, in case it got freed and recompiled.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004186 if (regprog_is_copy)
4187 cur->match.regprog = cur->hl.rm.regprog;
4188
Bram Moolenaar53989552019-12-23 22:59:18 +01004189 if (called_emsg > called_emsg_before || got_int || timed_out)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004190 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004191 // Error while handling regexp: stop using this regexp.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004192 if (shl == search_hl)
4193 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004194 // don't free regprog in the match list, it's a copy
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004195 vim_regfree(shl->rm.regprog);
4196 set_no_hlsearch(TRUE);
4197 }
4198 shl->rm.regprog = NULL;
4199 shl->lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004200 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004201 break;
4202 }
4203 }
4204 else if (cur != NULL)
4205 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4206 else
4207 nmatched = 0;
4208 if (nmatched == 0)
4209 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004210 shl->lnum = 0; // no match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004211 break;
4212 }
4213 if (shl->rm.startpos[0].lnum > 0
4214 || shl->rm.startpos[0].col >= mincol
4215 || nmatched > 1
4216 || shl->rm.endpos[0].col > mincol)
4217 {
4218 shl->lnum += shl->rm.startpos[0].lnum;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004219 break; // useful match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004220 }
4221 }
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004222}
4223
4224/*
4225 * Advance to the match in window "wp" line "lnum" or past it.
4226 */
4227 void
4228prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4229{
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004230 matchitem_T *cur; // points to the match list
4231 match_T *shl; // points to search_hl or a match
4232 int shl_flag; // flag to indicate whether search_hl
4233 // has been processed or not
4234 int pos_inprogress; // marks that position match search is
4235 // in progress
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004236 int n;
4237
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004238 // When using a multi-line pattern, start searching at the top
4239 // of the window or just after a closed fold.
4240 // Do this both for search_hl and the match list.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004241 cur = wp->w_match_head;
4242 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4243 while (cur != NULL || shl_flag == FALSE)
4244 {
4245 if (shl_flag == FALSE)
4246 {
4247 shl = search_hl;
4248 shl_flag = TRUE;
4249 }
4250 else
4251 shl = &cur->hl;
4252 if (shl->rm.regprog != NULL
4253 && shl->lnum == 0
4254 && re_multiline(shl->rm.regprog))
4255 {
4256 if (shl->first_lnum == 0)
4257 {
4258# ifdef FEAT_FOLDING
4259 for (shl->first_lnum = lnum;
4260 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4261 if (hasFoldingWin(wp, shl->first_lnum - 1,
4262 NULL, NULL, TRUE, NULL))
4263 break;
4264# else
4265 shl->first_lnum = wp->w_topline;
4266# endif
4267 }
4268 if (cur != NULL)
4269 cur->pos.cur = 0;
4270 pos_inprogress = TRUE;
4271 n = 0;
4272 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4273 || (cur != NULL && pos_inprogress)))
4274 {
4275 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4276 shl == search_hl ? NULL : cur);
4277 pos_inprogress = cur == NULL || cur->pos.cur == 0
4278 ? FALSE : TRUE;
4279 if (shl->lnum != 0)
4280 {
4281 shl->first_lnum = shl->lnum
4282 + shl->rm.endpos[0].lnum
4283 - shl->rm.startpos[0].lnum;
4284 n = shl->rm.endpos[0].col;
4285 }
4286 else
4287 {
4288 ++shl->first_lnum;
4289 n = 0;
4290 }
4291 }
4292 }
4293 if (shl != search_hl && cur != NULL)
4294 cur = cur->next;
4295 }
4296}
4297
4298/*
4299 * Prepare for 'hlsearch' and match highlighting in one window line.
4300 * Return TRUE if there is such highlighting and set "search_attr" to the
4301 * current highlight attribute.
4302 */
4303 int
4304prepare_search_hl_line(
4305 win_T *wp,
4306 linenr_T lnum,
4307 colnr_T mincol,
4308 char_u **line,
4309 match_T *search_hl,
4310 int *search_attr)
4311{
4312 matchitem_T *cur; // points to the match list
4313 match_T *shl; // points to search_hl or a match
4314 int shl_flag; // flag to indicate whether search_hl
4315 // has been processed or not
4316 int area_highlighting = FALSE;
4317
4318 /*
4319 * Handle highlighting the last used search pattern and matches.
4320 * Do this for both search_hl and the match list.
4321 * Do not use search_hl in a popup window.
4322 */
4323 cur = wp->w_match_head;
4324 shl_flag = WIN_IS_POPUP(wp);
4325 while (cur != NULL || shl_flag == FALSE)
4326 {
4327 if (shl_flag == FALSE)
4328 {
4329 shl = search_hl;
4330 shl_flag = TRUE;
4331 }
4332 else
4333 shl = &cur->hl;
4334 shl->startcol = MAXCOL;
4335 shl->endcol = MAXCOL;
4336 shl->attr_cur = 0;
4337 shl->is_addpos = FALSE;
4338 if (cur != NULL)
4339 cur->pos.cur = 0;
4340 next_search_hl(wp, search_hl, shl, lnum, mincol,
4341 shl == search_hl ? NULL : cur);
4342
4343 // Need to get the line again, a multi-line regexp may have made it
4344 // invalid.
4345 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4346
4347 if (shl->lnum != 0 && shl->lnum <= lnum)
4348 {
4349 if (shl->lnum == lnum)
4350 shl->startcol = shl->rm.startpos[0].col;
4351 else
4352 shl->startcol = 0;
4353 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4354 - shl->rm.startpos[0].lnum)
4355 shl->endcol = shl->rm.endpos[0].col;
4356 else
4357 shl->endcol = MAXCOL;
4358 // Highlight one character for an empty match.
4359 if (shl->startcol == shl->endcol)
4360 {
4361 if (has_mbyte && (*line)[shl->endcol] != NUL)
4362 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4363 else
4364 ++shl->endcol;
4365 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004366 if ((long)shl->startcol < mincol) // match at leftcol
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004367 {
4368 shl->attr_cur = shl->attr;
4369 *search_attr = shl->attr;
4370 }
4371 area_highlighting = TRUE;
4372 }
4373 if (shl != search_hl && cur != NULL)
4374 cur = cur->next;
4375 }
4376 return area_highlighting;
4377}
4378
4379/*
4380 * For a position in a line: Check for start/end of 'hlsearch' and other
4381 * matches.
4382 * After end, check for start/end of next match.
4383 * When another match, have to check for start again.
4384 * Watch out for matching an empty string!
Bram Moolenaar32aa1022019-11-02 22:54:41 +01004385 * Return the updated search_attr.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004386 */
4387 int
4388update_search_hl(
4389 win_T *wp,
4390 linenr_T lnum,
4391 colnr_T col,
4392 char_u **line,
4393 match_T *search_hl,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004394 int *has_match_conc UNUSED,
4395 int *match_conc UNUSED,
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004396 int did_line_attr,
4397 int lcs_eol_one)
4398{
4399 matchitem_T *cur; // points to the match list
4400 match_T *shl; // points to search_hl or a match
4401 int shl_flag; // flag to indicate whether search_hl
4402 // has been processed or not
4403 int pos_inprogress; // marks that position match search is in
4404 // progress
4405 int search_attr = 0;
4406
4407
4408 // Do this for 'search_hl' and the match list (ordered by priority).
4409 cur = wp->w_match_head;
4410 shl_flag = WIN_IS_POPUP(wp);
4411 while (cur != NULL || shl_flag == FALSE)
4412 {
4413 if (shl_flag == FALSE
4414 && ((cur != NULL
4415 && cur->priority > SEARCH_HL_PRIORITY)
4416 || cur == NULL))
4417 {
4418 shl = search_hl;
4419 shl_flag = TRUE;
4420 }
4421 else
4422 shl = &cur->hl;
4423 if (cur != NULL)
4424 cur->pos.cur = 0;
4425 pos_inprogress = TRUE;
4426 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4427 {
4428 if (shl->startcol != MAXCOL
4429 && col >= shl->startcol
4430 && col < shl->endcol)
4431 {
Bram Moolenaar1614a142019-10-06 22:00:13 +02004432 int next_col = col + mb_ptr2len(*line + col);
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004433
4434 if (shl->endcol < next_col)
4435 shl->endcol = next_col;
4436 shl->attr_cur = shl->attr;
4437# ifdef FEAT_CONCEAL
4438 // Match with the "Conceal" group results in hiding
4439 // the match.
4440 if (cur != NULL
4441 && shl != search_hl
4442 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4443 {
4444 *has_match_conc = col == shl->startcol ? 2 : 1;
4445 *match_conc = cur->conceal_char;
4446 }
4447 else
4448 *has_match_conc = *match_conc = 0;
4449# endif
4450 }
4451 else if (col == shl->endcol)
4452 {
4453 shl->attr_cur = 0;
4454 next_search_hl(wp, search_hl, shl, lnum, col,
4455 shl == search_hl ? NULL : cur);
4456 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4457
4458 // Need to get the line again, a multi-line regexp may have
4459 // made it invalid.
4460 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4461
4462 if (shl->lnum == lnum)
4463 {
4464 shl->startcol = shl->rm.startpos[0].col;
4465 if (shl->rm.endpos[0].lnum == 0)
4466 shl->endcol = shl->rm.endpos[0].col;
4467 else
4468 shl->endcol = MAXCOL;
4469
4470 if (shl->startcol == shl->endcol)
4471 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004472 // highlight empty match, try again after
4473 // it
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004474 if (has_mbyte)
4475 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4476 else
4477 ++shl->endcol;
4478 }
4479
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004480 // Loop to check if the match starts at the
4481 // current position
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004482 continue;
4483 }
4484 }
4485 break;
4486 }
4487 if (shl != search_hl && cur != NULL)
4488 cur = cur->next;
4489 }
4490
4491 // Use attributes from match with highest priority among 'search_hl' and
4492 // the match list.
4493 cur = wp->w_match_head;
4494 shl_flag = WIN_IS_POPUP(wp);
4495 while (cur != NULL || shl_flag == FALSE)
4496 {
4497 if (shl_flag == FALSE
4498 && ((cur != NULL
4499 && cur->priority > SEARCH_HL_PRIORITY)
4500 || cur == NULL))
4501 {
4502 shl = search_hl;
4503 shl_flag = TRUE;
4504 }
4505 else
4506 shl = &cur->hl;
4507 if (shl->attr_cur != 0)
4508 search_attr = shl->attr_cur;
4509 if (shl != search_hl && cur != NULL)
4510 cur = cur->next;
4511 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004512 // Only highlight one character after the last column.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004513 if (*(*line + col) == NUL && (did_line_attr >= 1
4514 || (wp->w_p_list && lcs_eol_one == -1)))
4515 search_attr = 0;
4516 return search_attr;
4517}
4518
4519 int
4520get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4521{
4522 long prevcol = curcol;
4523 int prevcol_hl_flag = FALSE;
4524 matchitem_T *cur; // points to the match list
4525
4526 // we're not really at that column when skipping some text
4527 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4528 ++prevcol;
4529
4530 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4531 prevcol_hl_flag = TRUE;
4532 else
4533 {
4534 cur = wp->w_match_head;
4535 while (cur != NULL)
4536 {
4537 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4538 {
4539 prevcol_hl_flag = TRUE;
4540 break;
4541 }
4542 cur = cur->next;
4543 }
4544 }
4545 return prevcol_hl_flag;
4546}
4547
4548/*
4549 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4550 * or match highlighting.
4551 */
4552 void
4553get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4554{
4555 matchitem_T *cur; // points to the match list
4556 match_T *shl; // points to search_hl or a match
4557 int shl_flag; // flag to indicate whether search_hl
4558 // has been processed or not
4559
4560 cur = wp->w_match_head;
4561 shl_flag = WIN_IS_POPUP(wp);
4562 while (cur != NULL || shl_flag == FALSE)
4563 {
4564 if (shl_flag == FALSE
4565 && ((cur != NULL
4566 && cur->priority > SEARCH_HL_PRIORITY)
4567 || cur == NULL))
4568 {
4569 shl = search_hl;
4570 shl_flag = TRUE;
4571 }
4572 else
4573 shl = &cur->hl;
4574 if (col - 1 == (long)shl->startcol
4575 && (shl == search_hl || !shl->is_addpos))
4576 *char_attr = shl->attr;
4577 if (shl != search_hl && cur != NULL)
4578 cur = cur->next;
4579 }
4580}
4581
4582#endif // FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004583
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004584#if defined(FEAT_EVAL) || defined(PROTO)
4585# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004586 static int
4587matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4588{
4589 dictitem_T *di;
4590
4591 if (tv->v_type != VAR_DICT)
4592 {
4593 emsg(_(e_dictreq));
4594 return FAIL;
4595 }
4596
4597 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4598 *conceal_char = dict_get_string(tv->vval.v_dict,
4599 (char_u *)"conceal", FALSE);
4600
4601 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4602 {
4603 *win = find_win_by_nr_or_id(&di->di_tv);
4604 if (*win == NULL)
4605 {
4606 emsg(_(e_invalwindow));
4607 return FAIL;
4608 }
4609 }
4610
4611 return OK;
4612}
4613#endif
4614
4615/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004616 * "clearmatches()" function
4617 */
4618 void
4619f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4620{
4621#ifdef FEAT_SEARCH_EXTRA
4622 win_T *win = get_optional_window(argvars, 0);
4623
4624 if (win != NULL)
4625 clear_matches(win);
4626#endif
4627}
4628
4629/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004630 * "getmatches()" function
4631 */
4632 void
4633f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4634{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004635# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004636 dict_T *dict;
4637 matchitem_T *cur;
4638 int i;
4639 win_T *win = get_optional_window(argvars, 0);
4640
4641 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4642 return;
4643
4644 cur = win->w_match_head;
4645 while (cur != NULL)
4646 {
4647 dict = dict_alloc();
4648 if (dict == NULL)
4649 return;
4650 if (cur->match.regprog == NULL)
4651 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004652 // match added with matchaddpos()
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004653 for (i = 0; i < MAXPOSMATCH; ++i)
4654 {
4655 llpos_T *llpos;
4656 char buf[30]; // use 30 to avoid compiler warning
4657 list_T *l;
4658
4659 llpos = &cur->pos.pos[i];
4660 if (llpos->lnum == 0)
4661 break;
4662 l = list_alloc();
4663 if (l == NULL)
4664 break;
4665 list_append_number(l, (varnumber_T)llpos->lnum);
4666 if (llpos->col > 0)
4667 {
4668 list_append_number(l, (varnumber_T)llpos->col);
4669 list_append_number(l, (varnumber_T)llpos->len);
4670 }
4671 sprintf(buf, "pos%d", i + 1);
4672 dict_add_list(dict, buf, l);
4673 }
4674 }
4675 else
4676 {
4677 dict_add_string(dict, "pattern", cur->pattern);
4678 }
4679 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4680 dict_add_number(dict, "priority", (long)cur->priority);
4681 dict_add_number(dict, "id", (long)cur->id);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004682# if defined(FEAT_CONCEAL)
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004683 if (cur->conceal_char)
4684 {
4685 char_u buf[MB_MAXBYTES + 1];
4686
4687 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4688 dict_add_string(dict, "conceal", (char_u *)&buf);
4689 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004690# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004691 list_append_dict(rettv->vval.v_list, dict);
4692 cur = cur->next;
4693 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004694# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004695}
4696
4697/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004698 * "setmatches()" function
4699 */
4700 void
4701f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4702{
4703#ifdef FEAT_SEARCH_EXTRA
4704 list_T *l;
4705 listitem_T *li;
4706 dict_T *d;
4707 list_T *s = NULL;
4708 win_T *win = get_optional_window(argvars, 1);
4709
4710 rettv->vval.v_number = -1;
4711 if (argvars[0].v_type != VAR_LIST)
4712 {
4713 emsg(_(e_listreq));
4714 return;
4715 }
4716 if (win == NULL)
4717 return;
4718
4719 if ((l = argvars[0].vval.v_list) != NULL)
4720 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004721 // To some extent make sure that we are dealing with a list from
4722 // "getmatches()".
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004723 li = l->lv_first;
4724 while (li != NULL)
4725 {
4726 if (li->li_tv.v_type != VAR_DICT
4727 || (d = li->li_tv.vval.v_dict) == NULL)
4728 {
4729 emsg(_(e_invarg));
4730 return;
4731 }
4732 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4733 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4734 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4735 && dict_find(d, (char_u *)"priority", -1) != NULL
4736 && dict_find(d, (char_u *)"id", -1) != NULL))
4737 {
4738 emsg(_(e_invarg));
4739 return;
4740 }
4741 li = li->li_next;
4742 }
4743
4744 clear_matches(win);
4745 li = l->lv_first;
4746 while (li != NULL)
4747 {
4748 int i = 0;
4749 char buf[30]; // use 30 to avoid compiler warning
4750 dictitem_T *di;
4751 char_u *group;
4752 int priority;
4753 int id;
4754 char_u *conceal;
4755
4756 d = li->li_tv.vval.v_dict;
4757 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4758 {
4759 if (s == NULL)
4760 {
4761 s = list_alloc();
4762 if (s == NULL)
4763 return;
4764 }
4765
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004766 // match from matchaddpos()
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004767 for (i = 1; i < 9; i++)
4768 {
4769 sprintf((char *)buf, (char *)"pos%d", i);
4770 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4771 {
4772 if (di->di_tv.v_type != VAR_LIST)
4773 return;
4774
4775 list_append_tv(s, &di->di_tv);
4776 s->lv_refcount++;
4777 }
4778 else
4779 break;
4780 }
4781 }
4782
4783 group = dict_get_string(d, (char_u *)"group", TRUE);
4784 priority = (int)dict_get_number(d, (char_u *)"priority");
4785 id = (int)dict_get_number(d, (char_u *)"id");
4786 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4787 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4788 : NULL;
4789 if (i == 0)
4790 {
4791 match_add(win, group,
4792 dict_get_string(d, (char_u *)"pattern", FALSE),
4793 priority, id, NULL, conceal);
4794 }
4795 else
4796 {
4797 match_add(win, group, NULL, priority, id, s, conceal);
4798 list_unref(s);
4799 s = NULL;
4800 }
4801 vim_free(group);
4802 vim_free(conceal);
4803
4804 li = li->li_next;
4805 }
4806 rettv->vval.v_number = 0;
4807 }
4808#endif
4809}
4810
4811/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004812 * "matchadd()" function
4813 */
4814 void
4815f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4816{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004817# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004818 char_u buf[NUMBUFLEN];
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004819 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4820 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4821 int prio = 10; // default priority
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004822 int id = -1;
4823 int error = FALSE;
4824 char_u *conceal_char = NULL;
4825 win_T *win = curwin;
4826
4827 rettv->vval.v_number = -1;
4828
4829 if (grp == NULL || pat == NULL)
4830 return;
4831 if (argvars[2].v_type != VAR_UNKNOWN)
4832 {
4833 prio = (int)tv_get_number_chk(&argvars[2], &error);
4834 if (argvars[3].v_type != VAR_UNKNOWN)
4835 {
4836 id = (int)tv_get_number_chk(&argvars[3], &error);
4837 if (argvars[4].v_type != VAR_UNKNOWN
4838 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4839 return;
4840 }
4841 }
4842 if (error == TRUE)
4843 return;
4844 if (id >= 1 && id <= 3)
4845 {
4846 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4847 return;
4848 }
4849
4850 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4851 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004852# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004853}
4854
4855/*
4856 * "matchaddpos()" function
4857 */
4858 void
4859f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4860{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004861# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004862 char_u buf[NUMBUFLEN];
4863 char_u *group;
4864 int prio = 10;
4865 int id = -1;
4866 int error = FALSE;
4867 list_T *l;
4868 char_u *conceal_char = NULL;
4869 win_T *win = curwin;
4870
4871 rettv->vval.v_number = -1;
4872
4873 group = tv_get_string_buf_chk(&argvars[0], buf);
4874 if (group == NULL)
4875 return;
4876
4877 if (argvars[1].v_type != VAR_LIST)
4878 {
4879 semsg(_(e_listarg), "matchaddpos()");
4880 return;
4881 }
4882 l = argvars[1].vval.v_list;
4883 if (l == NULL)
4884 return;
4885
4886 if (argvars[2].v_type != VAR_UNKNOWN)
4887 {
4888 prio = (int)tv_get_number_chk(&argvars[2], &error);
4889 if (argvars[3].v_type != VAR_UNKNOWN)
4890 {
4891 id = (int)tv_get_number_chk(&argvars[3], &error);
4892
4893 if (argvars[4].v_type != VAR_UNKNOWN
4894 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4895 return;
4896 }
4897 }
4898 if (error == TRUE)
4899 return;
4900
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004901 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004902 if (id == 1 || id == 2)
4903 {
4904 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4905 return;
4906 }
4907
4908 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4909 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004910# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004911}
4912
4913/*
4914 * "matcharg()" function
4915 */
4916 void
4917f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4918{
4919 if (rettv_list_alloc(rettv) == OK)
4920 {
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004921# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004922 int id = (int)tv_get_number(&argvars[0]);
4923 matchitem_T *m;
4924
4925 if (id >= 1 && id <= 3)
4926 {
4927 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4928 {
4929 list_append_string(rettv->vval.v_list,
4930 syn_id2name(m->hlg_id), -1);
4931 list_append_string(rettv->vval.v_list, m->pattern, -1);
4932 }
4933 else
4934 {
4935 list_append_string(rettv->vval.v_list, NULL, -1);
4936 list_append_string(rettv->vval.v_list, NULL, -1);
4937 }
4938 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004939# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004940 }
4941}
4942
4943/*
4944 * "matchdelete()" function
4945 */
4946 void
4947f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4948{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004949# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004950 win_T *win = get_optional_window(argvars, 1);
4951
4952 if (win == NULL)
4953 rettv->vval.v_number = -1;
4954 else
4955 rettv->vval.v_number = match_delete(win,
4956 (int)tv_get_number(&argvars[0]), TRUE);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004957# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004958}
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004959#endif
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004960
4961#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4962/*
4963 * ":[N]match {group} {pattern}"
4964 * Sets nextcmd to the start of the next command, if any. Also called when
4965 * skipping commands to find the next command.
4966 */
4967 void
4968ex_match(exarg_T *eap)
4969{
4970 char_u *p;
4971 char_u *g = NULL;
4972 char_u *end;
4973 int c;
4974 int id;
4975
4976 if (eap->line2 <= 3)
4977 id = eap->line2;
4978 else
4979 {
4980 emsg(_(e_invcmd));
4981 return;
4982 }
4983
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004984 // First clear any old pattern.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004985 if (!eap->skip)
4986 match_delete(curwin, id, FALSE);
4987
Bram Moolenaar1966c242020-04-20 22:42:32 +02004988 if (ends_excmd2(eap->cmd, eap->arg))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004989 end = eap->arg;
4990 else if ((STRNICMP(eap->arg, "none", 4) == 0
Bram Moolenaar1966c242020-04-20 22:42:32 +02004991 && (VIM_ISWHITE(eap->arg[4])
4992 || ends_excmd2(eap->arg, eap->arg + 4))))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004993 end = eap->arg + 4;
4994 else
4995 {
4996 p = skiptowhite(eap->arg);
4997 if (!eap->skip)
4998 g = vim_strnsave(eap->arg, (int)(p - eap->arg));
4999 p = skipwhite(p);
5000 if (*p == NUL)
5001 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02005002 // There must be two arguments.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02005003 vim_free(g);
5004 semsg(_(e_invarg2), eap->arg);
5005 return;
5006 }
Bram Moolenaare8c4abb2020-04-02 21:13:25 +02005007 end = skip_regexp(p + 1, *p, TRUE);
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02005008 if (!eap->skip)
5009 {
Bram Moolenaar1966c242020-04-20 22:42:32 +02005010 if (*end != NUL && !ends_excmd2(end, skipwhite(end + 1)))
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02005011 {
5012 vim_free(g);
5013 eap->errmsg = e_trailing;
5014 return;
5015 }
5016 if (*end != *p)
5017 {
5018 vim_free(g);
5019 semsg(_(e_invarg2), p);
5020 return;
5021 }
5022
5023 c = *end;
5024 *end = NUL;
5025 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
5026 vim_free(g);
5027 *end = c;
5028 }
5029 }
5030 eap->nextcmd = find_nextcmd(end);
5031}
5032#endif