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