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