statusline_spec.lua (52650B)
1 local t = require('test.testutil') 2 local n = require('test.functional.testnvim')() 3 local Screen = require('test.functional.ui.screen') 4 5 local assert_alive = n.assert_alive 6 local clear = n.clear 7 local command = n.command 8 local feed = n.feed 9 local eq = t.eq 10 local fn = n.fn 11 local api = n.api 12 local exec = n.exec 13 local exec_lua = n.exec_lua 14 local eval = n.eval 15 local sleep = vim.uv.sleep 16 local pcall_err = t.pcall_err 17 18 local mousemodels = { 'extend', 'popup', 'popup_setpos' } 19 20 for _, model in ipairs(mousemodels) do 21 describe('statusline clicks with mousemodel=' .. model, function() 22 local screen 23 24 before_each(function() 25 clear() 26 screen = Screen.new(40, 8) 27 screen:add_extra_attr_ids { 28 [100] = { bold = true, reverse = true, foreground = Screen.colors.Blue }, 29 } 30 command('set laststatus=2 mousemodel=' .. model) 31 exec([=[ 32 function! MyClickFunc(minwid, clicks, button, mods) 33 let g:testvar = printf("%d %d %s", a:minwid, a:clicks, a:button) 34 if a:mods !=# ' ' 35 let g:testvar ..= '(' .. a:mods .. ')' 36 endif 37 endfunction 38 let g:testvar = '' 39 ]=]) 40 end) 41 42 it('works', function() 43 api.nvim_set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) 44 api.nvim_input_mouse('left', 'press', '', 0, 6, 16) 45 eq('', eval('g:testvar')) 46 api.nvim_input_mouse('right', 'press', '', 0, 6, 29) 47 eq('', eval('g:testvar')) 48 api.nvim_input_mouse('left', 'press', '', 0, 6, 17) 49 eq('0 1 l', eval('g:testvar')) 50 api.nvim_input_mouse('left', 'press', '', 0, 6, 17) 51 eq('0 2 l', eval('g:testvar')) 52 api.nvim_input_mouse('left', 'press', '', 0, 6, 17) 53 eq('0 3 l', eval('g:testvar')) 54 api.nvim_input_mouse('left', 'press', '', 0, 6, 17) 55 eq('0 4 l', eval('g:testvar')) 56 api.nvim_input_mouse('right', 'press', '', 0, 6, 28) 57 eq('0 1 r', eval('g:testvar')) 58 api.nvim_input_mouse('right', 'press', '', 0, 6, 28) 59 eq('0 2 r', eval('g:testvar')) 60 api.nvim_input_mouse('right', 'press', '', 0, 6, 28) 61 eq('0 3 r', eval('g:testvar')) 62 api.nvim_input_mouse('right', 'press', '', 0, 6, 28) 63 eq('0 4 r', eval('g:testvar')) 64 api.nvim_input_mouse('x1', 'press', '', 0, 6, 17) 65 eq('0 1 x1', eval('g:testvar')) 66 api.nvim_input_mouse('x1', 'press', '', 0, 6, 17) 67 eq('0 2 x1', eval('g:testvar')) 68 api.nvim_input_mouse('x1', 'press', '', 0, 6, 17) 69 eq('0 3 x1', eval('g:testvar')) 70 api.nvim_input_mouse('x1', 'press', '', 0, 6, 17) 71 eq('0 4 x1', eval('g:testvar')) 72 api.nvim_input_mouse('x2', 'press', '', 0, 6, 28) 73 eq('0 1 x2', eval('g:testvar')) 74 api.nvim_input_mouse('x2', 'press', '', 0, 6, 28) 75 eq('0 2 x2', eval('g:testvar')) 76 api.nvim_input_mouse('x2', 'press', '', 0, 6, 28) 77 eq('0 3 x2', eval('g:testvar')) 78 api.nvim_input_mouse('x2', 'press', '', 0, 6, 28) 79 eq('0 4 x2', eval('g:testvar')) 80 end) 81 82 it('works with control characters and highlight', function() 83 api.nvim_set_option_value('statusline', '\t%#NonText#\1%0@MyClickFunc@\t\1%T\t%##\1', {}) 84 screen:expect { 85 grid = [[ 86 ^ | 87 {1:~ }|*5 88 {3:^I}{100:^A^I^A^I}{3:^A }| 89 | 90 ]], 91 } 92 api.nvim_input_mouse('right', 'press', '', 0, 6, 3) 93 eq('', eval('g:testvar')) 94 api.nvim_input_mouse('left', 'press', '', 0, 6, 8) 95 eq('', eval('g:testvar')) 96 api.nvim_input_mouse('right', 'press', '', 0, 6, 4) 97 eq('0 1 r', eval('g:testvar')) 98 api.nvim_input_mouse('left', 'press', '', 0, 6, 7) 99 eq('0 1 l', eval('g:testvar')) 100 end) 101 102 it('works with combined highlight attributes', function() 103 screen:add_extra_attr_ids({ 104 [131] = { reverse = true, bold = true, background = Screen.colors.LightMagenta }, 105 [132] = { 106 reverse = true, 107 foreground = Screen.colors.Magenta, 108 bold = true, 109 background = Screen.colors.LightMagenta, 110 }, 111 [133] = { reverse = true, bold = true, foreground = Screen.colors.Magenta1 }, 112 [134] = { 113 bold = true, 114 background = Screen.colors.LightMagenta, 115 reverse = true, 116 undercurl = true, 117 special = Screen.colors.Red, 118 }, 119 [135] = { 120 bold = true, 121 background = Screen.colors.LightMagenta, 122 reverse = true, 123 undercurl = true, 124 foreground = Screen.colors.Fuchsia, 125 special = Screen.colors.Red, 126 }, 127 }) 128 129 api.nvim_set_option_value( 130 'statusline', 131 '\t%#Pmenu#foo%$SpellBad$bar%$String$baz%#Constant#qux', 132 {} 133 ) 134 135 screen:expect([[ 136 ^ | 137 {1:~ }|*5 138 {3:^I}{131:foo}{134:bar}{135:baz}{133:qux }| 139 | 140 ]]) 141 end) 142 143 it('works for winbar', function() 144 api.nvim_set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) 145 api.nvim_input_mouse('left', 'press', '', 0, 0, 17) 146 eq('0 1 l', eval('g:testvar')) 147 api.nvim_input_mouse('right', 'press', '', 0, 0, 17) 148 eq('0 1 r', eval('g:testvar')) 149 end) 150 151 it('works for winbar in floating window', function() 152 api.nvim_open_win( 153 0, 154 true, 155 { width = 30, height = 4, relative = 'editor', row = 1, col = 5, border = 'single' } 156 ) 157 api.nvim_set_option_value( 158 'winbar', 159 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', 160 { scope = 'local' } 161 ) 162 api.nvim_input_mouse('left', 'press', '', 0, 2, 23) 163 eq('0 1 l', eval('g:testvar')) 164 end) 165 166 it('works when there are multiple windows', function() 167 command('split') 168 api.nvim_set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) 169 api.nvim_set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) 170 api.nvim_input_mouse('left', 'press', '', 0, 0, 17) 171 eq('0 1 l', eval('g:testvar')) 172 api.nvim_input_mouse('right', 'press', '', 0, 4, 17) 173 eq('0 1 r', eval('g:testvar')) 174 api.nvim_input_mouse('middle', 'press', '', 0, 3, 17) 175 eq('0 1 m', eval('g:testvar')) 176 api.nvim_input_mouse('left', 'press', '', 0, 6, 17) 177 eq('0 1 l', eval('g:testvar')) 178 end) 179 180 it('works with Lua function', function() 181 exec_lua([[ 182 function clicky_func(minwid, clicks, button, mods) 183 vim.g.testvar = string.format("%d %d %s", minwid, clicks, button) 184 end 185 ]]) 186 api.nvim_set_option_value( 187 'statusline', 188 'Not clicky stuff %0@v:lua.clicky_func@Clicky stuff%T', 189 {} 190 ) 191 api.nvim_input_mouse('left', 'press', '', 0, 6, 17) 192 eq('0 1 l', eval('g:testvar')) 193 end) 194 195 it('ignores unsupported click items', function() 196 command('tabnew | tabprevious') 197 api.nvim_set_option_value('statusline', '%2TNot clicky stuff%T', {}) 198 api.nvim_input_mouse('left', 'press', '', 0, 6, 0) 199 eq(1, api.nvim_get_current_tabpage()) 200 api.nvim_set_option_value('statusline', '%2XNot clicky stuff%X', {}) 201 api.nvim_input_mouse('left', 'press', '', 0, 6, 0) 202 eq(2, #api.nvim_list_tabpages()) 203 end) 204 205 it("right click works when statusline isn't focused #18994", function() 206 api.nvim_set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) 207 api.nvim_input_mouse('right', 'press', '', 0, 6, 17) 208 eq('0 1 r', eval('g:testvar')) 209 api.nvim_input_mouse('right', 'press', '', 0, 6, 17) 210 eq('0 2 r', eval('g:testvar')) 211 end) 212 213 it('works with modifiers #18994', function() 214 api.nvim_set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {}) 215 -- Note: alternate between left and right mouse buttons to avoid triggering multiclicks 216 api.nvim_input_mouse('left', 'press', 'S', 0, 6, 17) 217 eq('0 1 l(s )', eval('g:testvar')) 218 api.nvim_input_mouse('right', 'press', 'S', 0, 6, 17) 219 eq('0 1 r(s )', eval('g:testvar')) 220 api.nvim_input_mouse('left', 'press', 'A', 0, 6, 17) 221 eq('0 1 l( a )', eval('g:testvar')) 222 api.nvim_input_mouse('right', 'press', 'A', 0, 6, 17) 223 eq('0 1 r( a )', eval('g:testvar')) 224 api.nvim_input_mouse('left', 'press', 'AS', 0, 6, 17) 225 eq('0 1 l(s a )', eval('g:testvar')) 226 api.nvim_input_mouse('right', 'press', 'AS', 0, 6, 17) 227 eq('0 1 r(s a )', eval('g:testvar')) 228 api.nvim_input_mouse('left', 'press', 'T', 0, 6, 17) 229 eq('0 1 l( m)', eval('g:testvar')) 230 api.nvim_input_mouse('right', 'press', 'T', 0, 6, 17) 231 eq('0 1 r( m)', eval('g:testvar')) 232 api.nvim_input_mouse('left', 'press', 'TS', 0, 6, 17) 233 eq('0 1 l(s m)', eval('g:testvar')) 234 api.nvim_input_mouse('right', 'press', 'TS', 0, 6, 17) 235 eq('0 1 r(s m)', eval('g:testvar')) 236 api.nvim_input_mouse('left', 'press', 'C', 0, 6, 17) 237 eq('0 1 l( c )', eval('g:testvar')) 238 -- <C-RightMouse> is for tag jump 239 end) 240 241 it('works for global statusline with vertical splits #19186', function() 242 command('set laststatus=3') 243 api.nvim_set_option_value( 244 'statusline', 245 '%0@MyClickFunc@Clicky stuff%T %= %0@MyClickFunc@Clicky stuff%T', 246 {} 247 ) 248 command('vsplit') 249 screen:expect { 250 grid = [[ 251 ^ │ | 252 {1:~ }│{1:~ }|*5 253 {3:Clicky stuff Clicky stuff}| 254 | 255 ]], 256 } 257 258 -- clickable area on the right 259 api.nvim_input_mouse('left', 'press', '', 0, 6, 35) 260 eq('0 1 l', eval('g:testvar')) 261 api.nvim_input_mouse('right', 'press', '', 0, 6, 35) 262 eq('0 1 r', eval('g:testvar')) 263 264 -- clickable area on the left 265 api.nvim_input_mouse('left', 'press', '', 0, 6, 5) 266 eq('0 1 l', eval('g:testvar')) 267 api.nvim_input_mouse('right', 'press', '', 0, 6, 5) 268 eq('0 1 r', eval('g:testvar')) 269 end) 270 271 it('no memory leak with zero-width click labels', function() 272 command([[ 273 let &stl = '%@Test@%T%@MyClickFunc@%=%T%@Test@' 274 ]]) 275 api.nvim_input_mouse('left', 'press', '', 0, 6, 0) 276 eq('0 1 l', eval('g:testvar')) 277 api.nvim_input_mouse('right', 'press', '', 0, 6, 39) 278 eq('0 1 r', eval('g:testvar')) 279 end) 280 281 it('no memory leak with truncated click labels', function() 282 command([[ 283 let &stl = '%@MyClickFunc@foo%X' .. repeat('a', 40) .. '%<t%@Test@bar%X%@Test@baz' 284 ]]) 285 api.nvim_input_mouse('left', 'press', '', 0, 6, 2) 286 eq('0 1 l', eval('g:testvar')) 287 end) 288 end) 289 end 290 291 describe('global statusline', function() 292 local screen 293 294 before_each(function() 295 clear() 296 screen = Screen.new(60, 16) 297 screen:add_extra_attr_ids { 298 [100] = { foreground = Screen.colors.Magenta1, bold = true }, 299 } 300 command('set laststatus=3') 301 command('set ruler') 302 end) 303 304 it('works', function() 305 screen:expect([[ 306 ^ | 307 {1:~ }|*13 308 {3:[No Name] 0,0-1 All}| 309 | 310 ]]) 311 312 feed('i<CR><CR>') 313 screen:expect([[ 314 |*2 315 ^ | 316 {1:~ }|*11 317 {3:[No Name] [+] 3,1 All}| 318 {5:-- INSERT --} | 319 ]]) 320 end) 321 322 it('works with splits', function() 323 command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split') 324 screen:expect([[ 325 │ │ │^ | 326 {1:~ }│{1:~ }│{1:~}│{1:~ }|*3 327 {1:~ }├────────────────┤{1:~}│{1:~ }| 328 {1:~ }│ │{1:~}│{1:~ }| 329 {1:~ }│{1:~ }│{1:~}│{1:~ }| 330 {1:~ }│{1:~ }│{1:~}├────────────────────| 331 {1:~ }│{1:~ }│{1:~}│ | 332 ────────────────────┴────────────────┴─┤{1:~ }| 333 │{1:~ }| 334 {1:~ }│{1:~ }|*3 335 {3:[No Name] 0,0-1 All}| 336 | 337 ]]) 338 end) 339 340 it('works when switching between values of laststatus', function() 341 command('set laststatus=1') 342 screen:expect([[ 343 ^ | 344 {1:~ }|*14 345 0,0-1 All | 346 ]]) 347 348 command('set laststatus=3') 349 screen:expect([[ 350 ^ | 351 {1:~ }|*13 352 {3:[No Name] 0,0-1 All}| 353 | 354 ]]) 355 356 command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split') 357 command('set laststatus=2') 358 screen:expect([[ 359 │ │ │^ | 360 {1:~ }│{1:~ }│{1:~}│{1:~ }|*3 361 {1:~ }│{2:<-1 All}│{1:~}│{1:~ }| 362 {1:~ }│ │{1:~}│{1:~ }| 363 {1:~ }│{1:~ }│{1:~}│{1:~ }| 364 {1:~ }│{1:~ }│{1:~}│{3:< 0,0-1 All}| 365 {1:~ }│{1:~ }│{1:~}│ | 366 {2:< 0,0-1 All <-1 All <}│{1:~ }| 367 │{1:~ }| 368 {1:~ }│{1:~ }|*3 369 {2:[No Name] 0,0-1 All < 0,0-1 All}| 370 | 371 ]]) 372 373 command('set laststatus=3') 374 screen:expect([[ 375 │ │ │^ | 376 {1:~ }│{1:~ }│{1:~}│{1:~ }|*3 377 {1:~ }├────────────────┤{1:~}│{1:~ }| 378 {1:~ }│ │{1:~}│{1:~ }| 379 {1:~ }│{1:~ }│{1:~}│{1:~ }| 380 {1:~ }│{1:~ }│{1:~}├────────────────────| 381 {1:~ }│{1:~ }│{1:~}│ | 382 ────────────────────┴────────────────┴─┤{1:~ }| 383 │{1:~ }| 384 {1:~ }│{1:~ }|*3 385 {3:[No Name] 0,0-1 All}| 386 | 387 ]]) 388 389 command('set laststatus=0') 390 screen:expect([[ 391 │ │ │^ | 392 {1:~ }│{1:~ }│{1:~}│{1:~ }|*3 393 {1:~ }│{2:<-1 All}│{1:~}│{1:~ }| 394 {1:~ }│ │{1:~}│{1:~ }| 395 {1:~ }│{1:~ }│{1:~}│{1:~ }| 396 {1:~ }│{1:~ }│{1:~}│{3:< 0,0-1 All}| 397 {1:~ }│{1:~ }│{1:~}│ | 398 {2:< 0,0-1 All <-1 All <}│{1:~ }| 399 │{1:~ }| 400 {1:~ }│{1:~ }|*4 401 0,0-1 All | 402 ]]) 403 404 command('set laststatus=3') 405 screen:expect([[ 406 │ │ │^ | 407 {1:~ }│{1:~ }│{1:~}│{1:~ }|*3 408 {1:~ }├────────────────┤{1:~}│{1:~ }| 409 {1:~ }│ │{1:~}│{1:~ }| 410 {1:~ }│{1:~ }│{1:~}│{1:~ }| 411 {1:~ }│{1:~ }│{1:~}├────────────────────| 412 {1:~ }│{1:~ }│{1:~}│ | 413 ────────────────────┴────────────────┴─┤{1:~ }| 414 │{1:~ }| 415 {1:~ }│{1:~ }|*3 416 {3:[No Name] 0,0-1 All}| 417 | 418 ]]) 419 end) 420 421 it('win_move_statusline() can reduce cmdheight to 1', function() 422 eq(1, api.nvim_get_option_value('cmdheight', {})) 423 fn.win_move_statusline(0, -1) 424 eq(2, api.nvim_get_option_value('cmdheight', {})) 425 fn.win_move_statusline(0, -1) 426 eq(3, api.nvim_get_option_value('cmdheight', {})) 427 fn.win_move_statusline(0, 1) 428 eq(2, api.nvim_get_option_value('cmdheight', {})) 429 fn.win_move_statusline(0, 1) 430 eq(1, api.nvim_get_option_value('cmdheight', {})) 431 end) 432 433 it('mouse dragging can reduce cmdheight to 1', function() 434 command('set mouse=a') 435 api.nvim_input_mouse('left', 'press', '', 0, 14, 10) 436 eq(1, api.nvim_get_option_value('cmdheight', {})) 437 api.nvim_input_mouse('left', 'drag', '', 0, 13, 10) 438 eq(2, api.nvim_get_option_value('cmdheight', {})) 439 api.nvim_input_mouse('left', 'drag', '', 0, 12, 10) 440 eq(3, api.nvim_get_option_value('cmdheight', {})) 441 api.nvim_input_mouse('left', 'drag', '', 0, 13, 10) 442 eq(2, api.nvim_get_option_value('cmdheight', {})) 443 api.nvim_input_mouse('left', 'drag', '', 0, 14, 10) 444 eq(1, api.nvim_get_option_value('cmdheight', {})) 445 api.nvim_input_mouse('left', 'drag', '', 0, 15, 10) 446 eq(1, api.nvim_get_option_value('cmdheight', {})) 447 api.nvim_input_mouse('left', 'drag', '', 0, 14, 10) 448 eq(1, api.nvim_get_option_value('cmdheight', {})) 449 end) 450 451 it('cmdline row is correct after setting cmdheight #20514', function() 452 command('botright split test/functional/fixtures/bigfile.txt') 453 api.nvim_set_option_value('cmdheight', 1, {}) 454 feed('L') 455 screen:expect([[ 456 | 457 {1:~ }|*5 458 ────────────────────────────────────────────────────────────| 459 0000;<control>;Cc;0;BN;;;;;N;NULL;;;; | 460 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | 461 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | 462 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | 463 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | 464 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | 465 ^0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | 466 {3:test/functional/fixtures/bigfile.txt 7,1 Top}| 467 | 468 ]]) 469 feed('j') 470 screen:expect([[ 471 | 472 {1:~ }|*5 473 ────────────────────────────────────────────────────────────| 474 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | 475 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | 476 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | 477 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | 478 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | 479 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | 480 ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; | 481 {3:test/functional/fixtures/bigfile.txt 8,1 0%}| 482 | 483 ]]) 484 api.nvim_set_option_value('showtabline', 2, {}) 485 screen:expect([[ 486 {5: }{100:2}{5: t/f/f/bigfile.txt }{2: }| 487 | 488 {1:~ }|*5 489 ────────────────────────────────────────────────────────────| 490 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | 491 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | 492 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | 493 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | 494 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | 495 ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; | 496 {3:test/functional/fixtures/bigfile.txt 8,1 0%}| 497 | 498 ]]) 499 api.nvim_set_option_value('cmdheight', 0, {}) 500 screen:expect([[ 501 {5: }{100:2}{5: t/f/f/bigfile.txt }{2: }| 502 | 503 {1:~ }|*5 504 ────────────────────────────────────────────────────────────| 505 0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; | 506 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | 507 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | 508 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | 509 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | 510 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | 511 ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; | 512 {3:test/functional/fixtures/bigfile.txt 8,1 0%}| 513 ]]) 514 api.nvim_set_option_value('cmdheight', 1, {}) 515 screen:expect([[ 516 {5: }{100:2}{5: t/f/f/bigfile.txt }{2: }| 517 | 518 {1:~ }|*5 519 ────────────────────────────────────────────────────────────| 520 0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; | 521 0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; | 522 0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; | 523 0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; | 524 0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; | 525 ^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; | 526 {3:test/functional/fixtures/bigfile.txt 8,1 0%}| 527 | 528 ]]) 529 end) 530 531 it('vertical separator connector is not lost when switching window', function() 532 screen:add_extra_attr_ids { 533 [101] = { background = tonumber('0x282828') }, 534 [102] = { bold = true, background = tonumber('0x282828'), foreground = Screen.colors.Blue1 }, 535 } 536 command('hi NormalNC guibg=#282828') 537 command('vsplit | wincmd l | split') 538 -- Cursor is in top-right. Move to bottom-right. 539 feed('<C-w>j') 540 -- Verify the ├ connector is present. 541 screen:expect([[ 542 {101: }│{101: }| 543 {102:~ }│{102:~ }|*6 544 {102:~ }├─────────────────────────────| 545 {102:~ }│^ | 546 {102:~ }│{1:~ }|*5 547 {3:[No Name] 0,0-1 All}| 548 | 549 ]]) 550 -- Navigate from bottom-right to left. 551 feed('<C-w>h') 552 -- The ├ connector must still be present. 553 screen:expect([[ 554 ^ │{101: }| 555 {1:~ }│{102:~ }|*6 556 {1:~ }├─────────────────────────────| 557 {1:~ }│{101: }| 558 {1:~ }│{102:~ }|*5 559 {3:[No Name] 0,0-1 All}| 560 | 561 ]]) 562 end) 563 564 it('horizontal separator connector is not lost when switching window', function() 565 screen:add_extra_attr_ids { 566 [101] = { background = tonumber('0x282828') }, 567 [102] = { bold = true, background = tonumber('0x282828'), foreground = Screen.colors.Blue1 }, 568 } 569 command('hi NormalNC guibg=#282828') 570 command('split | wincmd j | vsplit') 571 -- Cursor is in bottom-left. Move to bottom-right. 572 feed('<C-w>l') 573 -- Verify the ┬ connector is present. 574 screen:expect([[ 575 {101: }| 576 {102:~ }|*6 577 ──────────────────────────────┬─────────────────────────────| 578 {101: }│^ | 579 {102:~ }│{1:~ }|*5 580 {3:[No Name] 0,0-1 All}| 581 | 582 ]]) 583 -- Navigate from bottom-right to top. 584 feed('<C-w>k') 585 -- The ┬ connector must still be present. 586 screen:expect([[ 587 ^ | 588 {1:~ }|*6 589 ──────────────────────────────┬─────────────────────────────| 590 {101: }│{101: }| 591 {102:~ }│{102:~ }|*5 592 {3:[No Name] 0,0-1 All}| 593 | 594 ]]) 595 end) 596 597 it('horizontal separators unchanged when failing to split-move window', function() 598 exec([[ 599 botright split 600 let &winwidth = &columns 601 let &winminwidth = &columns 602 ]]) 603 eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd L')) 604 command('mode') 605 screen:expect([[ 606 | 607 {1:~ }|*5 608 ────────────────────────────────────────────────────────────| 609 ^ | 610 {1:~ }|*6 611 {3:[No Name] 0,0-1 All}| 612 | 613 ]]) 614 615 -- Shouldn't gain a hsep if the global statusline is turned off. 616 command('set laststatus=2') 617 eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd L')) 618 command('mode') 619 screen:expect([[ 620 | 621 {1:~ }|*5 622 {2:[No Name] 0,0-1 All}| 623 ^ | 624 {1:~ }|*6 625 {3:[No Name] 0,0-1 All}| 626 | 627 ]]) 628 end) 629 end) 630 631 describe('statusline', function() 632 local screen 633 before_each(function() 634 clear() 635 screen = Screen.new(40, 8) 636 screen:add_extra_attr_ids { 637 [100] = { bold = true, reverse = true, foreground = Screen.colors.Blue }, 638 [101] = { reverse = true, bold = true, foreground = Screen.colors.SlateBlue }, 639 } 640 end) 641 642 it('does not crash if it has Arabic characters #19447', function() 643 api.nvim_set_option_value('statusline', 'غً', {}) 644 api.nvim_set_option_value('laststatus', 2, {}) 645 command('redraw!') 646 assert_alive() 647 end) 648 649 it('is redrawn with :resize from <Cmd> mapping #19629', function() 650 exec([[ 651 set laststatus=2 652 nnoremap <Up> <cmd>resize -1<CR> 653 nnoremap <Down> <cmd>resize +1<CR> 654 ]]) 655 feed('<Up>') 656 screen:expect([[ 657 ^ | 658 {1:~ }|*4 659 {3:[No Name] }| 660 |*2 661 ]]) 662 feed('<Down>') 663 screen:expect([[ 664 ^ | 665 {1:~ }|*5 666 {3:[No Name] }| 667 | 668 ]]) 669 end) 670 671 it('does not contain showmcd with showcmdloc=statusline when too narrow', function() 672 command('set showcmd') 673 command('set showcmdloc=statusline') 674 command('1vsplit') 675 screen:expect([[ 676 ^ │ | 677 {1:~}│{1:~ }|*5 678 {3:< }{2:[No Name] }| 679 | 680 ]]) 681 feed('1234') 682 screen:expect_unchanged() 683 end) 684 685 it('does not redraw unnecessarily after K_EVENT', function() 686 -- does not redraw on vim.schedule (#17937) 687 command([[ 688 set laststatus=2 689 let g:counter = 0 690 func Status() 691 let g:counter += 1 692 lua vim.schedule(function() end) 693 return g:counter 694 endfunc 695 set statusline=%!Status() 696 ]]) 697 sleep(50) 698 eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter')) 699 -- also in insert mode 700 feed('i') 701 sleep(50) 702 eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter')) 703 -- does not redraw on timer call (#14303) 704 command([[ 705 let g:counter = 0 706 func Timer(timer) 707 endfunc 708 call timer_start(1, 'Timer', {'repeat': 100}) 709 ]]) 710 sleep(50) 711 eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter')) 712 end) 713 714 it('is redrawn on various state changes', function() 715 -- recording state change #22683 716 command('set ls=2 stl=%{repeat(reg_recording(),5)}') 717 local s1 = [[ 718 ^ | 719 {1:~ }|*5 720 {3: }| 721 | 722 ]] 723 screen:expect(s1) 724 feed('qQ') 725 screen:expect([[ 726 ^ | 727 {1:~ }|*5 728 {3:QQQQQ }| 729 {5:recording @Q} | 730 ]]) 731 feed('q') 732 screen:expect(s1) 733 734 -- Visual mode change #23932 735 command('set ls=2 stl=%{mode(1)}') 736 local s2 = [[ 737 ^ | 738 {1:~ }|*5 739 {3:n }| 740 | 741 ]] 742 screen:expect(s2) 743 feed('v') 744 screen:expect([[ 745 ^ | 746 {1:~ }|*5 747 {3:v }| 748 {5:-- VISUAL --} | 749 ]]) 750 feed('V') 751 screen:expect([[ 752 ^ | 753 {1:~ }|*5 754 {3:V }| 755 {5:-- VISUAL LINE --} | 756 ]]) 757 feed('<C-V>') 758 screen:expect([[ 759 ^ | 760 {1:~ }|*5 761 {3:^V }| 762 {5:-- VISUAL BLOCK --} | 763 ]]) 764 feed('<Esc>') 765 screen:expect(s2) 766 767 -- Visual selection other end change #36280 768 exec([[ 769 function! DebugVisualSelection() 770 return printf("v %s %s", col("v"), col(".")) 771 endfunction 772 set statusline=%!DebugVisualSelection() 773 ]]) 774 feed('iabc<Esc>v') 775 screen:expect([[ 776 ab^c | 777 {1:~ }|*5 778 {3:v 3 3 }| 779 {5:-- VISUAL --} | 780 ]]) 781 feed('iw') 782 screen:expect([[ 783 {17:ab}^c | 784 {1:~ }|*5 785 {3:v 1 3 }| 786 {5:-- VISUAL --} | 787 ]]) 788 end) 789 790 it('ruler is redrawn in cmdline with redrawstatus #22804', function() 791 command([[ 792 let g:n = 'initial value' 793 set ls=1 ru ruf=%{g:n} 794 redraw 795 let g:n = 'other value' 796 redrawstatus 797 ]]) 798 screen:expect([[ 799 ^ | 800 {1:~ }|*6 801 other value | 802 ]]) 803 end) 804 805 it('hidden moves ruler to cmdline', function() 806 -- Use long ruler to check 'ruler' with 'rulerformat' set has correct width. 807 command [[ 808 set ruler rulerformat=%{winnr()}longlonglong ls=0 winwidth=10 809 split 810 wincmd b 811 vsplit 812 wincmd t 813 wincmd | 814 mode 815 ]] 816 -- Window 1 is current. It has a statusline, so cmdline should show the 817 -- last window's ruler, which has no statusline. 818 command '1wincmd w' 819 screen:expect([[ 820 ^ | 821 {1:~ }|*2 822 {3:[No Name] 1longlonglong}| 823 │ | 824 {1:~ }│{1:~ }|*2 825 3longlonglong | 826 ]]) 827 -- Window 2 is current. It has no statusline, so cmdline should show its 828 -- ruler instead. 829 command '2wincmd w' 830 screen:expect([[ 831 | 832 {1:~ }|*2 833 {2:[No Name] 1longlonglong}| 834 ^ │ | 835 {1:~ }│{1:~ }|*2 836 2longlonglong | 837 ]]) 838 -- Window 3 is current. Cmdline should again show its ruler. 839 command '3wincmd w' 840 screen:expect([[ 841 | 842 {1:~ }|*2 843 {2:[No Name] 1longlonglong}| 844 │^ | 845 {1:~ }│{1:~ }|*2 846 3longlonglong | 847 ]]) 848 end) 849 850 it('uses "stl" and "stlnc" fillchars even if they are the same #19803', function() 851 command('hi clear StatusLine') 852 command('hi clear StatusLineNC') 853 command('vsplit') 854 screen:expect([[ 855 ^ │ | 856 {1:~ }│{1:~ }|*5 857 [No Name] [No Name] | 858 | 859 ]]) 860 end) 861 862 it('showcmdloc=statusline works with vertical splits', function() 863 command('rightbelow vsplit') 864 command('set showcmd showcmdloc=statusline') 865 feed('1234') 866 screen:expect([[ 867 │^ | 868 {1:~ }│{1:~ }|*5 869 {2:[No Name] }{3:<o Name] 1234 }| 870 | 871 ]]) 872 feed('<Esc>') 873 command('set laststatus=3') 874 feed('1234') 875 screen:expect([[ 876 │^ | 877 {1:~ }│{1:~ }|*5 878 {3:[No Name] 1234 }| 879 | 880 ]]) 881 end) 882 883 it('keymap is shown with vertical splits #27269', function() 884 command('setlocal keymap=dvorak') 885 command('rightbelow vsplit') 886 screen:expect([[ 887 │^ | 888 {1:~ }│{1:~ }|*5 889 {2:[No Name] <en-dv> }{3:[No Name] <en-dv> }| 890 | 891 ]]) 892 893 command('set laststatus=3') 894 screen:expect([[ 895 │^ | 896 {1:~ }│{1:~ }|*5 897 {3:[No Name] <en-dv> }| 898 | 899 ]]) 900 end) 901 902 it("nested call from nvim_eval_statusline() doesn't overwrite items #32259", function() 903 exec_lua('vim.o.laststatus = 2') 904 exec_lua([[vim.o.statusline = '%#Special#B:%{nvim_eval_statusline("%f", []).str}']]) 905 screen:expect([[ 906 ^ | 907 {1:~ }|*5 908 {101:B:[No Name] }| 909 | 910 ]]) 911 end) 912 913 it('truncation inside nested nvim_eval_statusline does not crash #36616', function() 914 exec_lua(function() 915 function _G.statusline_truncating() 916 local win = vim.api.nvim_get_current_win() 917 local res = vim.api.nvim_eval_statusline('%f', { winid = win, maxwidth = 5 }) 918 return res.str 919 end 920 vim.o.laststatus = 2 921 vim.o.statusline = '%#Special#B:%{%v:lua.statusline_truncating()%}' 922 end) 923 local truncated = exec_lua(function() 924 return vim.api.nvim_eval_statusline('%f', { maxwidth = 5 }).str 925 end) 926 local rendered = exec_lua(function() 927 return vim.api.nvim_eval_statusline( 928 vim.o.statusline, 929 { winid = vim.api.nvim_get_current_win() } 930 ).str 931 end) 932 eq('B:' .. truncated, rendered) 933 end) 934 end) 935 936 describe('default statusline', function() 937 local screen 938 939 before_each(function() 940 clear() 941 screen = Screen.new(60, 16) 942 screen:add_extra_attr_ids { 943 [100] = { foreground = Screen.colors.Magenta1, bold = true }, 944 } 945 command('set laststatus=2') 946 command('set ruler') 947 end) 948 949 it('setting statusline to empty string sets default statusline', function() 950 exec_lua("vim.o.statusline = 'asdf'") 951 eq('asdf', eval('&statusline')) 952 screen:expect([[ 953 ^ | 954 {1:~ }|*13 955 {3:asdf }| 956 | 957 ]]) 958 959 local default_statusline = table.concat({ 960 '%<', 961 '%f %h%w%m%r ', 962 '%=', 963 "%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}", 964 "%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}", 965 "%{% &busy > 0 ? '◐ ' : '' %}", 966 "%{% luaeval('(package.loaded[''vim.diagnostic''] and #vim.diagnostic.count() ~= 0 and vim.diagnostic.status() .. '' '') or '''' ') %}", 967 "%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}", 968 }) 969 970 exec_lua("vim.o.statusline = ''") 971 eq(default_statusline, eval('&statusline')) 972 screen:expect([[ 973 ^ | 974 {1:~ }|*13 975 {3:[No Name] 0,0-1 All}| 976 | 977 ]]) 978 979 -- Reset to default (via the correct scope) if there's an error. 980 command('setglobal statusline=%{a%}') 981 eq(default_statusline, eval('&statusline')) 982 eq(default_statusline, eval('&g:statusline')) 983 eq('', eval('&l:statusline')) 984 command('redrawstatus') -- like Vim, statusline isn't immediately redrawn after an error 985 screen:expect([[ 986 ^ | 987 {1:~ }|*13 988 {3:[No Name] 0,0-1 All}| 989 {9:E121: Undefined variable: a} | 990 ]]) 991 command('setlocal statusline=%{b%}') 992 eq(default_statusline, eval('&statusline')) 993 eq(default_statusline, eval('&g:statusline')) 994 eq('', eval('&l:statusline')) 995 command('redrawstatus') -- like Vim, statusline isn't immediately redrawn after an error 996 screen:expect([[ 997 ^ | 998 {1:~ }|*13 999 {3:[No Name] 0,0-1 All}| 1000 {9:E121: Undefined variable: b} | 1001 ]]) 1002 end) 1003 1004 it('shows busy status when buffer is set to be busy', function() 1005 exec_lua("vim.o.statusline = ''") 1006 1007 screen:expect([[ 1008 ^ | 1009 {1:~ }|*13 1010 {3:[No Name] 0,0-1 All}| 1011 | 1012 ]]) 1013 exec_lua('vim.o.busy = 1') 1014 screen:expect([[ 1015 ^ | 1016 {1:~ }|*13 1017 {3:[No Name] ◐ 0,0-1 All}| 1018 | 1019 ]]) 1020 1021 exec_lua('vim.o.busy = 0') 1022 screen:expect([[ 1023 ^ | 1024 {1:~ }|*13 1025 {3:[No Name] 0,0-1 All}| 1026 | 1027 ]]) 1028 end) 1029 end) 1030 1031 describe("'statusline' in floatwin", function() 1032 local screen 1033 before_each(function() 1034 clear() 1035 screen = Screen.new(30, 20) 1036 screen:add_extra_attr_ids({ 1037 [101] = { foreground = Screen.colors.Magenta1, bold = true }, 1038 [102] = { 1039 foreground = Screen.colors.Magenta1, 1040 underline = true, 1041 background = Screen.colors.LightGray, 1042 bold = true, 1043 }, 1044 }) 1045 end) 1046 1047 it('controlled by ":setlocal statusline" and "style" and "laststatus"', function() 1048 local buf = api.nvim_create_buf(false, false) 1049 api.nvim_buf_set_lines(buf, 0, -1, false, { '1', '2', '3', '4' }) 1050 local cfg = { 1051 relative = 'editor', 1052 row = 1, 1053 col = 1, 1054 height = 4, 1055 width = 10, 1056 border = 'single', 1057 } 1058 local win = api.nvim_open_win(buf, true, cfg) 1059 local set_stl = [[setlocal stl=%f\ %m]] 1060 command('set laststatus=2') 1061 command(set_stl) 1062 local has_stl = [[ 1063 | 1064 {1:~}┌──────────┐{1: }| 1065 {1:~}│{4:^1 }│{1: }| 1066 {1:~}│{4:2 }│{1: }| 1067 {1:~}│{4:3 }│{1: }| 1068 {1:~}│{4:4 }│{1: }| 1069 {1:~}│{3:<Name] [+]}│{1: }| 1070 {1:~}└──────────┘{1: }| 1071 {1:~ }|*10 1072 {2:[No Name] }| 1073 | 1074 ]] 1075 screen:expect(has_stl) 1076 1077 -- setting the style will clear the statusline expression for floating windows 1078 api.nvim_win_set_config(win, { style = 'minimal' }) 1079 local without_stl = [[ 1080 | 1081 {1:~}┌──────────┐{1: }| 1082 {1:~}│{4:^1 }│{1: }| 1083 {1:~}│{4:2 }│{1: }| 1084 {1:~}│{4:3 }│{1: }| 1085 {1:~}│{4:4 }│{1: }| 1086 {1:~}└──────────┘{1: }| 1087 {1:~ }|*11 1088 {2:[No Name] }| 1089 | 1090 ]] 1091 screen:expect(without_stl) 1092 1093 -- no statusline is displayed because the statusline option was cleared 1094 api.nvim_win_set_config(win, cfg) 1095 screen:expect_unchanged() 1096 1097 -- displayed after the option is reset 1098 command(set_stl) 1099 screen:expect(has_stl) 1100 1101 -- Show in a new window in a new tab, then return to the previous tab; 1102 -- remove the statusline of the new window, When re-entering this new tab, 1103 -- the statusline of the new window is cleared 1104 command('tabnew') 1105 local win2 = api.nvim_open_win(buf, false, cfg) 1106 command(set_stl) 1107 screen:expect([[ 1108 {24: }{102:2}{24:+ [No Name] }{5: }{101:2}{5:+ [No Name] }{2: }{24:X}| 1109 ^ ┌──────────┐ | 1110 {1:~}│{4:1 }│{1: }| 1111 {1:~}│{4:2 }│{1: }| 1112 {1:~}│{4:3 }│{1: }| 1113 {1:~}│{4:4 }│{1: }| 1114 {1:~}└──────────┘{1: }| 1115 {1:~ }|*11 1116 {3:[No Name] }| 1117 | 1118 ]]) 1119 command('tabfirst') 1120 api.nvim_win_set_config(win2, { style = 'minimal' }) 1121 screen:expect([[ 1122 {5: }{101:2}{5:+ [No Name] }{24: }{102:2}{24:+ [No Name] }{2: }{24:X}| 1123 ┌──────────┐ | 1124 {1:~}│{4:^1 }│{1: }| 1125 {1:~}│{4:2 }│{1: }| 1126 {1:~}│{4:3 }│{1: }| 1127 {1:~}│{4:4 }│{1: }| 1128 {1:~}│{3:<Name] [+]}│{1: }| 1129 {1:~}└──────────┘{1: }| 1130 {1:~ }|*10 1131 {2:[No Name] }| 1132 | 1133 ]]) 1134 command('tabnext') 1135 screen:expect([[ 1136 {24: }{102:2}{24:+ [No Name] }{5: }{101:2}{5:+ [No Name] }{2: }{24:X}| 1137 ^ ┌──────────┐ | 1138 {1:~}│{4:1 }│{1: }| 1139 {1:~}│{4:2 }│{1: }| 1140 {1:~}│{4:3 }│{1: }| 1141 {1:~}│{4:4 }│{1: }| 1142 {1:~}└──────────┘{1: }| 1143 {1:~ }|*11 1144 {3:[No Name] }| 1145 | 1146 ]]) 1147 -- clear statusline when laststatus is 3 1148 command('tabclose | set laststatus=2') 1149 screen:expect([[ 1150 | 1151 {1:~}┌──────────┐{1: }| 1152 {1:~}│{4:^1 }│{1: }| 1153 {1:~}│{4:2 }│{1: }| 1154 {1:~}│{4:3 }│{1: }| 1155 {1:~}│{4:4 }│{1: }| 1156 {1:~}│{3:<Name] [+]}│{1: }| 1157 {1:~}└──────────┘{1: }| 1158 {1:~ }|*10 1159 {2:[No Name] }| 1160 | 1161 ]]) 1162 command('set laststatus=0') 1163 screen:expect([[ 1164 | 1165 {1:~}┌──────────┐{1: }| 1166 {1:~}│{4:^1 }│{1: }| 1167 {1:~}│{4:2 }│{1: }| 1168 {1:~}│{4:3 }│{1: }| 1169 {1:~}│{4:4 }│{1: }| 1170 {1:~}└──────────┘{1: }| 1171 {1:~ }|*12 1172 | 1173 ]]) 1174 1175 command('set laststatus=3') 1176 screen:expect([[ 1177 | 1178 {1:~}┌──────────┐{1: }| 1179 {1:~}│{4:^1 }│{1: }| 1180 {1:~}│{4:2 }│{1: }| 1181 {1:~}│{4:3 }│{1: }| 1182 {1:~}│{4:4 }│{1: }| 1183 {1:~}└──────────┘{1: }| 1184 {1:~ }|*11 1185 {3:[No Name] [+] }| 1186 | 1187 ]]) 1188 api.nvim_buf_set_name(buf, 'stl_test') 1189 screen:expect([[ 1190 | 1191 {1:~}┌──────────┐{1: }| 1192 {1:~}│{4:^1 }│{1: }| 1193 {1:~}│{4:2 }│{1: }| 1194 {1:~}│{4:3 }│{1: }| 1195 {1:~}│{4:4 }│{1: }| 1196 {1:~}└──────────┘{1: }| 1197 {1:~ }|*11 1198 {3:stl_test [+] }| 1199 | 1200 ]]) 1201 end) 1202 1203 it("clears inherited window-local 'statusline' on creation", function() 1204 command('set laststatus=2') 1205 api.nvim_set_option_value('statusline', 'global', {}) 1206 local curwin = api.nvim_get_current_win() 1207 api.nvim_set_option_value('statusline', 'split-local', { win = curwin }) 1208 api.nvim_open_win(0, true, { relative = 'editor', row = 1, col = 1, height = 2, width = 4 }) 1209 screen:expect([[ 1210 | 1211 {1:~}{4:^ }{1: }| 1212 {1:~}{11:~ }{1: }| 1213 {1:~ }|*15 1214 {2:split-local }| 1215 | 1216 ]]) 1217 end) 1218 end)