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