hlstate_spec.lua (19891B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 local tt = require('test.functional.testterm') 5 6 local clear, insert = n.clear, n.insert 7 local command = n.command 8 local api = n.api 9 local testprg = n.testprg 10 local skip = t.skip 11 local is_os = t.is_os 12 13 describe('ext_hlstate detailed highlights', function() 14 local screen 15 16 before_each(function() 17 clear() 18 command('syntax on') 19 command('hi VertSplit gui=reverse') 20 screen = Screen.new(40, 8, { ext_hlstate = true }) 21 end) 22 23 it('work with combined UI and syntax highlights', function() 24 insert([[ 25 these are some lines 26 with colorful text]]) 27 api.nvim_buf_add_highlight(0, -1, 'String', 0, 10, 14) 28 api.nvim_buf_add_highlight(0, -1, 'Statement', 1, 5, -1) 29 command('/th co') 30 31 screen:expect { 32 grid = [[ 33 these are {1:some} lines | 34 ^wi{2:th }{4:co}{3:lorful text} | 35 {5:~ }|*5 36 {8:search hit BOTTOM, continuing at TOP}{6: }| 37 ]], 38 attr_ids = { 39 [1] = { 40 { foreground = Screen.colors.Magenta1 }, 41 { { kind = 'syntax', hi_name = 'Constant' } }, 42 }, 43 [2] = { 44 { background = Screen.colors.Yellow1 }, 45 { { kind = 'ui', ui_name = 'Search', hi_name = 'Search' } }, 46 }, 47 [3] = { 48 { foreground = Screen.colors.Brown, bold = true }, 49 { { kind = 'syntax', hi_name = 'Statement' } }, 50 }, 51 [4] = { 52 { background = Screen.colors.Yellow1, bold = true, foreground = Screen.colors.Brown }, 53 { 3, 2 }, 54 }, 55 [5] = { 56 { foreground = Screen.colors.Blue, bold = true }, 57 { { kind = 'ui', ui_name = 'EndOfBuffer', hi_name = 'NonText' } }, 58 }, 59 [6] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } }, 60 [7] = { 61 { foreground = Screen.colors.Red1 }, 62 { { kind = 'syntax', hi_name = 'WarningMsg' } }, 63 }, 64 [8] = { { foreground = Screen.colors.Red1 }, { 6, 7 } }, 65 }, 66 } 67 end) 68 69 it('work with cleared UI highlights', function() 70 screen:set_default_attr_ids({ 71 [1] = { {}, { { hi_name = 'Normal', ui_name = 'WinSeparator', kind = 'ui' } } }, 72 [2] = { 73 { bold = true, foreground = Screen.colors.Blue1 }, 74 { { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } }, 75 }, 76 [3] = { 77 { bold = true, reverse = true }, 78 { { hi_name = 'StatusLine', ui_name = 'StatusLine', kind = 'ui' } }, 79 }, 80 [4] = { 81 { reverse = true }, 82 { { hi_name = 'StatusLineNC', ui_name = 'StatusLineNC', kind = 'ui' } }, 83 }, 84 [5] = { {}, { { hi_name = 'StatusLine', ui_name = 'StatusLine', kind = 'ui' } } }, 85 [6] = { {}, { { hi_name = 'StatusLineNC', ui_name = 'StatusLineNC', kind = 'ui' } } }, 86 [7] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } }, 87 }) 88 command('hi clear WinSeparator') 89 command('vsplit') 90 91 screen:expect([[ 92 ^ {1:│} | 93 {2:~ }{1:│}{2:~ }|*5 94 {3:[No Name] }{4:[No Name] }| 95 {7: }| 96 ]]) 97 98 command('hi clear StatusLine | hi clear StatuslineNC') 99 screen:expect([[ 100 ^ {1:│} | 101 {2:~ }{1:│}{2:~ }|*5 102 {5:[No Name] }{6:[No Name] }| 103 {7: }| 104 ]]) 105 106 -- redrawing is done even if visible highlights didn't change 107 command('wincmd w') 108 screen:expect([[ 109 {1:│}^ | 110 {2:~ }{1:│}{2:~ }|*5 111 {6:[No Name] }{5:[No Name] }| 112 {7: }| 113 ]]) 114 end) 115 116 it('work with window-local highlights', function() 117 screen:set_default_attr_ids({ 118 [1] = { 119 { foreground = Screen.colors.Brown }, 120 { { hi_name = 'LineNr', ui_name = 'LineNr', kind = 'ui' } }, 121 }, 122 [2] = { 123 { bold = true, foreground = Screen.colors.Blue1 }, 124 { { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } }, 125 }, 126 [3] = { 127 { bold = true, reverse = true }, 128 { { hi_name = 'StatusLine', ui_name = 'StatusLine', kind = 'ui' } }, 129 }, 130 [4] = { 131 { reverse = true }, 132 { { hi_name = 'StatusLineNC', ui_name = 'StatusLineNC', kind = 'ui' } }, 133 }, 134 [5] = { 135 { background = Screen.colors.Red, foreground = Screen.colors.Grey100 }, 136 { { hi_name = 'ErrorMsg', ui_name = 'LineNr', kind = 'ui' } }, 137 }, 138 [6] = { 139 { bold = true, reverse = true }, 140 { { hi_name = 'Normal', ui_name = 'Normal', kind = 'ui' } }, 141 }, 142 [7] = { { foreground = Screen.colors.Brown, bold = true, reverse = true }, { 6, 1 } }, 143 [8] = { { foreground = Screen.colors.Blue1, bold = true, reverse = true }, { 6, 14 } }, 144 [9] = { 145 { bold = true, foreground = Screen.colors.Brown }, 146 { { hi_name = 'NormalNC', ui_name = 'NormalNC', kind = 'ui' } }, 147 }, 148 [10] = { { bold = true, foreground = Screen.colors.Brown }, { 9, 1 } }, 149 [11] = { { bold = true, foreground = Screen.colors.Blue1 }, { 9, 14 } }, 150 [12] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } }, 151 [13] = { 152 { background = Screen.colors.Red1, foreground = Screen.colors.Gray100 }, 153 { { ui_name = 'LineNr', kind = 'ui', hi_name = 'LineNr' } }, 154 }, 155 [14] = { 156 { bold = true, foreground = Screen.colors.Blue }, 157 { { ui_name = 'EndOfBuffer', kind = 'ui', hi_name = 'EndOfBuffer' } }, 158 }, 159 }) 160 161 command('set number') 162 command('split') 163 -- NormalNC is not applied if not set, to avoid spurious redraws 164 screen:expect([[ 165 {1: 1 }^ | 166 {2:~ }|*2 167 {3:[No Name] }| 168 {1: 1 } | 169 {2:~ }| 170 {4:[No Name] }| 171 {12: }| 172 ]]) 173 174 command('set winhl=LineNr:ErrorMsg') 175 screen:expect { 176 grid = [[ 177 {13: 1 }^ | 178 {14:~ }|*2 179 {3:[No Name] }| 180 {1: 1 } | 181 {2:~ }| 182 {4:[No Name] }| 183 {12: }| 184 ]], 185 } 186 187 command('set winhl=Normal:MsgSeparator,NormalNC:Statement') 188 screen:expect([[ 189 {7: 1 }{6:^ }| 190 {8:~ }|*2 191 {3:[No Name] }| 192 {1: 1 } | 193 {2:~ }| 194 {4:[No Name] }| 195 {12: }| 196 ]]) 197 198 command('wincmd w') 199 screen:expect([[ 200 {10: 1 }{9: }| 201 {11:~ }|*2 202 {4:[No Name] }| 203 {1: 1 }^ | 204 {2:~ }| 205 {3:[No Name] }| 206 {12: }| 207 ]]) 208 end) 209 210 it('work with :terminal', function() 211 skip(is_os('win')) 212 213 screen:set_default_attr_ids({ 214 [1] = { {}, { { hi_name = 'TermCursorNC', ui_name = 'TermCursorNC', kind = 'ui' } } }, 215 [2] = { { foreground = tonumber('0x00ccff'), fg_indexed = true }, { { kind = 'term' } } }, 216 [3] = { 217 { bold = true, foreground = tonumber('0x00ccff'), fg_indexed = true }, 218 { 219 { kind = 'term' }, 220 }, 221 }, 222 [4] = { { foreground = tonumber('0x00ccff'), fg_indexed = true }, { 2, 1 } }, 223 [5] = { { foreground = tonumber('0x40ffff'), fg_indexed = true }, { { kind = 'term' } } }, 224 [6] = { { foreground = tonumber('0x40ffff'), fg_indexed = true }, { 5, 1 } }, 225 [7] = { {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } }, 226 }) 227 command(("enew | call jobstart(['%s'],{'term':v:true})"):format(testprg('tty-test'))) 228 screen:expect([[ 229 ^tty ready | 230 |*6 231 {7: }| 232 ]]) 233 234 tt.feed_data('x ') 235 tt.set_fg(45) 236 tt.feed_data('y ') 237 tt.set_bold() 238 tt.feed_data('z\n') 239 -- TODO(bfredl): check if this distinction makes sense 240 if is_os('win') then 241 screen:expect([[ 242 ^tty ready | 243 x {5:y z} | 244 |*5 245 {7: }| 246 ]]) 247 else 248 screen:expect([[ 249 ^tty ready | 250 x {2:y }{3:z} | 251 |*5 252 {7: }| 253 ]]) 254 end 255 256 tt.feed_termcode('[A') 257 tt.feed_termcode('[2C') 258 if is_os('win') then 259 screen:expect([[ 260 ^tty ready | 261 x {6:y}{5: z} | 262 |*5 263 {7: }| 264 ]]) 265 else 266 screen:expect([[ 267 ^tty ready | 268 x {2:y }{3:z} | 269 |*5 270 {7: }| 271 ]]) 272 end 273 end) 274 275 it('can use independent cterm and rgb colors', function() 276 -- tell test module to save all attributes (doesn't change nvim options) 277 screen:set_rgb_cterm(true) 278 279 screen:set_default_attr_ids({ 280 [1] = { 281 { bold = true, foreground = Screen.colors.Blue1 }, 282 { foreground = 12 }, 283 { { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } }, 284 }, 285 [2] = { 286 { reverse = true, foreground = Screen.colors.Red }, 287 { foreground = 10, italic = true }, 288 { { hi_name = 'NonText', ui_name = 'EndOfBuffer', kind = 'ui' } }, 289 }, 290 [3] = { {}, {}, { { hi_name = 'MsgArea', ui_name = 'MsgArea', kind = 'ui' } } }, 291 }) 292 screen:expect([[ 293 ^ | 294 {1:~ }|*6 295 {3: }| 296 ]]) 297 298 command('hi NonText guifg=Red gui=reverse ctermfg=Green cterm=italic') 299 screen:expect([[ 300 ^ | 301 {2:~ }|*6 302 {3: }| 303 ]]) 304 end) 305 306 it('combines deleted extmark highlights', function() 307 insert([[ 308 line1 309 line2 310 line3 311 line4 312 line5 313 line6]]) 314 315 screen:expect { 316 grid = [[ 317 line1 | 318 line2 | 319 line3 | 320 line4 | 321 line5 | 322 line^6 | 323 {1:~ }| 324 {2: }| 325 ]], 326 attr_ids = { 327 [1] = { 328 { foreground = Screen.colors.Blue, bold = true }, 329 { { ui_name = 'EndOfBuffer', hi_name = 'NonText', kind = 'ui' } }, 330 }, 331 [2] = { {}, { { ui_name = 'MsgArea', hi_name = 'MsgArea', kind = 'ui' } } }, 332 }, 333 } 334 335 local ns = api.nvim_create_namespace('test') 336 337 local add_indicator = function(line, col) 338 api.nvim_buf_set_extmark(0, ns, line, col, { 339 hl_mode = 'combine', 340 priority = 2, 341 right_gravity = false, 342 virt_text = { { '|', 'Delimiter' } }, 343 virt_text_win_col = 0, 344 virt_text_pos = 'overlay', 345 }) 346 end 347 348 add_indicator(1, 0) 349 add_indicator(2, 0) 350 add_indicator(3, 0) 351 add_indicator(4, 0) 352 353 screen:expect { 354 grid = [[ 355 line1 | 356 {1:|} line2 | 357 {1:|} line3 | 358 {1:|} line4 | 359 {1:|} line5 | 360 line^6 | 361 {2:~ }| 362 {3: }| 363 ]], 364 attr_ids = { 365 [1] = { 366 { foreground = Screen.colors.SlateBlue }, 367 { { hi_name = 'Special', kind = 'syntax' } }, 368 }, 369 [2] = { 370 { bold = true, foreground = Screen.colors.Blue }, 371 { { ui_name = 'EndOfBuffer', kind = 'ui', hi_name = 'NonText' } }, 372 }, 373 [3] = { {}, { { ui_name = 'MsgArea', kind = 'ui', hi_name = 'MsgArea' } } }, 374 }, 375 } 376 377 n.feed('3ggV2jd') 378 --screen:redraw_debug() 379 screen:expect { 380 grid = [[ 381 line1 | 382 {1:|} line2 | 383 {2:^|}ine6 | 384 {3:~ }|*4 385 {4:3 fewer lines }| 386 ]], 387 attr_ids = { 388 [1] = { 389 { foreground = Screen.colors.SlateBlue }, 390 { { kind = 'syntax', hi_name = 'Special' } }, 391 }, 392 [2] = { { foreground = Screen.colors.SlateBlue }, { 1, 1, 1 } }, 393 [3] = { 394 { bold = true, foreground = Screen.colors.Blue }, 395 { { kind = 'ui', ui_name = 'EndOfBuffer', hi_name = 'NonText' } }, 396 }, 397 [4] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } }, 398 }, 399 } 400 end) 401 402 it('removes deleted extmark highlights with invalidate', function() 403 insert([[ 404 line1 405 line2 406 line3 407 line4 408 line5 409 line6]]) 410 411 screen:expect { 412 grid = [[ 413 line1 | 414 line2 | 415 line3 | 416 line4 | 417 line5 | 418 line^6 | 419 {1:~ }| 420 {2: }| 421 ]], 422 attr_ids = { 423 [1] = { 424 { foreground = Screen.colors.Blue, bold = true }, 425 { { ui_name = 'EndOfBuffer', hi_name = 'NonText', kind = 'ui' } }, 426 }, 427 [2] = { {}, { { ui_name = 'MsgArea', hi_name = 'MsgArea', kind = 'ui' } } }, 428 }, 429 } 430 431 local ns = api.nvim_create_namespace('test') 432 433 local add_indicator = function(line, col) 434 api.nvim_buf_set_extmark(0, ns, line, col, { 435 hl_mode = 'combine', 436 priority = 2, 437 right_gravity = false, 438 virt_text = { { '|', 'Delimiter' } }, 439 virt_text_win_col = 0, 440 virt_text_pos = 'overlay', 441 invalidate = true, 442 }) 443 end 444 445 add_indicator(1, 0) 446 add_indicator(2, 0) 447 add_indicator(3, 0) 448 add_indicator(4, 0) 449 450 screen:expect { 451 grid = [[ 452 line1 | 453 {1:|} line2 | 454 {1:|} line3 | 455 {1:|} line4 | 456 {1:|} line5 | 457 line^6 | 458 {2:~ }| 459 {3: }| 460 ]], 461 attr_ids = { 462 [1] = { 463 { foreground = Screen.colors.SlateBlue }, 464 { { hi_name = 'Special', kind = 'syntax' } }, 465 }, 466 [2] = { 467 { bold = true, foreground = Screen.colors.Blue }, 468 { { ui_name = 'EndOfBuffer', kind = 'ui', hi_name = 'NonText' } }, 469 }, 470 [3] = { {}, { { ui_name = 'MsgArea', kind = 'ui', hi_name = 'MsgArea' } } }, 471 }, 472 } 473 474 n.feed('3ggV2jd') 475 --screen:redraw_debug() 476 screen:expect { 477 grid = [[ 478 line1 | 479 {1:|} line2 | 480 ^line6 | 481 {2:~ }|*4 482 {3:3 fewer lines }| 483 ]], 484 attr_ids = { 485 [1] = { 486 { foreground = Screen.colors.SlateBlue }, 487 { { kind = 'syntax', hi_name = 'Special' } }, 488 }, 489 [2] = { 490 { foreground = Screen.colors.Blue, bold = true }, 491 { { kind = 'ui', ui_name = 'EndOfBuffer', hi_name = 'NonText' } }, 492 }, 493 [3] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } }, 494 }, 495 } 496 end) 497 498 it('does not hang when combining too many highlights', function() 499 local num_lines = 500 500 insert('first line\n') 501 for _ = 1, num_lines do 502 api.nvim_paste(' line\n', false, -1) 503 end 504 insert('last line') 505 506 n.feed('gg') 507 screen:expect { 508 grid = [[ 509 ^first line | 510 line |*6 511 {1: }| 512 ]], 513 attr_ids = { 514 [1] = { {}, { { kind = 'ui', hi_name = 'MsgArea', ui_name = 'MsgArea' } } }, 515 }, 516 } 517 local ns = api.nvim_create_namespace('test') 518 519 local add_indicator = function(line, col) 520 api.nvim_buf_set_extmark(0, ns, line, col, { 521 hl_mode = 'combine', 522 priority = 2, 523 right_gravity = false, 524 virt_text = { { '|', 'Delimiter' } }, 525 virt_text_win_col = 0, 526 virt_text_pos = 'overlay', 527 }) 528 end 529 530 for i = 1, num_lines do 531 add_indicator(i, 0) 532 end 533 534 screen:expect { 535 grid = [[ 536 ^first line | 537 {1:|} line |*6 538 {2: }| 539 ]], 540 attr_ids = { 541 [1] = { 542 { foreground = Screen.colors.SlateBlue }, 543 { { kind = 'syntax', hi_name = 'Special' } }, 544 }, 545 [2] = { {}, { { kind = 'ui', ui_name = 'MsgArea', hi_name = 'MsgArea' } } }, 546 }, 547 } 548 549 n.feed(string.format('3ggV%ijd', num_lines - 2)) 550 --screen:redraw_debug(nil, nil, 100000) 551 552 local expected_ids = {} 553 for i = 1, num_lines - 1 do 554 expected_ids[i] = 1 555 end 556 screen:expect { 557 grid = string.format( 558 [[ 559 first line | 560 {1:|} line | 561 {2:^|}ast line | 562 {3:~ }|*4 563 {4:%-40s}| 564 ]], 565 tostring(num_lines - 1) .. ' fewer lines' 566 ), 567 attr_ids = { 568 [1] = { 569 { foreground = Screen.colors.SlateBlue }, 570 { { kind = 'syntax', hi_name = 'Special' } }, 571 }, 572 [2] = { { foreground = Screen.colors.SlateBlue }, expected_ids }, 573 [3] = { 574 { foreground = Screen.colors.Blue, bold = true }, 575 { { kind = 'ui', hi_name = 'NonText', ui_name = 'EndOfBuffer' } }, 576 }, 577 [4] = { {}, { { kind = 'ui', hi_name = 'MsgArea', ui_name = 'MsgArea' } } }, 578 }, 579 timeout = 100000, 580 } 581 end) 582 end)