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