blob: 0fdd93a2671f0ef9b7a119353316ecc75def7fc0 [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 {
1420 len = trans_special(&p, buf + off, FALSE, FALSE);
1421 if (len > 0) // recognized special char
1422 off += len;
1423 else // copy as normal char
1424 buf[off++] = *p++;
1425 }
1426 buf[off] = NUL;
1427 }
1428 if (error)
1429 break;
1430
1431 if (STRCMP(buf, "NONE") == 0) // resetting the value
1432 p = NULL;
1433 else
1434 p = vim_strsave(buf);
1435 if (key[2] == 'A')
1436 {
1437 vim_free(HL_TABLE()[idx].sg_start);
1438 HL_TABLE()[idx].sg_start = p;
1439 }
1440 else
1441 {
1442 vim_free(HL_TABLE()[idx].sg_stop);
1443 HL_TABLE()[idx].sg_stop = p;
1444 }
1445 }
1446 else
1447 {
1448 semsg(_("E423: Illegal argument: %s"), key_start);
1449 error = TRUE;
1450 break;
1451 }
1452 HL_TABLE()[idx].sg_cleared = FALSE;
1453
1454 /*
1455 * When highlighting has been given for a group, don't link it.
1456 */
1457 if (!init || !(HL_TABLE()[idx].sg_set & SG_LINK))
1458 HL_TABLE()[idx].sg_link = 0;
1459
1460 /*
1461 * Continue with next argument.
1462 */
1463 linep = skipwhite(linep);
1464 }
1465
1466 /*
1467 * If there is an error, and it's a new entry, remove it from the table.
1468 */
1469 if (error && idx == highlight_ga.ga_len)
1470 syn_unadd_group();
1471 else
1472 {
1473 if (is_normal_group)
1474 {
1475 HL_TABLE()[idx].sg_term_attr = 0;
1476 HL_TABLE()[idx].sg_cterm_attr = 0;
1477#ifdef FEAT_GUI
1478 HL_TABLE()[idx].sg_gui_attr = 0;
1479 /*
1480 * Need to update all groups, because they might be using "bg"
1481 * and/or "fg", which have been changed now.
1482 */
1483#endif
1484#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1485 if (USE_24BIT)
1486 {
1487 highlight_gui_started();
1488 did_highlight_changed = TRUE;
1489 redraw_all_later(NOT_VALID);
1490 }
1491#endif
1492 }
1493#ifdef FEAT_TERMINAL
1494 else if (is_terminal_group)
1495 set_terminal_default_colors(
1496 HL_TABLE()[idx].sg_cterm_fg, HL_TABLE()[idx].sg_cterm_bg);
1497#endif
1498#ifdef FEAT_GUI_X11
1499# ifdef FEAT_MENU
1500 else if (is_menu_group)
1501 {
1502 if (gui.in_use && do_colors)
1503 gui_mch_new_menu_colors();
1504 }
1505# endif
1506 else if (is_scrollbar_group)
1507 {
1508 if (gui.in_use && do_colors)
1509 gui_new_scrollbar_colors();
1510 else
1511 set_hl_attr(idx);
1512 }
1513# ifdef FEAT_BEVAL_GUI
1514 else if (is_tooltip_group)
1515 {
1516 if (gui.in_use && do_colors)
1517 gui_mch_new_tooltip_colors();
1518 }
1519# endif
1520#endif
1521 else
1522 set_hl_attr(idx);
1523#ifdef FEAT_EVAL
1524 HL_TABLE()[idx].sg_script_ctx = current_sctx;
1525 HL_TABLE()[idx].sg_script_ctx.sc_lnum += sourcing_lnum;
1526#endif
1527 }
1528
1529 vim_free(key);
1530 vim_free(arg);
1531
1532 // Only call highlight_changed() once, after a sequence of highlight
1533 // commands, and only if an attribute actually changed.
1534 if ((did_change
1535 || memcmp(&HL_TABLE()[idx], &item_before, sizeof(item_before)) != 0)
1536#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1537 && !did_highlight_changed
1538#endif
1539 )
1540 {
1541 // Do not trigger a redraw when highlighting is changed while
1542 // redrawing. This may happen when evaluating 'statusline' changes the
1543 // StatusLine group.
1544 if (!updating_screen)
1545 redraw_all_later(NOT_VALID);
1546 need_highlight_changed = TRUE;
1547 }
1548}
1549
1550#if defined(EXITFREE) || defined(PROTO)
1551 void
1552free_highlight(void)
1553{
1554 int i;
1555
1556 for (i = 0; i < highlight_ga.ga_len; ++i)
1557 {
1558 highlight_clear(i);
1559 vim_free(HL_TABLE()[i].sg_name);
1560 vim_free(HL_TABLE()[i].sg_name_u);
1561 }
1562 ga_clear(&highlight_ga);
1563}
1564#endif
1565
1566/*
1567 * Reset the cterm colors to what they were before Vim was started, if
1568 * possible. Otherwise reset them to zero.
1569 */
1570 void
1571restore_cterm_colors(void)
1572{
1573#if defined(MSWIN) && !defined(FEAT_GUI_MSWIN)
1574 // Since t_me has been set, this probably means that the user
1575 // wants to use this as default colors. Need to reset default
1576 // background/foreground colors.
1577 mch_set_normal_colors();
1578#else
1579# ifdef VIMDLL
1580 if (!gui.in_use)
1581 {
1582 mch_set_normal_colors();
1583 return;
1584 }
1585# endif
1586 cterm_normal_fg_color = 0;
1587 cterm_normal_fg_bold = 0;
1588 cterm_normal_bg_color = 0;
1589# ifdef FEAT_TERMGUICOLORS
1590 cterm_normal_fg_gui_color = INVALCOLOR;
1591 cterm_normal_bg_gui_color = INVALCOLOR;
1592# endif
1593#endif
1594}
1595
1596/*
1597 * Return TRUE if highlight group "idx" has any settings.
1598 * When "check_link" is TRUE also check for an existing link.
1599 */
1600 static int
1601hl_has_settings(int idx, int check_link)
1602{
1603 return ( HL_TABLE()[idx].sg_term_attr != 0
1604 || HL_TABLE()[idx].sg_cterm_attr != 0
1605 || HL_TABLE()[idx].sg_cterm_fg != 0
1606 || HL_TABLE()[idx].sg_cterm_bg != 0
1607#ifdef FEAT_GUI
1608 || HL_TABLE()[idx].sg_gui_attr != 0
1609 || HL_TABLE()[idx].sg_gui_fg_name != NULL
1610 || HL_TABLE()[idx].sg_gui_bg_name != NULL
1611 || HL_TABLE()[idx].sg_gui_sp_name != NULL
1612 || HL_TABLE()[idx].sg_font_name != NULL
1613#endif
1614 || (check_link && (HL_TABLE()[idx].sg_set & SG_LINK)));
1615}
1616
1617/*
1618 * Clear highlighting for one group.
1619 */
1620 static void
1621highlight_clear(int idx)
1622{
1623 HL_TABLE()[idx].sg_cleared = TRUE;
1624
1625 HL_TABLE()[idx].sg_term = 0;
1626 VIM_CLEAR(HL_TABLE()[idx].sg_start);
1627 VIM_CLEAR(HL_TABLE()[idx].sg_stop);
1628 HL_TABLE()[idx].sg_term_attr = 0;
1629 HL_TABLE()[idx].sg_cterm = 0;
1630 HL_TABLE()[idx].sg_cterm_bold = FALSE;
1631 HL_TABLE()[idx].sg_cterm_fg = 0;
1632 HL_TABLE()[idx].sg_cterm_bg = 0;
1633 HL_TABLE()[idx].sg_cterm_attr = 0;
1634#if defined(FEAT_GUI) || defined(FEAT_EVAL)
1635 HL_TABLE()[idx].sg_gui = 0;
1636 VIM_CLEAR(HL_TABLE()[idx].sg_gui_fg_name);
1637 VIM_CLEAR(HL_TABLE()[idx].sg_gui_bg_name);
1638 VIM_CLEAR(HL_TABLE()[idx].sg_gui_sp_name);
1639#endif
1640#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
1641 HL_TABLE()[idx].sg_gui_fg = INVALCOLOR;
1642 HL_TABLE()[idx].sg_gui_bg = INVALCOLOR;
1643#endif
1644#ifdef FEAT_GUI
1645 HL_TABLE()[idx].sg_gui_sp = INVALCOLOR;
1646 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1647 HL_TABLE()[idx].sg_font = NOFONT;
1648# ifdef FEAT_XFONTSET
1649 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1650 HL_TABLE()[idx].sg_fontset = NOFONTSET;
1651# endif
1652 VIM_CLEAR(HL_TABLE()[idx].sg_font_name);
1653 HL_TABLE()[idx].sg_gui_attr = 0;
1654#endif
1655#ifdef FEAT_EVAL
1656 // Clear the script ID only when there is no link, since that is not
1657 // cleared.
1658 if (HL_TABLE()[idx].sg_link == 0)
1659 {
1660 HL_TABLE()[idx].sg_script_ctx.sc_sid = 0;
1661 HL_TABLE()[idx].sg_script_ctx.sc_lnum = 0;
1662 }
1663#endif
1664}
1665
1666#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1667/*
1668 * Set the normal foreground and background colors according to the "Normal"
1669 * highlighting group. For X11 also set "Menu", "Scrollbar", and
1670 * "Tooltip" colors.
1671 */
1672 void
1673set_normal_colors(void)
1674{
1675# ifdef FEAT_GUI
1676# ifdef FEAT_TERMGUICOLORS
1677 if (gui.in_use)
1678# endif
1679 {
1680 if (set_group_colors((char_u *)"Normal",
1681 &gui.norm_pixel, &gui.back_pixel,
1682 FALSE, TRUE, FALSE))
1683 {
1684 gui_mch_new_colors();
1685 must_redraw = CLEAR;
1686 }
1687# ifdef FEAT_GUI_X11
1688 if (set_group_colors((char_u *)"Menu",
1689 &gui.menu_fg_pixel, &gui.menu_bg_pixel,
1690 TRUE, FALSE, FALSE))
1691 {
1692# ifdef FEAT_MENU
1693 gui_mch_new_menu_colors();
1694# endif
1695 must_redraw = CLEAR;
1696 }
1697# ifdef FEAT_BEVAL_GUI
1698 if (set_group_colors((char_u *)"Tooltip",
1699 &gui.tooltip_fg_pixel, &gui.tooltip_bg_pixel,
1700 FALSE, FALSE, TRUE))
1701 {
1702# ifdef FEAT_TOOLBAR
1703 gui_mch_new_tooltip_colors();
1704# endif
1705 must_redraw = CLEAR;
1706 }
1707# endif
1708 if (set_group_colors((char_u *)"Scrollbar",
1709 &gui.scroll_fg_pixel, &gui.scroll_bg_pixel,
1710 FALSE, FALSE, FALSE))
1711 {
1712 gui_new_scrollbar_colors();
1713 must_redraw = CLEAR;
1714 }
1715# endif
1716 }
1717# endif
1718# ifdef FEAT_TERMGUICOLORS
1719# ifdef FEAT_GUI
1720 else
1721# endif
1722 {
1723 int idx;
1724
1725 idx = syn_name2id((char_u *)"Normal") - 1;
1726 if (idx >= 0)
1727 {
1728 gui_do_one_color(idx, FALSE, FALSE);
1729
1730 // If the normal fg or bg color changed a complete redraw is
1731 // required.
1732 if (cterm_normal_fg_gui_color != HL_TABLE()[idx].sg_gui_fg
1733 || cterm_normal_bg_gui_color != HL_TABLE()[idx].sg_gui_bg)
1734 {
1735 // if the GUI color is INVALCOLOR then we use the default cterm
1736 // color
1737 cterm_normal_fg_gui_color = HL_TABLE()[idx].sg_gui_fg;
1738 cterm_normal_bg_gui_color = HL_TABLE()[idx].sg_gui_bg;
1739 must_redraw = CLEAR;
1740 }
1741 }
1742 }
1743# endif
1744}
1745#endif
1746
1747#if defined(FEAT_GUI) || defined(PROTO)
1748/*
1749 * Set the colors for "Normal", "Menu", "Tooltip" or "Scrollbar".
1750 */
1751 static int
1752set_group_colors(
1753 char_u *name,
1754 guicolor_T *fgp,
1755 guicolor_T *bgp,
1756 int do_menu,
1757 int use_norm,
1758 int do_tooltip)
1759{
1760 int idx;
1761
1762 idx = syn_name2id(name) - 1;
1763 if (idx >= 0)
1764 {
1765 gui_do_one_color(idx, do_menu, do_tooltip);
1766
1767 if (HL_TABLE()[idx].sg_gui_fg != INVALCOLOR)
1768 *fgp = HL_TABLE()[idx].sg_gui_fg;
1769 else if (use_norm)
1770 *fgp = gui.def_norm_pixel;
1771 if (HL_TABLE()[idx].sg_gui_bg != INVALCOLOR)
1772 *bgp = HL_TABLE()[idx].sg_gui_bg;
1773 else if (use_norm)
1774 *bgp = gui.def_back_pixel;
1775 return TRUE;
1776 }
1777 return FALSE;
1778}
1779
1780/*
1781 * Get the font of the "Normal" group.
1782 * Returns "" when it's not found or not set.
1783 */
1784 char_u *
1785hl_get_font_name(void)
1786{
1787 int id;
1788 char_u *s;
1789
1790 id = syn_name2id((char_u *)"Normal");
1791 if (id > 0)
1792 {
1793 s = HL_TABLE()[id - 1].sg_font_name;
1794 if (s != NULL)
1795 return s;
1796 }
1797 return (char_u *)"";
1798}
1799
1800/*
1801 * Set font for "Normal" group. Called by gui_mch_init_font() when a font has
1802 * actually chosen to be used.
1803 */
1804 void
1805hl_set_font_name(char_u *font_name)
1806{
1807 int id;
1808
1809 id = syn_name2id((char_u *)"Normal");
1810 if (id > 0)
1811 {
1812 vim_free(HL_TABLE()[id - 1].sg_font_name);
1813 HL_TABLE()[id - 1].sg_font_name = vim_strsave(font_name);
1814 }
1815}
1816
1817/*
1818 * Set background color for "Normal" group. Called by gui_set_bg_color()
1819 * when the color is known.
1820 */
1821 void
1822hl_set_bg_color_name(
1823 char_u *name) // must have been allocated
1824{
1825 int id;
1826
1827 if (name != NULL)
1828 {
1829 id = syn_name2id((char_u *)"Normal");
1830 if (id > 0)
1831 {
1832 vim_free(HL_TABLE()[id - 1].sg_gui_bg_name);
1833 HL_TABLE()[id - 1].sg_gui_bg_name = name;
1834 }
1835 }
1836}
1837
1838/*
1839 * Set foreground color for "Normal" group. Called by gui_set_fg_color()
1840 * when the color is known.
1841 */
1842 void
1843hl_set_fg_color_name(
1844 char_u *name) // must have been allocated
1845{
1846 int id;
1847
1848 if (name != NULL)
1849 {
1850 id = syn_name2id((char_u *)"Normal");
1851 if (id > 0)
1852 {
1853 vim_free(HL_TABLE()[id - 1].sg_gui_fg_name);
1854 HL_TABLE()[id - 1].sg_gui_fg_name = name;
1855 }
1856 }
1857}
1858
1859/*
1860 * Return the handle for a font name.
1861 * Returns NOFONT when failed.
1862 */
1863 static GuiFont
1864font_name2handle(char_u *name)
1865{
1866 if (STRCMP(name, "NONE") == 0)
1867 return NOFONT;
1868
1869 return gui_mch_get_font(name, TRUE);
1870}
1871
1872# ifdef FEAT_XFONTSET
1873/*
1874 * Return the handle for a fontset name.
1875 * Returns NOFONTSET when failed.
1876 */
1877 static GuiFontset
1878fontset_name2handle(char_u *name, int fixed_width)
1879{
1880 if (STRCMP(name, "NONE") == 0)
1881 return NOFONTSET;
1882
1883 return gui_mch_get_fontset(name, TRUE, fixed_width);
1884}
1885# endif
1886
1887/*
1888 * Get the font or fontset for one highlight group.
1889 */
1890 static void
1891hl_do_font(
1892 int idx,
1893 char_u *arg,
1894 int do_normal, // set normal font
1895 int do_menu UNUSED, // set menu font
1896 int do_tooltip UNUSED, // set tooltip font
1897 int free_font) // free current font/fontset
1898{
1899# ifdef FEAT_XFONTSET
1900 // If 'guifontset' is not empty, first try using the name as a
1901 // fontset. If that doesn't work, use it as a font name.
1902 if (*p_guifontset != NUL
1903# ifdef FONTSET_ALWAYS
1904 || do_menu
1905# endif
1906# ifdef FEAT_BEVAL_TIP
1907 // In Athena & Motif, the Tooltip highlight group is always a fontset
1908 || do_tooltip
1909# endif
1910 )
1911 {
1912 if (free_font)
1913 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
1914 HL_TABLE()[idx].sg_fontset = fontset_name2handle(arg, 0
1915# ifdef FONTSET_ALWAYS
1916 || do_menu
1917# endif
1918# ifdef FEAT_BEVAL_TIP
1919 || do_tooltip
1920# endif
1921 );
1922 }
1923 if (HL_TABLE()[idx].sg_fontset != NOFONTSET)
1924 {
1925 // If it worked and it's the Normal group, use it as the normal
1926 // fontset. Same for the Menu group.
1927 if (do_normal)
1928 gui_init_font(arg, TRUE);
1929# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1930 if (do_menu)
1931 {
1932# ifdef FONTSET_ALWAYS
1933 gui.menu_fontset = HL_TABLE()[idx].sg_fontset;
1934# else
1935 // YIKES! This is a bug waiting to crash the program
1936 gui.menu_font = HL_TABLE()[idx].sg_fontset;
1937# endif
1938 gui_mch_new_menu_font();
1939 }
1940# ifdef FEAT_BEVAL_GUI
1941 if (do_tooltip)
1942 {
1943 // The Athena widget set cannot currently handle switching between
1944 // displaying a single font and a fontset.
1945 // If the XtNinternational resource is set to True at widget
1946 // creation, then a fontset is always used, otherwise an
1947 // XFontStruct is used.
1948 gui.tooltip_fontset = (XFontSet)HL_TABLE()[idx].sg_fontset;
1949 gui_mch_new_tooltip_font();
1950 }
1951# endif
1952# endif
1953 }
1954 else
1955# endif
1956 {
1957 if (free_font)
1958 gui_mch_free_font(HL_TABLE()[idx].sg_font);
1959 HL_TABLE()[idx].sg_font = font_name2handle(arg);
1960 // If it worked and it's the Normal group, use it as the
1961 // normal font. Same for the Menu group.
1962 if (HL_TABLE()[idx].sg_font != NOFONT)
1963 {
1964 if (do_normal)
1965 gui_init_font(arg, FALSE);
1966#ifndef FONTSET_ALWAYS
1967# if (defined(FEAT_GUI_MOTIF) || defined(FEAT_GUI_ATHENA)) && defined(FEAT_MENU)
1968 if (do_menu)
1969 {
1970 gui.menu_font = HL_TABLE()[idx].sg_font;
1971 gui_mch_new_menu_font();
1972 }
1973# endif
1974#endif
1975 }
1976 }
1977}
1978
1979#endif // FEAT_GUI
1980
1981#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
1982/*
1983 * Return the handle for a color name.
1984 * Returns INVALCOLOR when failed.
1985 */
1986 guicolor_T
1987color_name2handle(char_u *name)
1988{
1989 if (STRCMP(name, "NONE") == 0)
1990 return INVALCOLOR;
1991
1992 if (STRICMP(name, "fg") == 0 || STRICMP(name, "foreground") == 0)
1993 {
1994#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
1995 if (gui.in_use)
1996#endif
1997#ifdef FEAT_GUI
1998 return gui.norm_pixel;
1999#endif
2000#ifdef FEAT_TERMGUICOLORS
2001 if (cterm_normal_fg_gui_color != INVALCOLOR)
2002 return cterm_normal_fg_gui_color;
2003 // Guess that the foreground is black or white.
2004 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "black" : "white"));
2005#endif
2006 }
2007 if (STRICMP(name, "bg") == 0 || STRICMP(name, "background") == 0)
2008 {
2009#if defined(FEAT_TERMGUICOLORS) && defined(FEAT_GUI)
2010 if (gui.in_use)
2011#endif
2012#ifdef FEAT_GUI
2013 return gui.back_pixel;
2014#endif
2015#ifdef FEAT_TERMGUICOLORS
2016 if (cterm_normal_bg_gui_color != INVALCOLOR)
2017 return cterm_normal_bg_gui_color;
2018 // Guess that the background is white or black.
2019 return GUI_GET_COLOR((char_u *)(*p_bg == 'l' ? "white" : "black"));
2020#endif
2021 }
2022
2023 return GUI_GET_COLOR(name);
2024}
2025#endif
2026
2027/*
2028 * Table with the specifications for an attribute number.
2029 * Note that this table is used by ALL buffers. This is required because the
2030 * GUI can redraw at any time for any buffer.
2031 */
2032static garray_T term_attr_table = {0, 0, 0, 0, NULL};
2033
2034#define TERM_ATTR_ENTRY(idx) ((attrentry_T *)term_attr_table.ga_data)[idx]
2035
2036static garray_T cterm_attr_table = {0, 0, 0, 0, NULL};
2037
2038#define CTERM_ATTR_ENTRY(idx) ((attrentry_T *)cterm_attr_table.ga_data)[idx]
2039
2040#ifdef FEAT_GUI
2041static garray_T gui_attr_table = {0, 0, 0, 0, NULL};
2042
2043#define GUI_ATTR_ENTRY(idx) ((attrentry_T *)gui_attr_table.ga_data)[idx]
2044#endif
2045
2046/*
2047 * Return the attr number for a set of colors and font.
2048 * Add a new entry to the term_attr_table, cterm_attr_table or gui_attr_table
2049 * if the combination is new.
2050 * Return 0 for error (no more room).
2051 */
2052 static int
2053get_attr_entry(garray_T *table, attrentry_T *aep)
2054{
2055 int i;
2056 attrentry_T *taep;
2057 static int recursive = FALSE;
2058
2059 /*
2060 * Init the table, in case it wasn't done yet.
2061 */
2062 table->ga_itemsize = sizeof(attrentry_T);
2063 table->ga_growsize = 7;
2064
2065 /*
2066 * Try to find an entry with the same specifications.
2067 */
2068 for (i = 0; i < table->ga_len; ++i)
2069 {
2070 taep = &(((attrentry_T *)table->ga_data)[i]);
2071 if ( aep->ae_attr == taep->ae_attr
2072 && (
2073#ifdef FEAT_GUI
2074 (table == &gui_attr_table
2075 && (aep->ae_u.gui.fg_color == taep->ae_u.gui.fg_color
2076 && aep->ae_u.gui.bg_color
2077 == taep->ae_u.gui.bg_color
2078 && aep->ae_u.gui.sp_color
2079 == taep->ae_u.gui.sp_color
2080 && aep->ae_u.gui.font == taep->ae_u.gui.font
2081# ifdef FEAT_XFONTSET
2082 && aep->ae_u.gui.fontset == taep->ae_u.gui.fontset
2083# endif
2084 ))
2085 ||
2086#endif
2087 (table == &term_attr_table
2088 && (aep->ae_u.term.start == NULL)
2089 == (taep->ae_u.term.start == NULL)
2090 && (aep->ae_u.term.start == NULL
2091 || STRCMP(aep->ae_u.term.start,
2092 taep->ae_u.term.start) == 0)
2093 && (aep->ae_u.term.stop == NULL)
2094 == (taep->ae_u.term.stop == NULL)
2095 && (aep->ae_u.term.stop == NULL
2096 || STRCMP(aep->ae_u.term.stop,
2097 taep->ae_u.term.stop) == 0))
2098 || (table == &cterm_attr_table
2099 && aep->ae_u.cterm.fg_color
2100 == taep->ae_u.cterm.fg_color
2101 && aep->ae_u.cterm.bg_color
2102 == taep->ae_u.cterm.bg_color
2103#ifdef FEAT_TERMGUICOLORS
2104 && aep->ae_u.cterm.fg_rgb
2105 == taep->ae_u.cterm.fg_rgb
2106 && aep->ae_u.cterm.bg_rgb
2107 == taep->ae_u.cterm.bg_rgb
2108#endif
2109 )))
2110
2111 return i + ATTR_OFF;
2112 }
2113
2114 if (table->ga_len + ATTR_OFF > MAX_TYPENR)
2115 {
2116 /*
2117 * Running out of attribute entries! remove all attributes, and
2118 * compute new ones for all groups.
2119 * When called recursively, we are really out of numbers.
2120 */
2121 if (recursive)
2122 {
2123 emsg(_("E424: Too many different highlighting attributes in use"));
2124 return 0;
2125 }
2126 recursive = TRUE;
2127
2128 clear_hl_tables();
2129
2130 must_redraw = CLEAR;
2131
2132 for (i = 0; i < highlight_ga.ga_len; ++i)
2133 set_hl_attr(i);
2134
2135 recursive = FALSE;
2136 }
2137
2138 /*
2139 * This is a new combination of colors and font, add an entry.
2140 */
2141 if (ga_grow(table, 1) == FAIL)
2142 return 0;
2143
2144 taep = &(((attrentry_T *)table->ga_data)[table->ga_len]);
2145 vim_memset(taep, 0, sizeof(attrentry_T));
2146 taep->ae_attr = aep->ae_attr;
2147#ifdef FEAT_GUI
2148 if (table == &gui_attr_table)
2149 {
2150 taep->ae_u.gui.fg_color = aep->ae_u.gui.fg_color;
2151 taep->ae_u.gui.bg_color = aep->ae_u.gui.bg_color;
2152 taep->ae_u.gui.sp_color = aep->ae_u.gui.sp_color;
2153 taep->ae_u.gui.font = aep->ae_u.gui.font;
2154# ifdef FEAT_XFONTSET
2155 taep->ae_u.gui.fontset = aep->ae_u.gui.fontset;
2156# endif
2157 }
2158#endif
2159 if (table == &term_attr_table)
2160 {
2161 if (aep->ae_u.term.start == NULL)
2162 taep->ae_u.term.start = NULL;
2163 else
2164 taep->ae_u.term.start = vim_strsave(aep->ae_u.term.start);
2165 if (aep->ae_u.term.stop == NULL)
2166 taep->ae_u.term.stop = NULL;
2167 else
2168 taep->ae_u.term.stop = vim_strsave(aep->ae_u.term.stop);
2169 }
2170 else if (table == &cterm_attr_table)
2171 {
2172 taep->ae_u.cterm.fg_color = aep->ae_u.cterm.fg_color;
2173 taep->ae_u.cterm.bg_color = aep->ae_u.cterm.bg_color;
2174#ifdef FEAT_TERMGUICOLORS
2175 taep->ae_u.cterm.fg_rgb = aep->ae_u.cterm.fg_rgb;
2176 taep->ae_u.cterm.bg_rgb = aep->ae_u.cterm.bg_rgb;
2177#endif
2178 }
2179 ++table->ga_len;
2180 return (table->ga_len - 1 + ATTR_OFF);
2181}
2182
2183#if defined(FEAT_TERMINAL) || defined(PROTO)
2184/*
2185 * Get an attribute index for a cterm entry.
2186 * Uses an existing entry when possible or adds one when needed.
2187 */
2188 int
2189get_cterm_attr_idx(int attr, int fg, int bg)
2190{
2191 attrentry_T at_en;
2192
2193 vim_memset(&at_en, 0, sizeof(attrentry_T));
2194#ifdef FEAT_TERMGUICOLORS
2195 at_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2196 at_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2197#endif
2198 at_en.ae_attr = attr;
2199 at_en.ae_u.cterm.fg_color = fg;
2200 at_en.ae_u.cterm.bg_color = bg;
2201 return get_attr_entry(&cterm_attr_table, &at_en);
2202}
2203#endif
2204
2205#if (defined(FEAT_TERMINAL) && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
2206/*
2207 * Get an attribute index for a 'termguicolors' entry.
2208 * Uses an existing entry when possible or adds one when needed.
2209 */
2210 int
2211get_tgc_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2212{
2213 attrentry_T at_en;
2214
2215 vim_memset(&at_en, 0, sizeof(attrentry_T));
2216 at_en.ae_attr = attr;
2217 if (fg == INVALCOLOR && bg == INVALCOLOR)
2218 {
2219 // If both GUI colors are not set fall back to the cterm colors. Helps
2220 // if the GUI only has an attribute, such as undercurl.
2221 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2222 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2223 }
2224 else
2225 {
2226 at_en.ae_u.cterm.fg_rgb = fg;
2227 at_en.ae_u.cterm.bg_rgb = bg;
2228 }
2229 return get_attr_entry(&cterm_attr_table, &at_en);
2230}
2231#endif
2232
2233#if (defined(FEAT_TERMINAL) && defined(FEAT_GUI)) || defined(PROTO)
2234/*
2235 * Get an attribute index for a cterm entry.
2236 * Uses an existing entry when possible or adds one when needed.
2237 */
2238 int
2239get_gui_attr_idx(int attr, guicolor_T fg, guicolor_T bg)
2240{
2241 attrentry_T at_en;
2242
2243 vim_memset(&at_en, 0, sizeof(attrentry_T));
2244 at_en.ae_attr = attr;
2245 at_en.ae_u.gui.fg_color = fg;
2246 at_en.ae_u.gui.bg_color = bg;
2247 return get_attr_entry(&gui_attr_table, &at_en);
2248}
2249#endif
2250
2251/*
2252 * Clear all highlight tables.
2253 */
2254 void
2255clear_hl_tables(void)
2256{
2257 int i;
2258 attrentry_T *taep;
2259
2260#ifdef FEAT_GUI
2261 ga_clear(&gui_attr_table);
2262#endif
2263 for (i = 0; i < term_attr_table.ga_len; ++i)
2264 {
2265 taep = &(((attrentry_T *)term_attr_table.ga_data)[i]);
2266 vim_free(taep->ae_u.term.start);
2267 vim_free(taep->ae_u.term.stop);
2268 }
2269 ga_clear(&term_attr_table);
2270 ga_clear(&cterm_attr_table);
2271}
2272
2273/*
2274 * Combine special attributes (e.g., for spelling) with other attributes
2275 * (e.g., for syntax highlighting).
2276 * "prim_attr" overrules "char_attr".
2277 * This creates a new group when required.
2278 * Since we expect there to be few spelling mistakes we don't cache the
2279 * result.
2280 * Return the resulting attributes.
2281 */
2282 int
2283hl_combine_attr(int char_attr, int prim_attr)
2284{
2285 attrentry_T *char_aep = NULL;
2286 attrentry_T *spell_aep;
2287 attrentry_T new_en;
2288
2289 if (char_attr == 0)
2290 return prim_attr;
2291 if (char_attr <= HL_ALL && prim_attr <= HL_ALL)
2292 return ATTR_COMBINE(char_attr, prim_attr);
2293#ifdef FEAT_GUI
2294 if (gui.in_use)
2295 {
2296 if (char_attr > HL_ALL)
2297 char_aep = syn_gui_attr2entry(char_attr);
2298 if (char_aep != NULL)
2299 new_en = *char_aep;
2300 else
2301 {
2302 vim_memset(&new_en, 0, sizeof(new_en));
2303 new_en.ae_u.gui.fg_color = INVALCOLOR;
2304 new_en.ae_u.gui.bg_color = INVALCOLOR;
2305 new_en.ae_u.gui.sp_color = INVALCOLOR;
2306 if (char_attr <= HL_ALL)
2307 new_en.ae_attr = char_attr;
2308 }
2309
2310 if (prim_attr <= HL_ALL)
2311 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2312 else
2313 {
2314 spell_aep = syn_gui_attr2entry(prim_attr);
2315 if (spell_aep != NULL)
2316 {
2317 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2318 spell_aep->ae_attr);
2319 if (spell_aep->ae_u.gui.fg_color != INVALCOLOR)
2320 new_en.ae_u.gui.fg_color = spell_aep->ae_u.gui.fg_color;
2321 if (spell_aep->ae_u.gui.bg_color != INVALCOLOR)
2322 new_en.ae_u.gui.bg_color = spell_aep->ae_u.gui.bg_color;
2323 if (spell_aep->ae_u.gui.sp_color != INVALCOLOR)
2324 new_en.ae_u.gui.sp_color = spell_aep->ae_u.gui.sp_color;
2325 if (spell_aep->ae_u.gui.font != NOFONT)
2326 new_en.ae_u.gui.font = spell_aep->ae_u.gui.font;
2327# ifdef FEAT_XFONTSET
2328 if (spell_aep->ae_u.gui.fontset != NOFONTSET)
2329 new_en.ae_u.gui.fontset = spell_aep->ae_u.gui.fontset;
2330# endif
2331 }
2332 }
2333 return get_attr_entry(&gui_attr_table, &new_en);
2334 }
2335#endif
2336
2337 if (IS_CTERM)
2338 {
2339 if (char_attr > HL_ALL)
2340 char_aep = syn_cterm_attr2entry(char_attr);
2341 if (char_aep != NULL)
2342 new_en = *char_aep;
2343 else
2344 {
2345 vim_memset(&new_en, 0, sizeof(new_en));
2346#ifdef FEAT_TERMGUICOLORS
2347 new_en.ae_u.cterm.bg_rgb = INVALCOLOR;
2348 new_en.ae_u.cterm.fg_rgb = INVALCOLOR;
2349#endif
2350 if (char_attr <= HL_ALL)
2351 new_en.ae_attr = char_attr;
2352 }
2353
2354 if (prim_attr <= HL_ALL)
2355 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2356 else
2357 {
2358 spell_aep = syn_cterm_attr2entry(prim_attr);
2359 if (spell_aep != NULL)
2360 {
2361 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr,
2362 spell_aep->ae_attr);
2363 if (spell_aep->ae_u.cterm.fg_color > 0)
2364 new_en.ae_u.cterm.fg_color = spell_aep->ae_u.cterm.fg_color;
2365 if (spell_aep->ae_u.cterm.bg_color > 0)
2366 new_en.ae_u.cterm.bg_color = spell_aep->ae_u.cterm.bg_color;
2367#ifdef FEAT_TERMGUICOLORS
2368 // If both fg and bg are not set fall back to cterm colors.
2369 // Helps for SpellBad which uses undercurl in the GUI.
2370 if (COLOR_INVALID(spell_aep->ae_u.cterm.fg_rgb)
2371 && COLOR_INVALID(spell_aep->ae_u.cterm.bg_rgb))
2372 {
2373 if (spell_aep->ae_u.cterm.fg_color > 0)
2374 new_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2375 if (spell_aep->ae_u.cterm.bg_color > 0)
2376 new_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2377 }
2378 else
2379 {
2380 if (spell_aep->ae_u.cterm.fg_rgb != INVALCOLOR)
2381 new_en.ae_u.cterm.fg_rgb = spell_aep->ae_u.cterm.fg_rgb;
2382 if (spell_aep->ae_u.cterm.bg_rgb != INVALCOLOR)
2383 new_en.ae_u.cterm.bg_rgb = spell_aep->ae_u.cterm.bg_rgb;
2384 }
2385#endif
2386 }
2387 }
2388 return get_attr_entry(&cterm_attr_table, &new_en);
2389 }
2390
2391 if (char_attr > HL_ALL)
2392 char_aep = syn_term_attr2entry(char_attr);
2393 if (char_aep != NULL)
2394 new_en = *char_aep;
2395 else
2396 {
2397 vim_memset(&new_en, 0, sizeof(new_en));
2398 if (char_attr <= HL_ALL)
2399 new_en.ae_attr = char_attr;
2400 }
2401
2402 if (prim_attr <= HL_ALL)
2403 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, prim_attr);
2404 else
2405 {
2406 spell_aep = syn_term_attr2entry(prim_attr);
2407 if (spell_aep != NULL)
2408 {
2409 new_en.ae_attr = ATTR_COMBINE(new_en.ae_attr, spell_aep->ae_attr);
2410 if (spell_aep->ae_u.term.start != NULL)
2411 {
2412 new_en.ae_u.term.start = spell_aep->ae_u.term.start;
2413 new_en.ae_u.term.stop = spell_aep->ae_u.term.stop;
2414 }
2415 }
2416 }
2417 return get_attr_entry(&term_attr_table, &new_en);
2418}
2419
2420#ifdef FEAT_GUI
2421 attrentry_T *
2422syn_gui_attr2entry(int attr)
2423{
2424 attr -= ATTR_OFF;
2425 if (attr >= gui_attr_table.ga_len) // did ":syntax clear"
2426 return NULL;
2427 return &(GUI_ATTR_ENTRY(attr));
2428}
2429#endif
2430
2431/*
2432 * Get the highlight attributes (HL_BOLD etc.) from an attribute nr.
2433 * Only to be used when "attr" > HL_ALL.
2434 */
2435 int
2436syn_attr2attr(int attr)
2437{
2438 attrentry_T *aep;
2439
2440#ifdef FEAT_GUI
2441 if (gui.in_use)
2442 aep = syn_gui_attr2entry(attr);
2443 else
2444#endif
2445 if (IS_CTERM)
2446 aep = syn_cterm_attr2entry(attr);
2447 else
2448 aep = syn_term_attr2entry(attr);
2449
2450 if (aep == NULL) // highlighting not set
2451 return 0;
2452 return aep->ae_attr;
2453}
2454
2455
2456 attrentry_T *
2457syn_term_attr2entry(int attr)
2458{
2459 attr -= ATTR_OFF;
2460 if (attr >= term_attr_table.ga_len) // did ":syntax clear"
2461 return NULL;
2462 return &(TERM_ATTR_ENTRY(attr));
2463}
2464
2465 attrentry_T *
2466syn_cterm_attr2entry(int attr)
2467{
2468 attr -= ATTR_OFF;
2469 if (attr >= cterm_attr_table.ga_len) // did ":syntax clear"
2470 return NULL;
2471 return &(CTERM_ATTR_ENTRY(attr));
2472}
2473
2474#define LIST_ATTR 1
2475#define LIST_STRING 2
2476#define LIST_INT 3
2477
2478 static void
2479highlight_list_one(int id)
2480{
2481 hl_group_T *sgp;
2482 int didh = FALSE;
2483
2484 sgp = &HL_TABLE()[id - 1]; // index is ID minus one
2485
2486 if (message_filtered(sgp->sg_name))
2487 return;
2488
2489 didh = highlight_list_arg(id, didh, LIST_ATTR,
2490 sgp->sg_term, NULL, "term");
2491 didh = highlight_list_arg(id, didh, LIST_STRING,
2492 0, sgp->sg_start, "start");
2493 didh = highlight_list_arg(id, didh, LIST_STRING,
2494 0, sgp->sg_stop, "stop");
2495
2496 didh = highlight_list_arg(id, didh, LIST_ATTR,
2497 sgp->sg_cterm, NULL, "cterm");
2498 didh = highlight_list_arg(id, didh, LIST_INT,
2499 sgp->sg_cterm_fg, NULL, "ctermfg");
2500 didh = highlight_list_arg(id, didh, LIST_INT,
2501 sgp->sg_cterm_bg, NULL, "ctermbg");
2502
2503#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2504 didh = highlight_list_arg(id, didh, LIST_ATTR,
2505 sgp->sg_gui, NULL, "gui");
2506 didh = highlight_list_arg(id, didh, LIST_STRING,
2507 0, sgp->sg_gui_fg_name, "guifg");
2508 didh = highlight_list_arg(id, didh, LIST_STRING,
2509 0, sgp->sg_gui_bg_name, "guibg");
2510 didh = highlight_list_arg(id, didh, LIST_STRING,
2511 0, sgp->sg_gui_sp_name, "guisp");
2512#endif
2513#ifdef FEAT_GUI
2514 didh = highlight_list_arg(id, didh, LIST_STRING,
2515 0, sgp->sg_font_name, "font");
2516#endif
2517
2518 if (sgp->sg_link && !got_int)
2519 {
2520 (void)syn_list_header(didh, 9999, id);
2521 didh = TRUE;
2522 msg_puts_attr("links to", HL_ATTR(HLF_D));
2523 msg_putchar(' ');
2524 msg_outtrans(HL_TABLE()[HL_TABLE()[id - 1].sg_link - 1].sg_name);
2525 }
2526
2527 if (!didh)
2528 highlight_list_arg(id, didh, LIST_STRING, 0, (char_u *)"cleared", "");
2529#ifdef FEAT_EVAL
2530 if (p_verbose > 0)
2531 last_set_msg(sgp->sg_script_ctx);
2532#endif
2533}
2534
2535 static int
2536highlight_list_arg(
2537 int id,
2538 int didh,
2539 int type,
2540 int iarg,
2541 char_u *sarg,
2542 char *name)
2543{
2544 char_u buf[100];
2545 char_u *ts;
2546 int i;
2547
2548 if (got_int)
2549 return FALSE;
2550 if (type == LIST_STRING ? (sarg != NULL) : (iarg != 0))
2551 {
2552 ts = buf;
2553 if (type == LIST_INT)
2554 sprintf((char *)buf, "%d", iarg - 1);
2555 else if (type == LIST_STRING)
2556 ts = sarg;
2557 else // type == LIST_ATTR
2558 {
2559 buf[0] = NUL;
2560 for (i = 0; hl_attr_table[i] != 0; ++i)
2561 {
2562 if (iarg & hl_attr_table[i])
2563 {
2564 if (buf[0] != NUL)
2565 vim_strcat(buf, (char_u *)",", 100);
2566 vim_strcat(buf, (char_u *)hl_name_table[i], 100);
2567 iarg &= ~hl_attr_table[i]; // don't want "inverse"
2568 }
2569 }
2570 }
2571
2572 (void)syn_list_header(didh,
2573 (int)(vim_strsize(ts) + STRLEN(name) + 1), id);
2574 didh = TRUE;
2575 if (!got_int)
2576 {
2577 if (*name != NUL)
2578 {
2579 msg_puts_attr(name, HL_ATTR(HLF_D));
2580 msg_puts_attr("=", HL_ATTR(HLF_D));
2581 }
2582 msg_outtrans(ts);
2583 }
2584 }
2585 return didh;
2586}
2587
2588#if (((defined(FEAT_EVAL) || defined(FEAT_PRINTER))) && defined(FEAT_SYN_HL)) || defined(PROTO)
2589/*
2590 * Return "1" if highlight group "id" has attribute "flag".
2591 * Return NULL otherwise.
2592 */
2593 char_u *
2594highlight_has_attr(
2595 int id,
2596 int flag,
2597 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2598{
2599 int attr;
2600
2601 if (id <= 0 || id > highlight_ga.ga_len)
2602 return NULL;
2603
2604#if defined(FEAT_GUI) || defined(FEAT_EVAL)
2605 if (modec == 'g')
2606 attr = HL_TABLE()[id - 1].sg_gui;
2607 else
2608#endif
2609 if (modec == 'c')
2610 attr = HL_TABLE()[id - 1].sg_cterm;
2611 else
2612 attr = HL_TABLE()[id - 1].sg_term;
2613
2614 if (attr & flag)
2615 return (char_u *)"1";
2616 return NULL;
2617}
2618#endif
2619
2620#if (defined(FEAT_SYN_HL) && defined(FEAT_EVAL)) || defined(PROTO)
2621/*
2622 * Return color name of highlight group "id".
2623 */
2624 char_u *
2625highlight_color(
2626 int id,
2627 char_u *what, // "font", "fg", "bg", "sp", "fg#", "bg#" or "sp#"
2628 int modec) // 'g' for GUI, 'c' for cterm, 't' for term
2629{
2630 static char_u name[20];
2631 int n;
2632 int fg = FALSE;
2633 int sp = FALSE;
2634 int font = FALSE;
2635
2636 if (id <= 0 || id > highlight_ga.ga_len)
2637 return NULL;
2638
2639 if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'g')
2640 fg = TRUE;
2641 else if (TOLOWER_ASC(what[0]) == 'f' && TOLOWER_ASC(what[1]) == 'o'
2642 && TOLOWER_ASC(what[2]) == 'n' && TOLOWER_ASC(what[3]) == 't')
2643 font = TRUE;
2644 else if (TOLOWER_ASC(what[0]) == 's' && TOLOWER_ASC(what[1]) == 'p')
2645 sp = TRUE;
2646 else if (!(TOLOWER_ASC(what[0]) == 'b' && TOLOWER_ASC(what[1]) == 'g'))
2647 return NULL;
2648 if (modec == 'g')
2649 {
2650# if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
2651# ifdef FEAT_GUI
2652 // return font name
2653 if (font)
2654 return HL_TABLE()[id - 1].sg_font_name;
2655# endif
2656
2657 // return #RRGGBB form (only possible when GUI is running)
2658 if ((USE_24BIT) && what[2] == '#')
2659 {
2660 guicolor_T color;
2661 long_u rgb;
2662 static char_u buf[10];
2663
2664 if (fg)
2665 color = HL_TABLE()[id - 1].sg_gui_fg;
2666 else if (sp)
2667# ifdef FEAT_GUI
2668 color = HL_TABLE()[id - 1].sg_gui_sp;
2669# else
2670 color = INVALCOLOR;
2671# endif
2672 else
2673 color = HL_TABLE()[id - 1].sg_gui_bg;
2674 if (color == INVALCOLOR)
2675 return NULL;
2676 rgb = (long_u)GUI_MCH_GET_RGB(color);
2677 sprintf((char *)buf, "#%02x%02x%02x",
2678 (unsigned)(rgb >> 16),
2679 (unsigned)(rgb >> 8) & 255,
2680 (unsigned)rgb & 255);
2681 return buf;
2682 }
2683# endif
2684 if (fg)
2685 return (HL_TABLE()[id - 1].sg_gui_fg_name);
2686 if (sp)
2687 return (HL_TABLE()[id - 1].sg_gui_sp_name);
2688 return (HL_TABLE()[id - 1].sg_gui_bg_name);
2689 }
2690 if (font || sp)
2691 return NULL;
2692 if (modec == 'c')
2693 {
2694 if (fg)
2695 n = HL_TABLE()[id - 1].sg_cterm_fg - 1;
2696 else
2697 n = HL_TABLE()[id - 1].sg_cterm_bg - 1;
2698 if (n < 0)
2699 return NULL;
2700 sprintf((char *)name, "%d", n);
2701 return name;
2702 }
2703 // term doesn't have color
2704 return NULL;
2705}
2706#endif
2707
2708#if (defined(FEAT_SYN_HL) \
2709 && (defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)) \
2710 && defined(FEAT_PRINTER)) || defined(PROTO)
2711/*
2712 * Return color name of highlight group "id" as RGB value.
2713 */
2714 long_u
2715highlight_gui_color_rgb(
2716 int id,
2717 int fg) // TRUE = fg, FALSE = bg
2718{
2719 guicolor_T color;
2720
2721 if (id <= 0 || id > highlight_ga.ga_len)
2722 return 0L;
2723
2724 if (fg)
2725 color = HL_TABLE()[id - 1].sg_gui_fg;
2726 else
2727 color = HL_TABLE()[id - 1].sg_gui_bg;
2728
2729 if (color == INVALCOLOR)
2730 return 0L;
2731
2732 return GUI_MCH_GET_RGB(color);
2733}
2734#endif
2735
2736/*
2737 * Output the syntax list header.
2738 * Return TRUE when started a new line.
2739 */
2740 int
2741syn_list_header(
2742 int did_header, // did header already
2743 int outlen, // length of string that comes
2744 int id) // highlight group id
2745{
2746 int endcol = 19;
2747 int newline = TRUE;
2748 int name_col = 0;
2749
2750 if (!did_header)
2751 {
2752 msg_putchar('\n');
2753 if (got_int)
2754 return TRUE;
2755 msg_outtrans(HL_TABLE()[id - 1].sg_name);
2756 name_col = msg_col;
2757 endcol = 15;
2758 }
2759 else if (msg_col + outlen + 1 >= Columns)
2760 {
2761 msg_putchar('\n');
2762 if (got_int)
2763 return TRUE;
2764 }
2765 else
2766 {
2767 if (msg_col >= endcol) // wrap around is like starting a new line
2768 newline = FALSE;
2769 }
2770
2771 if (msg_col >= endcol) // output at least one space
2772 endcol = msg_col + 1;
2773 if (Columns <= endcol) // avoid hang for tiny window
2774 endcol = Columns - 1;
2775
2776 msg_advance(endcol);
2777
2778 // Show "xxx" with the attributes.
2779 if (!did_header)
2780 {
2781 if (endcol == Columns - 1 && endcol <= name_col)
2782 msg_putchar(' ');
2783 msg_puts_attr("xxx", syn_id2attr(id));
2784 msg_putchar(' ');
2785 }
2786
2787 return newline;
2788}
2789
2790/*
2791 * Set the attribute numbers for a highlight group.
2792 * Called after one of the attributes has changed.
2793 */
2794 static void
2795set_hl_attr(
2796 int idx) // index in array
2797{
2798 attrentry_T at_en;
2799 hl_group_T *sgp = HL_TABLE() + idx;
2800
2801 // The "Normal" group doesn't need an attribute number
2802 if (sgp->sg_name_u != NULL && STRCMP(sgp->sg_name_u, "NORMAL") == 0)
2803 return;
2804
2805#ifdef FEAT_GUI
2806 /*
2807 * For the GUI mode: If there are other than "normal" highlighting
2808 * attributes, need to allocate an attr number.
2809 */
2810 if (sgp->sg_gui_fg == INVALCOLOR
2811 && sgp->sg_gui_bg == INVALCOLOR
2812 && sgp->sg_gui_sp == INVALCOLOR
2813 && sgp->sg_font == NOFONT
2814# ifdef FEAT_XFONTSET
2815 && sgp->sg_fontset == NOFONTSET
2816# endif
2817 )
2818 {
2819 sgp->sg_gui_attr = sgp->sg_gui;
2820 }
2821 else
2822 {
2823 at_en.ae_attr = sgp->sg_gui;
2824 at_en.ae_u.gui.fg_color = sgp->sg_gui_fg;
2825 at_en.ae_u.gui.bg_color = sgp->sg_gui_bg;
2826 at_en.ae_u.gui.sp_color = sgp->sg_gui_sp;
2827 at_en.ae_u.gui.font = sgp->sg_font;
2828# ifdef FEAT_XFONTSET
2829 at_en.ae_u.gui.fontset = sgp->sg_fontset;
2830# endif
2831 sgp->sg_gui_attr = get_attr_entry(&gui_attr_table, &at_en);
2832 }
2833#endif
2834 /*
2835 * For the term mode: If there are other than "normal" highlighting
2836 * attributes, need to allocate an attr number.
2837 */
2838 if (sgp->sg_start == NULL && sgp->sg_stop == NULL)
2839 sgp->sg_term_attr = sgp->sg_term;
2840 else
2841 {
2842 at_en.ae_attr = sgp->sg_term;
2843 at_en.ae_u.term.start = sgp->sg_start;
2844 at_en.ae_u.term.stop = sgp->sg_stop;
2845 sgp->sg_term_attr = get_attr_entry(&term_attr_table, &at_en);
2846 }
2847
2848 /*
2849 * For the color term mode: If there are other than "normal"
2850 * highlighting attributes, need to allocate an attr number.
2851 */
2852 if (sgp->sg_cterm_fg == 0 && sgp->sg_cterm_bg == 0
2853# ifdef FEAT_TERMGUICOLORS
2854 && sgp->sg_gui_fg == INVALCOLOR
2855 && sgp->sg_gui_bg == INVALCOLOR
2856# endif
2857 )
2858 sgp->sg_cterm_attr = sgp->sg_cterm;
2859 else
2860 {
2861 at_en.ae_attr = sgp->sg_cterm;
2862 at_en.ae_u.cterm.fg_color = sgp->sg_cterm_fg;
2863 at_en.ae_u.cterm.bg_color = sgp->sg_cterm_bg;
2864# ifdef FEAT_TERMGUICOLORS
2865# ifdef MSWIN
2866# ifdef VIMDLL
2867 // Only when not using the GUI.
2868 if (!gui.in_use && !gui.starting)
2869# endif
2870 {
2871 int id;
2872 guicolor_T fg, bg;
2873
2874 id = syn_name2id((char_u *)"Normal");
2875 if (id > 0)
2876 {
2877 syn_id2colors(id, &fg, &bg);
2878 if (sgp->sg_gui_fg == INVALCOLOR)
2879 sgp->sg_gui_fg = fg;
2880 if (sgp->sg_gui_bg == INVALCOLOR)
2881 sgp->sg_gui_bg = bg;
2882 }
2883
2884 }
2885# endif
2886 at_en.ae_u.cterm.fg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_fg);
2887 at_en.ae_u.cterm.bg_rgb = GUI_MCH_GET_RGB2(sgp->sg_gui_bg);
2888 if (at_en.ae_u.cterm.fg_rgb == INVALCOLOR
2889 && at_en.ae_u.cterm.bg_rgb == INVALCOLOR)
2890 {
2891 // If both fg and bg are invalid fall back to the cterm colors.
2892 // Helps when the GUI only uses an attribute, e.g. undercurl.
2893 at_en.ae_u.cterm.fg_rgb = CTERMCOLOR;
2894 at_en.ae_u.cterm.bg_rgb = CTERMCOLOR;
2895 }
2896# endif
2897 sgp->sg_cterm_attr = get_attr_entry(&cterm_attr_table, &at_en);
2898 }
2899}
2900
2901/*
2902 * Lookup a highlight group name and return its ID.
2903 * If it is not found, 0 is returned.
2904 */
2905 int
2906syn_name2id(char_u *name)
2907{
2908 int i;
2909 char_u name_u[200];
2910
2911 // Avoid using stricmp() too much, it's slow on some systems
2912 // Avoid alloc()/free(), these are slow too. ID names over 200 chars
2913 // don't deserve to be found!
2914 vim_strncpy(name_u, name, 199);
2915 vim_strup(name_u);
2916 for (i = highlight_ga.ga_len; --i >= 0; )
2917 if (HL_TABLE()[i].sg_name_u != NULL
2918 && STRCMP(name_u, HL_TABLE()[i].sg_name_u) == 0)
2919 break;
2920 return i + 1;
2921}
2922
2923/*
2924 * Lookup a highlight group name and return its attributes.
2925 * Return zero if not found.
2926 */
2927 int
2928syn_name2attr(char_u *name)
2929{
2930 int id = syn_name2id(name);
2931
2932 if (id != 0)
2933 return syn_id2attr(id);
2934 return 0;
2935}
2936
2937#if defined(FEAT_EVAL) || defined(PROTO)
2938/*
2939 * Return TRUE if highlight group "name" exists.
2940 */
2941 int
2942highlight_exists(char_u *name)
2943{
2944 return (syn_name2id(name) > 0);
2945}
2946
2947# if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
2948/*
2949 * Return the name of highlight group "id".
2950 * When not a valid ID return an empty string.
2951 */
2952 char_u *
2953syn_id2name(int id)
2954{
2955 if (id <= 0 || id > highlight_ga.ga_len)
2956 return (char_u *)"";
2957 return HL_TABLE()[id - 1].sg_name;
2958}
2959# endif
2960#endif
2961
2962/*
2963 * Like syn_name2id(), but take a pointer + length argument.
2964 */
2965 int
2966syn_namen2id(char_u *linep, int len)
2967{
2968 char_u *name;
2969 int id = 0;
2970
2971 name = vim_strnsave(linep, len);
2972 if (name != NULL)
2973 {
2974 id = syn_name2id(name);
2975 vim_free(name);
2976 }
2977 return id;
2978}
2979
2980/*
2981 * Find highlight group name in the table and return its ID.
2982 * The argument is a pointer to the name and the length of the name.
2983 * If it doesn't exist yet, a new entry is created.
2984 * Return 0 for failure.
2985 */
2986 int
2987syn_check_group(char_u *pp, int len)
2988{
2989 int id;
2990 char_u *name;
2991
2992 name = vim_strnsave(pp, len);
2993 if (name == NULL)
2994 return 0;
2995
2996 id = syn_name2id(name);
2997 if (id == 0) // doesn't exist yet
2998 id = syn_add_group(name);
2999 else
3000 vim_free(name);
3001 return id;
3002}
3003
3004/*
3005 * Add new highlight group and return its ID.
3006 * "name" must be an allocated string, it will be consumed.
3007 * Return 0 for failure.
3008 */
3009 static int
3010syn_add_group(char_u *name)
3011{
3012 char_u *p;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003013 char_u *name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003014
3015 // Check that the name is ASCII letters, digits and underscore.
3016 for (p = name; *p != NUL; ++p)
3017 {
3018 if (!vim_isprintc(*p))
3019 {
3020 emsg(_("E669: Unprintable character in group name"));
3021 vim_free(name);
3022 return 0;
3023 }
3024 else if (!ASCII_ISALNUM(*p) && *p != '_')
3025 {
3026 // This is an error, but since there previously was no check only
3027 // give a warning.
3028 msg_source(HL_ATTR(HLF_W));
3029 msg(_("W18: Invalid character in group name"));
3030 break;
3031 }
3032 }
3033
3034 /*
3035 * First call for this growarray: init growing array.
3036 */
3037 if (highlight_ga.ga_data == NULL)
3038 {
3039 highlight_ga.ga_itemsize = sizeof(hl_group_T);
3040 highlight_ga.ga_growsize = 10;
3041 }
3042
3043 if (highlight_ga.ga_len >= MAX_HL_ID)
3044 {
3045 emsg(_("E849: Too many highlight and syntax groups"));
3046 vim_free(name);
3047 return 0;
3048 }
3049
3050 /*
3051 * Make room for at least one other syntax_highlight entry.
3052 */
3053 if (ga_grow(&highlight_ga, 1) == FAIL)
3054 {
3055 vim_free(name);
3056 return 0;
3057 }
3058
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003059 name_up = vim_strsave_up(name);
3060 if (name_up == NULL)
3061 {
3062 vim_free(name);
3063 return 0;
3064 }
3065
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003066 vim_memset(&(HL_TABLE()[highlight_ga.ga_len]), 0, sizeof(hl_group_T));
3067 HL_TABLE()[highlight_ga.ga_len].sg_name = name;
Bram Moolenaar6f10c702019-08-20 22:58:37 +02003068 HL_TABLE()[highlight_ga.ga_len].sg_name_u = name_up;
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003069#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS)
3070 HL_TABLE()[highlight_ga.ga_len].sg_gui_bg = INVALCOLOR;
3071 HL_TABLE()[highlight_ga.ga_len].sg_gui_fg = INVALCOLOR;
3072# ifdef FEAT_GUI
3073 HL_TABLE()[highlight_ga.ga_len].sg_gui_sp = INVALCOLOR;
3074# endif
3075#endif
3076 ++highlight_ga.ga_len;
3077
3078 return highlight_ga.ga_len; // ID is index plus one
3079}
3080
3081/*
3082 * When, just after calling syn_add_group(), an error is discovered, this
3083 * function deletes the new name.
3084 */
3085 static void
3086syn_unadd_group(void)
3087{
3088 --highlight_ga.ga_len;
3089 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name);
3090 vim_free(HL_TABLE()[highlight_ga.ga_len].sg_name_u);
3091}
3092
3093/*
3094 * Translate a group ID to highlight attributes.
3095 */
3096 int
3097syn_id2attr(int hl_id)
3098{
3099 int attr;
3100 hl_group_T *sgp;
3101
3102 hl_id = syn_get_final_id(hl_id);
3103 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3104
3105#ifdef FEAT_GUI
3106 /*
3107 * Only use GUI attr when the GUI is being used.
3108 */
3109 if (gui.in_use)
3110 attr = sgp->sg_gui_attr;
3111 else
3112#endif
3113 if (IS_CTERM)
3114 attr = sgp->sg_cterm_attr;
3115 else
3116 attr = sgp->sg_term_attr;
3117
3118 return attr;
3119}
3120
3121#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3122/*
3123 * Get the GUI colors and attributes for a group ID.
3124 * NOTE: the colors will be INVALCOLOR when not set, the color otherwise.
3125 */
3126 int
3127syn_id2colors(int hl_id, guicolor_T *fgp, guicolor_T *bgp)
3128{
3129 hl_group_T *sgp;
3130
3131 hl_id = syn_get_final_id(hl_id);
3132 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3133
3134 *fgp = sgp->sg_gui_fg;
3135 *bgp = sgp->sg_gui_bg;
3136 return sgp->sg_gui;
3137}
3138#endif
3139
3140#if (defined(MSWIN) \
3141 && (!defined(FEAT_GUI_MSWIN) || defined(VIMDLL)) \
3142 && defined(FEAT_TERMGUICOLORS)) || defined(PROTO)
3143 void
3144syn_id2cterm_bg(int hl_id, int *fgp, int *bgp)
3145{
3146 hl_group_T *sgp;
3147
3148 hl_id = syn_get_final_id(hl_id);
3149 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3150 *fgp = sgp->sg_cterm_fg - 1;
3151 *bgp = sgp->sg_cterm_bg - 1;
3152}
3153#endif
3154
3155/*
3156 * Translate a group ID to the final group ID (following links).
3157 */
3158 int
3159syn_get_final_id(int hl_id)
3160{
3161 int count;
3162 hl_group_T *sgp;
3163
3164 if (hl_id > highlight_ga.ga_len || hl_id < 1)
3165 return 0; // Can be called from eval!!
3166
3167 /*
3168 * Follow links until there is no more.
3169 * Look out for loops! Break after 100 links.
3170 */
3171 for (count = 100; --count >= 0; )
3172 {
3173 sgp = &HL_TABLE()[hl_id - 1]; // index is ID minus one
3174 if (sgp->sg_link == 0 || sgp->sg_link > highlight_ga.ga_len)
3175 break;
3176 hl_id = sgp->sg_link;
3177 }
3178
3179 return hl_id;
3180}
3181
3182#if defined(FEAT_GUI) || defined(FEAT_TERMGUICOLORS) || defined(PROTO)
3183/*
3184 * Call this function just after the GUI has started.
3185 * Also called when 'termguicolors' was set, gui.in_use will be FALSE then.
3186 * It finds the font and color handles for the highlighting groups.
3187 */
3188 void
3189highlight_gui_started(void)
3190{
3191 int idx;
3192
3193 // First get the colors from the "Normal" and "Menu" group, if set
3194 if (USE_24BIT)
3195 set_normal_colors();
3196
3197 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3198 gui_do_one_color(idx, FALSE, FALSE);
3199
3200 highlight_changed();
3201}
3202
3203 static void
3204gui_do_one_color(
3205 int idx,
3206 int do_menu UNUSED, // TRUE: might set the menu font
3207 int do_tooltip UNUSED) // TRUE: might set the tooltip font
3208{
3209 int didit = FALSE;
3210
3211# ifdef FEAT_GUI
3212# ifdef FEAT_TERMGUICOLORS
3213 if (gui.in_use)
3214# endif
3215 if (HL_TABLE()[idx].sg_font_name != NULL)
3216 {
3217 hl_do_font(idx, HL_TABLE()[idx].sg_font_name, FALSE, do_menu,
3218 do_tooltip, TRUE);
3219 didit = TRUE;
3220 }
3221# endif
3222 if (HL_TABLE()[idx].sg_gui_fg_name != NULL)
3223 {
3224 HL_TABLE()[idx].sg_gui_fg =
3225 color_name2handle(HL_TABLE()[idx].sg_gui_fg_name);
3226 didit = TRUE;
3227 }
3228 if (HL_TABLE()[idx].sg_gui_bg_name != NULL)
3229 {
3230 HL_TABLE()[idx].sg_gui_bg =
3231 color_name2handle(HL_TABLE()[idx].sg_gui_bg_name);
3232 didit = TRUE;
3233 }
3234# ifdef FEAT_GUI
3235 if (HL_TABLE()[idx].sg_gui_sp_name != NULL)
3236 {
3237 HL_TABLE()[idx].sg_gui_sp =
3238 color_name2handle(HL_TABLE()[idx].sg_gui_sp_name);
3239 didit = TRUE;
3240 }
3241# endif
3242 if (didit) // need to get a new attr number
3243 set_hl_attr(idx);
3244}
3245#endif
3246
3247#if defined(USER_HIGHLIGHT) && defined(FEAT_STL_OPT)
3248/*
3249 * Apply difference between User[1-9] and HLF_S to HLF_SNC, HLF_ST or HLF_STNC.
3250 */
3251 static void
3252combine_stl_hlt(
3253 int id,
3254 int id_S,
3255 int id_alt,
3256 int hlcnt,
3257 int i,
3258 int hlf,
3259 int *table)
3260{
3261 hl_group_T *hlt = HL_TABLE();
3262
3263 if (id_alt == 0)
3264 {
3265 vim_memset(&hlt[hlcnt + i], 0, sizeof(hl_group_T));
3266 hlt[hlcnt + i].sg_term = highlight_attr[hlf];
3267 hlt[hlcnt + i].sg_cterm = highlight_attr[hlf];
3268# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3269 hlt[hlcnt + i].sg_gui = highlight_attr[hlf];
3270# endif
3271 }
3272 else
3273 mch_memmove(&hlt[hlcnt + i],
3274 &hlt[id_alt - 1],
3275 sizeof(hl_group_T));
3276 hlt[hlcnt + i].sg_link = 0;
3277
3278 hlt[hlcnt + i].sg_term ^=
3279 hlt[id - 1].sg_term ^ hlt[id_S - 1].sg_term;
3280 if (hlt[id - 1].sg_start != hlt[id_S - 1].sg_start)
3281 hlt[hlcnt + i].sg_start = hlt[id - 1].sg_start;
3282 if (hlt[id - 1].sg_stop != hlt[id_S - 1].sg_stop)
3283 hlt[hlcnt + i].sg_stop = hlt[id - 1].sg_stop;
3284 hlt[hlcnt + i].sg_cterm ^=
3285 hlt[id - 1].sg_cterm ^ hlt[id_S - 1].sg_cterm;
3286 if (hlt[id - 1].sg_cterm_fg != hlt[id_S - 1].sg_cterm_fg)
3287 hlt[hlcnt + i].sg_cterm_fg = hlt[id - 1].sg_cterm_fg;
3288 if (hlt[id - 1].sg_cterm_bg != hlt[id_S - 1].sg_cterm_bg)
3289 hlt[hlcnt + i].sg_cterm_bg = hlt[id - 1].sg_cterm_bg;
3290# if defined(FEAT_GUI) || defined(FEAT_EVAL)
3291 hlt[hlcnt + i].sg_gui ^=
3292 hlt[id - 1].sg_gui ^ hlt[id_S - 1].sg_gui;
3293# endif
3294# ifdef FEAT_GUI
3295 if (hlt[id - 1].sg_gui_fg != hlt[id_S - 1].sg_gui_fg)
3296 hlt[hlcnt + i].sg_gui_fg = hlt[id - 1].sg_gui_fg;
3297 if (hlt[id - 1].sg_gui_bg != hlt[id_S - 1].sg_gui_bg)
3298 hlt[hlcnt + i].sg_gui_bg = hlt[id - 1].sg_gui_bg;
3299 if (hlt[id - 1].sg_gui_sp != hlt[id_S - 1].sg_gui_sp)
3300 hlt[hlcnt + i].sg_gui_sp = hlt[id - 1].sg_gui_sp;
3301 if (hlt[id - 1].sg_font != hlt[id_S - 1].sg_font)
3302 hlt[hlcnt + i].sg_font = hlt[id - 1].sg_font;
3303# ifdef FEAT_XFONTSET
3304 if (hlt[id - 1].sg_fontset != hlt[id_S - 1].sg_fontset)
3305 hlt[hlcnt + i].sg_fontset = hlt[id - 1].sg_fontset;
3306# endif
3307# endif
3308 highlight_ga.ga_len = hlcnt + i + 1;
3309 set_hl_attr(hlcnt + i); // At long last we can apply
3310 table[i] = syn_id2attr(hlcnt + i + 1);
3311}
3312#endif
3313
3314/*
3315 * Translate the 'highlight' option into attributes in highlight_attr[] and
3316 * set up the user highlights User1..9. If FEAT_STL_OPT is in use, a set of
3317 * corresponding highlights to use on top of HLF_SNC is computed.
3318 * Called only when the 'highlight' option has been changed and upon first
3319 * screen redraw after any :highlight command.
3320 * Return FAIL when an invalid flag is found in 'highlight'. OK otherwise.
3321 */
3322 int
3323highlight_changed(void)
3324{
3325 int hlf;
3326 int i;
3327 char_u *p;
3328 int attr;
3329 char_u *end;
3330 int id;
3331#ifdef USER_HIGHLIGHT
3332 char_u userhl[30]; // use 30 to avoid compiler warning
3333# ifdef FEAT_STL_OPT
3334 int id_S = -1;
3335 int id_SNC = 0;
3336# ifdef FEAT_TERMINAL
3337 int id_ST = 0;
3338 int id_STNC = 0;
3339# endif
3340 int hlcnt;
3341# endif
3342#endif
3343 static int hl_flags[HLF_COUNT] = HL_FLAGS;
3344
3345 need_highlight_changed = FALSE;
3346
3347 /*
3348 * Clear all attributes.
3349 */
3350 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3351 highlight_attr[hlf] = 0;
3352
3353 /*
3354 * First set all attributes to their default value.
3355 * Then use the attributes from the 'highlight' option.
3356 */
3357 for (i = 0; i < 2; ++i)
3358 {
3359 if (i)
3360 p = p_hl;
3361 else
3362 p = get_highlight_default();
3363 if (p == NULL) // just in case
3364 continue;
3365
3366 while (*p)
3367 {
3368 for (hlf = 0; hlf < (int)HLF_COUNT; ++hlf)
3369 if (hl_flags[hlf] == *p)
3370 break;
3371 ++p;
3372 if (hlf == (int)HLF_COUNT || *p == NUL)
3373 return FAIL;
3374
3375 /*
3376 * Allow several hl_flags to be combined, like "bu" for
3377 * bold-underlined.
3378 */
3379 attr = 0;
3380 for ( ; *p && *p != ','; ++p) // parse upto comma
3381 {
3382 if (VIM_ISWHITE(*p)) // ignore white space
3383 continue;
3384
3385 if (attr > HL_ALL) // Combination with ':' is not allowed.
3386 return FAIL;
3387
3388 switch (*p)
3389 {
3390 case 'b': attr |= HL_BOLD;
3391 break;
3392 case 'i': attr |= HL_ITALIC;
3393 break;
3394 case '-':
3395 case 'n': // no highlighting
3396 break;
3397 case 'r': attr |= HL_INVERSE;
3398 break;
3399 case 's': attr |= HL_STANDOUT;
3400 break;
3401 case 'u': attr |= HL_UNDERLINE;
3402 break;
3403 case 'c': attr |= HL_UNDERCURL;
3404 break;
3405 case 't': attr |= HL_STRIKETHROUGH;
3406 break;
3407 case ':': ++p; // highlight group name
3408 if (attr || *p == NUL) // no combinations
3409 return FAIL;
3410 end = vim_strchr(p, ',');
3411 if (end == NULL)
3412 end = p + STRLEN(p);
3413 id = syn_check_group(p, (int)(end - p));
3414 if (id == 0)
3415 return FAIL;
3416 attr = syn_id2attr(id);
3417 p = end - 1;
3418#if defined(FEAT_STL_OPT) && defined(USER_HIGHLIGHT)
3419 if (hlf == (int)HLF_SNC)
3420 id_SNC = syn_get_final_id(id);
3421# ifdef FEAT_TERMINAL
3422 else if (hlf == (int)HLF_ST)
3423 id_ST = syn_get_final_id(id);
3424 else if (hlf == (int)HLF_STNC)
3425 id_STNC = syn_get_final_id(id);
3426# endif
3427 else if (hlf == (int)HLF_S)
3428 id_S = syn_get_final_id(id);
3429#endif
3430 break;
3431 default: return FAIL;
3432 }
3433 }
3434 highlight_attr[hlf] = attr;
3435
3436 p = skip_to_option_part(p); // skip comma and spaces
3437 }
3438 }
3439
3440#ifdef USER_HIGHLIGHT
3441 /*
3442 * Setup the user highlights
3443 *
3444 * Temporarily utilize 28 more hl entries:
3445 * 9 for User1-User9 combined with StatusLineNC
3446 * 9 for User1-User9 combined with StatusLineTerm
3447 * 9 for User1-User9 combined with StatusLineTermNC
3448 * 1 for StatusLine default
3449 * Have to be in there simultaneously in case of table overflows in
3450 * get_attr_entry()
3451 */
3452# ifdef FEAT_STL_OPT
3453 if (ga_grow(&highlight_ga, 28) == FAIL)
3454 return FAIL;
3455 hlcnt = highlight_ga.ga_len;
3456 if (id_S == -1)
3457 {
3458 // Make sure id_S is always valid to simplify code below. Use the last
3459 // entry.
3460 vim_memset(&HL_TABLE()[hlcnt + 27], 0, sizeof(hl_group_T));
3461 HL_TABLE()[hlcnt + 18].sg_term = highlight_attr[HLF_S];
3462 id_S = hlcnt + 19;
3463 }
3464# endif
3465 for (i = 0; i < 9; i++)
3466 {
3467 sprintf((char *)userhl, "User%d", i + 1);
3468 id = syn_name2id(userhl);
3469 if (id == 0)
3470 {
3471 highlight_user[i] = 0;
3472# ifdef FEAT_STL_OPT
3473 highlight_stlnc[i] = 0;
3474# ifdef FEAT_TERMINAL
3475 highlight_stlterm[i] = 0;
3476 highlight_stltermnc[i] = 0;
3477# endif
3478# endif
3479 }
3480 else
3481 {
3482 highlight_user[i] = syn_id2attr(id);
3483# ifdef FEAT_STL_OPT
3484 combine_stl_hlt(id, id_S, id_SNC, hlcnt, i,
3485 HLF_SNC, highlight_stlnc);
3486# ifdef FEAT_TERMINAL
3487 combine_stl_hlt(id, id_S, id_ST, hlcnt + 9, i,
3488 HLF_ST, highlight_stlterm);
3489 combine_stl_hlt(id, id_S, id_STNC, hlcnt + 18, i,
3490 HLF_STNC, highlight_stltermnc);
3491# endif
3492# endif
3493 }
3494 }
3495# ifdef FEAT_STL_OPT
3496 highlight_ga.ga_len = hlcnt;
3497# endif
3498
3499#endif // USER_HIGHLIGHT
3500
3501 return OK;
3502}
3503
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003504static void highlight_list(void);
3505static void highlight_list_two(int cnt, int attr);
3506
3507/*
3508 * Handle command line completion for :highlight command.
3509 */
3510 void
3511set_context_in_highlight_cmd(expand_T *xp, char_u *arg)
3512{
3513 char_u *p;
3514
3515 // Default: expand group names
3516 xp->xp_context = EXPAND_HIGHLIGHT;
3517 xp->xp_pattern = arg;
3518 include_link = 2;
3519 include_default = 1;
3520
3521 // (part of) subcommand already typed
3522 if (*arg != NUL)
3523 {
3524 p = skiptowhite(arg);
3525 if (*p != NUL) // past "default" or group name
3526 {
3527 include_default = 0;
3528 if (STRNCMP("default", arg, p - arg) == 0)
3529 {
3530 arg = skipwhite(p);
3531 xp->xp_pattern = arg;
3532 p = skiptowhite(arg);
3533 }
3534 if (*p != NUL) // past group name
3535 {
3536 include_link = 0;
3537 if (arg[1] == 'i' && arg[0] == 'N')
3538 highlight_list();
3539 if (STRNCMP("link", arg, p - arg) == 0
3540 || STRNCMP("clear", arg, p - arg) == 0)
3541 {
3542 xp->xp_pattern = skipwhite(p);
3543 p = skiptowhite(xp->xp_pattern);
3544 if (*p != NUL) // past first group name
3545 {
3546 xp->xp_pattern = skipwhite(p);
3547 p = skiptowhite(xp->xp_pattern);
3548 }
3549 }
3550 if (*p != NUL) // past group name(s)
3551 xp->xp_context = EXPAND_NOTHING;
3552 }
3553 }
3554 }
3555}
3556
3557/*
3558 * List highlighting matches in a nice way.
3559 */
3560 static void
3561highlight_list(void)
3562{
3563 int i;
3564
3565 for (i = 10; --i >= 0; )
3566 highlight_list_two(i, HL_ATTR(HLF_D));
3567 for (i = 40; --i >= 0; )
3568 highlight_list_two(99, 0);
3569}
3570
3571 static void
3572highlight_list_two(int cnt, int attr)
3573{
3574 msg_puts_attr(&("N \bI \b! \b"[cnt / 11]), attr);
3575 msg_clr_eos();
3576 out_flush();
3577 ui_delay(cnt == 99 ? 40L : (long)cnt * 50L, FALSE);
3578}
3579
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003580/*
3581 * Function given to ExpandGeneric() to obtain the list of group names.
3582 */
3583 char_u *
3584get_highlight_name(expand_T *xp UNUSED, int idx)
3585{
3586 return get_highlight_name_ext(xp, idx, TRUE);
3587}
3588
3589/*
3590 * Obtain a highlight group name.
3591 * When "skip_cleared" is TRUE don't return a cleared entry.
3592 */
3593 char_u *
3594get_highlight_name_ext(expand_T *xp UNUSED, int idx, int skip_cleared)
3595{
3596 if (idx < 0)
3597 return NULL;
3598
3599 // Items are never removed from the table, skip the ones that were
3600 // cleared.
3601 if (skip_cleared && idx < highlight_ga.ga_len && HL_TABLE()[idx].sg_cleared)
3602 return (char_u *)"";
3603
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003604 if (idx == highlight_ga.ga_len && include_none != 0)
3605 return (char_u *)"none";
3606 if (idx == highlight_ga.ga_len + include_none && include_default != 0)
3607 return (char_u *)"default";
3608 if (idx == highlight_ga.ga_len + include_none + include_default
3609 && include_link != 0)
3610 return (char_u *)"link";
3611 if (idx == highlight_ga.ga_len + include_none + include_default + 1
3612 && include_link != 0)
3613 return (char_u *)"clear";
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003614 if (idx >= highlight_ga.ga_len)
3615 return NULL;
3616 return HL_TABLE()[idx].sg_name;
3617}
Bram Moolenaarf9cc9f22019-07-14 21:29:22 +02003618
3619#if defined(FEAT_GUI) || defined(PROTO)
3620/*
3621 * Free all the highlight group fonts.
3622 * Used when quitting for systems which need it.
3623 */
3624 void
3625free_highlight_fonts(void)
3626{
3627 int idx;
3628
3629 for (idx = 0; idx < highlight_ga.ga_len; ++idx)
3630 {
3631 gui_mch_free_font(HL_TABLE()[idx].sg_font);
3632 HL_TABLE()[idx].sg_font = NOFONT;
3633# ifdef FEAT_XFONTSET
3634 gui_mch_free_fontset(HL_TABLE()[idx].sg_fontset);
3635 HL_TABLE()[idx].sg_fontset = NOFONTSET;
3636# endif
3637 }
3638
3639 gui_mch_free_font(gui.norm_font);
3640# ifdef FEAT_XFONTSET
3641 gui_mch_free_fontset(gui.fontset);
3642# endif
3643# ifndef FEAT_GUI_GTK
3644 gui_mch_free_font(gui.bold_font);
3645 gui_mch_free_font(gui.ital_font);
3646 gui_mch_free_font(gui.boldital_font);
3647# endif
3648}
3649#endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003650
3651
3652#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003653
3654# define SEARCH_HL_PRIORITY 0
3655
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003656/*
3657 * Add match to the match list of window 'wp'. The pattern 'pat' will be
3658 * highlighted with the group 'grp' with priority 'prio'.
3659 * Optionally, a desired ID 'id' can be specified (greater than or equal to 1).
3660 * If no particular ID is desired, -1 must be specified for 'id'.
3661 * Return ID of added match, -1 on failure.
3662 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003663 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003664match_add(
3665 win_T *wp,
3666 char_u *grp,
3667 char_u *pat,
3668 int prio,
3669 int id,
3670 list_T *pos_list,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003671 char_u *conceal_char UNUSED) // pointer to conceal replacement char
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003672{
3673 matchitem_T *cur;
3674 matchitem_T *prev;
3675 matchitem_T *m;
3676 int hlg_id;
3677 regprog_T *regprog = NULL;
3678 int rtype = SOME_VALID;
3679
3680 if (*grp == NUL || (pat != NULL && *pat == NUL))
3681 return -1;
3682 if (id < -1 || id == 0)
3683 {
3684 semsg(_("E799: Invalid ID: %d (must be greater than or equal to 1)"), id);
3685 return -1;
3686 }
3687 if (id != -1)
3688 {
3689 cur = wp->w_match_head;
3690 while (cur != NULL)
3691 {
3692 if (cur->id == id)
3693 {
3694 semsg(_("E801: ID already taken: %d"), id);
3695 return -1;
3696 }
3697 cur = cur->next;
3698 }
3699 }
3700 if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0)
3701 {
3702 semsg(_(e_nogroup), grp);
3703 return -1;
3704 }
3705 if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL)
3706 {
3707 semsg(_(e_invarg2), pat);
3708 return -1;
3709 }
3710
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003711 // Find available match ID.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003712 while (id == -1)
3713 {
3714 cur = wp->w_match_head;
3715 while (cur != NULL && cur->id != wp->w_next_match_id)
3716 cur = cur->next;
3717 if (cur == NULL)
3718 id = wp->w_next_match_id;
3719 wp->w_next_match_id++;
3720 }
3721
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003722 // Build new match.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003723 m = ALLOC_CLEAR_ONE(matchitem_T);
3724 m->id = id;
3725 m->priority = prio;
3726 m->pattern = pat == NULL ? NULL : vim_strsave(pat);
3727 m->hlg_id = hlg_id;
3728 m->match.regprog = regprog;
3729 m->match.rmm_ic = FALSE;
3730 m->match.rmm_maxcol = 0;
3731# if defined(FEAT_CONCEAL)
3732 m->conceal_char = 0;
3733 if (conceal_char != NULL)
3734 m->conceal_char = (*mb_ptr2char)(conceal_char);
3735# endif
3736
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003737 // Set up position matches
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003738 if (pos_list != NULL)
3739 {
3740 linenr_T toplnum = 0;
3741 linenr_T botlnum = 0;
3742 listitem_T *li;
3743 int i;
3744
3745 for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH;
3746 i++, li = li->li_next)
3747 {
3748 linenr_T lnum = 0;
3749 colnr_T col = 0;
3750 int len = 1;
3751 list_T *subl;
3752 listitem_T *subli;
3753 int error = FALSE;
3754
3755 if (li->li_tv.v_type == VAR_LIST)
3756 {
3757 subl = li->li_tv.vval.v_list;
3758 if (subl == NULL)
3759 goto fail;
3760 subli = subl->lv_first;
3761 if (subli == NULL)
3762 goto fail;
3763 lnum = tv_get_number_chk(&subli->li_tv, &error);
3764 if (error == TRUE)
3765 goto fail;
3766 if (lnum == 0)
3767 {
3768 --i;
3769 continue;
3770 }
3771 m->pos.pos[i].lnum = lnum;
3772 subli = subli->li_next;
3773 if (subli != NULL)
3774 {
3775 col = tv_get_number_chk(&subli->li_tv, &error);
3776 if (error == TRUE)
3777 goto fail;
3778 subli = subli->li_next;
3779 if (subli != NULL)
3780 {
3781 len = tv_get_number_chk(&subli->li_tv, &error);
3782 if (error == TRUE)
3783 goto fail;
3784 }
3785 }
3786 m->pos.pos[i].col = col;
3787 m->pos.pos[i].len = len;
3788 }
3789 else if (li->li_tv.v_type == VAR_NUMBER)
3790 {
3791 if (li->li_tv.vval.v_number == 0)
3792 {
3793 --i;
3794 continue;
3795 }
3796 m->pos.pos[i].lnum = li->li_tv.vval.v_number;
3797 m->pos.pos[i].col = 0;
3798 m->pos.pos[i].len = 0;
3799 }
3800 else
3801 {
3802 emsg(_("List or number required"));
3803 goto fail;
3804 }
3805 if (toplnum == 0 || lnum < toplnum)
3806 toplnum = lnum;
3807 if (botlnum == 0 || lnum >= botlnum)
3808 botlnum = lnum + 1;
3809 }
3810
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003811 // Calculate top and bottom lines for redrawing area
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003812 if (toplnum != 0)
3813 {
3814 if (wp->w_buffer->b_mod_set)
3815 {
3816 if (wp->w_buffer->b_mod_top > toplnum)
3817 wp->w_buffer->b_mod_top = toplnum;
3818 if (wp->w_buffer->b_mod_bot < botlnum)
3819 wp->w_buffer->b_mod_bot = botlnum;
3820 }
3821 else
3822 {
3823 wp->w_buffer->b_mod_set = TRUE;
3824 wp->w_buffer->b_mod_top = toplnum;
3825 wp->w_buffer->b_mod_bot = botlnum;
3826 wp->w_buffer->b_mod_xlines = 0;
3827 }
3828 m->pos.toplnum = toplnum;
3829 m->pos.botlnum = botlnum;
3830 rtype = VALID;
3831 }
3832 }
3833
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003834 // Insert new match. The match list is in ascending order with regard to
3835 // the match priorities.
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003836 cur = wp->w_match_head;
3837 prev = cur;
3838 while (cur != NULL && prio >= cur->priority)
3839 {
3840 prev = cur;
3841 cur = cur->next;
3842 }
3843 if (cur == prev)
3844 wp->w_match_head = m;
3845 else
3846 prev->next = m;
3847 m->next = cur;
3848
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003849 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003850 return id;
3851
3852fail:
3853 vim_free(m);
3854 return -1;
3855}
3856
3857/*
3858 * Delete match with ID 'id' in the match list of window 'wp'.
3859 * Print error messages if 'perr' is TRUE.
3860 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003861 static int
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003862match_delete(win_T *wp, int id, int perr)
3863{
3864 matchitem_T *cur = wp->w_match_head;
3865 matchitem_T *prev = cur;
3866 int rtype = SOME_VALID;
3867
3868 if (id < 1)
3869 {
3870 if (perr == TRUE)
3871 semsg(_("E802: Invalid ID: %d (must be greater than or equal to 1)"),
3872 id);
3873 return -1;
3874 }
3875 while (cur != NULL && cur->id != id)
3876 {
3877 prev = cur;
3878 cur = cur->next;
3879 }
3880 if (cur == NULL)
3881 {
3882 if (perr == TRUE)
3883 semsg(_("E803: ID not found: %d"), id);
3884 return -1;
3885 }
3886 if (cur == prev)
3887 wp->w_match_head = cur->next;
3888 else
3889 prev->next = cur->next;
3890 vim_regfree(cur->match.regprog);
3891 vim_free(cur->pattern);
3892 if (cur->pos.toplnum != 0)
3893 {
3894 if (wp->w_buffer->b_mod_set)
3895 {
3896 if (wp->w_buffer->b_mod_top > cur->pos.toplnum)
3897 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3898 if (wp->w_buffer->b_mod_bot < cur->pos.botlnum)
3899 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3900 }
3901 else
3902 {
3903 wp->w_buffer->b_mod_set = TRUE;
3904 wp->w_buffer->b_mod_top = cur->pos.toplnum;
3905 wp->w_buffer->b_mod_bot = cur->pos.botlnum;
3906 wp->w_buffer->b_mod_xlines = 0;
3907 }
3908 rtype = VALID;
3909 }
3910 vim_free(cur);
Bram Moolenaar06029a82019-07-24 14:25:26 +02003911 redraw_win_later(wp, rtype);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003912 return 0;
3913}
3914
3915/*
3916 * Delete all matches in the match list of window 'wp'.
3917 */
3918 void
3919clear_matches(win_T *wp)
3920{
3921 matchitem_T *m;
3922
3923 while (wp->w_match_head != NULL)
3924 {
3925 m = wp->w_match_head->next;
3926 vim_regfree(wp->w_match_head->match.regprog);
3927 vim_free(wp->w_match_head->pattern);
3928 vim_free(wp->w_match_head);
3929 wp->w_match_head = m;
3930 }
Bram Moolenaar4ef18dc2019-07-24 15:28:18 +02003931 redraw_win_later(wp, SOME_VALID);
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003932}
3933
3934/*
3935 * Get match from ID 'id' in window 'wp'.
3936 * Return NULL if match not found.
3937 */
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02003938 static matchitem_T *
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02003939get_match(win_T *wp, int id)
3940{
3941 matchitem_T *cur = wp->w_match_head;
3942
3943 while (cur != NULL && cur->id != id)
3944 cur = cur->next;
3945 return cur;
3946}
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003947
3948/*
3949 * Init for calling prepare_search_hl().
3950 */
3951 void
3952init_search_hl(win_T *wp, match_T *search_hl)
3953{
3954 matchitem_T *cur;
3955
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003956 // Setup for match and 'hlsearch' highlighting. Disable any previous
3957 // match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003958 cur = wp->w_match_head;
3959 while (cur != NULL)
3960 {
3961 cur->hl.rm = cur->match;
3962 if (cur->hlg_id == 0)
3963 cur->hl.attr = 0;
3964 else
3965 cur->hl.attr = syn_id2attr(cur->hlg_id);
3966 cur->hl.buf = wp->w_buffer;
3967 cur->hl.lnum = 0;
3968 cur->hl.first_lnum = 0;
3969# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003970 // Set the time limit to 'redrawtime'.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003971 profile_setlimit(p_rdt, &(cur->hl.tm));
3972# endif
3973 cur = cur->next;
3974 }
3975 search_hl->buf = wp->w_buffer;
3976 search_hl->lnum = 0;
3977 search_hl->first_lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003978 // time limit is set at the toplevel, for all windows
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003979}
3980
3981/*
3982 * If there is a match fill "shl" and return one.
3983 * Return zero otherwise.
3984 */
3985 static int
3986next_search_hl_pos(
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003987 match_T *shl, // points to a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003988 linenr_T lnum,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02003989 posmatch_T *posmatch, // match positions
3990 colnr_T mincol) // minimal column for a match
Bram Moolenaarbbca7732019-07-24 18:13:16 +02003991{
3992 int i;
3993 int found = -1;
3994
3995 for (i = posmatch->cur; i < MAXPOSMATCH; i++)
3996 {
3997 llpos_T *pos = &posmatch->pos[i];
3998
3999 if (pos->lnum == 0)
4000 break;
4001 if (pos->len == 0 && pos->col < mincol)
4002 continue;
4003 if (pos->lnum == lnum)
4004 {
4005 if (found >= 0)
4006 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004007 // if this match comes before the one at "found" then swap
4008 // them
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004009 if (pos->col < posmatch->pos[found].col)
4010 {
4011 llpos_T tmp = *pos;
4012
4013 *pos = posmatch->pos[found];
4014 posmatch->pos[found] = tmp;
4015 }
4016 }
4017 else
4018 found = i;
4019 }
4020 }
4021 posmatch->cur = 0;
4022 if (found >= 0)
4023 {
4024 colnr_T start = posmatch->pos[found].col == 0
4025 ? 0 : posmatch->pos[found].col - 1;
4026 colnr_T end = posmatch->pos[found].col == 0
4027 ? MAXCOL : start + posmatch->pos[found].len;
4028
4029 shl->lnum = lnum;
4030 shl->rm.startpos[0].lnum = 0;
4031 shl->rm.startpos[0].col = start;
4032 shl->rm.endpos[0].lnum = 0;
4033 shl->rm.endpos[0].col = end;
4034 shl->is_addpos = TRUE;
4035 posmatch->cur = found + 1;
4036 return 1;
4037 }
4038 return 0;
4039}
4040
4041/*
4042 * Search for a next 'hlsearch' or match.
4043 * Uses shl->buf.
4044 * Sets shl->lnum and shl->rm contents.
4045 * Note: Assumes a previous match is always before "lnum", unless
4046 * shl->lnum is zero.
4047 * Careful: Any pointers for buffer lines will become invalid.
4048 */
4049 static void
4050next_search_hl(
4051 win_T *win,
4052 match_T *search_hl,
4053 match_T *shl, // points to search_hl or a match
4054 linenr_T lnum,
4055 colnr_T mincol, // minimal column for a match
4056 matchitem_T *cur) // to retrieve match positions if any
4057{
4058 linenr_T l;
4059 colnr_T matchcol;
4060 long nmatched;
4061 int save_called_emsg = called_emsg;
4062
4063 // for :{range}s/pat only highlight inside the range
4064 if (lnum < search_first_line || lnum > search_last_line)
4065 {
4066 shl->lnum = 0;
4067 return;
4068 }
4069
4070 if (shl->lnum != 0)
4071 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004072 // Check for three situations:
4073 // 1. If the "lnum" is below a previous match, start a new search.
4074 // 2. If the previous match includes "mincol", use it.
4075 // 3. Continue after the previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004076 l = shl->lnum + shl->rm.endpos[0].lnum - shl->rm.startpos[0].lnum;
4077 if (lnum > l)
4078 shl->lnum = 0;
4079 else if (lnum < l || shl->rm.endpos[0].col > mincol)
4080 return;
4081 }
4082
4083 /*
4084 * Repeat searching for a match until one is found that includes "mincol"
4085 * or none is found in this line.
4086 */
4087 called_emsg = FALSE;
4088 for (;;)
4089 {
4090# ifdef FEAT_RELTIME
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004091 // Stop searching after passing the time limit.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004092 if (profile_passed_limit(&(shl->tm)))
4093 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004094 shl->lnum = 0; // no match found in time
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004095 break;
4096 }
4097# endif
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004098 // Three situations:
4099 // 1. No useful previous match: search from start of line.
4100 // 2. Not Vi compatible or empty match: continue at next character.
4101 // Break the loop if this is beyond the end of the line.
4102 // 3. Vi compatible searching: continue at end of previous match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004103 if (shl->lnum == 0)
4104 matchcol = 0;
4105 else if (vim_strchr(p_cpo, CPO_SEARCH) == NULL
4106 || (shl->rm.endpos[0].lnum == 0
4107 && shl->rm.endpos[0].col <= shl->rm.startpos[0].col))
4108 {
4109 char_u *ml;
4110
4111 matchcol = shl->rm.startpos[0].col;
4112 ml = ml_get_buf(shl->buf, lnum, FALSE) + matchcol;
4113 if (*ml == NUL)
4114 {
4115 ++matchcol;
4116 shl->lnum = 0;
4117 break;
4118 }
4119 if (has_mbyte)
4120 matchcol += mb_ptr2len(ml);
4121 else
4122 ++matchcol;
4123 }
4124 else
4125 matchcol = shl->rm.endpos[0].col;
4126
4127 shl->lnum = lnum;
4128 if (shl->rm.regprog != NULL)
4129 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004130 // Remember whether shl->rm is using a copy of the regprog in
4131 // cur->match.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004132 int regprog_is_copy = (shl != search_hl && cur != NULL
4133 && shl == &cur->hl
4134 && cur->match.regprog == cur->hl.rm.regprog);
4135 int timed_out = FALSE;
4136
4137 nmatched = vim_regexec_multi(&shl->rm, win, shl->buf, lnum,
4138 matchcol,
4139#ifdef FEAT_RELTIME
4140 &(shl->tm), &timed_out
4141#else
4142 NULL, NULL
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004143#endif
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004144 );
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004145 // Copy the regprog, in case it got freed and recompiled.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004146 if (regprog_is_copy)
4147 cur->match.regprog = cur->hl.rm.regprog;
4148
4149 if (called_emsg || got_int || timed_out)
4150 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004151 // Error while handling regexp: stop using this regexp.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004152 if (shl == search_hl)
4153 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004154 // don't free regprog in the match list, it's a copy
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004155 vim_regfree(shl->rm.regprog);
4156 set_no_hlsearch(TRUE);
4157 }
4158 shl->rm.regprog = NULL;
4159 shl->lnum = 0;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004160 got_int = FALSE; // avoid the "Type :quit to exit Vim" message
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004161 break;
4162 }
4163 }
4164 else if (cur != NULL)
4165 nmatched = next_search_hl_pos(shl, lnum, &(cur->pos), matchcol);
4166 else
4167 nmatched = 0;
4168 if (nmatched == 0)
4169 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004170 shl->lnum = 0; // no match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004171 break;
4172 }
4173 if (shl->rm.startpos[0].lnum > 0
4174 || shl->rm.startpos[0].col >= mincol
4175 || nmatched > 1
4176 || shl->rm.endpos[0].col > mincol)
4177 {
4178 shl->lnum += shl->rm.startpos[0].lnum;
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004179 break; // useful match found
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004180 }
4181 }
4182
4183 // Restore called_emsg for assert_fails().
4184 called_emsg = save_called_emsg;
4185}
4186
4187/*
4188 * Advance to the match in window "wp" line "lnum" or past it.
4189 */
4190 void
4191prepare_search_hl(win_T *wp, match_T *search_hl, linenr_T lnum)
4192{
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004193 matchitem_T *cur; // points to the match list
4194 match_T *shl; // points to search_hl or a match
4195 int shl_flag; // flag to indicate whether search_hl
4196 // has been processed or not
4197 int pos_inprogress; // marks that position match search is
4198 // in progress
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004199 int n;
4200
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004201 // When using a multi-line pattern, start searching at the top
4202 // of the window or just after a closed fold.
4203 // Do this both for search_hl and the match list.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004204 cur = wp->w_match_head;
4205 shl_flag = WIN_IS_POPUP(wp); // skip search_hl in a popup window
4206 while (cur != NULL || shl_flag == FALSE)
4207 {
4208 if (shl_flag == FALSE)
4209 {
4210 shl = search_hl;
4211 shl_flag = TRUE;
4212 }
4213 else
4214 shl = &cur->hl;
4215 if (shl->rm.regprog != NULL
4216 && shl->lnum == 0
4217 && re_multiline(shl->rm.regprog))
4218 {
4219 if (shl->first_lnum == 0)
4220 {
4221# ifdef FEAT_FOLDING
4222 for (shl->first_lnum = lnum;
4223 shl->first_lnum > wp->w_topline; --shl->first_lnum)
4224 if (hasFoldingWin(wp, shl->first_lnum - 1,
4225 NULL, NULL, TRUE, NULL))
4226 break;
4227# else
4228 shl->first_lnum = wp->w_topline;
4229# endif
4230 }
4231 if (cur != NULL)
4232 cur->pos.cur = 0;
4233 pos_inprogress = TRUE;
4234 n = 0;
4235 while (shl->first_lnum < lnum && (shl->rm.regprog != NULL
4236 || (cur != NULL && pos_inprogress)))
4237 {
4238 next_search_hl(wp, search_hl, shl, shl->first_lnum, (colnr_T)n,
4239 shl == search_hl ? NULL : cur);
4240 pos_inprogress = cur == NULL || cur->pos.cur == 0
4241 ? FALSE : TRUE;
4242 if (shl->lnum != 0)
4243 {
4244 shl->first_lnum = shl->lnum
4245 + shl->rm.endpos[0].lnum
4246 - shl->rm.startpos[0].lnum;
4247 n = shl->rm.endpos[0].col;
4248 }
4249 else
4250 {
4251 ++shl->first_lnum;
4252 n = 0;
4253 }
4254 }
4255 }
4256 if (shl != search_hl && cur != NULL)
4257 cur = cur->next;
4258 }
4259}
4260
4261/*
4262 * Prepare for 'hlsearch' and match highlighting in one window line.
4263 * Return TRUE if there is such highlighting and set "search_attr" to the
4264 * current highlight attribute.
4265 */
4266 int
4267prepare_search_hl_line(
4268 win_T *wp,
4269 linenr_T lnum,
4270 colnr_T mincol,
4271 char_u **line,
4272 match_T *search_hl,
4273 int *search_attr)
4274{
4275 matchitem_T *cur; // points to the match list
4276 match_T *shl; // points to search_hl or a match
4277 int shl_flag; // flag to indicate whether search_hl
4278 // has been processed or not
4279 int area_highlighting = FALSE;
4280
4281 /*
4282 * Handle highlighting the last used search pattern and matches.
4283 * Do this for both search_hl and the match list.
4284 * Do not use search_hl in a popup window.
4285 */
4286 cur = wp->w_match_head;
4287 shl_flag = WIN_IS_POPUP(wp);
4288 while (cur != NULL || shl_flag == FALSE)
4289 {
4290 if (shl_flag == FALSE)
4291 {
4292 shl = search_hl;
4293 shl_flag = TRUE;
4294 }
4295 else
4296 shl = &cur->hl;
4297 shl->startcol = MAXCOL;
4298 shl->endcol = MAXCOL;
4299 shl->attr_cur = 0;
4300 shl->is_addpos = FALSE;
4301 if (cur != NULL)
4302 cur->pos.cur = 0;
4303 next_search_hl(wp, search_hl, shl, lnum, mincol,
4304 shl == search_hl ? NULL : cur);
4305
4306 // Need to get the line again, a multi-line regexp may have made it
4307 // invalid.
4308 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4309
4310 if (shl->lnum != 0 && shl->lnum <= lnum)
4311 {
4312 if (shl->lnum == lnum)
4313 shl->startcol = shl->rm.startpos[0].col;
4314 else
4315 shl->startcol = 0;
4316 if (lnum == shl->lnum + shl->rm.endpos[0].lnum
4317 - shl->rm.startpos[0].lnum)
4318 shl->endcol = shl->rm.endpos[0].col;
4319 else
4320 shl->endcol = MAXCOL;
4321 // Highlight one character for an empty match.
4322 if (shl->startcol == shl->endcol)
4323 {
4324 if (has_mbyte && (*line)[shl->endcol] != NUL)
4325 shl->endcol += (*mb_ptr2len)((*line) + shl->endcol);
4326 else
4327 ++shl->endcol;
4328 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004329 if ((long)shl->startcol < mincol) // match at leftcol
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004330 {
4331 shl->attr_cur = shl->attr;
4332 *search_attr = shl->attr;
4333 }
4334 area_highlighting = TRUE;
4335 }
4336 if (shl != search_hl && cur != NULL)
4337 cur = cur->next;
4338 }
4339 return area_highlighting;
4340}
4341
4342/*
4343 * For a position in a line: Check for start/end of 'hlsearch' and other
4344 * matches.
4345 * After end, check for start/end of next match.
4346 * When another match, have to check for start again.
4347 * Watch out for matching an empty string!
4348 * Return the udpated search_attr.
4349 */
4350 int
4351update_search_hl(
4352 win_T *wp,
4353 linenr_T lnum,
4354 colnr_T col,
4355 char_u **line,
4356 match_T *search_hl,
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004357 int *has_match_conc UNUSED,
4358 int *match_conc UNUSED,
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004359 int did_line_attr,
4360 int lcs_eol_one)
4361{
4362 matchitem_T *cur; // points to the match list
4363 match_T *shl; // points to search_hl or a match
4364 int shl_flag; // flag to indicate whether search_hl
4365 // has been processed or not
4366 int pos_inprogress; // marks that position match search is in
4367 // progress
4368 int search_attr = 0;
4369
4370
4371 // Do this for 'search_hl' and the match list (ordered by priority).
4372 cur = wp->w_match_head;
4373 shl_flag = WIN_IS_POPUP(wp);
4374 while (cur != NULL || shl_flag == FALSE)
4375 {
4376 if (shl_flag == FALSE
4377 && ((cur != NULL
4378 && cur->priority > SEARCH_HL_PRIORITY)
4379 || cur == NULL))
4380 {
4381 shl = search_hl;
4382 shl_flag = TRUE;
4383 }
4384 else
4385 shl = &cur->hl;
4386 if (cur != NULL)
4387 cur->pos.cur = 0;
4388 pos_inprogress = TRUE;
4389 while (shl->rm.regprog != NULL || (cur != NULL && pos_inprogress))
4390 {
4391 if (shl->startcol != MAXCOL
4392 && col >= shl->startcol
4393 && col < shl->endcol)
4394 {
Bram Moolenaar1614a142019-10-06 22:00:13 +02004395 int next_col = col + mb_ptr2len(*line + col);
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004396
4397 if (shl->endcol < next_col)
4398 shl->endcol = next_col;
4399 shl->attr_cur = shl->attr;
4400# ifdef FEAT_CONCEAL
4401 // Match with the "Conceal" group results in hiding
4402 // the match.
4403 if (cur != NULL
4404 && shl != search_hl
4405 && syn_name2id((char_u *)"Conceal") == cur->hlg_id)
4406 {
4407 *has_match_conc = col == shl->startcol ? 2 : 1;
4408 *match_conc = cur->conceal_char;
4409 }
4410 else
4411 *has_match_conc = *match_conc = 0;
4412# endif
4413 }
4414 else if (col == shl->endcol)
4415 {
4416 shl->attr_cur = 0;
4417 next_search_hl(wp, search_hl, shl, lnum, col,
4418 shl == search_hl ? NULL : cur);
4419 pos_inprogress = !(cur == NULL || cur->pos.cur == 0);
4420
4421 // Need to get the line again, a multi-line regexp may have
4422 // made it invalid.
4423 *line = ml_get_buf(wp->w_buffer, lnum, FALSE);
4424
4425 if (shl->lnum == lnum)
4426 {
4427 shl->startcol = shl->rm.startpos[0].col;
4428 if (shl->rm.endpos[0].lnum == 0)
4429 shl->endcol = shl->rm.endpos[0].col;
4430 else
4431 shl->endcol = MAXCOL;
4432
4433 if (shl->startcol == shl->endcol)
4434 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004435 // highlight empty match, try again after
4436 // it
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004437 if (has_mbyte)
4438 shl->endcol += (*mb_ptr2len)(*line + shl->endcol);
4439 else
4440 ++shl->endcol;
4441 }
4442
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004443 // Loop to check if the match starts at the
4444 // current position
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004445 continue;
4446 }
4447 }
4448 break;
4449 }
4450 if (shl != search_hl && cur != NULL)
4451 cur = cur->next;
4452 }
4453
4454 // Use attributes from match with highest priority among 'search_hl' and
4455 // the match list.
4456 cur = wp->w_match_head;
4457 shl_flag = WIN_IS_POPUP(wp);
4458 while (cur != NULL || shl_flag == FALSE)
4459 {
4460 if (shl_flag == FALSE
4461 && ((cur != NULL
4462 && cur->priority > SEARCH_HL_PRIORITY)
4463 || cur == NULL))
4464 {
4465 shl = search_hl;
4466 shl_flag = TRUE;
4467 }
4468 else
4469 shl = &cur->hl;
4470 if (shl->attr_cur != 0)
4471 search_attr = shl->attr_cur;
4472 if (shl != search_hl && cur != NULL)
4473 cur = cur->next;
4474 }
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004475 // Only highlight one character after the last column.
Bram Moolenaarbbca7732019-07-24 18:13:16 +02004476 if (*(*line + col) == NUL && (did_line_attr >= 1
4477 || (wp->w_p_list && lcs_eol_one == -1)))
4478 search_attr = 0;
4479 return search_attr;
4480}
4481
4482 int
4483get_prevcol_hl_flag(win_T *wp, match_T *search_hl, long curcol)
4484{
4485 long prevcol = curcol;
4486 int prevcol_hl_flag = FALSE;
4487 matchitem_T *cur; // points to the match list
4488
4489 // we're not really at that column when skipping some text
4490 if ((long)(wp->w_p_wrap ? wp->w_skipcol : wp->w_leftcol) > prevcol)
4491 ++prevcol;
4492
4493 if (!search_hl->is_addpos && prevcol == (long)search_hl->startcol)
4494 prevcol_hl_flag = TRUE;
4495 else
4496 {
4497 cur = wp->w_match_head;
4498 while (cur != NULL)
4499 {
4500 if (!cur->hl.is_addpos && prevcol == (long)cur->hl.startcol)
4501 {
4502 prevcol_hl_flag = TRUE;
4503 break;
4504 }
4505 cur = cur->next;
4506 }
4507 }
4508 return prevcol_hl_flag;
4509}
4510
4511/*
4512 * Get highlighting for the char after the text in "char_attr" from 'hlsearch'
4513 * or match highlighting.
4514 */
4515 void
4516get_search_match_hl(win_T *wp, match_T *search_hl, long col, int *char_attr)
4517{
4518 matchitem_T *cur; // points to the match list
4519 match_T *shl; // points to search_hl or a match
4520 int shl_flag; // flag to indicate whether search_hl
4521 // has been processed or not
4522
4523 cur = wp->w_match_head;
4524 shl_flag = WIN_IS_POPUP(wp);
4525 while (cur != NULL || shl_flag == FALSE)
4526 {
4527 if (shl_flag == FALSE
4528 && ((cur != NULL
4529 && cur->priority > SEARCH_HL_PRIORITY)
4530 || cur == NULL))
4531 {
4532 shl = search_hl;
4533 shl_flag = TRUE;
4534 }
4535 else
4536 shl = &cur->hl;
4537 if (col - 1 == (long)shl->startcol
4538 && (shl == search_hl || !shl->is_addpos))
4539 *char_attr = shl->attr;
4540 if (shl != search_hl && cur != NULL)
4541 cur = cur->next;
4542 }
4543}
4544
4545#endif // FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004546
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004547#if defined(FEAT_EVAL) || defined(PROTO)
4548# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004549 static int
4550matchadd_dict_arg(typval_T *tv, char_u **conceal_char, win_T **win)
4551{
4552 dictitem_T *di;
4553
4554 if (tv->v_type != VAR_DICT)
4555 {
4556 emsg(_(e_dictreq));
4557 return FAIL;
4558 }
4559
4560 if (dict_find(tv->vval.v_dict, (char_u *)"conceal", -1) != NULL)
4561 *conceal_char = dict_get_string(tv->vval.v_dict,
4562 (char_u *)"conceal", FALSE);
4563
4564 if ((di = dict_find(tv->vval.v_dict, (char_u *)"window", -1)) != NULL)
4565 {
4566 *win = find_win_by_nr_or_id(&di->di_tv);
4567 if (*win == NULL)
4568 {
4569 emsg(_(e_invalwindow));
4570 return FAIL;
4571 }
4572 }
4573
4574 return OK;
4575}
4576#endif
4577
4578/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004579 * "clearmatches()" function
4580 */
4581 void
4582f_clearmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4583{
4584#ifdef FEAT_SEARCH_EXTRA
4585 win_T *win = get_optional_window(argvars, 0);
4586
4587 if (win != NULL)
4588 clear_matches(win);
4589#endif
4590}
4591
4592/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004593 * "getmatches()" function
4594 */
4595 void
4596f_getmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4597{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004598# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004599 dict_T *dict;
4600 matchitem_T *cur;
4601 int i;
4602 win_T *win = get_optional_window(argvars, 0);
4603
4604 if (rettv_list_alloc(rettv) == FAIL || win == NULL)
4605 return;
4606
4607 cur = win->w_match_head;
4608 while (cur != NULL)
4609 {
4610 dict = dict_alloc();
4611 if (dict == NULL)
4612 return;
4613 if (cur->match.regprog == NULL)
4614 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004615 // match added with matchaddpos()
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004616 for (i = 0; i < MAXPOSMATCH; ++i)
4617 {
4618 llpos_T *llpos;
4619 char buf[30]; // use 30 to avoid compiler warning
4620 list_T *l;
4621
4622 llpos = &cur->pos.pos[i];
4623 if (llpos->lnum == 0)
4624 break;
4625 l = list_alloc();
4626 if (l == NULL)
4627 break;
4628 list_append_number(l, (varnumber_T)llpos->lnum);
4629 if (llpos->col > 0)
4630 {
4631 list_append_number(l, (varnumber_T)llpos->col);
4632 list_append_number(l, (varnumber_T)llpos->len);
4633 }
4634 sprintf(buf, "pos%d", i + 1);
4635 dict_add_list(dict, buf, l);
4636 }
4637 }
4638 else
4639 {
4640 dict_add_string(dict, "pattern", cur->pattern);
4641 }
4642 dict_add_string(dict, "group", syn_id2name(cur->hlg_id));
4643 dict_add_number(dict, "priority", (long)cur->priority);
4644 dict_add_number(dict, "id", (long)cur->id);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004645# if defined(FEAT_CONCEAL)
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004646 if (cur->conceal_char)
4647 {
4648 char_u buf[MB_MAXBYTES + 1];
4649
4650 buf[(*mb_char2bytes)((int)cur->conceal_char, buf)] = NUL;
4651 dict_add_string(dict, "conceal", (char_u *)&buf);
4652 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004653# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004654 list_append_dict(rettv->vval.v_list, dict);
4655 cur = cur->next;
4656 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004657# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004658}
4659
4660/*
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004661 * "setmatches()" function
4662 */
4663 void
4664f_setmatches(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4665{
4666#ifdef FEAT_SEARCH_EXTRA
4667 list_T *l;
4668 listitem_T *li;
4669 dict_T *d;
4670 list_T *s = NULL;
4671 win_T *win = get_optional_window(argvars, 1);
4672
4673 rettv->vval.v_number = -1;
4674 if (argvars[0].v_type != VAR_LIST)
4675 {
4676 emsg(_(e_listreq));
4677 return;
4678 }
4679 if (win == NULL)
4680 return;
4681
4682 if ((l = argvars[0].vval.v_list) != NULL)
4683 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004684 // To some extent make sure that we are dealing with a list from
4685 // "getmatches()".
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004686 li = l->lv_first;
4687 while (li != NULL)
4688 {
4689 if (li->li_tv.v_type != VAR_DICT
4690 || (d = li->li_tv.vval.v_dict) == NULL)
4691 {
4692 emsg(_(e_invarg));
4693 return;
4694 }
4695 if (!(dict_find(d, (char_u *)"group", -1) != NULL
4696 && (dict_find(d, (char_u *)"pattern", -1) != NULL
4697 || dict_find(d, (char_u *)"pos1", -1) != NULL)
4698 && dict_find(d, (char_u *)"priority", -1) != NULL
4699 && dict_find(d, (char_u *)"id", -1) != NULL))
4700 {
4701 emsg(_(e_invarg));
4702 return;
4703 }
4704 li = li->li_next;
4705 }
4706
4707 clear_matches(win);
4708 li = l->lv_first;
4709 while (li != NULL)
4710 {
4711 int i = 0;
4712 char buf[30]; // use 30 to avoid compiler warning
4713 dictitem_T *di;
4714 char_u *group;
4715 int priority;
4716 int id;
4717 char_u *conceal;
4718
4719 d = li->li_tv.vval.v_dict;
4720 if (dict_find(d, (char_u *)"pattern", -1) == NULL)
4721 {
4722 if (s == NULL)
4723 {
4724 s = list_alloc();
4725 if (s == NULL)
4726 return;
4727 }
4728
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004729 // match from matchaddpos()
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004730 for (i = 1; i < 9; i++)
4731 {
4732 sprintf((char *)buf, (char *)"pos%d", i);
4733 if ((di = dict_find(d, (char_u *)buf, -1)) != NULL)
4734 {
4735 if (di->di_tv.v_type != VAR_LIST)
4736 return;
4737
4738 list_append_tv(s, &di->di_tv);
4739 s->lv_refcount++;
4740 }
4741 else
4742 break;
4743 }
4744 }
4745
4746 group = dict_get_string(d, (char_u *)"group", TRUE);
4747 priority = (int)dict_get_number(d, (char_u *)"priority");
4748 id = (int)dict_get_number(d, (char_u *)"id");
4749 conceal = dict_find(d, (char_u *)"conceal", -1) != NULL
4750 ? dict_get_string(d, (char_u *)"conceal", TRUE)
4751 : NULL;
4752 if (i == 0)
4753 {
4754 match_add(win, group,
4755 dict_get_string(d, (char_u *)"pattern", FALSE),
4756 priority, id, NULL, conceal);
4757 }
4758 else
4759 {
4760 match_add(win, group, NULL, priority, id, s, conceal);
4761 list_unref(s);
4762 s = NULL;
4763 }
4764 vim_free(group);
4765 vim_free(conceal);
4766
4767 li = li->li_next;
4768 }
4769 rettv->vval.v_number = 0;
4770 }
4771#endif
4772}
4773
4774/*
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004775 * "matchadd()" function
4776 */
4777 void
4778f_matchadd(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4779{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004780# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004781 char_u buf[NUMBUFLEN];
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004782 char_u *grp = tv_get_string_buf_chk(&argvars[0], buf); // group
4783 char_u *pat = tv_get_string_buf_chk(&argvars[1], buf); // pattern
4784 int prio = 10; // default priority
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004785 int id = -1;
4786 int error = FALSE;
4787 char_u *conceal_char = NULL;
4788 win_T *win = curwin;
4789
4790 rettv->vval.v_number = -1;
4791
4792 if (grp == NULL || pat == NULL)
4793 return;
4794 if (argvars[2].v_type != VAR_UNKNOWN)
4795 {
4796 prio = (int)tv_get_number_chk(&argvars[2], &error);
4797 if (argvars[3].v_type != VAR_UNKNOWN)
4798 {
4799 id = (int)tv_get_number_chk(&argvars[3], &error);
4800 if (argvars[4].v_type != VAR_UNKNOWN
4801 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4802 return;
4803 }
4804 }
4805 if (error == TRUE)
4806 return;
4807 if (id >= 1 && id <= 3)
4808 {
4809 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4810 return;
4811 }
4812
4813 rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL,
4814 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004815# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004816}
4817
4818/*
4819 * "matchaddpos()" function
4820 */
4821 void
4822f_matchaddpos(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4823{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004824# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004825 char_u buf[NUMBUFLEN];
4826 char_u *group;
4827 int prio = 10;
4828 int id = -1;
4829 int error = FALSE;
4830 list_T *l;
4831 char_u *conceal_char = NULL;
4832 win_T *win = curwin;
4833
4834 rettv->vval.v_number = -1;
4835
4836 group = tv_get_string_buf_chk(&argvars[0], buf);
4837 if (group == NULL)
4838 return;
4839
4840 if (argvars[1].v_type != VAR_LIST)
4841 {
4842 semsg(_(e_listarg), "matchaddpos()");
4843 return;
4844 }
4845 l = argvars[1].vval.v_list;
4846 if (l == NULL)
4847 return;
4848
4849 if (argvars[2].v_type != VAR_UNKNOWN)
4850 {
4851 prio = (int)tv_get_number_chk(&argvars[2], &error);
4852 if (argvars[3].v_type != VAR_UNKNOWN)
4853 {
4854 id = (int)tv_get_number_chk(&argvars[3], &error);
4855
4856 if (argvars[4].v_type != VAR_UNKNOWN
4857 && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL)
4858 return;
4859 }
4860 }
4861 if (error == TRUE)
4862 return;
4863
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004864 // id == 3 is ok because matchaddpos() is supposed to substitute :3match
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004865 if (id == 1 || id == 2)
4866 {
4867 semsg(_("E798: ID is reserved for \":match\": %d"), id);
4868 return;
4869 }
4870
4871 rettv->vval.v_number = match_add(win, group, NULL, prio, id, l,
4872 conceal_char);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004873# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004874}
4875
4876/*
4877 * "matcharg()" function
4878 */
4879 void
4880f_matcharg(typval_T *argvars UNUSED, typval_T *rettv)
4881{
4882 if (rettv_list_alloc(rettv) == OK)
4883 {
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004884# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004885 int id = (int)tv_get_number(&argvars[0]);
4886 matchitem_T *m;
4887
4888 if (id >= 1 && id <= 3)
4889 {
4890 if ((m = (matchitem_T *)get_match(curwin, id)) != NULL)
4891 {
4892 list_append_string(rettv->vval.v_list,
4893 syn_id2name(m->hlg_id), -1);
4894 list_append_string(rettv->vval.v_list, m->pattern, -1);
4895 }
4896 else
4897 {
4898 list_append_string(rettv->vval.v_list, NULL, -1);
4899 list_append_string(rettv->vval.v_list, NULL, -1);
4900 }
4901 }
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004902# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004903 }
4904}
4905
4906/*
4907 * "matchdelete()" function
4908 */
4909 void
4910f_matchdelete(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
4911{
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004912# ifdef FEAT_SEARCH_EXTRA
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004913 win_T *win = get_optional_window(argvars, 1);
4914
4915 if (win == NULL)
4916 rettv->vval.v_number = -1;
4917 else
4918 rettv->vval.v_number = match_delete(win,
4919 (int)tv_get_number(&argvars[0]), TRUE);
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004920# endif
Bram Moolenaar29b7d7a2019-07-22 23:03:57 +02004921}
Bram Moolenaarc61a48d2019-07-22 23:16:33 +02004922#endif
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004923
4924#if defined(FEAT_SEARCH_EXTRA) || defined(PROTO)
4925/*
4926 * ":[N]match {group} {pattern}"
4927 * Sets nextcmd to the start of the next command, if any. Also called when
4928 * skipping commands to find the next command.
4929 */
4930 void
4931ex_match(exarg_T *eap)
4932{
4933 char_u *p;
4934 char_u *g = NULL;
4935 char_u *end;
4936 int c;
4937 int id;
4938
4939 if (eap->line2 <= 3)
4940 id = eap->line2;
4941 else
4942 {
4943 emsg(_(e_invcmd));
4944 return;
4945 }
4946
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004947 // First clear any old pattern.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004948 if (!eap->skip)
4949 match_delete(curwin, id, FALSE);
4950
4951 if (ends_excmd(*eap->arg))
4952 end = eap->arg;
4953 else if ((STRNICMP(eap->arg, "none", 4) == 0
4954 && (VIM_ISWHITE(eap->arg[4]) || ends_excmd(eap->arg[4]))))
4955 end = eap->arg + 4;
4956 else
4957 {
4958 p = skiptowhite(eap->arg);
4959 if (!eap->skip)
4960 g = vim_strnsave(eap->arg, (int)(p - eap->arg));
4961 p = skipwhite(p);
4962 if (*p == NUL)
4963 {
Bram Moolenaar1f164b12019-07-24 19:00:36 +02004964 // There must be two arguments.
Bram Moolenaar7dfb0162019-07-24 16:00:39 +02004965 vim_free(g);
4966 semsg(_(e_invarg2), eap->arg);
4967 return;
4968 }
4969 end = skip_regexp(p + 1, *p, TRUE, NULL);
4970 if (!eap->skip)
4971 {
4972 if (*end != NUL && !ends_excmd(*skipwhite(end + 1)))
4973 {
4974 vim_free(g);
4975 eap->errmsg = e_trailing;
4976 return;
4977 }
4978 if (*end != *p)
4979 {
4980 vim_free(g);
4981 semsg(_(e_invarg2), p);
4982 return;
4983 }
4984
4985 c = *end;
4986 *end = NUL;
4987 match_add(curwin, g, p + 1, 10, id, NULL, NULL);
4988 vim_free(g);
4989 *end = c;
4990 }
4991 }
4992 eap->nextcmd = find_nextcmd(end);
4993}
4994#endif