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