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