blob: 19a9c50a20e0dbaecc3ad572196674ad28dc1ecc [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 {
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100751 if (SOURCING_NAME == NULL && !dodefault)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200752 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;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +0100766 HL_TABLE()[from_id - 1].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +0200767#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;
Bram Moolenaar1a47ae32019-12-29 23:04:25 +01001521 HL_TABLE()[idx].sg_script_ctx.sc_lnum += SOURCING_LNUM;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02001522#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
Bram Moolenaar50985eb2020-01-27 22:09:39 +01003742 range_list_materialize(pos_list);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003743 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3744 i++, li = li->li_next)
3745 {
3746 linenr_T lnum = 0;
3747 colnr_T col = 0;
3748 int len = 1;
3749 list_T *subl;
3750 listitem_T *subli;
3751 int error = FALSE;
3752
3753 if (li->li_tv.v_type == VAR_LIST)
3754 {
3755 subl = li->li_tv.vval.v_list;
3756 if (subl == NULL)
3757 goto fail;
3758 subli = subl->lv_first;
3759 if (subli == NULL)
3760 goto fail;
3761 lnum = tv_get_number_chk(&subli->li_tv, &error);
3762 if (error == TRUE)
3763 goto fail;
3764 if (lnum == 0)
3765 {
3766 --i;
3767 continue;
3768 }
3769 m->pos.pos[i].lnum = lnum;
3770 subli = subli->li_next;
3771 if (subli != NULL)
3772 {
3773 col = tv_get_number_chk(&subli->li_tv, &error);
3774 if (error == TRUE)
3775 goto fail;
3776 subli = subli->li_next;
3777 if (subli != NULL)
3778 {
3779 len = tv_get_number_chk(&subli->li_tv, &error);
3780 if (error == TRUE)
3781 goto fail;
3782 }
3783 }
3784 m->pos.pos[i].col = col;
3785 m->pos.pos[i].len = len;
3786 }
3787 else if (li->li_tv.v_type == VAR_NUMBER)
3788 {
3789 if (li->li_tv.vval.v_number == 0)
3790 {
3791 --i;
3792 continue;
3793 }
3794 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3795 m->pos.pos[i].col = 0;
3796 m->pos.pos[i].len = 0;
3797 }
3798 else
3799 {
Bram Moolenaar1500a422019-12-24 15:38:21 +01003800 emsg(_("E290: List or number required"));
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003801 goto fail;
3802 }
3803 if (toplnum == 0 || lnum < toplnum)
3804 toplnum = lnum;
3805 if (botlnum == 0 || lnum >= botlnum)
3806 botlnum = lnum + 1;
3807 }
3808
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003809 // Calculate top and bottom lines for redrawing area
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003810 if (toplnum != 0)
3811 {
3812 if (wp->w_buffer->b_mod_set)
3813 {
3814 if (wp->w_buffer->b_mod_top > toplnum)
3815 wp->w_buffer->b_mod_top = toplnum;
3816 if (wp->w_buffer->b_mod_bot < botlnum)
3817 wp->w_buffer->b_mod_bot = botlnum;
3818 }
3819 else
3820 {
3821 wp->w_buffer->b_mod_set = TRUE;
3822 wp->w_buffer->b_mod_top = toplnum;
3823 wp->w_buffer->b_mod_bot = botlnum;
3824 wp->w_buffer->b_mod_xlines = 0;
3825 }
3826 m->pos.toplnum = toplnum;
3827 m->pos.botlnum = botlnum;
3828 rtype = VALID;
3829 }
3830 }
3831
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003832 // Insert new match. The match list is in ascending order with regard to
3833 // the match priorities.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003834 cur = wp->w_match_head;
3835 prev = cur;
3836 while (cur != NULL && prio >= cur->priority)
3837 {
3838 prev = cur;
3839 cur = cur->next;
3840 }
3841 if (cur == prev)
3842 wp->w_match_head = m;
3843 else
3844 prev->next = m;
3845 m->next = cur;
3846
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003847 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003848 return id;
3849
3850fail:
3851 vim_free(m);
3852 return -1;
3853}
3854
3855/*
3856 * Delete match with ID 'id' in the match list of window 'wp'.
3857 * Print error messages if 'perr' is TRUE.
3858 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003859 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003860match_delete(win_T *wp, int id, int perr)
3861{
3862 matchitem_T *cur = wp->w_match_head;
3863 matchitem_T *prev = cur;
3864 int rtype = SOME_VALID;
3865
3866 if (id < 1)
3867 {
3868 if (perr == TRUE)
3869 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3870 id);
3871 return -1;
3872 }
3873 while (cur != NULL && cur->id != id)
3874 {
3875 prev = cur;
3876 cur = cur->next;
3877 }
3878 if (cur == NULL)
3879 {
3880 if (perr == TRUE)
3881 semsg(_("E803: ID not found: %d"), id);
3882 return -1;
3883 }
3884 if (cur == prev)
3885 wp->w_match_head = cur->next;
3886 else
3887 prev->next = cur->next;
3888 vim_regfree(cur->match.regprog);
3889 vim_free(cur->pattern);
3890 if (cur->pos.toplnum != 0)
3891 {
3892 if (wp->w_buffer->b_mod_set)
3893 {
3894 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3895 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3896 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3897 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3898 }
3899 else
3900 {
3901 wp->w_buffer->b_mod_set = TRUE;
3902 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3903 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3904 wp->w_buffer->b_mod_xlines = 0;
3905 }
3906 rtype = VALID;
3907 }
3908 vim_free(cur);
Bram Moolenaar06029a82019-07-24 14:25:26 +02003909 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003910 return 0;
3911}
3912
3913/*
3914 * Delete all matches in the match list of window 'wp'.
3915 */
3916 void
3917clear_matches(win_T *wp)
3918{
3919 matchitem_T *m;
3920
3921 while (wp->w_match_head != NULL)
3922 {
3923 m = wp->w_match_head->next;
3924 vim_regfree(wp->w_match_head->match.regprog);
3925 vim_free(wp->w_match_head->pattern);
3926 vim_free(wp->w_match_head);
3927 wp->w_match_head = m;
3928 }
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003929 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003930}
3931
3932/*
3933 * Get match from ID 'id' in window 'wp'.
3934 * Return NULL if match not found.
3935 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003936 static matchitem_T *
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003937get_match(win_T *wp, int id)
3938{
3939 matchitem_T *cur = wp->w_match_head;
3940
3941 while (cur != NULL && cur->id != id)
3942 cur = cur->next;
3943 return cur;
3944}
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003945
3946/*
3947 * Init for calling prepare_search_hl().
3948 */
3949 void
3950init_search_hl(win_T *wp, match_T *search_hl)
3951{
3952 matchitem_T *cur;
3953
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003954 // Setup for match and 'hlsearch' highlighting. Disable any previous
3955 // match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003956 cur = wp->w_match_head;
3957 while (cur != NULL)
3958 {
3959 cur->hl.rm = cur->match;
3960 if (cur->hlg_id == 0)
3961 cur->hl.attr = 0;
3962 else
3963 cur->hl.attr = syn_id2attr(cur->hlg_id);
3964 cur->hl.buf = wp->w_buffer;
3965 cur->hl.lnum = 0;
3966 cur->hl.first_lnum = 0;
3967# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003968 // Set the time limit to 'redrawtime'.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003969 profile_setlimit(p_rdt, &(cur->hl.tm));
3970# endif
3971 cur = cur->next;
3972 }
3973 search_hl->buf = wp->w_buffer;
3974 search_hl->lnum = 0;
3975 search_hl->first_lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003976 // time limit is set at the toplevel, for all windows
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003977}
3978
3979/*
3980 * If there is a match fill "shl" and return one.
3981 * Return zero otherwise.
3982 */
3983 static int
3984next_search_hl_pos(
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003985 match_T *shl, // points to a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003986 linenr_T lnum,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003987 posmatch_T *posmatch, // match positions
3988 colnr_T mincol) // minimal column for a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003989{
3990 int i;
3991 int found = -1;
3992
3993 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
3994 {
3995 llpos_T *pos = &posmatch->pos[i];
3996
3997 if (pos->lnum == 0)
3998 break;
3999 if (pos->len == 0 && pos->col < mincol)
4000 continue;
4001 if (pos->lnum == lnum)
4002 {
4003 if (found >= 0)
4004 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004005 // if this match comes before the one at "found" then swap
4006 // them
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004007 if (pos->col < posmatch->pos[found].col)
4008 {
4009 llpos_T tmp = *pos;
4010
4011 *pos = posmatch->pos[found];
4012 posmatch->pos[found] = tmp;
4013 }
4014 }
4015 else
4016 found = i;
4017 }
4018 }
4019 posmatch->cur = 0;
4020 if (found >= 0)
4021 {
4022 colnr_T start = posmatch->pos[found].col == 0
4023 ? 0 : posmatch->pos[found].col - 1;
4024 colnr_T end = posmatch->pos[found].col == 0
4025 ? MAXCOL : start + posmatch->pos[found].len;
4026
4027 shl->lnum = lnum;
4028 shl->rm.startpos[0].lnum = 0;
4029 shl->rm.startpos[0].col = start;
4030 shl->rm.endpos[0].lnum = 0;
4031 shl->rm.endpos[0].col = end;
4032 shl->is_addpos = TRUE;
4033 posmatch->cur = found + 1;
4034 return 1;
4035 }
4036 return 0;
4037}
4038
4039/*
4040 * Search for a next 'hlsearch' or match.
4041 * Uses shl->buf.
4042 * Sets shl->lnum and shl->rm contents.
4043 * Note: Assumes a previous match is always before "lnum", unless
4044 * shl->lnum is zero.
4045 * Careful: Any pointers for buffer lines will become invalid.
4046 */
4047 static void
4048next_search_hl(
4049 win_T *win,
4050 match_T *search_hl,
4051 match_T *shl, // points to search_hl or a match
4052 linenr_T lnum,
4053 colnr_T mincol, // minimal column for a match
4054 matchitem_T *cur) // to retrieve match positions if any
4055{
4056 linenr_T l;
4057 colnr_T matchcol;
4058 long nmatched;
Bram Moolenaar53989552019-12-23 22:59:18 +01004059 int called_emsg_before = called_emsg;
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004060
4061 // for :{range}s/pat only highlight inside the range
4062 if (lnum < search_first_line || lnum > search_last_line)
4063 {
4064 shl->lnum = 0;
4065 return;
4066 }
4067
4068 if (shl->lnum != 0)
4069 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004070 // Check for three situations:
4071 // 1. If the "lnum" is below a previous match, start a new search.
4072 // 2. If the previous match includes "mincol", use it.
4073 // 3. Continue after the previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004074 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4075 if (lnum > l)
4076 shl->lnum = 0;
4077 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4078 return;
4079 }
4080
4081 /*
4082 * Repeat searching for a match until one is found that includes "mincol"
4083 * or none is found in this line.
4084 */
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004085 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
Bram Moolenaar53989552019-12-23 22:59:18 +01004146 if (called_emsg > called_emsg_before || got_int || timed_out)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004147 {
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 }
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004179}
4180
4181/*
4182 * Advance to the match in window "wp" line "lnum" or past it.
4183 */
4184 void
4185prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4186{
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004187 matchitem_T *cur; // points to the match list
4188 match_T *shl; // points to search_hl or a match
4189 int shl_flag; // flag to indicate whether search_hl
4190 // has been processed or not
4191 int pos_inprogress; // marks that position match search is
4192 // in progress
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004193 int n;
4194
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004195 // When using a multi-line pattern, start searching at the top
4196 // of the window or just after a closed fold.
4197 // Do this both for search_hl and the match list.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004198 cur = wp->w_match_head;
4199 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4200 while (cur != NULL || shl_flag == FALSE)
4201 {
4202 if (shl_flag == FALSE)
4203 {
4204 shl = search_hl;
4205 shl_flag = TRUE;
4206 }
4207 else
4208 shl = &cur->hl;
4209 if (shl->rm.regprog != NULL
4210 && shl->lnum == 0
4211 && re_multiline(shl->rm.regprog))
4212 {
4213 if (shl->first_lnum == 0)
4214 {
4215# ifdef FEAT_FOLDING
4216 for (shl->first_lnum = lnum;
4217 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4218 if (hasFoldingWin(wp, shl->first_lnum - 1,
4219 NULL, NULL, TRUE, NULL))
4220 break;
4221# else
4222 shl->first_lnum = wp->w_topline;
4223# endif
4224 }
4225 if (cur != NULL)
4226 cur->pos.cur = 0;
4227 pos_inprogress = TRUE;
4228 n = 0;
4229 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4230 || (cur != NULL && pos_inprogress)))
4231 {
4232 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4233 shl == search_hl ? NULL : cur);
4234 pos_inprogress = cur == NULL || cur->pos.cur == 0
4235 ? FALSE : TRUE;
4236 if (shl->lnum != 0)
4237 {
4238 shl->first_lnum = shl->lnum
4239 + shl->rm.endpos[0].lnum
4240 - shl->rm.startpos[0].lnum;
4241 n = shl->rm.endpos[0].col;
4242 }
4243 else
4244 {
4245 ++shl->first_lnum;
4246 n = 0;
4247 }
4248 }
4249 }
4250 if (shl != search_hl && cur != NULL)
4251 cur = cur->next;
4252 }
4253}
4254
4255/*
4256 * Prepare for 'hlsearch' and match highlighting in one window line.
4257 * Return TRUE if there is such highlighting and set "search_attr" to the
4258 * current highlight attribute.
4259 */
4260 int
4261prepare_search_hl_line(
4262 win_T *wp,
4263 linenr_T lnum,
4264 colnr_T mincol,
4265 char_u **line,
4266 match_T *search_hl,
4267 int *search_attr)
4268{
4269 matchitem_T *cur; // points to the match list
4270 match_T *shl; // points to search_hl or a match
4271 int shl_flag; // flag to indicate whether search_hl
4272 // has been processed or not
4273 int area_highlighting = FALSE;
4274
4275 /*
4276 * Handle highlighting the last used search pattern and matches.
4277 * Do this for both search_hl and the match list.
4278 * Do not use search_hl in a popup window.
4279 */
4280 cur = wp->w_match_head;
4281 shl_flag = WIN_IS_POPUP(wp);
4282 while (cur != NULL || shl_flag == FALSE)
4283 {
4284 if (shl_flag == FALSE)
4285 {
4286 shl = search_hl;
4287 shl_flag = TRUE;
4288 }
4289 else
4290 shl = &cur->hl;
4291 shl->startcol = MAXCOL;
4292 shl->endcol = MAXCOL;
4293 shl->attr_cur = 0;
4294 shl->is_addpos = FALSE;
4295 if (cur != NULL)
4296 cur->pos.cur = 0;
4297 next_search_hl(wp, search_hl, shl, lnum, mincol,
4298 shl == search_hl ? NULL : cur);
4299
4300 // Need to get the line again, a multi-line regexp may have made it
4301 // invalid.
4302 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4303
4304 if (shl->lnum != 0 && shl->lnum <= lnum)
4305 {
4306 if (shl->lnum == lnum)
4307 shl->startcol = shl->rm.startpos[0].col;
4308 else
4309 shl->startcol = 0;
4310 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4311 - shl->rm.startpos[0].lnum)
4312 shl->endcol = shl->rm.endpos[0].col;
4313 else
4314 shl->endcol = MAXCOL;
4315 // Highlight one character for an empty match.
4316 if (shl->startcol == shl->endcol)
4317 {
4318 if (has_mbyte && (*line)[shl->endcol] != NUL)
4319 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4320 else
4321 ++shl->endcol;
4322 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004323 if ((long)shl->startcol < mincol) // match at leftcol
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004324 {
4325 shl->attr_cur = shl->attr;
4326 *search_attr = shl->attr;
4327 }
4328 area_highlighting = TRUE;
4329 }
4330 if (shl != search_hl && cur != NULL)
4331 cur = cur->next;
4332 }
4333 return area_highlighting;
4334}
4335
4336/*
4337 * For a position in a line: Check for start/end of 'hlsearch' and other
4338 * matches.
4339 * After end, check for start/end of next match.
4340 * When another match, have to check for start again.
4341 * Watch out for matching an empty string!
Bram Moolenaar32aa1022019-11-02 22:54:41 +01004342 * Return the updated search_attr.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004343 */
4344 int
4345update_search_hl(
4346 win_T *wp,
4347 linenr_T lnum,
4348 colnr_T col,
4349 char_u **line,
4350 match_T *search_hl,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004351 int *has_match_conc UNUSED,
4352 int *match_conc UNUSED,
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004353 int did_line_attr,
4354 int lcs_eol_one)
4355{
4356 matchitem_T *cur; // points to the match list
4357 match_T *shl; // points to search_hl or a match
4358 int shl_flag; // flag to indicate whether search_hl
4359 // has been processed or not
4360 int pos_inprogress; // marks that position match search is in
4361 // progress
4362 int search_attr = 0;
4363
4364
4365 // Do this for 'search_hl' and the match list (ordered by priority).
4366 cur = wp->w_match_head;
4367 shl_flag = WIN_IS_POPUP(wp);
4368 while (cur != NULL || shl_flag == FALSE)
4369 {
4370 if (shl_flag == FALSE
4371 && ((cur != NULL
4372 && cur->priority > SEARCH_HL_PRIORITY)
4373 || cur == NULL))
4374 {
4375 shl = search_hl;
4376 shl_flag = TRUE;
4377 }
4378 else
4379 shl = &cur->hl;
4380 if (cur != NULL)
4381 cur->pos.cur = 0;
4382 pos_inprogress = TRUE;
4383 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4384 {
4385 if (shl->startcol != MAXCOL
4386 && col >= shl->startcol
4387 && col < shl->endcol)
4388 {
Bram Moolenaar1614a142019-10-06 22:00:13 +02004389 int next_col = col + mb_ptr2len(*line + col);
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004390
4391 if (shl->endcol < next_col)
4392 shl->endcol = next_col;
4393 shl->attr_cur = shl->attr;
4394# ifdef FEAT_CONCEAL
4395 // Match with the "Conceal" group results in hiding
4396 // the match.
4397 if (cur != NULL
4398 && shl != search_hl
4399 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4400 {
4401 *has_match_conc = col == shl->startcol ? 2 : 1;
4402 *match_conc = cur->conceal_char;
4403 }
4404 else
4405 *has_match_conc = *match_conc = 0;
4406# endif
4407 }
4408 else if (col == shl->endcol)
4409 {
4410 shl->attr_cur = 0;
4411 next_search_hl(wp, search_hl, shl, lnum, col,
4412 shl == search_hl ? NULL : cur);
4413 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4414
4415 // Need to get the line again, a multi-line regexp may have
4416 // made it invalid.
4417 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4418
4419 if (shl->lnum == lnum)
4420 {
4421 shl->startcol = shl->rm.startpos[0].col;
4422 if (shl->rm.endpos[0].lnum == 0)
4423 shl->endcol = shl->rm.endpos[0].col;
4424 else
4425 shl->endcol = MAXCOL;
4426
4427 if (shl->startcol == shl->endcol)
4428 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004429 // highlight empty match, try again after
4430 // it
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004431 if (has_mbyte)
4432 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4433 else
4434 ++shl->endcol;
4435 }
4436
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004437 // Loop to check if the match starts at the
4438 // current position
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004439 continue;
4440 }
4441 }
4442 break;
4443 }
4444 if (shl != search_hl && cur != NULL)
4445 cur = cur->next;
4446 }
4447
4448 // Use attributes from match with highest priority among 'search_hl' and
4449 // the match list.
4450 cur = wp->w_match_head;
4451 shl_flag = WIN_IS_POPUP(wp);
4452 while (cur != NULL || shl_flag == FALSE)
4453 {
4454 if (shl_flag == FALSE
4455 && ((cur != NULL
4456 && cur->priority > SEARCH_HL_PRIORITY)
4457 || cur == NULL))
4458 {
4459 shl = search_hl;
4460 shl_flag = TRUE;
4461 }
4462 else
4463 shl = &cur->hl;
4464 if (shl->attr_cur != 0)
4465 search_attr = shl->attr_cur;
4466 if (shl != search_hl && cur != NULL)
4467 cur = cur->next;
4468 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004469 // Only highlight one character after the last column.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004470 if (*(*line + col) == NUL && (did_line_attr >= 1
4471 || (wp->w_p_list && lcs_eol_one == -1)))
4472 search_attr = 0;
4473 return search_attr;
4474}
4475
4476 int
4477get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4478{
4479 long prevcol = curcol;
4480 int prevcol_hl_flag = FALSE;
4481 matchitem_T *cur; // points to the match list
4482
4483 // we're not really at that column when skipping some text
4484 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4485 ++prevcol;
4486
4487 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4488 prevcol_hl_flag = TRUE;
4489 else
4490 {
4491 cur = wp->w_match_head;
4492 while (cur != NULL)
4493 {
4494 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4495 {
4496 prevcol_hl_flag = TRUE;
4497 break;
4498 }
4499 cur = cur->next;
4500 }
4501 }
4502 return prevcol_hl_flag;
4503}
4504
4505/*
4506 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4507 * or match highlighting.
4508 */
4509 void
4510get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4511{
4512 matchitem_T *cur; // points to the match list
4513 match_T *shl; // points to search_hl or a match
4514 int shl_flag; // flag to indicate whether search_hl
4515 // has been processed or not
4516
4517 cur = wp->w_match_head;
4518 shl_flag = WIN_IS_POPUP(wp);
4519 while (cur != NULL || shl_flag == FALSE)
4520 {
4521 if (shl_flag == FALSE
4522 && ((cur != NULL
4523 && cur->priority > SEARCH_HL_PRIORITY)
4524 || cur == NULL))
4525 {
4526 shl = search_hl;
4527 shl_flag = TRUE;
4528 }
4529 else
4530 shl = &cur->hl;
4531 if (col - 1 == (long)shl->startcol
4532 && (shl == search_hl || !shl->is_addpos))
4533 *char_attr = shl->attr;
4534 if (shl != search_hl && cur != NULL)
4535 cur = cur->next;
4536 }
4537}
4538
4539#endif // FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004540
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004541#if defined(FEAT_EVAL) || defined(PROTO)
4542# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004543 static int
4544matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4545{
4546 dictitem_T *di;
4547
4548 if (tv->v_type != VAR_DICT)
4549 {
4550 emsg(_(e_dictreq));
4551 return FAIL;
4552 }
4553
4554 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4555 *conceal_char = dict_get_string(tv->vval.v_dict,
4556 (char_u *)"conceal", FALSE);
4557
4558 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4559 {
4560 *win = find_win_by_nr_or_id(&di->di_tv);
4561 if (*win == NULL)
4562 {
4563 emsg(_(e_invalwindow));
4564 return FAIL;
4565 }
4566 }
4567
4568 return OK;
4569}
4570#endif
4571
4572/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004573 * "clearmatches()" function
4574 */
4575 void
4576f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4577{
4578#ifdef FEAT_SEARCH_EXTRA
4579 win_T *win = get_optional_window(argvars, 0);
4580
4581 if (win != NULL)
4582 clear_matches(win);
4583#endif
4584}
4585
4586/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004587 * "getmatches()" function
4588 */
4589 void
4590f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4591{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004592# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004593 dict_T *dict;
4594 matchitem_T *cur;
4595 int i;
4596 win_T *win = get_optional_window(argvars, 0);
4597
4598 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4599 return;
4600
4601 cur = win->w_match_head;
4602 while (cur != NULL)
4603 {
4604 dict = dict_alloc();
4605 if (dict == NULL)
4606 return;
4607 if (cur->match.regprog == NULL)
4608 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004609 // match added with matchaddpos()
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004610 for (i = 0; i < MAXPOSMATCH; ++i)
4611 {
4612 llpos_T *llpos;
4613 char buf[30]; // use 30 to avoid compiler warning
4614 list_T *l;
4615
4616 llpos = &cur->pos.pos[i];
4617 if (llpos->lnum == 0)
4618 break;
4619 l = list_alloc();
4620 if (l == NULL)
4621 break;
4622 list_append_number(l, (varnumber_T)llpos->lnum);
4623 if (llpos->col > 0)
4624 {
4625 list_append_number(l, (varnumber_T)llpos->col);
4626 list_append_number(l, (varnumber_T)llpos->len);
4627 }
4628 sprintf(buf, "pos%d", i + 1);
4629 dict_add_list(dict, buf, l);
4630 }
4631 }
4632 else
4633 {
4634 dict_add_string(dict, "pattern", cur->pattern);
4635 }
4636 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4637 dict_add_number(dict, "priority", (long)cur->priority);
4638 dict_add_number(dict, "id", (long)cur->id);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004639# if defined(FEAT_CONCEAL)
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004640 if (cur->conceal_char)
4641 {
4642 char_u buf[MB_MAXBYTES + 1];
4643
4644 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4645 dict_add_string(dict, "conceal", (char_u *)&buf);
4646 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004647# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004648 list_append_dict(rettv->vval.v_list, dict);
4649 cur = cur->next;
4650 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004651# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004652}
4653
4654/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004655 * "setmatches()" function
4656 */
4657 void
4658f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4659{
4660#ifdef FEAT_SEARCH_EXTRA
4661 list_T *l;
4662 listitem_T *li;
4663 dict_T *d;
4664 list_T *s = NULL;
4665 win_T *win = get_optional_window(argvars, 1);
4666
4667 rettv->vval.v_number = -1;
4668 if (argvars[0].v_type != VAR_LIST)
4669 {
4670 emsg(_(e_listreq));
4671 return;
4672 }
4673 if (win == NULL)
4674 return;
4675
4676 if ((l = argvars[0].vval.v_list) != NULL)
4677 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004678 // To some extent make sure that we are dealing with a list from
4679 // "getmatches()".
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004680 li = l->lv_first;
4681 while (li != NULL)
4682 {
4683 if (li->li_tv.v_type != VAR_DICT
4684 || (d = li->li_tv.vval.v_dict) == NULL)
4685 {
4686 emsg(_(e_invarg));
4687 return;
4688 }
4689 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4690 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4691 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4692 && dict_find(d, (char_u *)"priority", -1) != NULL
4693 && dict_find(d, (char_u *)"id", -1) != NULL))
4694 {
4695 emsg(_(e_invarg));
4696 return;
4697 }
4698 li = li->li_next;
4699 }
4700
4701 clear_matches(win);
4702 li = l->lv_first;
4703 while (li != NULL)
4704 {
4705 int i = 0;
4706 char buf[30]; // use 30 to avoid compiler warning
4707 dictitem_T *di;
4708 char_u *group;
4709 int priority;
4710 int id;
4711 char_u *conceal;
4712
4713 d = li->li_tv.vval.v_dict;
4714 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4715 {
4716 if (s == NULL)
4717 {
4718 s = list_alloc();
4719 if (s == NULL)
4720 return;
4721 }
4722
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004723 // match from matchaddpos()
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004724 for (i = 1; i < 9; i++)
4725 {
4726 sprintf((char *)buf, (char *)"pos%d", i);
4727 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4728 {
4729 if (di->di_tv.v_type != VAR_LIST)
4730 return;
4731
4732 list_append_tv(s, &di->di_tv);
4733 s->lv_refcount++;
4734 }
4735 else
4736 break;
4737 }
4738 }
4739
4740 group = dict_get_string(d, (char_u *)"group", TRUE);
4741 priority = (int)dict_get_number(d, (char_u *)"priority");
4742 id = (int)dict_get_number(d, (char_u *)"id");
4743 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4744 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4745 : NULL;
4746 if (i == 0)
4747 {
4748 match_add(win, group,
4749 dict_get_string(d, (char_u *)"pattern", FALSE),
4750 priority, id, NULL, conceal);
4751 }
4752 else
4753 {
4754 match_add(win, group, NULL, priority, id, s, conceal);
4755 list_unref(s);
4756 s = NULL;
4757 }
4758 vim_free(group);
4759 vim_free(conceal);
4760
4761 li = li->li_next;
4762 }
4763 rettv->vval.v_number = 0;
4764 }
4765#endif
4766}
4767
4768/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004769 * "matchadd()" function
4770 */
4771 void
4772f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4773{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004774# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004775 char_u buf[NUMBUFLEN];
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004776 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4777 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4778 int prio = 10; // default priority
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004779 int id = -1;
4780 int error = FALSE;
4781 char_u *conceal_char = NULL;
4782 win_T *win = curwin;
4783
4784 rettv->vval.v_number = -1;
4785
4786 if (grp == NULL || pat == NULL)
4787 return;
4788 if (argvars[2].v_type != VAR_UNKNOWN)
4789 {
4790 prio = (int)tv_get_number_chk(&argvars[2], &error);
4791 if (argvars[3].v_type != VAR_UNKNOWN)
4792 {
4793 id = (int)tv_get_number_chk(&argvars[3], &error);
4794 if (argvars[4].v_type != VAR_UNKNOWN
4795 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4796 return;
4797 }
4798 }
4799 if (error == TRUE)
4800 return;
4801 if (id >= 1 && id <= 3)
4802 {
4803 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4804 return;
4805 }
4806
4807 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4808 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004809# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004810}
4811
4812/*
4813 * "matchaddpos()" function
4814 */
4815 void
4816f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4817{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004818# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004819 char_u buf[NUMBUFLEN];
4820 char_u *group;
4821 int prio = 10;
4822 int id = -1;
4823 int error = FALSE;
4824 list_T *l;
4825 char_u *conceal_char = NULL;
4826 win_T *win = curwin;
4827
4828 rettv->vval.v_number = -1;
4829
4830 group = tv_get_string_buf_chk(&argvars[0], buf);
4831 if (group == NULL)
4832 return;
4833
4834 if (argvars[1].v_type != VAR_LIST)
4835 {
4836 semsg(_(e_listarg), "matchaddpos()");
4837 return;
4838 }
4839 l = argvars[1].vval.v_list;
4840 if (l == NULL)
4841 return;
4842
4843 if (argvars[2].v_type != VAR_UNKNOWN)
4844 {
4845 prio = (int)tv_get_number_chk(&argvars[2], &error);
4846 if (argvars[3].v_type != VAR_UNKNOWN)
4847 {
4848 id = (int)tv_get_number_chk(&argvars[3], &error);
4849
4850 if (argvars[4].v_type != VAR_UNKNOWN
4851 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4852 return;
4853 }
4854 }
4855 if (error == TRUE)
4856 return;
4857
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004858 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004859 if (id == 1 || id == 2)
4860 {
4861 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4862 return;
4863 }
4864
4865 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4866 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004867# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004868}
4869
4870/*
4871 * "matcharg()" function
4872 */
4873 void
4874f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4875{
4876 if (rettv_list_alloc(rettv) == OK)
4877 {
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004878# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004879 int id = (int)tv_get_number(&argvars[0]);
4880 matchitem_T *m;
4881
4882 if (id >= 1 && id <= 3)
4883 {
4884 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4885 {
4886 list_append_string(rettv->vval.v_list,
4887 syn_id2name(m->hlg_id), -1);
4888 list_append_string(rettv->vval.v_list, m->pattern, -1);
4889 }
4890 else
4891 {
4892 list_append_string(rettv->vval.v_list, NULL, -1);
4893 list_append_string(rettv->vval.v_list, NULL, -1);
4894 }
4895 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004896# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004897 }
4898}
4899
4900/*
4901 * "matchdelete()" function
4902 */
4903 void
4904f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4905{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004906# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004907 win_T *win = get_optional_window(argvars, 1);
4908
4909 if (win == NULL)
4910 rettv->vval.v_number = -1;
4911 else
4912 rettv->vval.v_number = match_delete(win,
4913 (int)tv_get_number(&argvars[0]), TRUE);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004914# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004915}
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004916#endif
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004917
4918#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4919/*
4920 * ":[N]match {group} {pattern}"
4921 * Sets nextcmd to the start of the next command, if any. Also called when
4922 * skipping commands to find the next command.
4923 */
4924 void
4925ex_match(exarg_T *eap)
4926{
4927 char_u *p;
4928 char_u *g = NULL;
4929 char_u *end;
4930 int c;
4931 int id;
4932
4933 if (eap->line2 <= 3)
4934 id = eap->line2;
4935 else
4936 {
4937 emsg(_(e_invcmd));
4938 return;
4939 }
4940
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004941 // First clear any old pattern.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004942 if (!eap->skip)
4943 match_delete(curwin, id, FALSE);
4944
4945 if (ends_excmd(*eap->arg))
4946 end = eap->arg;
4947 else if ((STRNICMP(eap->arg, "none", 4) == 0
4948 && (VIM_ISWHITE(eap->arg[4]) || ends_excmd(eap->arg[4]))))
4949 end = eap->arg + 4;
4950 else
4951 {
4952 p = skiptowhite(eap->arg);
4953 if (!eap->skip)
4954 g = vim_strnsave(eap->arg, (int)(p - eap->arg));
4955 p = skipwhite(p);
4956 if (*p == NUL)
4957 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004958 // There must be two arguments.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004959 vim_free(g);
4960 semsg(_(e_invarg2), eap->arg);
4961 return;
4962 }
4963 end = skip_regexp(p + 1, *p, TRUE, NULL);
4964 if (!eap->skip)
4965 {
4966 if (*end != NUL && !ends_excmd(*skipwhite(end + 1)))
4967 {
4968 vim_free(g);
4969 eap->errmsg = e_trailing;
4970 return;
4971 }
4972 if (*end != *p)
4973 {
4974 vim_free(g);
4975 semsg(_(e_invarg2), p);
4976 return;
4977 }
4978
4979 c = *end;
4980 *end = NUL;
4981 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
4982 vim_free(g);
4983 *end = c;
4984 }
4985 }
4986 eap->nextcmd = find_nextcmd(end);
4987}
4988#endif