blob: 44888e97e5940d385d442fcdc4fdc03acc655954 [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) \
Bram Moolenaar219c7d02020-02-01 21:57:29 +01003137 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3138 && defined(FEAT_TERMGUICOLORS)) \
3139 || defined(FEAT_TERMINAL) || defined(PROTO)
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003140 void
3141syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3142{
3143 hl_group_T *sgp;
3144
3145 hl_id = syn_get_final_id(hl_id);
3146 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3147 *fgp = sgp->sg_cterm_fg - 1;
3148 *bgp = sgp->sg_cterm_bg - 1;
3149}
3150#endif
3151
3152/*
3153 * Translate a group ID to the final group ID (following links).
3154 */
3155 int
3156syn_get_final_id(int hl_id)
3157{
3158 int count;
3159 hl_group_T *sgp;
3160
3161 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3162 return 0; // Can be called from eval!!
3163
3164 /*
3165 * Follow links until there is no more.
3166 * Look out for loops! Break after 100 links.
3167 */
3168 for (count = 100; --count >= 0; )
3169 {
3170 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3171 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3172 break;
3173 hl_id = sgp->sg_link;
3174 }
3175
3176 return hl_id;
3177}
3178
3179#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3180/*
3181 * Call this function just after the GUI has started.
3182 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3183 * It finds the font and color handles for the highlighting groups.
3184 */
3185 void
3186highlight_gui_started(void)
3187{
3188 int idx;
3189
3190 // First get the colors from the "Normal" and "Menu" group, if set
3191 if (USE_24BIT)
3192 set_normal_colors();
3193
3194 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3195 gui_do_one_color(idx, FALSE, FALSE);
3196
3197 highlight_changed();
3198}
3199
3200 static void
3201gui_do_one_color(
3202 int idx,
3203 int do_menu UNUSED, // TRUE: might set the menu font
3204 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3205{
3206 int didit = FALSE;
3207
3208# ifdef FEAT_GUI
3209# ifdef FEAT_TERMGUICOLORS
3210 if (gui.in_use)
3211# endif
3212 if (HL_TABLE()[idx].sg_font_name != NULL)
3213 {
3214 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3215 do_tooltip, TRUE);
3216 didit = TRUE;
3217 }
3218# endif
3219 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3220 {
3221 HL_TABLE()[idx].sg_gui_fg =
3222 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3223 didit = TRUE;
3224 }
3225 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3226 {
3227 HL_TABLE()[idx].sg_gui_bg =
3228 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3229 didit = TRUE;
3230 }
3231# ifdef FEAT_GUI
3232 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3233 {
3234 HL_TABLE()[idx].sg_gui_sp =
3235 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3236 didit = TRUE;
3237 }
3238# endif
3239 if (didit) // need to get a new attr number
3240 set_hl_attr(idx);
3241}
3242#endif
3243
3244#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3245/*
3246 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3247 */
3248 static void
3249combine_stl_hlt(
3250 int id,
3251 int id_S,
3252 int id_alt,
3253 int hlcnt,
3254 int i,
3255 int hlf,
3256 int *table)
3257{
3258 hl_group_T *hlt = HL_TABLE();
3259
3260 if (id_alt == 0)
3261 {
3262 vim_memset(&hlt[hlcnt + i], 0, sizeof(hl_group_T));
3263 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3264 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3265# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3266 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3267# endif
3268 }
3269 else
3270 mch_memmove(&hlt[hlcnt + i],
3271 &hlt[id_alt - 1],
3272 sizeof(hl_group_T));
3273 hlt[hlcnt + i].sg_link = 0;
3274
3275 hlt[hlcnt + i].sg_term ^=
3276 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3277 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3278 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3279 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3280 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3281 hlt[hlcnt + i].sg_cterm ^=
3282 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3283 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3284 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3285 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3286 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3287# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3288 hlt[hlcnt + i].sg_gui ^=
3289 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3290# endif
3291# ifdef FEAT_GUI
3292 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3293 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3294 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3295 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3296 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3297 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3298 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3299 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3300# ifdef FEAT_XFONTSET
3301 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3302 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3303# endif
3304# endif
3305 highlight_ga.ga_len = hlcnt + i + 1;
3306 set_hl_attr(hlcnt + i); // At long last we can apply
3307 table[i] = syn_id2attr(hlcnt + i + 1);
3308}
3309#endif
3310
3311/*
3312 * Translate the 'highlight' option into attributes in highlight_attr[] and
3313 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3314 * corresponding highlights to use on top of HLF_SNC is computed.
3315 * Called only when the 'highlight' option has been changed and upon first
3316 * screen redraw after any :highlight command.
3317 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3318 */
3319 int
3320highlight_changed(void)
3321{
3322 int hlf;
3323 int i;
3324 char_u *p;
3325 int attr;
3326 char_u *end;
3327 int id;
3328#ifdef USER_HIGHLIGHT
3329 char_u userhl[30]; // use 30 to avoid compiler warning
3330# ifdef FEAT_STL_OPT
3331 int id_S = -1;
3332 int id_SNC = 0;
3333# ifdef FEAT_TERMINAL
3334 int id_ST = 0;
3335 int id_STNC = 0;
3336# endif
3337 int hlcnt;
3338# endif
3339#endif
3340 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3341
3342 need_highlight_changed = FALSE;
3343
3344 /*
3345 * Clear all attributes.
3346 */
3347 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3348 highlight_attr[hlf] = 0;
3349
3350 /*
3351 * First set all attributes to their default value.
3352 * Then use the attributes from the 'highlight' option.
3353 */
3354 for (i = 0; i < 2; ++i)
3355 {
3356 if (i)
3357 p = p_hl;
3358 else
3359 p = get_highlight_default();
3360 if (p == NULL) // just in case
3361 continue;
3362
3363 while (*p)
3364 {
3365 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3366 if (hl_flags[hlf] == *p)
3367 break;
3368 ++p;
3369 if (hlf == (int)HLF_COUNT || *p == NUL)
3370 return FAIL;
3371
3372 /*
3373 * Allow several hl_flags to be combined, like "bu" for
3374 * bold-underlined.
3375 */
3376 attr = 0;
Bram Moolenaarc95e8d62019-12-05 18:35:44 +01003377 for ( ; *p && *p != ','; ++p) // parse up to comma
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003378 {
3379 if (VIM_ISWHITE(*p)) // ignore white space
3380 continue;
3381
3382 if (attr > HL_ALL) // Combination with ':' is not allowed.
3383 return FAIL;
3384
3385 switch (*p)
3386 {
3387 case 'b': attr |= HL_BOLD;
3388 break;
3389 case 'i': attr |= HL_ITALIC;
3390 break;
3391 case '-':
3392 case 'n': // no highlighting
3393 break;
3394 case 'r': attr |= HL_INVERSE;
3395 break;
3396 case 's': attr |= HL_STANDOUT;
3397 break;
3398 case 'u': attr |= HL_UNDERLINE;
3399 break;
3400 case 'c': attr |= HL_UNDERCURL;
3401 break;
3402 case 't': attr |= HL_STRIKETHROUGH;
3403 break;
3404 case ':': ++p; // highlight group name
3405 if (attr || *p == NUL) // no combinations
3406 return FAIL;
3407 end = vim_strchr(p, ',');
3408 if (end == NULL)
3409 end = p + STRLEN(p);
3410 id = syn_check_group(p, (int)(end - p));
3411 if (id == 0)
3412 return FAIL;
3413 attr = syn_id2attr(id);
3414 p = end - 1;
3415#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3416 if (hlf == (int)HLF_SNC)
3417 id_SNC = syn_get_final_id(id);
3418# ifdef FEAT_TERMINAL
3419 else if (hlf == (int)HLF_ST)
3420 id_ST = syn_get_final_id(id);
3421 else if (hlf == (int)HLF_STNC)
3422 id_STNC = syn_get_final_id(id);
3423# endif
3424 else if (hlf == (int)HLF_S)
3425 id_S = syn_get_final_id(id);
3426#endif
3427 break;
3428 default: return FAIL;
3429 }
3430 }
3431 highlight_attr[hlf] = attr;
3432
3433 p = skip_to_option_part(p); // skip comma and spaces
3434 }
3435 }
3436
3437#ifdef USER_HIGHLIGHT
3438 /*
3439 * Setup the user highlights
3440 *
3441 * Temporarily utilize 28 more hl entries:
3442 * 9 for User1-User9 combined with StatusLineNC
3443 * 9 for User1-User9 combined with StatusLineTerm
3444 * 9 for User1-User9 combined with StatusLineTermNC
3445 * 1 for StatusLine default
3446 * Have to be in there simultaneously in case of table overflows in
3447 * get_attr_entry()
3448 */
3449# ifdef FEAT_STL_OPT
3450 if (ga_grow(&highlight_ga, 28) == FAIL)
3451 return FAIL;
3452 hlcnt = highlight_ga.ga_len;
3453 if (id_S == -1)
3454 {
3455 // Make sure id_S is always valid to simplify code below. Use the last
3456 // entry.
3457 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(hl_group_T));
3458 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3459 id_S = hlcnt + 19;
3460 }
3461# endif
3462 for (i = 0; i < 9; i++)
3463 {
3464 sprintf((char *)userhl, "User%d", i + 1);
3465 id = syn_name2id(userhl);
3466 if (id == 0)
3467 {
3468 highlight_user[i] = 0;
3469# ifdef FEAT_STL_OPT
3470 highlight_stlnc[i] = 0;
3471# ifdef FEAT_TERMINAL
3472 highlight_stlterm[i] = 0;
3473 highlight_stltermnc[i] = 0;
3474# endif
3475# endif
3476 }
3477 else
3478 {
3479 highlight_user[i] = syn_id2attr(id);
3480# ifdef FEAT_STL_OPT
3481 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3482 HLF_SNC, highlight_stlnc);
3483# ifdef FEAT_TERMINAL
3484 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3485 HLF_ST, highlight_stlterm);
3486 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3487 HLF_STNC, highlight_stltermnc);
3488# endif
3489# endif
3490 }
3491 }
3492# ifdef FEAT_STL_OPT
3493 highlight_ga.ga_len = hlcnt;
3494# endif
3495
3496#endif // USER_HIGHLIGHT
3497
3498 return OK;
3499}
3500
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003501static void highlight_list(void);
3502static void highlight_list_two(int cnt, int attr);
3503
3504/*
3505 * Handle command line completion for :highlight command.
3506 */
3507 void
3508set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3509{
3510 char_u *p;
3511
3512 // Default: expand group names
3513 xp->xp_context = EXPAND_HIGHLIGHT;
3514 xp->xp_pattern = arg;
3515 include_link = 2;
3516 include_default = 1;
3517
3518 // (part of) subcommand already typed
3519 if (*arg != NUL)
3520 {
3521 p = skiptowhite(arg);
3522 if (*p != NUL) // past "default" or group name
3523 {
3524 include_default = 0;
3525 if (STRNCMP("default", arg, p - arg) == 0)
3526 {
3527 arg = skipwhite(p);
3528 xp->xp_pattern = arg;
3529 p = skiptowhite(arg);
3530 }
3531 if (*p != NUL) // past group name
3532 {
3533 include_link = 0;
3534 if (arg[1] == 'i' && arg[0] == 'N')
3535 highlight_list();
3536 if (STRNCMP("link", arg, p - arg) == 0
3537 || STRNCMP("clear", arg, p - arg) == 0)
3538 {
3539 xp->xp_pattern = skipwhite(p);
3540 p = skiptowhite(xp->xp_pattern);
3541 if (*p != NUL) // past first group name
3542 {
3543 xp->xp_pattern = skipwhite(p);
3544 p = skiptowhite(xp->xp_pattern);
3545 }
3546 }
3547 if (*p != NUL) // past group name(s)
3548 xp->xp_context = EXPAND_NOTHING;
3549 }
3550 }
3551 }
3552}
3553
3554/*
3555 * List highlighting matches in a nice way.
3556 */
3557 static void
3558highlight_list(void)
3559{
3560 int i;
3561
3562 for (i = 10; --i >= 0; )
3563 highlight_list_two(i, HL_ATTR(HLF_D));
3564 for (i = 40; --i >= 0; )
3565 highlight_list_two(99, 0);
3566}
3567
3568 static void
3569highlight_list_two(int cnt, int attr)
3570{
3571 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3572 msg_clr_eos();
3573 out_flush();
3574 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3575}
3576
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003577/*
3578 * Function given to ExpandGeneric() to obtain the list of group names.
3579 */
3580 char_u *
3581get_highlight_name(expand_T *xp UNUSED, int idx)
3582{
3583 return get_highlight_name_ext(xp, idx, TRUE);
3584}
3585
3586/*
3587 * Obtain a highlight group name.
3588 * When "skip_cleared" is TRUE don't return a cleared entry.
3589 */
3590 char_u *
3591get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3592{
3593 if (idx < 0)
3594 return NULL;
3595
3596 // Items are never removed from the table, skip the ones that were
3597 // cleared.
3598 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3599 return (char_u *)"";
3600
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003601 if (idx == highlight_ga.ga_len && include_none != 0)
3602 return (char_u *)"none";
3603 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3604 return (char_u *)"default";
3605 if (idx == highlight_ga.ga_len + include_none + include_default
3606 && include_link != 0)
3607 return (char_u *)"link";
3608 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3609 && include_link != 0)
3610 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003611 if (idx >= highlight_ga.ga_len)
3612 return NULL;
3613 return HL_TABLE()[idx].sg_name;
3614}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003615
3616#if defined(FEAT_GUI) || defined(PROTO)
3617/*
3618 * Free all the highlight group fonts.
3619 * Used when quitting for systems which need it.
3620 */
3621 void
3622free_highlight_fonts(void)
3623{
3624 int idx;
3625
3626 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3627 {
3628 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3629 HL_TABLE()[idx].sg_font = NOFONT;
3630# ifdef FEAT_XFONTSET
3631 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3632 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3633# endif
3634 }
3635
3636 gui_mch_free_font(gui.norm_font);
3637# ifdef FEAT_XFONTSET
3638 gui_mch_free_fontset(gui.fontset);
3639# endif
3640# ifndef FEAT_GUI_GTK
3641 gui_mch_free_font(gui.bold_font);
3642 gui_mch_free_font(gui.ital_font);
3643 gui_mch_free_font(gui.boldital_font);
3644# endif
3645}
3646#endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003647
3648
3649#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003650
3651# define SEARCH_HL_PRIORITY 0
3652
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003653/*
3654 * Add match to the match list of window 'wp'. The pattern 'pat' will be
3655 * highlighted with the group 'grp' with priority 'prio'.
3656 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3657 * If no particular ID is desired, -1 must be specified for 'id'.
3658 * Return ID of added match, -1 on failure.
3659 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003660 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003661match_add(
3662 win_T *wp,
3663 char_u *grp,
3664 char_u *pat,
3665 int prio,
3666 int id,
3667 list_T *pos_list,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003668 char_u *conceal_char UNUSED) // pointer to conceal replacement char
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003669{
3670 matchitem_T *cur;
3671 matchitem_T *prev;
3672 matchitem_T *m;
3673 int hlg_id;
3674 regprog_T *regprog = NULL;
3675 int rtype = SOME_VALID;
3676
3677 if (*grp == NUL || (pat != NULL && *pat == NUL))
3678 return -1;
3679 if (id < -1 || id == 0)
3680 {
Bram Moolenaar32aa1022019-11-02 22:54:41 +01003681 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"),
3682 id);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003683 return -1;
3684 }
3685 if (id != -1)
3686 {
3687 cur = wp->w_match_head;
3688 while (cur != NULL)
3689 {
3690 if (cur->id == id)
3691 {
3692 semsg(_("E801: ID already taken: %d"), id);
3693 return -1;
3694 }
3695 cur = cur->next;
3696 }
3697 }
3698 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3699 {
3700 semsg(_(e_nogroup), grp);
3701 return -1;
3702 }
3703 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3704 {
3705 semsg(_(e_invarg2), pat);
3706 return -1;
3707 }
3708
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003709 // Find available match ID.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003710 while (id == -1)
3711 {
3712 cur = wp->w_match_head;
3713 while (cur != NULL && cur->id != wp->w_next_match_id)
3714 cur = cur->next;
3715 if (cur == NULL)
3716 id = wp->w_next_match_id;
3717 wp->w_next_match_id++;
3718 }
3719
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003720 // Build new match.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003721 m = ALLOC_CLEAR_ONE(matchitem_T);
3722 m->id = id;
3723 m->priority = prio;
3724 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3725 m->hlg_id = hlg_id;
3726 m->match.regprog = regprog;
3727 m->match.rmm_ic = FALSE;
3728 m->match.rmm_maxcol = 0;
3729# if defined(FEAT_CONCEAL)
3730 m->conceal_char = 0;
3731 if (conceal_char != NULL)
3732 m->conceal_char = (*mb_ptr2char)(conceal_char);
3733# endif
3734
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003735 // Set up position matches
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003736 if (pos_list != NULL)
3737 {
3738 linenr_T toplnum = 0;
3739 linenr_T botlnum = 0;
3740 listitem_T *li;
3741 int i;
3742
Bram Moolenaar50985eb2020-01-27 22:09:39 +01003743 range_list_materialize(pos_list);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003744 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3745 i++, li = li->li_next)
3746 {
3747 linenr_T lnum = 0;
3748 colnr_T col = 0;
3749 int len = 1;
3750 list_T *subl;
3751 listitem_T *subli;
3752 int error = FALSE;
3753
3754 if (li->li_tv.v_type == VAR_LIST)
3755 {
3756 subl = li->li_tv.vval.v_list;
3757 if (subl == NULL)
3758 goto fail;
3759 subli = subl->lv_first;
3760 if (subli == NULL)
3761 goto fail;
3762 lnum = tv_get_number_chk(&subli->li_tv, &error);
3763 if (error == TRUE)
3764 goto fail;
3765 if (lnum == 0)
3766 {
3767 --i;
3768 continue;
3769 }
3770 m->pos.pos[i].lnum = lnum;
3771 subli = subli->li_next;
3772 if (subli != NULL)
3773 {
3774 col = tv_get_number_chk(&subli->li_tv, &error);
3775 if (error == TRUE)
3776 goto fail;
3777 subli = subli->li_next;
3778 if (subli != NULL)
3779 {
3780 len = tv_get_number_chk(&subli->li_tv, &error);
3781 if (error == TRUE)
3782 goto fail;
3783 }
3784 }
3785 m->pos.pos[i].col = col;
3786 m->pos.pos[i].len = len;
3787 }
3788 else if (li->li_tv.v_type == VAR_NUMBER)
3789 {
3790 if (li->li_tv.vval.v_number == 0)
3791 {
3792 --i;
3793 continue;
3794 }
3795 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3796 m->pos.pos[i].col = 0;
3797 m->pos.pos[i].len = 0;
3798 }
3799 else
3800 {
Bram Moolenaar1500a422019-12-24 15:38:21 +01003801 emsg(_("E290: List or number required"));
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003802 goto fail;
3803 }
3804 if (toplnum == 0 || lnum < toplnum)
3805 toplnum = lnum;
3806 if (botlnum == 0 || lnum >= botlnum)
3807 botlnum = lnum + 1;
3808 }
3809
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003810 // Calculate top and bottom lines for redrawing area
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003811 if (toplnum != 0)
3812 {
3813 if (wp->w_buffer->b_mod_set)
3814 {
3815 if (wp->w_buffer->b_mod_top > toplnum)
3816 wp->w_buffer->b_mod_top = toplnum;
3817 if (wp->w_buffer->b_mod_bot < botlnum)
3818 wp->w_buffer->b_mod_bot = botlnum;
3819 }
3820 else
3821 {
3822 wp->w_buffer->b_mod_set = TRUE;
3823 wp->w_buffer->b_mod_top = toplnum;
3824 wp->w_buffer->b_mod_bot = botlnum;
3825 wp->w_buffer->b_mod_xlines = 0;
3826 }
3827 m->pos.toplnum = toplnum;
3828 m->pos.botlnum = botlnum;
3829 rtype = VALID;
3830 }
3831 }
3832
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003833 // Insert new match. The match list is in ascending order with regard to
3834 // the match priorities.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003835 cur = wp->w_match_head;
3836 prev = cur;
3837 while (cur != NULL && prio >= cur->priority)
3838 {
3839 prev = cur;
3840 cur = cur->next;
3841 }
3842 if (cur == prev)
3843 wp->w_match_head = m;
3844 else
3845 prev->next = m;
3846 m->next = cur;
3847
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003848 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003849 return id;
3850
3851fail:
3852 vim_free(m);
3853 return -1;
3854}
3855
3856/*
3857 * Delete match with ID 'id' in the match list of window 'wp'.
3858 * Print error messages if 'perr' is TRUE.
3859 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003860 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003861match_delete(win_T *wp, int id, int perr)
3862{
3863 matchitem_T *cur = wp->w_match_head;
3864 matchitem_T *prev = cur;
3865 int rtype = SOME_VALID;
3866
3867 if (id < 1)
3868 {
3869 if (perr == TRUE)
3870 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3871 id);
3872 return -1;
3873 }
3874 while (cur != NULL && cur->id != id)
3875 {
3876 prev = cur;
3877 cur = cur->next;
3878 }
3879 if (cur == NULL)
3880 {
3881 if (perr == TRUE)
3882 semsg(_("E803: ID not found: %d"), id);
3883 return -1;
3884 }
3885 if (cur == prev)
3886 wp->w_match_head = cur->next;
3887 else
3888 prev->next = cur->next;
3889 vim_regfree(cur->match.regprog);
3890 vim_free(cur->pattern);
3891 if (cur->pos.toplnum != 0)
3892 {
3893 if (wp->w_buffer->b_mod_set)
3894 {
3895 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3896 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3897 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3898 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3899 }
3900 else
3901 {
3902 wp->w_buffer->b_mod_set = TRUE;
3903 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3904 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3905 wp->w_buffer->b_mod_xlines = 0;
3906 }
3907 rtype = VALID;
3908 }
3909 vim_free(cur);
Bram Moolenaar06029a82019-07-24 14:25:26 +02003910 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003911 return 0;
3912}
3913
3914/*
3915 * Delete all matches in the match list of window 'wp'.
3916 */
3917 void
3918clear_matches(win_T *wp)
3919{
3920 matchitem_T *m;
3921
3922 while (wp->w_match_head != NULL)
3923 {
3924 m = wp->w_match_head->next;
3925 vim_regfree(wp->w_match_head->match.regprog);
3926 vim_free(wp->w_match_head->pattern);
3927 vim_free(wp->w_match_head);
3928 wp->w_match_head = m;
3929 }
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003930 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003931}
3932
3933/*
3934 * Get match from ID 'id' in window 'wp'.
3935 * Return NULL if match not found.
3936 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003937 static matchitem_T *
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003938get_match(win_T *wp, int id)
3939{
3940 matchitem_T *cur = wp->w_match_head;
3941
3942 while (cur != NULL && cur->id != id)
3943 cur = cur->next;
3944 return cur;
3945}
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003946
3947/*
3948 * Init for calling prepare_search_hl().
3949 */
3950 void
3951init_search_hl(win_T *wp, match_T *search_hl)
3952{
3953 matchitem_T *cur;
3954
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003955 // Setup for match and 'hlsearch' highlighting. Disable any previous
3956 // match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003957 cur = wp->w_match_head;
3958 while (cur != NULL)
3959 {
3960 cur->hl.rm = cur->match;
3961 if (cur->hlg_id == 0)
3962 cur->hl.attr = 0;
3963 else
3964 cur->hl.attr = syn_id2attr(cur->hlg_id);
3965 cur->hl.buf = wp->w_buffer;
3966 cur->hl.lnum = 0;
3967 cur->hl.first_lnum = 0;
3968# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003969 // Set the time limit to 'redrawtime'.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003970 profile_setlimit(p_rdt, &(cur->hl.tm));
3971# endif
3972 cur = cur->next;
3973 }
3974 search_hl->buf = wp->w_buffer;
3975 search_hl->lnum = 0;
3976 search_hl->first_lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003977 // time limit is set at the toplevel, for all windows
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003978}
3979
3980/*
3981 * If there is a match fill "shl" and return one.
3982 * Return zero otherwise.
3983 */
3984 static int
3985next_search_hl_pos(
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003986 match_T *shl, // points to a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003987 linenr_T lnum,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003988 posmatch_T *posmatch, // match positions
3989 colnr_T mincol) // minimal column for a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003990{
3991 int i;
3992 int found = -1;
3993
3994 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
3995 {
3996 llpos_T *pos = &posmatch->pos[i];
3997
3998 if (pos->lnum == 0)
3999 break;
4000 if (pos->len == 0 && pos->col < mincol)
4001 continue;
4002 if (pos->lnum == lnum)
4003 {
4004 if (found >= 0)
4005 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004006 // if this match comes before the one at "found" then swap
4007 // them
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004008 if (pos->col < posmatch->pos[found].col)
4009 {
4010 llpos_T tmp = *pos;
4011
4012 *pos = posmatch->pos[found];
4013 posmatch->pos[found] = tmp;
4014 }
4015 }
4016 else
4017 found = i;
4018 }
4019 }
4020 posmatch->cur = 0;
4021 if (found >= 0)
4022 {
4023 colnr_T start = posmatch->pos[found].col == 0
4024 ? 0 : posmatch->pos[found].col - 1;
4025 colnr_T end = posmatch->pos[found].col == 0
4026 ? MAXCOL : start + posmatch->pos[found].len;
4027
4028 shl->lnum = lnum;
4029 shl->rm.startpos[0].lnum = 0;
4030 shl->rm.startpos[0].col = start;
4031 shl->rm.endpos[0].lnum = 0;
4032 shl->rm.endpos[0].col = end;
4033 shl->is_addpos = TRUE;
4034 posmatch->cur = found + 1;
4035 return 1;
4036 }
4037 return 0;
4038}
4039
4040/*
4041 * Search for a next 'hlsearch' or match.
4042 * Uses shl->buf.
4043 * Sets shl->lnum and shl->rm contents.
4044 * Note: Assumes a previous match is always before "lnum", unless
4045 * shl->lnum is zero.
4046 * Careful: Any pointers for buffer lines will become invalid.
4047 */
4048 static void
4049next_search_hl(
4050 win_T *win,
4051 match_T *search_hl,
4052 match_T *shl, // points to search_hl or a match
4053 linenr_T lnum,
4054 colnr_T mincol, // minimal column for a match
4055 matchitem_T *cur) // to retrieve match positions if any
4056{
4057 linenr_T l;
4058 colnr_T matchcol;
4059 long nmatched;
Bram Moolenaar53989552019-12-23 22:59:18 +01004060 int called_emsg_before = called_emsg;
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004061
4062 // for :{range}s/pat only highlight inside the range
4063 if (lnum < search_first_line || lnum > search_last_line)
4064 {
4065 shl->lnum = 0;
4066 return;
4067 }
4068
4069 if (shl->lnum != 0)
4070 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004071 // Check for three situations:
4072 // 1. If the "lnum" is below a previous match, start a new search.
4073 // 2. If the previous match includes "mincol", use it.
4074 // 3. Continue after the previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004075 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4076 if (lnum > l)
4077 shl->lnum = 0;
4078 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4079 return;
4080 }
4081
4082 /*
4083 * Repeat searching for a match until one is found that includes "mincol"
4084 * or none is found in this line.
4085 */
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004086 for (;;)
4087 {
4088# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004089 // Stop searching after passing the time limit.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004090 if (profile_passed_limit(&(shl->tm)))
4091 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004092 shl->lnum = 0; // no match found in time
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004093 break;
4094 }
4095# endif
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004096 // Three situations:
4097 // 1. No useful previous match: search from start of line.
4098 // 2. Not Vi compatible or empty match: continue at next character.
4099 // Break the loop if this is beyond the end of the line.
4100 // 3. Vi compatible searching: continue at end of previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004101 if (shl->lnum == 0)
4102 matchcol = 0;
4103 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4104 || (shl->rm.endpos[0].lnum == 0
4105 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4106 {
4107 char_u *ml;
4108
4109 matchcol = shl->rm.startpos[0].col;
4110 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4111 if (*ml == NUL)
4112 {
4113 ++matchcol;
4114 shl->lnum = 0;
4115 break;
4116 }
4117 if (has_mbyte)
4118 matchcol += mb_ptr2len(ml);
4119 else
4120 ++matchcol;
4121 }
4122 else
4123 matchcol = shl->rm.endpos[0].col;
4124
4125 shl->lnum = lnum;
4126 if (shl->rm.regprog != NULL)
4127 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004128 // Remember whether shl->rm is using a copy of the regprog in
4129 // cur->match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004130 int regprog_is_copy = (shl != search_hl && cur != NULL
4131 && shl == &cur->hl
4132 && cur->match.regprog == cur->hl.rm.regprog);
4133 int timed_out = FALSE;
4134
4135 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4136 matchcol,
4137#ifdef FEAT_RELTIME
4138 &(shl->tm), &timed_out
4139#else
4140 NULL, NULL
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004141#endif
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004142 );
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004143 // Copy the regprog, in case it got freed and recompiled.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004144 if (regprog_is_copy)
4145 cur->match.regprog = cur->hl.rm.regprog;
4146
Bram Moolenaar53989552019-12-23 22:59:18 +01004147 if (called_emsg > called_emsg_before || got_int || timed_out)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004148 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004149 // Error while handling regexp: stop using this regexp.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004150 if (shl == search_hl)
4151 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004152 // don't free regprog in the match list, it's a copy
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004153 vim_regfree(shl->rm.regprog);
4154 set_no_hlsearch(TRUE);
4155 }
4156 shl->rm.regprog = NULL;
4157 shl->lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004158 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004159 break;
4160 }
4161 }
4162 else if (cur != NULL)
4163 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4164 else
4165 nmatched = 0;
4166 if (nmatched == 0)
4167 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004168 shl->lnum = 0; // no match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004169 break;
4170 }
4171 if (shl->rm.startpos[0].lnum > 0
4172 || shl->rm.startpos[0].col >= mincol
4173 || nmatched > 1
4174 || shl->rm.endpos[0].col > mincol)
4175 {
4176 shl->lnum += shl->rm.startpos[0].lnum;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004177 break; // useful match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004178 }
4179 }
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004180}
4181
4182/*
4183 * Advance to the match in window "wp" line "lnum" or past it.
4184 */
4185 void
4186prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4187{
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004188 matchitem_T *cur; // points to the match list
4189 match_T *shl; // points to search_hl or a match
4190 int shl_flag; // flag to indicate whether search_hl
4191 // has been processed or not
4192 int pos_inprogress; // marks that position match search is
4193 // in progress
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004194 int n;
4195
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004196 // When using a multi-line pattern, start searching at the top
4197 // of the window or just after a closed fold.
4198 // Do this both for search_hl and the match list.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004199 cur = wp->w_match_head;
4200 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4201 while (cur != NULL || shl_flag == FALSE)
4202 {
4203 if (shl_flag == FALSE)
4204 {
4205 shl = search_hl;
4206 shl_flag = TRUE;
4207 }
4208 else
4209 shl = &cur->hl;
4210 if (shl->rm.regprog != NULL
4211 && shl->lnum == 0
4212 && re_multiline(shl->rm.regprog))
4213 {
4214 if (shl->first_lnum == 0)
4215 {
4216# ifdef FEAT_FOLDING
4217 for (shl->first_lnum = lnum;
4218 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4219 if (hasFoldingWin(wp, shl->first_lnum - 1,
4220 NULL, NULL, TRUE, NULL))
4221 break;
4222# else
4223 shl->first_lnum = wp->w_topline;
4224# endif
4225 }
4226 if (cur != NULL)
4227 cur->pos.cur = 0;
4228 pos_inprogress = TRUE;
4229 n = 0;
4230 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4231 || (cur != NULL && pos_inprogress)))
4232 {
4233 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4234 shl == search_hl ? NULL : cur);
4235 pos_inprogress = cur == NULL || cur->pos.cur == 0
4236 ? FALSE : TRUE;
4237 if (shl->lnum != 0)
4238 {
4239 shl->first_lnum = shl->lnum
4240 + shl->rm.endpos[0].lnum
4241 - shl->rm.startpos[0].lnum;
4242 n = shl->rm.endpos[0].col;
4243 }
4244 else
4245 {
4246 ++shl->first_lnum;
4247 n = 0;
4248 }
4249 }
4250 }
4251 if (shl != search_hl && cur != NULL)
4252 cur = cur->next;
4253 }
4254}
4255
4256/*
4257 * Prepare for 'hlsearch' and match highlighting in one window line.
4258 * Return TRUE if there is such highlighting and set "search_attr" to the
4259 * current highlight attribute.
4260 */
4261 int
4262prepare_search_hl_line(
4263 win_T *wp,
4264 linenr_T lnum,
4265 colnr_T mincol,
4266 char_u **line,
4267 match_T *search_hl,
4268 int *search_attr)
4269{
4270 matchitem_T *cur; // points to the match list
4271 match_T *shl; // points to search_hl or a match
4272 int shl_flag; // flag to indicate whether search_hl
4273 // has been processed or not
4274 int area_highlighting = FALSE;
4275
4276 /*
4277 * Handle highlighting the last used search pattern and matches.
4278 * Do this for both search_hl and the match list.
4279 * Do not use search_hl in a popup window.
4280 */
4281 cur = wp->w_match_head;
4282 shl_flag = WIN_IS_POPUP(wp);
4283 while (cur != NULL || shl_flag == FALSE)
4284 {
4285 if (shl_flag == FALSE)
4286 {
4287 shl = search_hl;
4288 shl_flag = TRUE;
4289 }
4290 else
4291 shl = &cur->hl;
4292 shl->startcol = MAXCOL;
4293 shl->endcol = MAXCOL;
4294 shl->attr_cur = 0;
4295 shl->is_addpos = FALSE;
4296 if (cur != NULL)
4297 cur->pos.cur = 0;
4298 next_search_hl(wp, search_hl, shl, lnum, mincol,
4299 shl == search_hl ? NULL : cur);
4300
4301 // Need to get the line again, a multi-line regexp may have made it
4302 // invalid.
4303 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4304
4305 if (shl->lnum != 0 && shl->lnum <= lnum)
4306 {
4307 if (shl->lnum == lnum)
4308 shl->startcol = shl->rm.startpos[0].col;
4309 else
4310 shl->startcol = 0;
4311 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4312 - shl->rm.startpos[0].lnum)
4313 shl->endcol = shl->rm.endpos[0].col;
4314 else
4315 shl->endcol = MAXCOL;
4316 // Highlight one character for an empty match.
4317 if (shl->startcol == shl->endcol)
4318 {
4319 if (has_mbyte && (*line)[shl->endcol] != NUL)
4320 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4321 else
4322 ++shl->endcol;
4323 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004324 if ((long)shl->startcol < mincol) // match at leftcol
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004325 {
4326 shl->attr_cur = shl->attr;
4327 *search_attr = shl->attr;
4328 }
4329 area_highlighting = TRUE;
4330 }
4331 if (shl != search_hl && cur != NULL)
4332 cur = cur->next;
4333 }
4334 return area_highlighting;
4335}
4336
4337/*
4338 * For a position in a line: Check for start/end of 'hlsearch' and other
4339 * matches.
4340 * After end, check for start/end of next match.
4341 * When another match, have to check for start again.
4342 * Watch out for matching an empty string!
Bram Moolenaar32aa1022019-11-02 22:54:41 +01004343 * Return the updated search_attr.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004344 */
4345 int
4346update_search_hl(
4347 win_T *wp,
4348 linenr_T lnum,
4349 colnr_T col,
4350 char_u **line,
4351 match_T *search_hl,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004352 int *has_match_conc UNUSED,
4353 int *match_conc UNUSED,
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004354 int did_line_attr,
4355 int lcs_eol_one)
4356{
4357 matchitem_T *cur; // points to the match list
4358 match_T *shl; // points to search_hl or a match
4359 int shl_flag; // flag to indicate whether search_hl
4360 // has been processed or not
4361 int pos_inprogress; // marks that position match search is in
4362 // progress
4363 int search_attr = 0;
4364
4365
4366 // Do this for 'search_hl' and the match list (ordered by priority).
4367 cur = wp->w_match_head;
4368 shl_flag = WIN_IS_POPUP(wp);
4369 while (cur != NULL || shl_flag == FALSE)
4370 {
4371 if (shl_flag == FALSE
4372 && ((cur != NULL
4373 && cur->priority > SEARCH_HL_PRIORITY)
4374 || cur == NULL))
4375 {
4376 shl = search_hl;
4377 shl_flag = TRUE;
4378 }
4379 else
4380 shl = &cur->hl;
4381 if (cur != NULL)
4382 cur->pos.cur = 0;
4383 pos_inprogress = TRUE;
4384 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4385 {
4386 if (shl->startcol != MAXCOL
4387 && col >= shl->startcol
4388 && col < shl->endcol)
4389 {
Bram Moolenaar1614a142019-10-06 22:00:13 +02004390 int next_col = col + mb_ptr2len(*line + col);
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004391
4392 if (shl->endcol < next_col)
4393 shl->endcol = next_col;
4394 shl->attr_cur = shl->attr;
4395# ifdef FEAT_CONCEAL
4396 // Match with the "Conceal" group results in hiding
4397 // the match.
4398 if (cur != NULL
4399 && shl != search_hl
4400 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4401 {
4402 *has_match_conc = col == shl->startcol ? 2 : 1;
4403 *match_conc = cur->conceal_char;
4404 }
4405 else
4406 *has_match_conc = *match_conc = 0;
4407# endif
4408 }
4409 else if (col == shl->endcol)
4410 {
4411 shl->attr_cur = 0;
4412 next_search_hl(wp, search_hl, shl, lnum, col,
4413 shl == search_hl ? NULL : cur);
4414 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4415
4416 // Need to get the line again, a multi-line regexp may have
4417 // made it invalid.
4418 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4419
4420 if (shl->lnum == lnum)
4421 {
4422 shl->startcol = shl->rm.startpos[0].col;
4423 if (shl->rm.endpos[0].lnum == 0)
4424 shl->endcol = shl->rm.endpos[0].col;
4425 else
4426 shl->endcol = MAXCOL;
4427
4428 if (shl->startcol == shl->endcol)
4429 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004430 // highlight empty match, try again after
4431 // it
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004432 if (has_mbyte)
4433 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4434 else
4435 ++shl->endcol;
4436 }
4437
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004438 // Loop to check if the match starts at the
4439 // current position
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004440 continue;
4441 }
4442 }
4443 break;
4444 }
4445 if (shl != search_hl && cur != NULL)
4446 cur = cur->next;
4447 }
4448
4449 // Use attributes from match with highest priority among 'search_hl' and
4450 // the match list.
4451 cur = wp->w_match_head;
4452 shl_flag = WIN_IS_POPUP(wp);
4453 while (cur != NULL || shl_flag == FALSE)
4454 {
4455 if (shl_flag == FALSE
4456 && ((cur != NULL
4457 && cur->priority > SEARCH_HL_PRIORITY)
4458 || cur == NULL))
4459 {
4460 shl = search_hl;
4461 shl_flag = TRUE;
4462 }
4463 else
4464 shl = &cur->hl;
4465 if (shl->attr_cur != 0)
4466 search_attr = shl->attr_cur;
4467 if (shl != search_hl && cur != NULL)
4468 cur = cur->next;
4469 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004470 // Only highlight one character after the last column.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004471 if (*(*line + col) == NUL && (did_line_attr >= 1
4472 || (wp->w_p_list && lcs_eol_one == -1)))
4473 search_attr = 0;
4474 return search_attr;
4475}
4476
4477 int
4478get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4479{
4480 long prevcol = curcol;
4481 int prevcol_hl_flag = FALSE;
4482 matchitem_T *cur; // points to the match list
4483
4484 // we're not really at that column when skipping some text
4485 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4486 ++prevcol;
4487
4488 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4489 prevcol_hl_flag = TRUE;
4490 else
4491 {
4492 cur = wp->w_match_head;
4493 while (cur != NULL)
4494 {
4495 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4496 {
4497 prevcol_hl_flag = TRUE;
4498 break;
4499 }
4500 cur = cur->next;
4501 }
4502 }
4503 return prevcol_hl_flag;
4504}
4505
4506/*
4507 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4508 * or match highlighting.
4509 */
4510 void
4511get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4512{
4513 matchitem_T *cur; // points to the match list
4514 match_T *shl; // points to search_hl or a match
4515 int shl_flag; // flag to indicate whether search_hl
4516 // has been processed or not
4517
4518 cur = wp->w_match_head;
4519 shl_flag = WIN_IS_POPUP(wp);
4520 while (cur != NULL || shl_flag == FALSE)
4521 {
4522 if (shl_flag == FALSE
4523 && ((cur != NULL
4524 && cur->priority > SEARCH_HL_PRIORITY)
4525 || cur == NULL))
4526 {
4527 shl = search_hl;
4528 shl_flag = TRUE;
4529 }
4530 else
4531 shl = &cur->hl;
4532 if (col - 1 == (long)shl->startcol
4533 && (shl == search_hl || !shl->is_addpos))
4534 *char_attr = shl->attr;
4535 if (shl != search_hl && cur != NULL)
4536 cur = cur->next;
4537 }
4538}
4539
4540#endif // FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004541
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004542#if defined(FEAT_EVAL) || defined(PROTO)
4543# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004544 static int
4545matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4546{
4547 dictitem_T *di;
4548
4549 if (tv->v_type != VAR_DICT)
4550 {
4551 emsg(_(e_dictreq));
4552 return FAIL;
4553 }
4554
4555 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4556 *conceal_char = dict_get_string(tv->vval.v_dict,
4557 (char_u *)"conceal", FALSE);
4558
4559 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4560 {
4561 *win = find_win_by_nr_or_id(&di->di_tv);
4562 if (*win == NULL)
4563 {
4564 emsg(_(e_invalwindow));
4565 return FAIL;
4566 }
4567 }
4568
4569 return OK;
4570}
4571#endif
4572
4573/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004574 * "clearmatches()" function
4575 */
4576 void
4577f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4578{
4579#ifdef FEAT_SEARCH_EXTRA
4580 win_T *win = get_optional_window(argvars, 0);
4581
4582 if (win != NULL)
4583 clear_matches(win);
4584#endif
4585}
4586
4587/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004588 * "getmatches()" function
4589 */
4590 void
4591f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4592{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004593# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004594 dict_T *dict;
4595 matchitem_T *cur;
4596 int i;
4597 win_T *win = get_optional_window(argvars, 0);
4598
4599 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4600 return;
4601
4602 cur = win->w_match_head;
4603 while (cur != NULL)
4604 {
4605 dict = dict_alloc();
4606 if (dict == NULL)
4607 return;
4608 if (cur->match.regprog == NULL)
4609 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004610 // match added with matchaddpos()
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004611 for (i = 0; i < MAXPOSMATCH; ++i)
4612 {
4613 llpos_T *llpos;
4614 char buf[30]; // use 30 to avoid compiler warning
4615 list_T *l;
4616
4617 llpos = &cur->pos.pos[i];
4618 if (llpos->lnum == 0)
4619 break;
4620 l = list_alloc();
4621 if (l == NULL)
4622 break;
4623 list_append_number(l, (varnumber_T)llpos->lnum);
4624 if (llpos->col > 0)
4625 {
4626 list_append_number(l, (varnumber_T)llpos->col);
4627 list_append_number(l, (varnumber_T)llpos->len);
4628 }
4629 sprintf(buf, "pos%d", i + 1);
4630 dict_add_list(dict, buf, l);
4631 }
4632 }
4633 else
4634 {
4635 dict_add_string(dict, "pattern", cur->pattern);
4636 }
4637 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4638 dict_add_number(dict, "priority", (long)cur->priority);
4639 dict_add_number(dict, "id", (long)cur->id);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004640# if defined(FEAT_CONCEAL)
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004641 if (cur->conceal_char)
4642 {
4643 char_u buf[MB_MAXBYTES + 1];
4644
4645 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4646 dict_add_string(dict, "conceal", (char_u *)&buf);
4647 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004648# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004649 list_append_dict(rettv->vval.v_list, dict);
4650 cur = cur->next;
4651 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004652# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004653}
4654
4655/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004656 * "setmatches()" function
4657 */
4658 void
4659f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4660{
4661#ifdef FEAT_SEARCH_EXTRA
4662 list_T *l;
4663 listitem_T *li;
4664 dict_T *d;
4665 list_T *s = NULL;
4666 win_T *win = get_optional_window(argvars, 1);
4667
4668 rettv->vval.v_number = -1;
4669 if (argvars[0].v_type != VAR_LIST)
4670 {
4671 emsg(_(e_listreq));
4672 return;
4673 }
4674 if (win == NULL)
4675 return;
4676
4677 if ((l = argvars[0].vval.v_list) != NULL)
4678 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004679 // To some extent make sure that we are dealing with a list from
4680 // "getmatches()".
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004681 li = l->lv_first;
4682 while (li != NULL)
4683 {
4684 if (li->li_tv.v_type != VAR_DICT
4685 || (d = li->li_tv.vval.v_dict) == NULL)
4686 {
4687 emsg(_(e_invarg));
4688 return;
4689 }
4690 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4691 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4692 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4693 && dict_find(d, (char_u *)"priority", -1) != NULL
4694 && dict_find(d, (char_u *)"id", -1) != NULL))
4695 {
4696 emsg(_(e_invarg));
4697 return;
4698 }
4699 li = li->li_next;
4700 }
4701
4702 clear_matches(win);
4703 li = l->lv_first;
4704 while (li != NULL)
4705 {
4706 int i = 0;
4707 char buf[30]; // use 30 to avoid compiler warning
4708 dictitem_T *di;
4709 char_u *group;
4710 int priority;
4711 int id;
4712 char_u *conceal;
4713
4714 d = li->li_tv.vval.v_dict;
4715 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4716 {
4717 if (s == NULL)
4718 {
4719 s = list_alloc();
4720 if (s == NULL)
4721 return;
4722 }
4723
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004724 // match from matchaddpos()
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004725 for (i = 1; i < 9; i++)
4726 {
4727 sprintf((char *)buf, (char *)"pos%d", i);
4728 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4729 {
4730 if (di->di_tv.v_type != VAR_LIST)
4731 return;
4732
4733 list_append_tv(s, &di->di_tv);
4734 s->lv_refcount++;
4735 }
4736 else
4737 break;
4738 }
4739 }
4740
4741 group = dict_get_string(d, (char_u *)"group", TRUE);
4742 priority = (int)dict_get_number(d, (char_u *)"priority");
4743 id = (int)dict_get_number(d, (char_u *)"id");
4744 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4745 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4746 : NULL;
4747 if (i == 0)
4748 {
4749 match_add(win, group,
4750 dict_get_string(d, (char_u *)"pattern", FALSE),
4751 priority, id, NULL, conceal);
4752 }
4753 else
4754 {
4755 match_add(win, group, NULL, priority, id, s, conceal);
4756 list_unref(s);
4757 s = NULL;
4758 }
4759 vim_free(group);
4760 vim_free(conceal);
4761
4762 li = li->li_next;
4763 }
4764 rettv->vval.v_number = 0;
4765 }
4766#endif
4767}
4768
4769/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004770 * "matchadd()" function
4771 */
4772 void
4773f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4774{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004775# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004776 char_u buf[NUMBUFLEN];
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004777 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4778 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4779 int prio = 10; // default priority
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004780 int id = -1;
4781 int error = FALSE;
4782 char_u *conceal_char = NULL;
4783 win_T *win = curwin;
4784
4785 rettv->vval.v_number = -1;
4786
4787 if (grp == NULL || pat == NULL)
4788 return;
4789 if (argvars[2].v_type != VAR_UNKNOWN)
4790 {
4791 prio = (int)tv_get_number_chk(&argvars[2], &error);
4792 if (argvars[3].v_type != VAR_UNKNOWN)
4793 {
4794 id = (int)tv_get_number_chk(&argvars[3], &error);
4795 if (argvars[4].v_type != VAR_UNKNOWN
4796 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4797 return;
4798 }
4799 }
4800 if (error == TRUE)
4801 return;
4802 if (id >= 1 && id <= 3)
4803 {
4804 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4805 return;
4806 }
4807
4808 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4809 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004810# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004811}
4812
4813/*
4814 * "matchaddpos()" function
4815 */
4816 void
4817f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4818{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004819# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004820 char_u buf[NUMBUFLEN];
4821 char_u *group;
4822 int prio = 10;
4823 int id = -1;
4824 int error = FALSE;
4825 list_T *l;
4826 char_u *conceal_char = NULL;
4827 win_T *win = curwin;
4828
4829 rettv->vval.v_number = -1;
4830
4831 group = tv_get_string_buf_chk(&argvars[0], buf);
4832 if (group == NULL)
4833 return;
4834
4835 if (argvars[1].v_type != VAR_LIST)
4836 {
4837 semsg(_(e_listarg), "matchaddpos()");
4838 return;
4839 }
4840 l = argvars[1].vval.v_list;
4841 if (l == NULL)
4842 return;
4843
4844 if (argvars[2].v_type != VAR_UNKNOWN)
4845 {
4846 prio = (int)tv_get_number_chk(&argvars[2], &error);
4847 if (argvars[3].v_type != VAR_UNKNOWN)
4848 {
4849 id = (int)tv_get_number_chk(&argvars[3], &error);
4850
4851 if (argvars[4].v_type != VAR_UNKNOWN
4852 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4853 return;
4854 }
4855 }
4856 if (error == TRUE)
4857 return;
4858
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004859 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004860 if (id == 1 || id == 2)
4861 {
4862 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4863 return;
4864 }
4865
4866 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4867 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004868# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004869}
4870
4871/*
4872 * "matcharg()" function
4873 */
4874 void
4875f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4876{
4877 if (rettv_list_alloc(rettv) == OK)
4878 {
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004879# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004880 int id = (int)tv_get_number(&argvars[0]);
4881 matchitem_T *m;
4882
4883 if (id >= 1 && id <= 3)
4884 {
4885 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4886 {
4887 list_append_string(rettv->vval.v_list,
4888 syn_id2name(m->hlg_id), -1);
4889 list_append_string(rettv->vval.v_list, m->pattern, -1);
4890 }
4891 else
4892 {
4893 list_append_string(rettv->vval.v_list, NULL, -1);
4894 list_append_string(rettv->vval.v_list, NULL, -1);
4895 }
4896 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004897# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004898 }
4899}
4900
4901/*
4902 * "matchdelete()" function
4903 */
4904 void
4905f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4906{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004907# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004908 win_T *win = get_optional_window(argvars, 1);
4909
4910 if (win == NULL)
4911 rettv->vval.v_number = -1;
4912 else
4913 rettv->vval.v_number = match_delete(win,
4914 (int)tv_get_number(&argvars[0]), TRUE);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004915# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004916}
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004917#endif
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004918
4919#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4920/*
4921 * ":[N]match {group} {pattern}"
4922 * Sets nextcmd to the start of the next command, if any. Also called when
4923 * skipping commands to find the next command.
4924 */
4925 void
4926ex_match(exarg_T *eap)
4927{
4928 char_u *p;
4929 char_u *g = NULL;
4930 char_u *end;
4931 int c;
4932 int id;
4933
4934 if (eap->line2 <= 3)
4935 id = eap->line2;
4936 else
4937 {
4938 emsg(_(e_invcmd));
4939 return;
4940 }
4941
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004942 // First clear any old pattern.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004943 if (!eap->skip)
4944 match_delete(curwin, id, FALSE);
4945
4946 if (ends_excmd(*eap->arg))
4947 end = eap->arg;
4948 else if ((STRNICMP(eap->arg, "none", 4) == 0
4949 && (VIM_ISWHITE(eap->arg[4]) || ends_excmd(eap->arg[4]))))
4950 end = eap->arg + 4;
4951 else
4952 {
4953 p = skiptowhite(eap->arg);
4954 if (!eap->skip)
4955 g = vim_strnsave(eap->arg, (int)(p - eap->arg));
4956 p = skipwhite(p);
4957 if (*p == NUL)
4958 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004959 // There must be two arguments.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004960 vim_free(g);
4961 semsg(_(e_invarg2), eap->arg);
4962 return;
4963 }
4964 end = skip_regexp(p + 1, *p, TRUE, NULL);
4965 if (!eap->skip)
4966 {
4967 if (*end != NUL && !ends_excmd(*skipwhite(end + 1)))
4968 {
4969 vim_free(g);
4970 eap->errmsg = e_trailing;
4971 return;
4972 }
4973 if (*end != *p)
4974 {
4975 vim_free(g);
4976 semsg(_(e_invarg2), p);
4977 return;
4978 }
4979
4980 c = *end;
4981 *end = NUL;
4982 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
4983 vim_free(g);
4984 *end = c;
4985 }
4986 }
4987 eap->nextcmd = find_nextcmd(end);
4988}
4989#endif