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