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