Files
neovim/test/functional/ui/statusline_spec.lua
Sébastien Hoffmann 444d0b8b6c feat(statusline)!: allow %= in item groups, scope %< to item groups #40369
Problem: Separation markers (%=) are ignored within item groups. This
lead to a regression when the C implementation of the statusline was
replaced with a default expression. When the user configured a custom
ruler expression with a %= and used the overloaded item group syntax to
set the ruler width, the separation marker worked in the ruler, but not
when the ruler was incorporated into the statusline where the item group
syntax was interpreted in the usual way.

Solution: Analogously to top-level behaviour, expand separation markers
evenly within item groups until `minwid` is reached (if set).

ref https://github.com/neovim/neovim/pull/33036
fix https://github.com/neovim/neovim/issues/39984
ref https://github.com/neovim/neovim/issues/40247

Problem: The recursion offset into the static `stl_items` was not taken
into account when adjusting the item count after truncation.

Steps to reproduce: first prepare `stl_items`:
  set stl=%{%repeat('%#Error#',10)%}
then watch how the Error highlight leaks into the recursive call:
  set stl=%l%l%l%{%nvim_eval_statusline('test%l%<',{'maxwidth':3,'highlights':1}).highlights%}

ref https://github.com/neovim/neovim/issues/32259

* fix(statusline): consistent truncation at multicell character

Problem 1: truncation of item groups at multicell character didn't take
into account that minwid can be specified as a negative number.

Problem 2: after truncation at top-level from the right at multicell
character, the returned width was always `maxwidth`, even though the
actual width was reduced. In vim, this can be observed as a statusline
that is not fully drawn until the edge of the screen:
  vim --clean +"set ls=2 stl=%{%repeat('x',&columns-2)%}🙂x%<"

Problem 3: after truncation at top-level from the left at multicell
character, the resulting gap to reach `maxwidth` again was filled with
fillchars, but then the final NUL was not set correctly.
This can be seen in the following example, where the statuscolumn spills
into the editing area starting from line 10:
  nvim --clean +"set number stc=%<x🙂%{repeat('x',43)}%l" +"norm yy10p"

Solution: fix the small errors and, at top-level, consistently reduce
the size instead of compensating with fillchars. In the case of the
statusline and the winbar, the remaining place is filled with the
configured fillchars in `win_redr_custom`, after `build_stl_str_hl` has
returned. In all other cases (title, icon, statuscol, tabline, ruler),
there seems to be no point in adding additional spaces at the end.

* feat(statusline)!: scope %< to item groups

Problem:
Previously, item groups were only truncated at the beginning, which is
often not desired. In the example
  %.15(path: %f%)
the group's title/label is truncated away:
  <th/to/file.txt
Truncation markers (%<) in item groups were processed at the top-level
in the end, which can be confusing. Only the first %< is used for the
whole string, and it is used even if the containing item group is
hidden. Additionally, in the case of hidden item groups, the marker's
position was not adapted. For example,
  %(hidden%<%)%f
had the effect of truncating the path somewhere in the middle:
  /path/<file.txt

Solution:
Make truncation consistent with top-level behaviour, which has a better
default of truncating at the first `Normal` item, i.e.
  path: <file.txt
and allows for fine-grained control with truncation markers (%<). E.g.
  %.15(path: %f%<%)
now yields
  path: /path/to>
The original behaviour can be restored like so:
  %.15(%<path: %f%)

BREAKING CHANGE: %< is no longer processed at top-level
- the default truncation behaviour has changed: now at first item
- truncation markers inside item groups don't affect truncation outside
  of the item group anymore
- several truncation markers can now have an effect when separated with
  item groups, whereas previously only the first one globally had

ref https://github.com/neovim/neovim/issues/39984
2026-06-27 11:10:05 -04:00

1470 lines
61 KiB
Lua

local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local tt = require('test.functional.testterm')
local Screen = require('test.functional.ui.screen')
local assert_alive = n.assert_alive
local clear = n.clear
local command = n.command
local feed = n.feed
local eq = t.eq
local fn = n.fn
local api = n.api
local exec = n.exec
local exec_lua = n.exec_lua
local eval = n.eval
local sleep = vim.uv.sleep
local pcall_err = t.pcall_err
local testprg = n.testprg
local expect_exitcode = tt.expect_exitcode
local mousemodels = { 'extend', 'popup', 'popup_setpos' }
for _, model in ipairs(mousemodels) do
describe('statusline clicks with mousemodel=' .. model, function()
local screen
before_each(function()
clear()
screen = Screen.new(40, 8)
screen:add_extra_attr_ids {
[100] = { bold = true, reverse = true, foreground = Screen.colors.Blue },
}
command('set laststatus=2 mousemodel=' .. model)
exec([=[
function! MyClickFunc(minwid, clicks, button, mods)
let g:testvar = printf("%d %d %s", a:minwid, a:clicks, a:button)
if a:mods !=# ' '
let g:testvar ..= '(' .. a:mods .. ')'
endif
endfunction
let g:testvar = ''
]=])
end)
it('works', function()
api.nvim_set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {})
api.nvim_input_mouse('left', 'press', '', 0, 6, 16)
eq('', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 29)
eq('', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', '', 0, 6, 17)
eq('0 1 l', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', '', 0, 6, 17)
eq('0 2 l', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', '', 0, 6, 17)
eq('0 3 l', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', '', 0, 6, 17)
eq('0 4 l', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 28)
eq('0 1 r', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 28)
eq('0 2 r', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 28)
eq('0 3 r', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 28)
eq('0 4 r', eval('g:testvar'))
api.nvim_input_mouse('x1', 'press', '', 0, 6, 17)
eq('0 1 x1', eval('g:testvar'))
api.nvim_input_mouse('x1', 'press', '', 0, 6, 17)
eq('0 2 x1', eval('g:testvar'))
api.nvim_input_mouse('x1', 'press', '', 0, 6, 17)
eq('0 3 x1', eval('g:testvar'))
api.nvim_input_mouse('x1', 'press', '', 0, 6, 17)
eq('0 4 x1', eval('g:testvar'))
api.nvim_input_mouse('x2', 'press', '', 0, 6, 28)
eq('0 1 x2', eval('g:testvar'))
api.nvim_input_mouse('x2', 'press', '', 0, 6, 28)
eq('0 2 x2', eval('g:testvar'))
api.nvim_input_mouse('x2', 'press', '', 0, 6, 28)
eq('0 3 x2', eval('g:testvar'))
api.nvim_input_mouse('x2', 'press', '', 0, 6, 28)
eq('0 4 x2', eval('g:testvar'))
end)
it('works with control characters and highlight', function()
api.nvim_set_option_value('statusline', '\t%#NonText#\1%0@MyClickFunc@\t\1%T\t%##\1', {})
screen:expect {
grid = [[
^ |
{1:~ }|*5
{3:^I}{100:^A^I^A^I}{3:^A }|
|
]],
}
api.nvim_input_mouse('right', 'press', '', 0, 6, 3)
eq('', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', '', 0, 6, 8)
eq('', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 4)
eq('0 1 r', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', '', 0, 6, 7)
eq('0 1 l', eval('g:testvar'))
end)
it('works with combined highlight attributes', function()
screen:add_extra_attr_ids({
[131] = { reverse = true, bold = true, background = Screen.colors.LightMagenta },
[132] = {
reverse = true,
foreground = Screen.colors.Magenta,
bold = true,
background = Screen.colors.LightMagenta,
},
[133] = { reverse = true, bold = true, foreground = Screen.colors.Magenta1 },
[134] = {
bold = true,
background = Screen.colors.LightMagenta,
reverse = true,
undercurl = true,
special = Screen.colors.Red,
},
[135] = {
bold = true,
background = Screen.colors.LightMagenta,
reverse = true,
undercurl = true,
foreground = Screen.colors.Fuchsia,
special = Screen.colors.Red,
},
})
api.nvim_set_option_value(
'statusline',
'\t%#Pmenu#foo%$SpellBad$bar%$String$baz%#Constant#qux',
{}
)
screen:expect([[
^ |
{1:~ }|*5
{3:^I}{131:foo}{134:bar}{135:baz}{133:qux }|
|
]])
end)
it('works for winbar', function()
api.nvim_set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {})
api.nvim_input_mouse('left', 'press', '', 0, 0, 17)
eq('0 1 l', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 0, 17)
eq('0 1 r', eval('g:testvar'))
end)
it('works for winbar in floating window', function()
api.nvim_open_win(
0,
true,
{ width = 30, height = 4, relative = 'editor', row = 1, col = 5, border = 'single' }
)
api.nvim_set_option_value(
'winbar',
'Not clicky stuff %0@MyClickFunc@Clicky stuff%T',
{ scope = 'local' }
)
api.nvim_input_mouse('left', 'press', '', 0, 2, 23)
eq('0 1 l', eval('g:testvar'))
end)
it('works when there are multiple windows', function()
command('split')
api.nvim_set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {})
api.nvim_set_option_value('winbar', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {})
api.nvim_input_mouse('left', 'press', '', 0, 0, 17)
eq('0 1 l', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 4, 17)
eq('0 1 r', eval('g:testvar'))
api.nvim_input_mouse('middle', 'press', '', 0, 3, 17)
eq('0 1 m', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', '', 0, 6, 17)
eq('0 1 l', eval('g:testvar'))
end)
it('works with Lua function', function()
exec_lua([[
function clicky_func(minwid, clicks, button, mods)
vim.g.testvar = string.format("%d %d %s", minwid, clicks, button)
end
]])
api.nvim_set_option_value(
'statusline',
'Not clicky stuff %0@v:lua.clicky_func@Clicky stuff%T',
{}
)
api.nvim_input_mouse('left', 'press', '', 0, 6, 17)
eq('0 1 l', eval('g:testvar'))
end)
it('ignores unsupported click items', function()
command('tabnew | tabprevious')
api.nvim_set_option_value('statusline', '%2TNot clicky stuff%T', {})
api.nvim_input_mouse('left', 'press', '', 0, 6, 0)
eq(1, api.nvim_get_current_tabpage())
api.nvim_set_option_value('statusline', '%2XNot clicky stuff%X', {})
api.nvim_input_mouse('left', 'press', '', 0, 6, 0)
eq(2, #api.nvim_list_tabpages())
end)
it("right click works when statusline isn't focused #18994", function()
api.nvim_set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {})
api.nvim_input_mouse('right', 'press', '', 0, 6, 17)
eq('0 1 r', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 17)
eq('0 2 r', eval('g:testvar'))
end)
it('works with modifiers #18994', function()
api.nvim_set_option_value('statusline', 'Not clicky stuff %0@MyClickFunc@Clicky stuff%T', {})
-- Note: alternate between left and right mouse buttons to avoid triggering multiclicks
api.nvim_input_mouse('left', 'press', 'S', 0, 6, 17)
eq('0 1 l(s )', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', 'S', 0, 6, 17)
eq('0 1 r(s )', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', 'A', 0, 6, 17)
eq('0 1 l( a )', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', 'A', 0, 6, 17)
eq('0 1 r( a )', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', 'AS', 0, 6, 17)
eq('0 1 l(s a )', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', 'AS', 0, 6, 17)
eq('0 1 r(s a )', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', 'T', 0, 6, 17)
eq('0 1 l( m)', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', 'T', 0, 6, 17)
eq('0 1 r( m)', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', 'TS', 0, 6, 17)
eq('0 1 l(s m)', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', 'TS', 0, 6, 17)
eq('0 1 r(s m)', eval('g:testvar'))
api.nvim_input_mouse('left', 'press', 'C', 0, 6, 17)
eq('0 1 l( c )', eval('g:testvar'))
-- <C-RightMouse> is for tag jump
end)
it('works for global statusline with vertical splits #19186', function()
command('set laststatus=3')
api.nvim_set_option_value(
'statusline',
'%0@MyClickFunc@Clicky stuff%T %= %0@MyClickFunc@Clicky stuff%T',
{}
)
command('vsplit')
screen:expect {
grid = [[
^ │ |
{1:~ }│{1:~ }|*5
{3:Clicky stuff Clicky stuff}|
|
]],
}
-- clickable area on the right
api.nvim_input_mouse('left', 'press', '', 0, 6, 35)
eq('0 1 l', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 35)
eq('0 1 r', eval('g:testvar'))
-- clickable area on the left
api.nvim_input_mouse('left', 'press', '', 0, 6, 5)
eq('0 1 l', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 5)
eq('0 1 r', eval('g:testvar'))
end)
it('no memory leak with zero-width click labels', function()
command([[
let &stl = '%@Test@%T%@MyClickFunc@%=%T%@Test@'
]])
api.nvim_input_mouse('left', 'press', '', 0, 6, 0)
eq('0 1 l', eval('g:testvar'))
api.nvim_input_mouse('right', 'press', '', 0, 6, 39)
eq('0 1 r', eval('g:testvar'))
end)
it('no memory leak with truncated click labels', function()
command([[
let &stl = '%@MyClickFunc@foo%X' .. repeat('a', 40) .. '%<t%@Test@bar%X%@Test@baz'
]])
api.nvim_input_mouse('left', 'press', '', 0, 6, 2)
eq('0 1 l', eval('g:testvar'))
end)
end)
end
describe('global statusline', function()
local screen
before_each(function()
clear()
screen = Screen.new(60, 16)
screen:add_extra_attr_ids {
[100] = { foreground = Screen.colors.Magenta1, bold = true },
}
command('set laststatus=3')
command('set ruler')
end)
it('works', function()
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
|
]])
feed('i<CR><CR>')
screen:expect([[
|*2
^ |
{1:~ }|*11
{3:[No Name] [+] 3,1 All}|
{5:-- INSERT --} |
]])
end)
it('works with splits', function()
command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
screen:expect([[
│ │ │^ |
{1:~ }│{1:~ }│{1:~}│{1:~ }|*3
{1:~ }├────────────────┤{1:~}│{1:~ }|
{1:~ }│ │{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}├────────────────────|
{1:~ }│{1:~ }│{1:~}│ |
────────────────────┴────────────────┴─┤{1:~ }|
│{1:~ }|
{1:~ }│{1:~ }|*3
{3:[No Name] 0,0-1 All}|
|
]])
end)
it('works when switching between values of laststatus', function()
command('set laststatus=1')
screen:expect([[
^ |
{1:~ }|*14
0,0-1 All |
]])
command('set laststatus=3')
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
|
]])
command('vsplit | split | vsplit | vsplit | wincmd l | split | 2wincmd l | split')
command('set laststatus=2')
screen:expect([[
│ │ │^ |
{1:~ }│{1:~ }│{1:~}│{1:~ }|*3
{1:~ }│{2:<-1 All}│{1:~}│{1:~ }|
{1:~ }│ │{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}│{3:< 0,0-1 All}|
{1:~ }│{1:~ }│{1:~}│ |
{2:< 0,0-1 All <-1 All <}│{1:~ }|
│{1:~ }|
{1:~ }│{1:~ }|*3
{2:[No Name] 0,0-1 All < 0,0-1 All}|
|
]])
command('set laststatus=3')
screen:expect([[
│ │ │^ |
{1:~ }│{1:~ }│{1:~}│{1:~ }|*3
{1:~ }├────────────────┤{1:~}│{1:~ }|
{1:~ }│ │{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}├────────────────────|
{1:~ }│{1:~ }│{1:~}│ |
────────────────────┴────────────────┴─┤{1:~ }|
│{1:~ }|
{1:~ }│{1:~ }|*3
{3:[No Name] 0,0-1 All}|
|
]])
command('set laststatus=0')
screen:expect([[
│ │ │^ |
{1:~ }│{1:~ }│{1:~}│{1:~ }|*3
{1:~ }│{2:<-1 All}│{1:~}│{1:~ }|
{1:~ }│ │{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}│{3:< 0,0-1 All}|
{1:~ }│{1:~ }│{1:~}│ |
{2:< 0,0-1 All <-1 All <}│{1:~ }|
│{1:~ }|
{1:~ }│{1:~ }|*4
0,0-1 All |
]])
command('set laststatus=3')
screen:expect([[
│ │ │^ |
{1:~ }│{1:~ }│{1:~}│{1:~ }|*3
{1:~ }├────────────────┤{1:~}│{1:~ }|
{1:~ }│ │{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}│{1:~ }|
{1:~ }│{1:~ }│{1:~}├────────────────────|
{1:~ }│{1:~ }│{1:~}│ |
────────────────────┴────────────────┴─┤{1:~ }|
│{1:~ }|
{1:~ }│{1:~ }|*3
{3:[No Name] 0,0-1 All}|
|
]])
end)
it('win_move_statusline() can reduce cmdheight to 1', function()
eq(1, api.nvim_get_option_value('cmdheight', {}))
fn.win_move_statusline(0, -1)
eq(2, api.nvim_get_option_value('cmdheight', {}))
fn.win_move_statusline(0, -1)
eq(3, api.nvim_get_option_value('cmdheight', {}))
fn.win_move_statusline(0, 1)
eq(2, api.nvim_get_option_value('cmdheight', {}))
fn.win_move_statusline(0, 1)
eq(1, api.nvim_get_option_value('cmdheight', {}))
end)
it('mouse dragging can reduce cmdheight to 1', function()
command('set mouse=a')
api.nvim_input_mouse('left', 'press', '', 0, 14, 10)
eq(1, api.nvim_get_option_value('cmdheight', {}))
api.nvim_input_mouse('left', 'drag', '', 0, 13, 10)
eq(2, api.nvim_get_option_value('cmdheight', {}))
api.nvim_input_mouse('left', 'drag', '', 0, 12, 10)
eq(3, api.nvim_get_option_value('cmdheight', {}))
api.nvim_input_mouse('left', 'drag', '', 0, 13, 10)
eq(2, api.nvim_get_option_value('cmdheight', {}))
api.nvim_input_mouse('left', 'drag', '', 0, 14, 10)
eq(1, api.nvim_get_option_value('cmdheight', {}))
api.nvim_input_mouse('left', 'drag', '', 0, 15, 10)
eq(1, api.nvim_get_option_value('cmdheight', {}))
api.nvim_input_mouse('left', 'drag', '', 0, 14, 10)
eq(1, api.nvim_get_option_value('cmdheight', {}))
end)
it('cmdline row is correct after setting cmdheight #20514', function()
command('botright split test/functional/fixtures/bigfile.txt')
api.nvim_set_option_value('cmdheight', 1, {})
feed('L')
screen:expect([[
|
{1:~ }|*5
────────────────────────────────────────────────────────────|
0000;<control>;Cc;0;BN;;;;;N;NULL;;;; |
0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; |
0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
^0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
{3:test/functional/fixtures/bigfile.txt 7,1 Top}|
|
]])
feed('j')
screen:expect([[
|
{1:~ }|*5
────────────────────────────────────────────────────────────|
0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; |
0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; |
{3:test/functional/fixtures/bigfile.txt 8,1 0%}|
|
]])
api.nvim_set_option_value('showtabline', 2, {})
screen:expect([[
{5: }{100:2}{5: t/f/f/bigfile.txt }{2: }|
|
{1:~ }|*5
────────────────────────────────────────────────────────────|
0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; |
{3:test/functional/fixtures/bigfile.txt 8,1 0%}|
|
]])
api.nvim_set_option_value('cmdheight', 0, {})
screen:expect([[
{5: }{100:2}{5: t/f/f/bigfile.txt }{2: }|
|
{1:~ }|*5
────────────────────────────────────────────────────────────|
0001;<control>;Cc;0;BN;;;;;N;START OF HEADING;;;; |
0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; |
{3:test/functional/fixtures/bigfile.txt 8,1 0%}|
]])
api.nvim_set_option_value('cmdheight', 1, {})
screen:expect([[
{5: }{100:2}{5: t/f/f/bigfile.txt }{2: }|
|
{1:~ }|*5
────────────────────────────────────────────────────────────|
0002;<control>;Cc;0;BN;;;;;N;START OF TEXT;;;; |
0003;<control>;Cc;0;BN;;;;;N;END OF TEXT;;;; |
0004;<control>;Cc;0;BN;;;;;N;END OF TRANSMISSION;;;; |
0005;<control>;Cc;0;BN;;;;;N;ENQUIRY;;;; |
0006;<control>;Cc;0;BN;;;;;N;ACKNOWLEDGE;;;; |
^0007;<control>;Cc;0;BN;;;;;N;BELL;;;; |
{3:test/functional/fixtures/bigfile.txt 8,1 0%}|
|
]])
end)
it('vertical separator connector is not lost when switching window', function()
screen:add_extra_attr_ids {
[101] = { background = tonumber('0x282828') },
[102] = { bold = true, background = tonumber('0x282828'), foreground = Screen.colors.Blue1 },
}
command('hi NormalNC guibg=#282828')
command('vsplit | wincmd l | split')
-- Cursor is in top-right. Move to bottom-right.
feed('<C-w>j')
-- Verify the ├ connector is present.
screen:expect([[
{101: }│{101: }|
{102:~ }│{102:~ }|*6
{102:~ }├─────────────────────────────|
{102:~ }│^ |
{102:~ }│{1:~ }|*5
{3:[No Name] 0,0-1 All}|
|
]])
-- Navigate from bottom-right to left.
feed('<C-w>h')
-- The ├ connector must still be present.
screen:expect([[
^ │{101: }|
{1:~ }│{102:~ }|*6
{1:~ }├─────────────────────────────|
{1:~ }│{101: }|
{1:~ }│{102:~ }|*5
{3:[No Name] 0,0-1 All}|
|
]])
end)
it('horizontal separator connector is not lost when switching window', function()
screen:add_extra_attr_ids {
[101] = { background = tonumber('0x282828') },
[102] = { bold = true, background = tonumber('0x282828'), foreground = Screen.colors.Blue1 },
}
command('hi NormalNC guibg=#282828')
command('split | wincmd j | vsplit')
-- Cursor is in bottom-left. Move to bottom-right.
feed('<C-w>l')
-- Verify the ┬ connector is present.
screen:expect([[
{101: }|
{102:~ }|*6
──────────────────────────────┬─────────────────────────────|
{101: }│^ |
{102:~ }│{1:~ }|*5
{3:[No Name] 0,0-1 All}|
|
]])
-- Navigate from bottom-right to top.
feed('<C-w>k')
-- The ┬ connector must still be present.
screen:expect([[
^ |
{1:~ }|*6
──────────────────────────────┬─────────────────────────────|
{101: }│{101: }|
{102:~ }│{102:~ }|*5
{3:[No Name] 0,0-1 All}|
|
]])
end)
it('horizontal separators unchanged when failing to split-move window', function()
exec([[
botright split
let &winwidth = &columns
let &winminwidth = &columns
]])
eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd L'))
command('mode')
screen:expect([[
|
{1:~ }|*5
────────────────────────────────────────────────────────────|
^ |
{1:~ }|*6
{3:[No Name] 0,0-1 All}|
|
]])
-- Shouldn't gain a hsep if the global statusline is turned off.
command('set laststatus=2')
eq('Vim(wincmd):E36: Not enough room', pcall_err(command, 'wincmd L'))
command('mode')
screen:expect([[
|
{1:~ }|*5
{2:[No Name] 0,0-1 All}|
^ |
{1:~ }|*6
{3:[No Name] 0,0-1 All}|
|
]])
end)
end)
describe('statusline', function()
local screen
before_each(function()
clear()
screen = Screen.new(40, 8)
screen:add_extra_attr_ids {
[100] = { bold = true, reverse = true, foreground = Screen.colors.Blue },
[101] = { reverse = true, bold = true, foreground = Screen.colors.SlateBlue },
}
end)
it('does not crash if it has Arabic characters #19447', function()
api.nvim_set_option_value('statusline', 'غً', {})
api.nvim_set_option_value('laststatus', 2, {})
command('redraw!')
assert_alive()
end)
it('is redrawn with :resize from <Cmd> mapping #19629', function()
exec([[
set laststatus=2
nnoremap <Up> <cmd>resize -1<CR>
nnoremap <Down> <cmd>resize +1<CR>
]])
feed('<Up>')
screen:expect([[
^ |
{1:~ }|*4
{3:[No Name] }|
|*2
]])
feed('<Down>')
screen:expect([[
^ |
{1:~ }|*5
{3:[No Name] }|
|
]])
end)
it('does not contain showmcd with showcmdloc=statusline when too narrow', function()
command('set showcmd')
command('set showcmdloc=statusline')
command('1vsplit')
screen:expect([[
^ │ |
{1:~}│{1:~ }|*5
{3:< }{2:[No Name] }|
|
]])
feed('1234')
screen:expect_unchanged()
end)
it('does not redraw unnecessarily after K_EVENT', function()
-- does not redraw on vim.schedule (#17937)
command([[
set laststatus=2
let g:counter = 0
func Status()
let g:counter += 1
lua vim.schedule(function() end)
return g:counter
endfunc
set statusline=%!Status()
]])
sleep(50)
eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter'))
-- also in insert mode
feed('i')
sleep(50)
eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter'))
-- does not redraw on timer call (#14303)
command([[
let g:counter = 0
func Timer(timer)
endfunc
call timer_start(1, 'Timer', {'repeat': 100})
]])
sleep(50)
eq(1, eval('g:counter < 50'), 'g:counter=' .. eval('g:counter'))
end)
it('is redrawn on various state changes', function()
-- recording state change #22683
command('set ls=2 stl=%{repeat(reg_recording(),5)}')
local s1 = [[
^ |
{1:~ }|*5
{3: }|
|
]]
screen:expect(s1)
feed('qQ')
screen:expect([[
^ |
{1:~ }|*5
{3:QQQQQ }|
{5:recording @Q} |
]])
feed('q')
screen:expect(s1)
-- Visual mode change #23932
command('set ls=2 stl=%{mode(1)}')
local s2 = [[
^ |
{1:~ }|*5
{3:n }|
|
]]
screen:expect(s2)
feed('v')
screen:expect([[
^ |
{1:~ }|*5
{3:v }|
{5:-- VISUAL --} |
]])
feed('V')
screen:expect([[
^ |
{1:~ }|*5
{3:V }|
{5:-- VISUAL LINE --} |
]])
feed('<C-V>')
screen:expect([[
^ |
{1:~ }|*5
{3:^V }|
{5:-- VISUAL BLOCK --} |
]])
feed('<Esc>')
screen:expect(s2)
-- Visual selection other end change #36280
exec([[
function! DebugVisualSelection()
return printf("v %s %s", col("v"), col("."))
endfunction
set statusline=%!DebugVisualSelection()
]])
feed('iabc<Esc>v')
screen:expect([[
ab^c |
{1:~ }|*5
{3:v 3 3 }|
{5:-- VISUAL --} |
]])
feed('iw')
screen:expect([[
{17:ab}^c |
{1:~ }|*5
{3:v 1 3 }|
{5:-- VISUAL --} |
]])
end)
it('ruler is redrawn in cmdline with redrawstatus #22804', function()
command([[
let g:n = 'initial value'
set ls=1 ru ruf=%{g:n}
redraw
let g:n = 'other value'
redrawstatus
]])
screen:expect([[
^ |
{1:~ }|*6
other value |
]])
end)
it('ruler is cleared when window without statusline is closed', function()
local cfg = { relative = 'editor', row = 1, col = 1, height = 1, width = 1 }
local win = api.nvim_open_win(0, true, cfg)
-- Last line ruler does not take on window-local highlights #38777
command('hi link MsgArea Normal | setlocal winhl=Normal:Statement | set ruler laststatus=2')
screen:expect([[
|
{1:~}{15:^ }{1: }|
{1:~ }|*4
{2:[No Name] 0,0-1 All}|
0,0-1 All |
]])
api.nvim_win_close(win, true)
screen:expect([[
^ |
{1:~ }|*5
{3:[No Name] 0,0-1 All}|
|
]])
end)
it('hidden moves ruler to cmdline', function()
-- Use long ruler to check 'ruler' with 'rulerformat' set has correct width.
command [[
set ruler rulerformat=%{winnr()}longlonglong ls=0 winwidth=10
split
wincmd b
vsplit
wincmd t
wincmd |
mode
]]
-- Window 1 is current. It has a statusline, so cmdline should show the
-- last window's ruler, which has no statusline.
command '1wincmd w'
screen:expect([[
^ |
{1:~ }|*2
{3:[No Name] 1longlonglong}|
│ |
{1:~ }│{1:~ }|*2
3longlonglong |
]])
-- Window 2 is current. It has no statusline, so cmdline should show its
-- ruler instead.
command '2wincmd w'
screen:expect([[
|
{1:~ }|*2
{2:[No Name] 1longlonglong}|
^ │ |
{1:~ }│{1:~ }|*2
2longlonglong |
]])
-- Window 3 is current. Cmdline should again show its ruler.
command '3wincmd w'
screen:expect([[
|
{1:~ }|*2
{2:[No Name] 1longlonglong}|
│^ |
{1:~ }│{1:~ }|*2
3longlonglong |
]])
end)
it('uses "stl" and "stlnc" fillchars even if they are the same #19803', function()
command('hi clear StatusLine')
command('hi clear StatusLineNC')
command('vsplit')
screen:expect([[
^ │ |
{1:~ }│{1:~ }|*5
[No Name] [No Name] |
|
]])
end)
it('showcmdloc=statusline works with vertical splits', function()
command('rightbelow vsplit')
command('set showcmd showcmdloc=statusline')
feed('1234')
screen:expect([[
│^ |
{1:~ }│{1:~ }|*5
{2:[No Name] }{3:<o Name] 1234 }|
|
]])
feed('<Esc>')
command('set laststatus=3')
feed('1234')
screen:expect([[
│^ |
{1:~ }│{1:~ }|*5
{3:[No Name] 1234 }|
|
]])
end)
it('keymap is shown with vertical splits #27269', function()
command('setlocal keymap=dvorak')
command('rightbelow vsplit')
screen:expect([[
│^ |
{1:~ }│{1:~ }|*5
{2:[No Name] <en-dv> }{3:[No Name] <en-dv> }|
|
]])
command('set laststatus=3')
screen:expect([[
│^ |
{1:~ }│{1:~ }|*5
{3:[No Name] <en-dv> }|
|
]])
end)
it("nested call from nvim_eval_statusline() doesn't overwrite items #32259", function()
exec_lua('vim.o.laststatus = 2')
exec_lua([[vim.o.statusline = '%#Special#B:%{nvim_eval_statusline("%f", []).str}']])
screen:expect([[
^ |
{1:~ }|*5
{101:B:[No Name] }|
|
]])
end)
it('truncation inside nested nvim_eval_statusline does not crash #36616', function()
exec_lua(function()
function _G.statusline_truncating()
local win = vim.api.nvim_get_current_win()
local res = vim.api.nvim_eval_statusline('%f', { winid = win, maxwidth = 5 })
return res.str
end
vim.o.laststatus = 2
vim.o.statusline = '%#Special#B:%{%v:lua.statusline_truncating()%}'
end)
local truncated = exec_lua(function()
return vim.api.nvim_eval_statusline('%f', { maxwidth = 5 }).str
end)
local rendered = exec_lua(function()
return vim.api.nvim_eval_statusline(
vim.o.statusline,
{ winid = vim.api.nvim_get_current_win() }
).str
end)
eq('B:' .. truncated, rendered)
end)
it('truncation inside nested nvim_eval_statusline correctly adjusts highlights', function()
exec_lua('vim.o.laststatus = 2')
exec_lua([[vim.o.statusline = "%{%repeat('%#Error#',20)%}"]])
exec_lua([[
vim.o.statusline = "%l%l%l%l%{%nvim_eval_statusline('test%#Error#%l%<',{'maxwidth':4,'highlights':1}).highlights%}"
]])
screen:expect([[
^ |
{1:~ }|*5
{3:< 'groups': ['StatusLine'], 'start': 0}]}|
|
]])
end)
it('no cmdline ruler for autocmd window #39938', function()
command('set ruler laststatus=2')
api.nvim_create_autocmd('BufDelete', { command = 'redrawstatus' })
screen:expect([[
^ |
{1:~ }|*5
{3:[No Name] 0,0-1 All}|
|
]])
api.nvim_exec_autocmds('BufDelete', { buf = api.nvim_create_buf(true, true) })
screen:expect_unchanged(true)
end)
it('active window is correct after nvim_exec_autocmds({buf}) #40153', function()
command('set laststatus=2')
local caller_win = api.nvim_get_current_win()
command('split')
local target = api.nvim_create_buf(true, false)
api.nvim_set_current_buf(target)
api.nvim_set_current_win(caller_win)
exec_lua(function(buf)
_G.in_aucmd_statusline = false
_G.statusline_contexts = {}
_G.Status = function()
local current = vim.api.nvim_get_current_win()
local statusline = vim.g.statusline_winid
local focus = current == statusline
table.insert(_G.statusline_contexts, {
current = current,
focus = focus,
in_aucmd = _G.in_aucmd_statusline,
statusline = statusline,
})
return focus and 'CUR' or 'nc'
end
vim.o.statusline = '%!v:lua.Status()'
vim.api.nvim_create_autocmd('User', {
buffer = buf,
callback = function(ev)
_G.in_aucmd_statusline = true
vim.api.nvim__redraw({ buf = ev.buf, statusline = true })
_G.in_aucmd_statusline = false
end,
})
end, target)
-- Ignore statusline evaluations from setup; only the autocmd redraw matters.
exec_lua('_G.statusline_contexts = {}')
api.nvim_exec_autocmds('User', { buf = target })
screen:expect([[
|
{1:~ }|*2
{2:nc }|
^ |
{1:~ }|
{3:CUR }|
|
]])
-- `%!` may evaluate during the callback, but must not see the target as focused.
eq(
{},
exec_lua(function(win)
return vim
.iter(_G.statusline_contexts)
:filter(function(context)
return context.in_aucmd and context.focus and context.current ~= win
end)
:totable()
end, caller_win)
)
end)
it('statusline evaluation during nvim_exec_autocmds({buf}) #40153', function()
command('set laststatus=2')
local caller_win = api.nvim_get_current_win()
command('split')
local target = api.nvim_create_buf(true, false)
api.nvim_set_current_buf(target)
api.nvim_set_current_win(caller_win)
exec_lua(function(buf)
_G.in_aucmd_statusline = false
_G.statusline_contexts = {}
_G.Status = function()
table.insert(_G.statusline_contexts, {
in_aucmd = _G.in_aucmd_statusline,
current = vim.api.nvim_get_current_win(),
actual = tonumber(vim.g.actual_curwin or -1),
})
return vim.api.nvim_get_current_win() == tonumber(vim.g.actual_curwin or -1) and 'CUR'
or 'nc'
end
-- `%{%...%}` sets g:actual_curwin while evaluating the statusline.
vim.o.statusline = '%{%v:lua.Status()%}'
vim.api.nvim_create_autocmd('User', {
buffer = buf,
callback = function(ev)
_G.in_aucmd_statusline = true
vim.api.nvim__redraw({ buf = ev.buf, statusline = true })
_G.in_aucmd_statusline = false
end,
})
end, target)
screen:expect([[
|
{1:~ }|*2
{2:nc }|
^ |
{1:~ }|
{3:CUR }|
|
]])
-- Ignore statusline evaluations from setup; only the autocmd redraw matters.
exec_lua('_G.statusline_contexts = {}')
api.nvim_exec_autocmds('User', { buf = target })
screen:expect({ unchanged = true })
local contexts = exec_lua(function()
return _G.statusline_contexts
end)
eq(true, #contexts > 0)
eq(caller_win, contexts[#contexts].actual)
end)
end)
describe('default statusline', function()
local screen
before_each(function()
clear()
screen = Screen.new(60, 16)
screen:add_extra_attr_ids {
[100] = { foreground = Screen.colors.Magenta1, bold = true },
[131] = { foreground = Screen.colors.NvimDarkGreen },
}
command('set laststatus=2')
command('set ruler')
end)
it('setting statusline to empty string sets default statusline', function()
exec_lua("vim.o.statusline = 'asdf'")
eq('asdf', eval('&statusline'))
screen:expect([[
^ |
{1:~ }|*13
{3:asdf }|
|
]])
local default_statusline = table.concat({
'%<',
'%f %h%w%m%r ',
"%{% v:lua.require('vim._core.util').term_exitcode() %}",
'%=',
"%{% luaeval('(package.loaded[''vim.ui''] and vim.api.nvim_get_current_win() == tonumber(vim.g.actual_curwin or -1) and vim.ui.progress_status()) or '''' ')%}",
"%{% &showcmdloc == 'statusline' ? '%-10.S ' : '' %}",
"%{% exists('b:keymap_name') ? '<'..b:keymap_name..'> ' : '' %}",
"%{% &busy > 0 ? '◐ ' : '' %}",
"%{% luaeval('(package.loaded[''vim.diagnostic''] and next(vim.diagnostic.count()) and vim.diagnostic.status() .. '' '') or '''' ') %}",
"%{% &ruler ? ( &rulerformat == '' ? '%-14.(%l,%c%V%) %P' : &rulerformat ) : '' %}",
})
exec_lua("vim.o.statusline = ''")
eq(default_statusline, eval('&statusline'))
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
|
]])
-- Reset to default (via the correct scope) if there's an error.
command('setglobal statusline=%{a%}')
eq(default_statusline, eval('&statusline'))
eq(default_statusline, eval('&g:statusline'))
eq('', eval('&l:statusline'))
command('redrawstatus') -- like Vim, statusline isn't immediately redrawn after an error
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
{9:E121: Undefined variable: a} |
]])
command('setlocal statusline=%{b%}')
eq(default_statusline, eval('&statusline'))
eq(default_statusline, eval('&g:statusline'))
eq('', eval('&l:statusline'))
command('redrawstatus') -- like Vim, statusline isn't immediately redrawn after an error
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
{9:E121: Undefined variable: b} |
]])
end)
it('shows busy status when buffer is set to be busy', function()
exec_lua("vim.o.statusline = ''")
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
|
]])
exec_lua('vim.o.busy = 1')
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] ◐ 0,0-1 All}|
|
]])
exec_lua('vim.o.busy = 0')
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 0,0-1 All}|
|
]])
end)
it('shows exit code when terminal exits #14986', function()
exec_lua("vim.o.statusline = ''")
api.nvim_set_option_value('shell', testprg('shell-test'), {})
api.nvim_set_option_value('shellcmdflag', 'EXIT', {})
api.nvim_set_option_value('shellxquote', '', {}) -- win: avoid extra quotes
command('terminal 9')
screen:expect({ any = '%[Exit: 9%]' })
expect_exitcode(9)
end)
it('shows and updates progress status', function()
exec_lua("vim.o.statusline = ''")
local function get_progress()
return exec_lua(function()
local stl_str = vim.ui.progress_status()
return vim.api.nvim_eval_statusline(stl_str, {}).str
end)
end
eq('', get_progress())
---@type integer|string
local id1 = api.nvim_echo(
{ { 'searching...' } },
true,
{ kind = 'progress', source = 'tests', title = 'test', status = 'running', percent = 10 }
)
eq('10%(1) ', get_progress())
api.nvim_echo({ { 'searching' } }, true, {
id = id1,
kind = 'progress',
source = 'tests',
percent = 50,
status = 'running',
title = 'terminal(ripgrep)',
})
eq('50%(1) ', get_progress())
api.nvim_echo({ { 'searching...' } }, true, {
kind = 'progress',
source = 'tests',
title = 'second-item',
status = 'running',
percent = 20,
})
eq('35%(2) ', get_progress())
api.nvim_echo({ { 'searching' } }, true, {
id = id1,
kind = 'progress',
source = 'tests',
percent = 100,
status = 'success',
title = 'terminal(ripgrep)',
})
eq('20%(1) ', get_progress())
exec('redrawstatus')
screen:expect([[
^ |
{1:~ }|*13
{3:[No Name] 20%(1) 0,0-1 All}|
{131:terminal(ripgrep)}: {19:100% }searching |
]])
-- Progress_status only shown on active window
exec('split')
screen:expect([[
^ |
{1:~ }|*6
{3:[No Name] 20%(1) 0,0-1 All}|
|
{1:~ }|*5
{2:[No Name] 0,0-1 All}|
{131:terminal(ripgrep)}: {19:100% }searching |
]])
exec('wincmd w')
screen:expect([[
|
{1:~ }|*6
{2:[No Name] 0,0-1 All}|
^ |
{1:~ }|*5
{3:[No Name] 20%(1) 0,0-1 All}|
{131:terminal(ripgrep)}: {19:100% }searching |
]])
end)
end)
describe("'statusline' in floatwin", function()
local screen
before_each(function()
clear()
screen = Screen.new(30, 20)
screen:add_extra_attr_ids({
[101] = { foreground = Screen.colors.Magenta1, bold = true },
[102] = {
foreground = Screen.colors.Magenta1,
underline = true,
background = Screen.colors.LightGray,
bold = true,
},
})
end)
it('controlled by ":setlocal statusline" and "style" and "laststatus"', function()
local buf = api.nvim_create_buf(false, false)
api.nvim_buf_set_lines(buf, 0, -1, false, { '1', '2', '3', '4' })
local cfg = {
relative = 'editor',
row = 1,
col = 1,
height = 4,
width = 10,
border = 'single',
}
local win = api.nvim_open_win(buf, true, cfg)
local set_stl = [[setlocal stl=%f\ %m]]
command('set laststatus=2')
command(set_stl)
local has_stl = [[
|
{1:~}┌──────────┐{1: }|
{1:~}│{4:^1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}│{3:<Name] [+]}│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*10
{2:[No Name] }|
|
]]
screen:expect(has_stl)
-- setting the style will clear the statusline expression for floating windows
api.nvim_win_set_config(win, { style = 'minimal' })
local without_stl = [[
|
{1:~}┌──────────┐{1: }|
{1:~}│{4:^1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*11
{2:[No Name] }|
|
]]
screen:expect(without_stl)
-- no statusline is displayed because the statusline option was cleared
api.nvim_win_set_config(win, cfg)
screen:expect_unchanged()
-- displayed after the option is reset
command(set_stl)
screen:expect(has_stl)
-- Show in a new window in a new tab, then return to the previous tab;
-- remove the statusline of the new window, When re-entering this new tab,
-- the statusline of the new window is cleared
command('tabnew')
local win2 = api.nvim_open_win(buf, false, cfg)
command(set_stl)
screen:expect([[
{24: }{102:2}{24:+ [No Name] }{5: }{101:2}{5:+ [No Name] }{2: }{24:X}|
^ ┌──────────┐ |
{1:~}│{4:1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*11
{3:[No Name] }|
|
]])
command('tabfirst')
api.nvim_win_set_config(win2, { style = 'minimal' })
screen:expect([[
{5: }{101:2}{5:+ [No Name] }{24: }{102:2}{24:+ [No Name] }{2: }{24:X}|
┌──────────┐ |
{1:~}│{4:^1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}│{3:<Name] [+]}│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*10
{2:[No Name] }|
|
]])
command('tabnext')
screen:expect([[
{24: }{102:2}{24:+ [No Name] }{5: }{101:2}{5:+ [No Name] }{2: }{24:X}|
^ ┌──────────┐ |
{1:~}│{4:1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*11
{3:[No Name] }|
|
]])
-- clear statusline when laststatus is 3
command('tabclose | set laststatus=2')
screen:expect([[
|
{1:~}┌──────────┐{1: }|
{1:~}│{4:^1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}│{3:<Name] [+]}│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*10
{2:[No Name] }|
|
]])
command('set laststatus=0')
screen:expect([[
|
{1:~}┌──────────┐{1: }|
{1:~}│{4:^1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*12
|
]])
command('set laststatus=3')
screen:expect([[
|
{1:~}┌──────────┐{1: }|
{1:~}│{4:^1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*11
{3:[No Name] [+] }|
|
]])
api.nvim_buf_set_name(buf, 'stl_test')
screen:expect([[
|
{1:~}┌──────────┐{1: }|
{1:~}│{4:^1 }│{1: }|
{1:~}│{4:2 }│{1: }|
{1:~}│{4:3 }│{1: }|
{1:~}│{4:4 }│{1: }|
{1:~}└──────────┘{1: }|
{1:~ }|*11
{3:stl_test [+] }|
|
]])
end)
it("clears inherited window-local 'statusline' on creation", function()
command('set laststatus=2')
api.nvim_set_option_value('statusline', 'global', {})
local curwin = api.nvim_get_current_win()
api.nvim_set_option_value('statusline', 'split-local', { win = curwin })
api.nvim_open_win(0, true, { relative = 'editor', row = 1, col = 1, height = 2, width = 4 })
screen:expect([[
|
{1:~}{4:^ }{1: }|
{1:~}{11:~ }{1: }|
{1:~ }|*15
{2:split-local }|
|
]])
end)
end)