aboutsummaryrefslogtreecommitdiff
path: root/test/functional/ui
diff options
context:
space:
mode:
Diffstat (limited to 'test/functional/ui')
-rw-r--r--test/functional/ui/bufhl_spec.lua559
-rw-r--r--test/functional/ui/cmdline_highlight_spec.lua991
-rw-r--r--test/functional/ui/cmdline_spec.lua608
-rw-r--r--test/functional/ui/cursor_spec.lua290
-rw-r--r--test/functional/ui/diff_spec.lua959
-rw-r--r--test/functional/ui/embed_spec.lua82
-rw-r--r--test/functional/ui/fold_spec.lua235
-rw-r--r--test/functional/ui/highlight_spec.lua1267
-rw-r--r--test/functional/ui/hlstate_spec.lua287
-rw-r--r--test/functional/ui/inccommand_spec.lua2574
-rw-r--r--test/functional/ui/input_spec.lua109
-rw-r--r--test/functional/ui/mode_spec.lua189
-rw-r--r--test/functional/ui/mouse_spec.lua1077
-rw-r--r--test/functional/ui/multibyte_spec.lua185
-rw-r--r--test/functional/ui/multigrid_spec.lua1524
-rw-r--r--test/functional/ui/options_spec.lua124
-rw-r--r--test/functional/ui/output_spec.lua234
-rw-r--r--test/functional/ui/popupmenu_spec.lua610
-rw-r--r--test/functional/ui/quickfix_spec.lua196
-rw-r--r--test/functional/ui/screen.lua1201
-rw-r--r--test/functional/ui/screen_basic_spec.lua961
-rw-r--r--test/functional/ui/searchhl_spec.lua456
-rw-r--r--test/functional/ui/sign_spec.lua155
-rw-r--r--test/functional/ui/spell_spec.lua49
-rw-r--r--test/functional/ui/syntax_conceal_spec.lua742
-rw-r--r--test/functional/ui/tabline_spec.lua54
-rw-r--r--test/functional/ui/wildmode_spec.lua257
27 files changed, 14628 insertions, 1347 deletions
diff --git a/test/functional/ui/bufhl_spec.lua b/test/functional/ui/bufhl_spec.lua
index f91aa8d402..bcccef84b6 100644
--- a/test/functional/ui/bufhl_spec.lua
+++ b/test/functional/ui/bufhl_spec.lua
@@ -1,53 +1,45 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
-local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
-local execute, request, neq = helpers.execute, helpers.request, helpers.neq
+local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
+local command, neq = helpers.command, helpers.neq
+local meths = helpers.meths
+local curbufmeths, eq = helpers.curbufmeths, helpers.eq
describe('Buffer highlighting', function()
local screen
- local curbuf
-
- local hl_colors = {
- NonText = Screen.colors.Blue,
- Question = Screen.colors.SeaGreen,
- String = Screen.colors.Fuchsia,
- Statement = Screen.colors.Brown,
- Special = Screen.colors.SlateBlue,
- Identifier = Screen.colors.DarkCyan
- }
before_each(function()
clear()
- execute("syntax on")
+ command('syntax on')
screen = Screen.new(40, 8)
screen:attach()
- screen:set_default_attr_ignore( {{bold=true, foreground=hl_colors.NonText}} )
screen:set_default_attr_ids({
- [1] = {foreground = hl_colors.String},
- [2] = {foreground = hl_colors.Statement, bold = true},
- [3] = {foreground = hl_colors.Special},
- [4] = {bold = true, foreground = hl_colors.Special},
- [5] = {foreground = hl_colors.Identifier},
- [6] = {bold = true},
- [7] = {underline = true, bold = true, foreground = hl_colors.Special},
- [8] = {foreground = hl_colors.Special, underline = true}
+ [1] = {bold=true, foreground=Screen.colors.Blue},
+ [2] = {foreground = Screen.colors.Fuchsia}, -- String
+ [3] = {foreground = Screen.colors.Brown, bold = true}, -- Statement
+ [4] = {foreground = Screen.colors.SlateBlue}, -- Special
+ [5] = {bold = true, foreground = Screen.colors.SlateBlue},
+ [6] = {foreground = Screen.colors.DarkCyan}, -- Identifier
+ [7] = {bold = true},
+ [8] = {underline = true, bold = true, foreground = Screen.colors.SlateBlue},
+ [9] = {foreground = Screen.colors.SlateBlue, underline = true},
+ [10] = {foreground = Screen.colors.Red},
+ [11] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [12] = {foreground = Screen.colors.Blue1},
+ [13] = {background = Screen.colors.LightGrey},
+ [14] = {background = Screen.colors.Gray90},
+ [15] = {background = Screen.colors.Gray90, bold = true, foreground = Screen.colors.Brown},
+ [16] = {foreground = Screen.colors.Magenta, background = Screen.colors.Gray90},
})
- curbuf = request('vim_get_current_buffer')
end)
after_each(function()
screen:detach()
end)
- local function add_hl(...)
- return request('buffer_add_highlight', curbuf, ...)
- end
-
- local function clear_hl(...)
- return request('buffer_clear_highlight', curbuf, ...)
- end
-
+ local add_highlight = curbufmeths.add_highlight
+ local clear_namespace = curbufmeths.clear_namespace
it('works', function()
insert([[
@@ -58,54 +50,54 @@ describe('Buffer highlighting', function()
screen:expect([[
these are some lines |
with colorful tex^t |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
|
]])
- add_hl(-1, "String", 0 , 10, 14)
- add_hl(-1, "Statement", 1 , 5, -1)
+ add_highlight(-1, "String", 0 , 10, 14)
+ add_highlight(-1, "Statement", 1 , 5, -1)
screen:expect([[
- these are {1:some} lines |
- with {2:colorful tex^t} |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ these are {2:some} lines |
+ with {3:colorful tex^t} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
|
]])
feed("ggo<esc>")
screen:expect([[
- these are {1:some} lines |
+ these are {2:some} lines |
^ |
- with {2:colorful text} |
- ~ |
- ~ |
- ~ |
- ~ |
+ with {3:colorful text} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
|
]])
- clear_hl(-1, 0 , -1)
+ clear_namespace(-1, 0, -1)
screen:expect([[
these are some lines |
^ |
with colorful text |
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
|
]])
end)
- describe('support adding multiple sources', function()
+ describe('support using multiple namespaces', function()
local id1, id2
before_each(function()
insert([[
@@ -114,98 +106,116 @@ describe('Buffer highlighting', function()
combining highlights
from different sources]])
- execute("hi ImportantWord gui=bold cterm=bold")
- id1 = add_hl(0, "ImportantWord", 0, 2, 8)
- add_hl(id1, "ImportantWord", 1, 12, -1)
- add_hl(id1, "ImportantWord", 2, 0, 9)
- add_hl(id1, "ImportantWord", 3, 5, 14)
-
- id2 = add_hl(0, "Special", 0, 2, 8)
- add_hl(id2, "Identifier", 1, 3, 8)
- add_hl(id2, "Special", 1, 14, 20)
- add_hl(id2, "Underlined", 2, 6, 12)
- add_hl(id2, "Underlined", 3, 0, 9)
+ command("hi ImportantWord gui=bold cterm=bold")
+ id1 = add_highlight(0, "ImportantWord", 0, 2, 8)
+ add_highlight(id1, "ImportantWord", 1, 12, -1)
+ add_highlight(id1, "ImportantWord", 2, 0, 9)
+ add_highlight(id1, "ImportantWord", 3, 5, 14)
+
+ -- add_highlight can be called like this to get a new source
+ -- without adding any highlight
+ id2 = add_highlight(0, "", 0, 0, 0)
neq(id1, id2)
+ add_highlight(id2, "Special", 0, 2, 8)
+ add_highlight(id2, "Identifier", 1, 3, 8)
+ add_highlight(id2, "Special", 1, 14, 20)
+ add_highlight(id2, "Underlined", 2, 6, 12)
+ add_highlight(id2, "Underlined", 3, 0, 9)
+
screen:expect([[
- a {4:longer} example |
- in {5:order} to {6:de}{4:monstr}{6:ate} |
- {6:combin}{7:ing}{8: hi}ghlights |
- {8:from }{7:diff}{6:erent} source^s |
- ~ |
- ~ |
- ~ |
- :hi ImportantWord gui=bold cterm=bold |
+ a {5:longer} example |
+ in {6:order} to {7:de}{5:monstr}{7:ate} |
+ {7:combin}{8:ing}{9: hi}ghlights |
+ {9:from }{8:diff}{7:erent} source^s |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
]])
end)
it('and clearing the first added', function()
- clear_hl(id1, 0, -1)
+ clear_namespace(id1, 0, -1)
+ screen:expect([[
+ a {4:longer} example |
+ in {6:order} to de{4:monstr}ate |
+ combin{9:ing hi}ghlights |
+ {9:from diff}erent source^s |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('and clearing using deprecated name', function()
+ curbufmeths.clear_highlight(id1, 0, -1)
screen:expect([[
- a {3:longer} example |
- in {5:order} to de{3:monstr}ate |
- combin{8:ing hi}ghlights |
- {8:from diff}erent source^s |
- ~ |
- ~ |
- ~ |
- :hi ImportantWord gui=bold cterm=bold |
+ a {4:longer} example |
+ in {6:order} to de{4:monstr}ate |
+ combin{9:ing hi}ghlights |
+ {9:from diff}erent source^s |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
]])
end)
it('and clearing the second added', function()
- clear_hl(id2, 0, -1)
+ clear_namespace(id2, 0, -1)
screen:expect([[
- a {6:longer} example |
- in order to {6:demonstrate} |
- {6:combining} highlights |
- from {6:different} source^s |
- ~ |
- ~ |
- ~ |
- :hi ImportantWord gui=bold cterm=bold |
+ a {7:longer} example |
+ in order to {7:demonstrate} |
+ {7:combining} highlights |
+ from {7:different} source^s |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
]])
end)
it('and clearing line ranges', function()
- clear_hl(-1, 0, 1)
- clear_hl(id1, 1, 2)
- clear_hl(id2, 2, -1)
+ clear_namespace(-1, 0, 1)
+ clear_namespace(id1, 1, 2)
+ clear_namespace(id2, 2, -1)
screen:expect([[
a longer example |
- in {5:order} to de{3:monstr}ate |
- {6:combining} highlights |
- from {6:different} source^s |
- ~ |
- ~ |
- ~ |
- :hi ImportantWord gui=bold cterm=bold |
+ in {6:order} to de{4:monstr}ate |
+ {7:combining} highlights |
+ from {7:different} source^s |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
]])
end)
it('and renumbering lines', function()
feed('3Gddggo<esc>')
screen:expect([[
- a {4:longer} example |
+ a {5:longer} example |
^ |
- in {5:order} to {6:de}{4:monstr}{6:ate} |
- {8:from }{7:diff}{6:erent} sources |
- ~ |
- ~ |
- ~ |
+ in {6:order} to {7:de}{5:monstr}{7:ate} |
+ {9:from }{8:diff}{7:erent} sources |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
|
]])
- execute(':3move 4')
+ command(':3move 4')
screen:expect([[
- a {4:longer} example |
+ a {5:longer} example |
+ |
+ {9:from }{8:diff}{7:erent} sources |
+ ^in {6:order} to {7:de}{5:monstr}{7:ate} |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
|
- {8:from }{7:diff}{6:erent} sources |
- ^in {5:order} to {6:de}{4:monstr}{6:ate} |
- ~ |
- ~ |
- ~ |
- ::3move 4 |
]])
end)
end)
@@ -213,30 +223,30 @@ describe('Buffer highlighting', function()
it('prioritizes latest added highlight', function()
insert([[
three overlapping colors]])
- add_hl(0, "Identifier", 0, 6, 17)
- add_hl(0, "String", 0, 14, 23)
- local id = add_hl(0, "Special", 0, 0, 9)
+ add_highlight(0, "Identifier", 0, 6, 17)
+ add_highlight(0, "String", 0, 14, 23)
+ local id = add_highlight(0, "Special", 0, 0, 9)
screen:expect([[
- {3:three ove}{5:rlapp}{1:ing color}^s |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {4:three ove}{6:rlapp}{2:ing color}^s |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
|
]])
- clear_hl(id, 0, 1)
+ clear_namespace(id, 0, 1)
screen:expect([[
- three {5:overlapp}{1:ing color}^s |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ three {6:overlapp}{2:ing color}^s |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
|
]])
end)
@@ -244,18 +254,277 @@ describe('Buffer highlighting', function()
it('works with multibyte text', function()
insert([[
Ta båten över sjön!]])
- add_hl(-1, "Identifier", 0, 3, 9)
- add_hl(-1, "String", 0, 16, 21)
+ add_highlight(-1, "Identifier", 0, 3, 9)
+ add_highlight(-1, "String", 0, 16, 21)
screen:expect([[
- Ta {5:båten} över {1:sjön}^! |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ Ta {6:båten} över {2:sjön}^! |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('works with new syntax groups', function()
+ insert([[
+ fancy code in a new fancy language]])
+ add_highlight(-1, "FancyLangItem", 0, 0, 5)
+ screen:expect([[
+ fancy code in a new fancy languag^e |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
|
]])
+
+ command('hi FancyLangItem guifg=red')
+ screen:expect([[
+ {10:fancy} code in a new fancy languag^e |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ describe('virtual text annotations', function()
+ local set_virtual_text = curbufmeths.set_virtual_text
+ local id1, id2
+ before_each(function()
+ insert([[
+ 1 + 2
+ 3 +
+ x = 4]])
+ feed('O<esc>20A5, <esc>gg')
+ screen:expect([[
+ ^1 + 2 |
+ 3 + |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, |
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ id1 = set_virtual_text(0, 0, {{"=", "Statement"}, {" 3", "Number"}}, {})
+ set_virtual_text(id1, 1, {{"ERROR:", "ErrorMsg"}, {" invalid syntax"}}, {})
+ id2 = set_virtual_text(0, 2, {{"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."}}, {})
+ neq(id2, id1)
+
+ end)
+
+ it('works', function()
+ screen:expect([[
+ ^1 + 2 {3:=}{2: 3} |
+ 3 + {11:ERROR:} invalid syntax |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ clear_namespace(id1, 0, -1)
+ screen:expect([[
+ ^1 + 2 |
+ 3 + |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ -- Handles doublewidth chars, leaving a space if truncating
+ -- in the middle of a char
+ eq(-1, set_virtual_text(-1, 1, {{"暗x事zz速野谷質結育副住新覚丸活解終事", "Comment"}}, {}))
+ screen:expect([[
+ ^1 + 2 |
+ 3 + {12:暗x事zz速野谷質結育副住新覚丸活解終 }|
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed("2Gx")
+ screen:expect([[
+ 1 + 2 |
+ ^ + {12:暗x事zz速野谷質結育副住新覚丸活解終事}|
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed("2Gdd")
+ screen:expect([[
+ 1 + 2 |
+ ^5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('is not highlighted by visual selection', function()
+ feed("ggVG")
+ screen:expect([[
+ {13:1 + 2} {3:=}{2: 3} |
+ {13:3 +} {11:ERROR:} invalid syntax |
+ {13:5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}|
+ {13:, 5, 5, 5, 5, 5, 5, } Lorem ipsum dolor s|
+ ^x{13: = 4} |
+ {1:~ }|
+ {1:~ }|
+ {7:-- VISUAL LINE --} |
+ ]])
+
+ feed("<esc>")
+ screen:expect([[
+ 1 + 2 {3:=}{2: 3} |
+ 3 + {11:ERROR:} invalid syntax |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ ^x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ -- special case: empty line has extra eol highlight
+ feed("ggd$")
+ screen:expect([[
+ ^ {3:=}{2: 3} |
+ 3 + {11:ERROR:} invalid syntax |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed("jvk")
+ screen:expect([[
+ ^ {3:=}{2: 3} |
+ {13:3} + {11:ERROR:} invalid syntax |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ {7:-- VISUAL --} |
+ ]])
+
+ feed("o")
+ screen:expect([[
+ {13: }{3:=}{2: 3} |
+ ^3 + {11:ERROR:} invalid syntax |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ {7:-- VISUAL --} |
+ ]])
+ end)
+
+
+ it('works with listchars', function()
+ command("set list listchars+=eol:$")
+ screen:expect([[
+ ^1 + 2{1:$}{3:=}{2: 3} |
+ 3 +{1:$}{11:ERROR:} invalid syntax |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5,{1:-$}Lorem ipsum dolor s|
+ x = 4{1:$} |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ clear_namespace(-1, 0, -1)
+ screen:expect([[
+ ^1 + 2{1:$} |
+ 3 +{1:$} |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5,{1:-$} |
+ x = 4{1:$} |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('works with cursorline', function()
+ command("set cursorline")
+
+ screen:expect([[
+ {14:^1 + 2 }{15:=}{16: 3}{14: }|
+ 3 + {11:ERROR:} invalid syntax |
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed('j')
+ screen:expect([[
+ 1 + 2 {3:=}{2: 3} |
+ {14:^3 + }{11:ERROR:}{14: invalid syntax }|
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5|
+ , 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+
+ feed('j')
+ screen:expect([[
+ 1 + 2 {3:=}{2: 3} |
+ 3 + {11:ERROR:} invalid syntax |
+ {14:^5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5}|
+ {14:, 5, 5, 5, 5, 5, 5, Lorem ipsum dolor s}|
+ x = 4 |
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+ end)
+
+ it('and virtual text use the same namespace counter', function()
+ local set_virtual_text = curbufmeths.set_virtual_text
+ eq(1, add_highlight(0, "String", 0 , 0, -1))
+ eq(2, set_virtual_text(0, 0, {{"= text", "Comment"}}, {}))
+ eq(3, meths.create_namespace("my-ns"))
+ eq(4, add_highlight(0, "String", 0 , 0, -1))
+ eq(5, set_virtual_text(0, 0, {{"= text", "Comment"}}, {}))
+ eq(6, meths.create_namespace("other-ns"))
end)
end)
diff --git a/test/functional/ui/cmdline_highlight_spec.lua b/test/functional/ui/cmdline_highlight_spec.lua
new file mode 100644
index 0000000000..1568b7816e
--- /dev/null
+++ b/test/functional/ui/cmdline_highlight_spec.lua
@@ -0,0 +1,991 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local eq = helpers.eq
+local feed = helpers.feed
+local clear = helpers.clear
+local meths = helpers.meths
+local funcs = helpers.funcs
+local source = helpers.source
+local dedent = helpers.dedent
+local command = helpers.command
+local curbufmeths = helpers.curbufmeths
+
+local screen
+
+-- Bug in input() handling: :redraw! will erase the whole prompt up until
+-- user types something. It exists in Vim as well, so using `h<BS>` as
+-- a workaround.
+local function redraw_input()
+ feed('{REDRAW}h<BS>')
+end
+
+before_each(function()
+ clear()
+ screen = Screen.new(40, 8)
+ screen:attach()
+ command("set display-=msgsep")
+ source([[
+ highlight RBP1 guibg=Red
+ highlight RBP2 guibg=Yellow
+ highlight RBP3 guibg=Green
+ highlight RBP4 guibg=Blue
+ let g:NUM_LVLS = 4
+ function Redraw()
+ mode
+ return ''
+ endfunction
+ let g:id = ''
+ cnoremap <expr> {REDRAW} Redraw()
+ function DoPrompt(do_return) abort
+ let id = g:id
+ let Cb = g:Nvim_color_input{g:id}
+ let out = input({'prompt': ':', 'highlight': Cb})
+ let g:out{id} = out
+ return (a:do_return ? out : '')
+ endfunction
+ nnoremap <expr> {PROMPT} DoPrompt(0)
+ cnoremap <expr> {PROMPT} DoPrompt(1)
+ function RainBowParens(cmdline)
+ let ret = []
+ let i = 0
+ let lvl = 0
+ while i < len(a:cmdline)
+ if a:cmdline[i] is# '('
+ call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
+ let lvl += 1
+ elseif a:cmdline[i] is# ')'
+ let lvl -= 1
+ call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
+ endif
+ let i += 1
+ endwhile
+ return ret
+ endfunction
+ function SplittedMultibyteStart(cmdline)
+ let ret = []
+ let i = 0
+ while i < len(a:cmdline)
+ let char = nr2char(char2nr(a:cmdline[i:]))
+ if a:cmdline[i:i + len(char) - 1] is# char
+ if len(char) > 1
+ call add(ret, [i + 1, i + len(char), 'RBP2'])
+ endif
+ let i += len(char)
+ else
+ let i += 1
+ endif
+ endwhile
+ return ret
+ endfunction
+ function SplittedMultibyteEnd(cmdline)
+ let ret = []
+ let i = 0
+ while i < len(a:cmdline)
+ let char = nr2char(char2nr(a:cmdline[i:]))
+ if a:cmdline[i:i + len(char) - 1] is# char
+ if len(char) > 1
+ call add(ret, [i, i + 1, 'RBP1'])
+ endif
+ let i += len(char)
+ else
+ let i += 1
+ endif
+ endwhile
+ return ret
+ endfunction
+ function Echoing(cmdline)
+ echo 'HERE'
+ return v:_null_list
+ endfunction
+ function Echoning(cmdline)
+ echon 'HERE'
+ return v:_null_list
+ endfunction
+ function Echomsging(cmdline)
+ echomsg 'HERE'
+ return v:_null_list
+ endfunction
+ function Echoerring(cmdline)
+ echoerr 'HERE'
+ return v:_null_list
+ endfunction
+ function Redrawing(cmdline)
+ redraw!
+ return v:_null_list
+ endfunction
+ function Throwing(cmdline)
+ throw "ABC"
+ return v:_null_list
+ endfunction
+ function Halting(cmdline)
+ while 1
+ endwhile
+ endfunction
+ function ReturningGlobal(cmdline)
+ return g:callback_return
+ endfunction
+ function ReturningGlobal2(cmdline)
+ return g:callback_return[:len(a:cmdline)-1]
+ endfunction
+ function ReturningGlobalN(n, cmdline)
+ return g:callback_return{a:n}
+ endfunction
+ let g:recording_calls = []
+ function Recording(cmdline)
+ call add(g:recording_calls, a:cmdline)
+ return []
+ endfunction
+ ]])
+ screen:set_default_attr_ids({
+ RBP1={background = Screen.colors.Red},
+ RBP2={background = Screen.colors.Yellow},
+ RBP3={background = Screen.colors.Green},
+ RBP4={background = Screen.colors.Blue},
+ EOB={bold = true, foreground = Screen.colors.Blue1},
+ ERR={foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ SK={foreground = Screen.colors.Blue},
+ PE={bold = true, foreground = Screen.colors.SeaGreen4},
+ NUM={foreground = Screen.colors.Blue2},
+ NPAR={foreground = Screen.colors.Yellow},
+ SQ={foreground = Screen.colors.Blue3},
+ SB={foreground = Screen.colors.Blue4},
+ E={foreground = Screen.colors.Red, background = Screen.colors.Blue},
+ M={bold = true},
+ })
+end)
+
+local function set_color_cb(funcname, callback_return, id)
+ meths.set_var('id', id or '')
+ if id and id ~= '' and funcs.exists('*' .. funcname .. 'N') then
+ command(('let g:Nvim_color_input%s = {cmdline -> %sN(%s, cmdline)}'):format(
+ id, funcname, id))
+ if callback_return then
+ meths.set_var('callback_return' .. id, callback_return)
+ end
+ else
+ meths.set_var('Nvim_color_input', funcname)
+ if callback_return then
+ meths.set_var('callback_return', callback_return)
+ end
+ end
+end
+local function start_prompt(text)
+ feed('{PROMPT}' .. (text or ''))
+end
+
+describe('Command-line coloring', function()
+ it('works', function()
+ set_color_cb('RainBowParens')
+ meths.set_option('more', false)
+ start_prompt()
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :^ |
+ ]])
+ feed('e')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :e^ |
+ ]])
+ feed('cho ')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo ^ |
+ ]])
+ feed('(')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo {RBP1:(}^ |
+ ]])
+ feed('(')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo {RBP1:(}{RBP2:(}^ |
+ ]])
+ feed('42')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo {RBP1:(}{RBP2:(}42^ |
+ ]])
+ feed('))')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo {RBP1:(}{RBP2:(}42{RBP2:)}{RBP1:)}^ |
+ ]])
+ feed('<BS>')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ |
+ ]])
+ redraw_input()
+ screen:expect{grid=[[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo {RBP1:(}{RBP2:(}42{RBP2:)}^ |
+ ]], reset=true}
+ end)
+ for _, func_part in ipairs({'', 'n', 'msg'}) do
+ it('disables :echo' .. func_part .. ' messages', function()
+ set_color_cb('Echo' .. func_part .. 'ing')
+ start_prompt('echo')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo^ |
+ ]])
+ end)
+ end
+ it('does the right thing when hl start appears to split multibyte char',
+ function()
+ set_color_cb('SplittedMultibyteStart')
+ start_prompt('echo "«')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo " |
+ {ERR:E5405: Chunk 0 start 7 splits multibyte }|
+ {ERR:character} |
+ :echo "«^ |
+ ]])
+ feed('»')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo " |
+ {ERR:E5405: Chunk 0 start 7 splits multibyte }|
+ {ERR:character} |
+ :echo "«»^ |
+ ]])
+ end)
+ it('does the right thing when hl end appears to split multibyte char',
+ function()
+ set_color_cb('SplittedMultibyteEnd')
+ start_prompt('echo "«')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo " |
+ {ERR:E5406: Chunk 0 end 7 splits multibyte ch}|
+ {ERR:aracter} |
+ :echo "«^ |
+ ]])
+ end)
+ it('does the right thing when errorring', function()
+ set_color_cb('Echoerring')
+ start_prompt('e')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ : |
+ {ERR:E5407: Callback has thrown an exception:}|
+ {ERR: Vim(echoerr):HERE} |
+ :e^ |
+ ]])
+ end)
+ it('silences :echo', function()
+ set_color_cb('Echoing')
+ start_prompt('e')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :e^ |
+ ]])
+ eq('', meths.command_output('messages'))
+ end)
+ it('silences :echon', function()
+ set_color_cb('Echoning')
+ start_prompt('e')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :e^ |
+ ]])
+ eq('', meths.command_output('messages'))
+ end)
+ it('silences :echomsg', function()
+ set_color_cb('Echomsging')
+ start_prompt('e')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :e^ |
+ ]])
+ eq('', meths.command_output('messages'))
+ end)
+ it('does the right thing when throwing', function()
+ set_color_cb('Throwing')
+ start_prompt('e')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ : |
+ {ERR:E5407: Callback has thrown an exception:}|
+ {ERR: ABC} |
+ :e^ |
+ ]])
+ end)
+ it('stops executing callback after a number of errors', function()
+ set_color_cb('SplittedMultibyteStart')
+ start_prompt('let x = "«»«»«»«»«»"\n')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :let x = " |
+ {ERR:E5405: Chunk 0 start 10 splits multibyte}|
+ {ERR: character} |
+ ^:let x = "«»«»«»«»«»" |
+ ]])
+ feed('\n')
+ screen:expect([[
+ ^ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ |
+ ]])
+ eq('let x = "«»«»«»«»«»"', meths.get_var('out'))
+ local msg = '\nE5405: Chunk 0 start 10 splits multibyte character'
+ eq(msg:rep(1), funcs.execute('messages'))
+ end)
+ it('allows interrupting callback with <C-c>', function()
+ set_color_cb('Halting')
+ start_prompt('echo 42')
+ screen:expect([[
+ ^ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ |
+ ]])
+ screen:sleep(500)
+ feed('<C-c>')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ : |
+ {ERR:E5407: Callback has thrown an exception:}|
+ {ERR: Keyboard interrupt} |
+ :echo 42^ |
+ ]])
+ redraw_input()
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo 42^ |
+ ]])
+ feed('\n')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ ^:echo 42 |
+ ]])
+ feed('\n')
+ eq('echo 42', meths.get_var('out'))
+ feed('<C-c>')
+ screen:expect([[
+ ^ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ Type :qa! and pr...nges and exit Nvim |
+ ]])
+ end)
+ it('works fine with NUL, NL, CR', function()
+ set_color_cb('RainBowParens')
+ start_prompt('echo ("<C-v><CR><C-v><Nul><C-v><NL>")')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo {RBP1:(}"{SK:^M^@^@}"{RBP1:)}^ |
+ ]])
+ end)
+ it('errors out when callback returns something wrong', function()
+ command('cnoremap + ++')
+ set_color_cb('ReturningGlobal', '')
+ start_prompt('#')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ : |
+ {ERR:E5400: Callback should return list} |
+ :#^ |
+ ]])
+
+ feed('<CR><CR><CR>')
+ set_color_cb('ReturningGlobal', {{0, 1, 'Normal'}, 42})
+ start_prompt('#')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ : |
+ {ERR:E5401: List item 1 is not a List} |
+ :#^ |
+ ]])
+
+ feed('<CR><CR><CR>')
+ set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1}})
+ start_prompt('+')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :+ |
+ {ERR:E5402: List item 1 has incorrect length:}|
+ {ERR: 1 /= 3} |
+ :++^ |
+ ]])
+
+ feed('<CR><CR><CR>')
+ set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {2, 3, 'Normal'}})
+ start_prompt('+')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :+ |
+ {ERR:E5403: Chunk 1 start 2 not in range [1, }|
+ {ERR:2)} |
+ :++^ |
+ ]])
+
+ feed('<CR><CR><CR>')
+ set_color_cb('ReturningGlobal2', {{0, 1, 'Normal'}, {1, 3, 'Normal'}})
+ start_prompt('+')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :+ |
+ {ERR:E5404: Chunk 1 end 3 not in range (1, 2]}|
+ |
+ :++^ |
+ ]])
+ end)
+ it('does not error out when called from a errorred out cycle', function()
+ set_color_cb('ReturningGlobal', {{0, 1, 'Normal'}})
+ feed(dedent([[
+ :set regexpengine=2
+ :for pat in [' \ze*', ' \zs*']
+ : try
+ : let l = matchlist('x x', pat)
+ : $put =input({'prompt':'>','highlight':'ReturningGlobal'})
+ :
+ : $put ='E888 NOT detected for ' . pat
+ : catch
+ : $put =input({'prompt':'>','highlight':'ReturningGlobal'})
+ :
+ : $put ='E888 detected for ' . pat
+ : endtry
+ :endfor
+ :
+ :
+ :
+ :
+ :
+ :
+ ]]))
+ eq({'', ':', 'E888 detected for \\ze*', ':', 'E888 detected for \\zs*'},
+ curbufmeths.get_lines(0, -1, false))
+ eq('', funcs.execute('messages'))
+ end)
+ it('allows nesting input()s', function()
+ set_color_cb('ReturningGlobal', {{0, 1, 'RBP1'}}, '')
+ start_prompt('1')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :{RBP1:1}^ |
+ ]])
+
+ set_color_cb('ReturningGlobal', {{0, 1, 'RBP2'}}, '1')
+ start_prompt('2')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :{RBP2:2}^ |
+ ]])
+
+ set_color_cb('ReturningGlobal', {{0, 1, 'RBP3'}}, '2')
+ start_prompt('3')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :{RBP3:3}^ |
+ ]])
+
+ set_color_cb('ReturningGlobal', {{0, 1, 'RBP4'}}, '3')
+ start_prompt('4')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :{RBP4:4}^ |
+ ]])
+
+ feed('<CR>')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :{RBP3:3}4^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :{RBP2:2}34^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :{RBP1:1}234^ |
+ ]])
+ feed('<CR><CR><C-l>')
+ screen:expect([[
+ ^ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ |
+ ]])
+ eq('1234', meths.get_var('out'))
+ eq('234', meths.get_var('out1'))
+ eq('34', meths.get_var('out2'))
+ eq('4', meths.get_var('out3'))
+ eq(0, funcs.exists('g:out4'))
+ end)
+ it('runs callback with the same data only once', function()
+ local function new_recording_calls(...)
+ eq({...}, meths.get_var('recording_calls'))
+ meths.set_var('recording_calls', {})
+ end
+ set_color_cb('Recording')
+ start_prompt('')
+ -- Regression test. Disambiguation:
+ --
+ -- new_recording_calls(expected_result) -- (actual_before_fix)
+ --
+ feed('a')
+ new_recording_calls('a') -- ('a', 'a')
+ feed('b')
+ new_recording_calls('ab') -- ('a', 'ab', 'ab')
+ feed('c')
+ new_recording_calls('abc') -- ('ab', 'abc', 'abc')
+ feed('<BS>')
+ new_recording_calls('ab') -- ('abc', 'ab', 'ab')
+ feed('<BS>')
+ new_recording_calls('a') -- ('ab', 'a', 'a')
+ feed('<BS>')
+ new_recording_calls() -- ('a')
+ feed('<CR><CR>')
+ eq('', meths.get_var('out'))
+ end)
+ it('does not crash when callback has caught not-a-editor-command exception',
+ function()
+ source([[
+ function CaughtExc(cmdline) abort
+ try
+ gibberish
+ catch
+ " Do nothing
+ endtry
+ return []
+ endfunction
+ ]])
+ set_color_cb('CaughtExc')
+ start_prompt('1')
+ eq(1, meths.eval('1'))
+ end)
+end)
+describe('Ex commands coloring', function()
+ it('works', function()
+ meths.set_var('Nvim_color_cmdline', 'RainBowParens')
+ feed(':echo (((1)))')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :echo {RBP1:(}{RBP2:(}{RBP3:(}1{RBP3:)}{RBP2:)}{RBP1:)}^ |
+ ]])
+ end)
+ it('still executes command-line even if errored out', function()
+ meths.set_var('Nvim_color_cmdline', 'SplittedMultibyteStart')
+ feed(':let x = "«"\n')
+ eq('«', meths.get_var('x'))
+ local msg = 'E5405: Chunk 0 start 10 splits multibyte character'
+ eq('\n'..msg, funcs.execute('messages'))
+ end)
+ it('does not error out when called from a errorred out cycle', function()
+ -- Apparently when there is a cycle in which one of the commands errors out
+ -- this error may be caught by color_cmdline before it is presented to the
+ -- user.
+ feed(dedent([[
+ :set regexpengine=2
+ :for pat in [' \ze*', ' \zs*']
+ : try
+ : let l = matchlist('x x', pat)
+ : $put ='E888 NOT detected for ' . pat
+ : catch
+ : $put ='E888 detected for ' . pat
+ : endtry
+ :endfor
+ ]]))
+ eq({'', 'E888 detected for \\ze*', 'E888 detected for \\zs*'},
+ curbufmeths.get_lines(0, -1, false))
+ eq('', funcs.execute('messages'))
+ end)
+ it('does not crash when using `n` in debug mode', function()
+ feed(':debug execute "echo 1"\n')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ Entering Debug mode. Type "cont" to con|
+ tinue. |
+ cmd: execute "echo 1" |
+ >^ |
+ ]])
+ feed('n\n')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ Entering Debug mode. Type "cont" to con|
+ tinue. |
+ cmd: execute "echo 1" |
+ >n |
+ 1 |
+ {PE:Press ENTER or type command to continue}^ |
+ ]])
+ feed('\n')
+ screen:expect([[
+ ^ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ |
+ ]])
+ end)
+ it('mapping error does not cancel prompt', function()
+ command("cnoremap <expr> x execute('throw 42')[-1]")
+ feed(':#x')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :# |
+ {ERR:Error detected while processing :} |
+ {ERR:E605: Exception not caught: 42} |
+ :#^ |
+ ]])
+ feed('<CR>')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :# |
+ {ERR:Error detected while processing :} |
+ {ERR:E605: Exception not caught: 42} |
+ {ERR:E749: empty buffer} |
+ {PE:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<CR>')
+ eq('Error detected while processing :\nE605: Exception not caught: 42\nE749: empty buffer',
+ meths.command_output('messages'))
+ end)
+ it('errors out when failing to get callback', function()
+ meths.set_var('Nvim_color_cmdline', 42)
+ feed(':#')
+ screen:expect([[
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ : |
+ {ERR:E5408: Unable to get g:Nvim_color_cmdlin}|
+ {ERR:e callback: Vim:E6000: Argument is not a}|
+ {ERR: function or function name} |
+ :#^ |
+ ]])
+ end)
+end)
+describe('Expressions coloring support', function()
+ it('works', function()
+ meths.command('hi clear NvimNumber')
+ meths.command('hi clear NvimNestingParenthesis')
+ meths.command('hi NvimNumber guifg=Blue2')
+ meths.command('hi NvimNestingParenthesis guifg=Yellow')
+ feed(':echo <C-r>=(((1)))')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ ={NPAR:(((}{NUM:1}{NPAR:)))}^ |
+ ]])
+ end)
+ it('does not use Nvim_color_expr', function()
+ meths.set_var('Nvim_color_expr', 42)
+ -- Used to error out due to failing to get callback.
+ meths.command('hi clear NvimNumber')
+ meths.command('hi NvimNumber guifg=Blue2')
+ feed(':<C-r>=1')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ ={NUM:1}^ |
+ ]])
+ end)
+ it('works correctly with non-ASCII and control characters', function()
+ meths.command('hi clear NvimStringBody')
+ meths.command('hi clear NvimStringQuote')
+ meths.command('hi clear NvimInvalid')
+ meths.command('hi NvimStringQuote guifg=Blue3')
+ meths.command('hi NvimStringBody guifg=Blue4')
+ meths.command('hi NvimInvalid guifg=Red guibg=Blue')
+ feed('i<C-r>="«»"«»')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ ={SQ:"}{SB:«»}{SQ:"}{E:«»}^ |
+ ]])
+ feed('<C-c>')
+ screen:expect([[
+ ^ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {M:-- INSERT --} |
+ ]])
+ feed('<Esc>')
+ screen:expect([[
+ ^ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ |
+ ]])
+ feed(':<C-\\>e"<C-v><C-x>"<C-v><C-x>')
+ -- TODO(ZyX-I): Parser highlighting should not override special character
+ -- highlighting.
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ ={SQ:"}{SB:^X}{SQ:"}{ERR:^X}^ |
+ ]])
+ feed('<C-c>')
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ :^ |
+ ]])
+ funcs.setreg('a', {'\192'})
+ feed('<C-r>="<C-r><C-r>a"<C-r><C-r>a"foo"')
+ -- TODO(ZyX-I): Parser highlighting should not override special character
+ -- highlighting.
+ screen:expect([[
+ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ ={SQ:"}{SB:<c0>}{SQ:"}{E:<c0>"}{SB:foo}{E:"}^ |
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
new file mode 100644
index 0000000000..5d112d7f35
--- /dev/null
+++ b/test/functional/ui/cmdline_spec.lua
@@ -0,0 +1,608 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, feed = helpers.clear, helpers.feed
+local source = helpers.source
+local command = helpers.command
+
+local function test_cmdline(linegrid)
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(25, 5)
+ screen:attach({rgb=true, ext_cmdline=true, ext_linegrid=linegrid})
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {reverse = true},
+ [3] = {bold = true, reverse = true},
+ [4] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [5] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ })
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('works', function()
+ feed(':')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{""}},
+ pos = 0,
+ }}}
+
+ feed('sign')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign"}},
+ pos = 4,
+ }}}
+
+ feed('<Left>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign"}},
+ pos = 3,
+ }}}
+
+ feed('<bs>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sin"}},
+ pos = 2,
+ }}}
+
+ feed('<Esc>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ describe("redraws statusline on entering", function()
+ before_each(function()
+ command('set laststatus=2')
+ command('set statusline=%{mode()}')
+ end)
+
+ it('from normal mode', function()
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {3:n }|
+ |
+ ]]}
+
+ feed(':')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {3:c }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{""}},
+ pos = 0,
+ }}}
+ end)
+
+ it('but not with scrolled messages', function()
+ screen:try_resize(35,10)
+ feed(':echoerr doesnotexist<cr>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3: }|
+ {4:E121: Undefined variable: doesnotex}|
+ {4:ist} |
+ {5:Press ENTER or type command to cont}|
+ {5:inue}^ |
+ ]]}
+ feed(':echoerr doesnotexist<cr>')
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {3: }|
+ {4:E121: Undefined variable: doesnotex}|
+ {4:ist} |
+ {5:Press ENTER or type command to cont}|
+ {4:E121: Undefined variable: doesnotex}|
+ {4:ist} |
+ {5:Press ENTER or type command to cont}|
+ {5:inue}^ |
+ ]]}
+
+ feed(':echoerr doesnotexist<cr>')
+ screen:expect{grid=[[
+ {4:E121: Undefined variable: doesnotex}|
+ {4:ist} |
+ {5:Press ENTER or type command to cont}|
+ {4:E121: Undefined variable: doesnotex}|
+ {4:ist} |
+ {5:Press ENTER or type command to cont}|
+ {4:E121: Undefined variable: doesnotex}|
+ {4:ist} |
+ {5:Press ENTER or type command to cont}|
+ {5:inue}^ |
+ ]]}
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3:n }|
+ |
+ ]]}
+ end)
+ end)
+
+ it("works with input()", function()
+ feed(':call input("input", "default")<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ prompt = "input",
+ content = {{"default"}},
+ pos = 7,
+ }}}
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it("works with special chars and nested cmdline", function()
+ feed(':xx<c-r>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"xx"}},
+ pos = 2,
+ special = {'"', true},
+ }}}
+
+ feed('=')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"xx"}},
+ pos = 2,
+ special = {'"', true},
+ }, {
+ firstc = "=",
+ content = {{""}},
+ pos = 0,
+ }}}
+
+ feed('1+2')
+ local expectation = {{
+ firstc = ":",
+ content = {{"xx"}},
+ pos = 2,
+ special = {'"', true},
+ }, {
+ firstc = "=",
+ content = {{"1"}, {"+"}, {"2"}},
+ pos = 3,
+ }}
+
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline=expectation}
+
+ -- erase information, so we check if it is retransmitted
+ command("mode")
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline=expectation, reset=true}
+
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"xx3"}},
+ pos = 3,
+ }}}
+
+ feed('<esc>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+ end)
+
+ it("works with function definitions", function()
+ feed(':function Foo()<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ indent = 2,
+ firstc = ":",
+ content = {{""}},
+ pos = 0,
+ }}, cmdline_block = {
+ {{'function Foo()'}},
+ }}
+
+ feed('line1<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ indent = 2,
+ firstc = ":",
+ content = {{""}},
+ pos = 0,
+ }}, cmdline_block = {
+ {{'function Foo()'}},
+ {{' line1'}},
+ }}
+
+ command("mode")
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ indent = 2,
+ firstc = ":",
+ content = {{""}},
+ pos = 0,
+ }}, cmdline_block = {
+ {{'function Foo()'}},
+ {{' line1'}},
+ }, reset=true}
+
+ feed('endfunction<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ -- Try once more, to check buffer is reinitialized. #8007
+ feed(':function Bar()<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ indent = 2,
+ firstc = ":",
+ content = {{""}},
+ pos = 0,
+ }}, cmdline_block = {
+ {{'function Bar()'}},
+ }}
+
+ feed('endfunction<cr>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]]}
+
+ end)
+
+ it("works with cmdline window", function()
+ feed(':make')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"make"}},
+ pos = 4,
+ }}}
+
+ feed('<c-f>')
+ screen:expect{grid=[[
+ |
+ {2:[No Name] }|
+ {1::}make^ |
+ {3:[Command Line] }|
+ |
+ ]]}
+
+ -- nested cmdline
+ feed(':yank')
+ screen:expect{grid=[[
+ |
+ {2:[No Name] }|
+ {1::}make^ |
+ {3:[Command Line] }|
+ |
+ ]], cmdline={nil, {
+ firstc = ":",
+ content = {{"yank"}},
+ pos = 4,
+ }}}
+
+ command("mode")
+ screen:expect{grid=[[
+ |
+ {2:[No Name] }|
+ {1::}make^ |
+ {3:[Command Line] }|
+ |
+ ]], cmdline={nil, {
+ firstc = ":",
+ content = {{"yank"}},
+ pos = 4,
+ }}, reset=true}
+
+ feed("<c-c>")
+ screen:expect{grid=[[
+ |
+ {2:[No Name] }|
+ {1::}make^ |
+ {3:[Command Line] }|
+ |
+ ]]}
+
+ feed("<c-c>")
+ screen:expect{grid=[[
+ ^ |
+ {2:[No Name] }|
+ {1::}make |
+ {3:[Command Line] }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"make"}},
+ pos = 4,
+ }}}
+
+ command("redraw!")
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"make"}},
+ pos = 4,
+ }}}
+ end)
+
+ it('works with inputsecret()', function()
+ feed(":call inputsecret('secret:')<cr>abc123")
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ prompt = "secret:",
+ content = {{"******"}},
+ pos = 6,
+ }}}
+ end)
+
+ it('works with highlighted cmdline', function()
+ source([[
+ highlight RBP1 guibg=Red
+ highlight RBP2 guibg=Yellow
+ highlight RBP3 guibg=Green
+ highlight RBP4 guibg=Blue
+ let g:NUM_LVLS = 4
+ function RainBowParens(cmdline)
+ let ret = []
+ let i = 0
+ let lvl = 0
+ while i < len(a:cmdline)
+ if a:cmdline[i] is# '('
+ call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
+ let lvl += 1
+ elseif a:cmdline[i] is# ')'
+ let lvl -= 1
+ call add(ret, [i, i + 1, 'RBP' . ((lvl % g:NUM_LVLS) + 1)])
+ endif
+ let i += 1
+ endwhile
+ return ret
+ endfunction
+ map <f5> :let x = input({'prompt':'>','highlight':'RainBowParens'})<cr>
+ "map <f5> :let x = input({'prompt':'>'})<cr>
+ ]])
+ screen:set_default_attr_ids({
+ RBP1={background = Screen.colors.Red},
+ RBP2={background = Screen.colors.Yellow},
+ EOB={bold = true, foreground = Screen.colors.Blue1},
+ })
+ feed('<f5>(a(b)a)')
+ screen:expect{grid=[[
+ ^ |
+ {EOB:~ }|
+ {EOB:~ }|
+ {EOB:~ }|
+ |
+ ]], cmdline={{
+ prompt = '>',
+ content = {{'(', 'RBP1'}, {'a'}, {'(', 'RBP2'}, {'b'},
+ { ')', 'RBP2'}, {'a'}, {')', 'RBP1'}},
+ pos = 7,
+ }}}
+ end)
+
+ it('works together with ext_wildmenu', function()
+ local expected = {
+ 'define',
+ 'jump',
+ 'list',
+ 'place',
+ 'undefine',
+ 'unplace',
+ }
+
+ command('set wildmode=full')
+ command('set wildmenu')
+ screen:set_option('ext_wildmenu', true)
+ feed(':sign <tab>')
+
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign define"}},
+ pos = 11,
+ }}, wildmenu_items=expected, wildmenu_pos=0}
+
+ feed('<tab>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign jump"}},
+ pos = 9,
+ }}, wildmenu_items=expected, wildmenu_pos=1}
+
+ feed('<left><left>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign "}},
+ pos = 5,
+ }}, wildmenu_items=expected, wildmenu_pos=-1}
+
+ feed('<right>')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign define"}},
+ pos = 11,
+ }}, wildmenu_items=expected, wildmenu_pos=0}
+
+ feed('a')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]], cmdline={{
+ firstc = ":",
+ content = {{"sign definea"}},
+ pos = 12,
+ }}}
+ end)
+end
+
+-- the representation of cmdline and cmdline_block contents changed with ext_linegrid
+-- (which uses indexed highlights) so make sure to test both
+describe('ui/ext_cmdline', function() test_cmdline(true) end)
+describe('ui/ext_cmdline (legacy highlights)', function() test_cmdline(false) end)
diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua
new file mode 100644
index 0000000000..3e0370db14
--- /dev/null
+++ b/test/functional/ui/cursor_spec.lua
@@ -0,0 +1,290 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, meths = helpers.clear, helpers.meths
+local eq = helpers.eq
+local command = helpers.command
+
+describe('ui/cursor', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(25, 5)
+ screen:attach()
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it("'guicursor' is published as a UI event", function()
+ local expected_mode_info = {
+ [1] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 0,
+ cursor_shape = 'block',
+ name = 'normal',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ mouse_shape = 0,
+ short_name = 'n' },
+ [2] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 0,
+ cursor_shape = 'block',
+ name = 'visual',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ mouse_shape = 0,
+ short_name = 'v' },
+ [3] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 25,
+ cursor_shape = 'vertical',
+ name = 'insert',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ mouse_shape = 0,
+ short_name = 'i' },
+ [4] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 20,
+ cursor_shape = 'horizontal',
+ name = 'replace',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ mouse_shape = 0,
+ short_name = 'r' },
+ [5] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 0,
+ cursor_shape = 'block',
+ name = 'cmdline_normal',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ mouse_shape = 0,
+ short_name = 'c' },
+ [6] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 25,
+ cursor_shape = 'vertical',
+ name = 'cmdline_insert',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ mouse_shape = 0,
+ short_name = 'ci' },
+ [7] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 20,
+ cursor_shape = 'horizontal',
+ name = 'cmdline_replace',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ mouse_shape = 0,
+ short_name = 'cr' },
+ [8] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 20,
+ cursor_shape = 'horizontal',
+ name = 'operator',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ mouse_shape = 0,
+ short_name = 'o' },
+ [9] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 25,
+ cursor_shape = 'vertical',
+ name = 'visual_select',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ mouse_shape = 0,
+ short_name = 've' },
+ [10] = {
+ name = 'cmdline_hover',
+ mouse_shape = 0,
+ short_name = 'e' },
+ [11] = {
+ name = 'statusline_hover',
+ mouse_shape = 0,
+ short_name = 's' },
+ [12] = {
+ name = 'statusline_drag',
+ mouse_shape = 0,
+ short_name = 'sd' },
+ [13] = {
+ name = 'vsep_hover',
+ mouse_shape = 0,
+ short_name = 'vs' },
+ [14] = {
+ name = 'vsep_drag',
+ mouse_shape = 0,
+ short_name = 'vd' },
+ [15] = {
+ name = 'more',
+ mouse_shape = 0,
+ short_name = 'm' },
+ [16] = {
+ name = 'more_lastline',
+ mouse_shape = 0,
+ short_name = 'ml' },
+ [17] = {
+ blinkoff = 0,
+ blinkon = 0,
+ blinkwait = 0,
+ cell_percentage = 0,
+ cursor_shape = 'block',
+ name = 'showmatch',
+ hl_id = 0,
+ id_lm = 0,
+ attr = {},
+ attr_lm = {},
+ short_name = 'sm' },
+ }
+
+ screen:expect(function()
+ -- Default 'guicursor', published on startup.
+ eq(expected_mode_info, screen._mode_info)
+ eq(true, screen._cursor_style_enabled)
+ eq('normal', screen.mode)
+ end)
+
+ -- Event is published ONLY if the cursor style changed.
+ screen._mode_info = nil
+ command("echo 'test'")
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ test |
+ ]], condition=function()
+ eq(nil, screen._mode_info)
+ end}
+
+ -- Change the cursor style.
+ helpers.command('hi Cursor guibg=DarkGray')
+ helpers.command('set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr-o:hor20'
+ ..',a:blinkwait700-blinkoff400-blinkon250-Cursor/lCursor'
+ ..',sm:block-blinkwait175-blinkoff150-blinkon175')
+
+ -- Update the expected values.
+ for _, m in ipairs(expected_mode_info) do
+ if m.name == 'showmatch' then
+ if m.blinkon then m.blinkon = 175 end
+ if m.blinkoff then m.blinkoff = 150 end
+ if m.blinkwait then m.blinkwait = 175 end
+ else
+ if m.blinkon then m.blinkon = 250 end
+ if m.blinkoff then m.blinkoff = 400 end
+ if m.blinkwait then m.blinkwait = 700 end
+ end
+ if m.hl_id then
+ m.hl_id = 49
+ m.attr = {background = Screen.colors.DarkGray}
+ end
+ if m.id_lm then m.id_lm = 50 end
+ end
+
+ -- Assert the new expectation.
+ screen:expect(function()
+ eq(expected_mode_info, screen._mode_info)
+ eq(true, screen._cursor_style_enabled)
+ eq('normal', screen.mode)
+ end)
+
+ -- Change hl groups only, should update the styles
+ helpers.command('hi Cursor guibg=Red')
+ helpers.command('hi lCursor guibg=Green')
+
+ -- Update the expected values.
+ for _, m in ipairs(expected_mode_info) do
+ if m.hl_id then
+ m.attr = {background = Screen.colors.Red}
+ end
+ if m.id_lm then
+ m.attr_lm = {background = Screen.colors.Green}
+ end
+ end
+ -- Assert the new expectation.
+ screen:expect(function()
+ eq(expected_mode_info, screen._mode_info)
+ eq(true, screen._cursor_style_enabled)
+ eq('normal', screen.mode)
+ end)
+
+ -- Another cursor style.
+ meths.set_option('guicursor', 'n-v-c:ver35-blinkwait171-blinkoff172-blinkon173'
+ ..',ve:hor35,o:ver50,i-ci:block,r-cr:hor90,sm:ver42')
+ screen:expect(function()
+ local named = {}
+ for _, m in ipairs(screen._mode_info) do
+ named[m.name] = m
+ end
+ eq('vertical', named.normal.cursor_shape)
+ eq(35, named.normal.cell_percentage)
+ eq('horizontal', named.visual_select.cursor_shape)
+ eq(35, named.visual_select.cell_percentage)
+ eq('vertical', named.operator.cursor_shape)
+ eq(50, named.operator.cell_percentage)
+ eq('block', named.insert.cursor_shape)
+ eq('vertical', named.showmatch.cursor_shape)
+ eq(90, named.cmdline_replace.cell_percentage)
+ eq(171, named.normal.blinkwait)
+ eq(172, named.normal.blinkoff)
+ eq(173, named.normal.blinkon)
+ eq(42, named.showmatch.cell_percentage)
+ end)
+ end)
+
+ it("empty 'guicursor' sets cursor_shape=block in all modes", function()
+ meths.set_option('guicursor', '')
+ screen:expect(function()
+ -- Empty 'guicursor' sets enabled=false.
+ eq(false, screen._cursor_style_enabled)
+ for _, m in ipairs(screen._mode_info) do
+ if m['cursor_shape'] ~= nil then
+ eq('block', m.cursor_shape)
+ eq(0, m.blinkon)
+ end
+ end
+ end)
+ end)
+
+end)
diff --git a/test/functional/ui/diff_spec.lua b/test/functional/ui/diff_spec.lua
new file mode 100644
index 0000000000..8e6756e550
--- /dev/null
+++ b/test/functional/ui/diff_spec.lua
@@ -0,0 +1,959 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local feed = helpers.feed
+local clear = helpers.clear
+local write_file = helpers.write_file
+
+describe('Diff mode screen', function()
+ local fname = 'Xtest-functional-diff-screen-1'
+ local fname_2 = fname .. '.2'
+ local screen
+
+ local reread = function()
+ feed(':e<cr><c-w>w:e<cr><c-w>w')
+ end
+
+ setup(function()
+ clear()
+ os.remove(fname)
+ os.remove(fname_2)
+ end)
+
+ teardown(function()
+ os.remove(fname)
+ os.remove(fname_2)
+ end)
+
+ before_each(function()
+ clear()
+ feed(':e ' .. fname_2 .. '<cr>')
+ feed(':vnew ' .. fname .. '<cr>')
+ feed(':diffthis<cr>')
+ feed('<c-w>w:diffthis<cr><c-w>w')
+
+ screen = Screen.new(40, 16)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
+ [2] = {background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1},
+ [3] = {reverse = true},
+ [4] = {background = Screen.colors.LightBlue},
+ [5] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
+ [6] = {bold = true, foreground = Screen.colors.Blue1},
+ [7] = {bold = true, reverse = true},
+ [8] = {bold = true, background = Screen.colors.Red},
+ [9] = {background = Screen.colors.LightMagenta},
+ })
+ end)
+
+ it('Add a line in beginning of file 2', function()
+ write_file(fname, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", false)
+ write_file(fname_2, "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", false)
+ reread()
+
+ feed(':set diffopt=filler<cr>')
+ screen:expect([[
+ {1: }{2:------------------}{3:│}{1: }{4:0 }|
+ {1: }^1 {3:│}{1: }1 |
+ {1: }2 {3:│}{1: }2 |
+ {1: }3 {3:│}{1: }3 |
+ {1: }4 {3:│}{1: }4 |
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler |
+ ]])
+
+ feed(':set diffopt+=internal<cr>')
+ screen:expect([[
+ {1: }{2:------------------}{3:│}{1: }{4:0 }|
+ {1: }^1 {3:│}{1: }1 |
+ {1: }2 {3:│}{1: }2 |
+ {1: }3 {3:│}{1: }3 |
+ {1: }4 {3:│}{1: }4 |
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=internal |
+ ]])
+ end)
+
+ it('Add a line in beginning of file 1', function()
+ write_file(fname, "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", false)
+ write_file(fname_2, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", false)
+ reread()
+
+ feed(":set diffopt=filler<cr>")
+ screen:expect([[
+ {1: }{4:^0 }{3:│}{1: }{2:-----------------}|
+ {1: }1 {3:│}{1: }1 |
+ {1: }2 {3:│}{1: }2 |
+ {1: }3 {3:│}{1: }3 |
+ {1: }4 {3:│}{1: }4 |
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler |
+ ]])
+
+ feed(":set diffopt+=internal<cr>")
+ screen:expect([[
+ {1: }{4:^0 }{3:│}{1: }{2:-----------------}|
+ {1: }1 {3:│}{1: }1 |
+ {1: }2 {3:│}{1: }2 |
+ {1: }3 {3:│}{1: }3 |
+ {1: }4 {3:│}{1: }4 |
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1:+ }{5:+-- 4 lines: 7···}{3:│}{1:+ }{5:+-- 4 lines: 7··}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=internal |
+ ]])
+ end)
+
+ it('Add a line at the end of file 2', function()
+ write_file(fname, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", false)
+ write_file(fname_2, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n", false)
+ reread()
+
+ feed(":set diffopt=filler<cr>")
+ screen:expect([[
+ {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1: }7 {3:│}{1: }7 |
+ {1: }8 {3:│}{1: }8 |
+ {1: }9 {3:│}{1: }9 |
+ {1: }10 {3:│}{1: }10 |
+ {1: }{2:------------------}{3:│}{1: }{4:11 }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler |
+ ]])
+
+ feed(":set diffopt+=internal<cr>")
+ screen:expect([[
+ {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1: }7 {3:│}{1: }7 |
+ {1: }8 {3:│}{1: }8 |
+ {1: }9 {3:│}{1: }9 |
+ {1: }10 {3:│}{1: }10 |
+ {1: }{2:------------------}{3:│}{1: }{4:11 }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=internal |
+ ]])
+ end)
+
+ it('Add a line at the end of file 1', function()
+ write_file(fname, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n", false)
+ write_file(fname_2, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", false)
+ reread()
+
+ feed(":set diffopt=filler<cr>")
+ screen:expect([[
+ {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1: }7 {3:│}{1: }7 |
+ {1: }8 {3:│}{1: }8 |
+ {1: }9 {3:│}{1: }9 |
+ {1: }10 {3:│}{1: }10 |
+ {1: }{4:11 }{3:│}{1: }{2:-----------------}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler |
+ ]])
+
+ feed(":set diffopt+=internal<cr>")
+ screen:expect([[
+ {1:+ }{5:^+-- 4 lines: 1···}{3:│}{1:+ }{5:+-- 4 lines: 1··}|
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1: }7 {3:│}{1: }7 |
+ {1: }8 {3:│}{1: }8 |
+ {1: }9 {3:│}{1: }9 |
+ {1: }10 {3:│}{1: }10 |
+ {1: }{4:11 }{3:│}{1: }{2:-----------------}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=internal |
+ ]])
+ end)
+
+ it('Add a line in the middle of file 2, remove on at the end of file 1', function()
+ write_file(fname, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n", false)
+ write_file(fname_2, "1\n2\n3\n4\n4\n5\n6\n7\n8\n9\n10\n", false)
+ reread()
+
+ feed(':set diffopt=filler<cr>')
+ screen:expect([[
+ {1: }^1 {3:│}{1: }1 |
+ {1: }2 {3:│}{1: }2 |
+ {1: }3 {3:│}{1: }3 |
+ {1: }4 {3:│}{1: }4 |
+ {1: }{2:------------------}{3:│}{1: }{4:4 }|
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1: }7 {3:│}{1: }7 |
+ {1: }8 {3:│}{1: }8 |
+ {1: }9 {3:│}{1: }9 |
+ {1: }10 {3:│}{1: }10 |
+ {1: }{4:11 }{3:│}{1: }{2:-----------------}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler |
+ ]])
+
+ feed(':set diffopt+=internal<cr>')
+ screen:expect([[
+ {1: }^1 {3:│}{1: }1 |
+ {1: }2 {3:│}{1: }2 |
+ {1: }3 {3:│}{1: }3 |
+ {1: }4 {3:│}{1: }4 |
+ {1: }{2:------------------}{3:│}{1: }{4:4 }|
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1: }7 {3:│}{1: }7 |
+ {1: }8 {3:│}{1: }8 |
+ {1: }9 {3:│}{1: }9 |
+ {1: }10 {3:│}{1: }10 |
+ {1: }{4:11 }{3:│}{1: }{2:-----------------}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=internal |
+ ]])
+ end)
+
+ it('Add a line in the middle of file 1, remove on at the end of file 2', function()
+ write_file(fname, "1\n2\n3\n4\n4\n5\n6\n7\n8\n9\n10\n", false)
+ write_file(fname_2, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n", false)
+ reread()
+
+ feed(':set diffopt=filler<cr>')
+ screen:expect([[
+ {1: }^1 {3:│}{1: }1 |
+ {1: }2 {3:│}{1: }2 |
+ {1: }3 {3:│}{1: }3 |
+ {1: }4 {3:│}{1: }4 |
+ {1: }{4:4 }{3:│}{1: }{2:-----------------}|
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1: }7 {3:│}{1: }7 |
+ {1: }8 {3:│}{1: }8 |
+ {1: }9 {3:│}{1: }9 |
+ {1: }10 {3:│}{1: }10 |
+ {1: }{2:------------------}{3:│}{1: }{4:11 }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler |
+ ]])
+
+ feed(':set diffopt+=internal<cr>')
+ screen:expect([[
+ {1: }^1 {3:│}{1: }1 |
+ {1: }2 {3:│}{1: }2 |
+ {1: }3 {3:│}{1: }3 |
+ {1: }4 {3:│}{1: }4 |
+ {1: }{4:4 }{3:│}{1: }{2:-----------------}|
+ {1: }5 {3:│}{1: }5 |
+ {1: }6 {3:│}{1: }6 |
+ {1: }7 {3:│}{1: }7 |
+ {1: }8 {3:│}{1: }8 |
+ {1: }9 {3:│}{1: }9 |
+ {1: }10 {3:│}{1: }10 |
+ {1: }{2:------------------}{3:│}{1: }{4:11 }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=internal |
+ ]])
+ end)
+
+ describe('normal/patience/histogram diff algorithm', function()
+ setup(function()
+ local f1 = [[#include <stdio.h>
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+ int i;
+ for(i = 0; i < 10; i++)
+ {
+ printf("Your answer is: ");
+ printf("%d\n", foo);
+ }
+}
+
+int fact(int n)
+{
+ if(n > 1)
+ {
+ return fact(n-1) * n;
+ }
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ frobnitz(fact(10));
+}]]
+ write_file(fname, f1, false)
+ local f2 = [[#include <stdio.h>
+
+int fib(int n)
+{
+ if(n > 2)
+ {
+ return fib(n-1) + fib(n-2);
+ }
+ return 1;
+}
+
+// Frobs foo heartily
+int frobnitz(int foo)
+{
+ int i;
+ for(i = 0; i < 10; i++)
+ {
+ printf("%d\n", foo);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ frobnitz(fib(10));
+}]]
+ write_file(fname_2, f2, false)
+ end)
+
+ it('diffopt=+algorithm:myers', function()
+ reread()
+ feed(':set diffopt=internal,filler<cr>')
+ screen:expect([[
+ {1: }^#include <stdio.h>{3:│}{1: }#include <stdio.h|
+ {1: } {3:│}{1: } |
+ {1: }{8:// Frobs foo heart}{3:│}{1: }{8:int fib(int n)}{9: }|
+ {1: }{4:int frobnitz(int f}{3:│}{1: }{2:-----------------}|
+ {1: }{ {3:│}{1: }{ |
+ {1: }{9: i}{8:nt i;}{9: }{3:│}{1: }{9: i}{8:f(n > 2)}{9: }|
+ {1: }{4: for(i = 0; i <}{3:│}{1: }{2:-----------------}|
+ {1: } { {3:│}{1: } { |
+ {1: }{9: }{8:printf("Yo}{3:│}{1: }{9: }{8:return fi}|
+ {1: }{4: printf("%d}{3:│}{1: }{2:-----------------}|
+ {1: } } {3:│}{1: } } |
+ {1: }{2:------------------}{3:│}{1: }{4: return 1; }|
+ {1: }} {3:│}{1: }} |
+ {1: } {3:│}{1: } |
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=internal,filler |
+ ]])
+
+ feed('G')
+ screen:expect([[
+ {1: }{2:------------------}{3:│}{1: }{4:int frobnitz(int }|
+ {1: }{ {3:│}{1: }{ |
+ {1: }{9: i}{8:f(n > 1)}{9: }{3:│}{1: }{9: i}{8:nt i;}{9: }|
+ {1: }{2:------------------}{3:│}{1: }{4: for(i = 0; i }|
+ {1: } { {3:│}{1: } { |
+ {1: }{9: }{8:return fac}{3:│}{1: }{9: }{8:printf("%}|
+ {1: } } {3:│}{1: } } |
+ {1: }{4: return 1; }{3:│}{1: }{2:-----------------}|
+ {1: }} {3:│}{1: }} |
+ {1: } {3:│}{1: } |
+ {1: }int main(int argc,{3:│}{1: }int main(int argc|
+ {1: }{ {3:│}{1: }{ |
+ {1: }{9: frobnitz(f}{8:act}{9:(}{3:│}{1: }{9: frobnitz(f}{8:ib}{9:(}|
+ {1: }^} {3:│}{1: }} |
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=internal,filler |
+ ]])
+ end)
+
+ it('diffopt+=algorithm:patience', function()
+ reread()
+ feed(':set diffopt=internal,filler,algorithm:patience<cr>')
+ screen:expect([[
+ {1: }^#include <stdio.h>{3:│}{1: }#include <stdio.h|
+ {1: } {3:│}{1: } |
+ {1: }{2:------------------}{3:│}{1: }{4:int fib(int n) }|
+ {1: }{2:------------------}{3:│}{1: }{4:{ }|
+ {1: }{2:------------------}{3:│}{1: }{4: if(n > 2) }|
+ {1: }{2:------------------}{3:│}{1: }{4: { }|
+ {1: }{2:------------------}{3:│}{1: }{4: return fi}|
+ {1: }{2:------------------}{3:│}{1: }{4: } }|
+ {1: }{2:------------------}{3:│}{1: }{4: return 1; }|
+ {1: }{2:------------------}{3:│}{1: }{4:} }|
+ {1: }{2:------------------}{3:│}{1: }{4: }|
+ {1: }// Frobs foo heart{3:│}{1: }// Frobs foo hear|
+ {1: }int frobnitz(int f{3:│}{1: }int frobnitz(int |
+ {1: }{ {3:│}{1: }{ |
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ |
+ ]])
+
+ feed('G')
+ screen:expect([[
+ {1: } {3:│}{1: } |
+ {1: }{4:int fact(int n) }{3:│}{1: }{2:-----------------}|
+ {1: }{4:{ }{3:│}{1: }{2:-----------------}|
+ {1: }{4: if(n > 1) }{3:│}{1: }{2:-----------------}|
+ {1: }{4: { }{3:│}{1: }{2:-----------------}|
+ {1: }{4: return fac}{3:│}{1: }{2:-----------------}|
+ {1: }{4: } }{3:│}{1: }{2:-----------------}|
+ {1: }{4: return 1; }{3:│}{1: }{2:-----------------}|
+ {1: }{4:} }{3:│}{1: }{2:-----------------}|
+ {1: }{4: }{3:│}{1: }{2:-----------------}|
+ {1: }int main(int argc,{3:│}{1: }int main(int argc|
+ {1: }{ {3:│}{1: }{ |
+ {1: }{9: frobnitz(f}{8:act}{9:(}{3:│}{1: }{9: frobnitz(f}{8:ib}{9:(}|
+ {1: }^} {3:│}{1: }} |
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ |
+ ]])
+ end)
+
+ it('diffopt+=algorithm:histogram', function()
+ reread()
+ feed(':set diffopt=internal,filler,algorithm:histogram<cr>')
+ screen:expect([[
+ {1: }^#include <stdio.h>{3:│}{1: }#include <stdio.h|
+ {1: } {3:│}{1: } |
+ {1: }{2:------------------}{3:│}{1: }{4:int fib(int n) }|
+ {1: }{2:------------------}{3:│}{1: }{4:{ }|
+ {1: }{2:------------------}{3:│}{1: }{4: if(n > 2) }|
+ {1: }{2:------------------}{3:│}{1: }{4: { }|
+ {1: }{2:------------------}{3:│}{1: }{4: return fi}|
+ {1: }{2:------------------}{3:│}{1: }{4: } }|
+ {1: }{2:------------------}{3:│}{1: }{4: return 1; }|
+ {1: }{2:------------------}{3:│}{1: }{4:} }|
+ {1: }{2:------------------}{3:│}{1: }{4: }|
+ {1: }// Frobs foo heart{3:│}{1: }// Frobs foo hear|
+ {1: }int frobnitz(int f{3:│}{1: }int frobnitz(int |
+ {1: }{ {3:│}{1: }{ |
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ |
+ ]])
+
+ feed('G')
+ screen:expect([[
+ {1: } {3:│}{1: } |
+ {1: }{4:int fact(int n) }{3:│}{1: }{2:-----------------}|
+ {1: }{4:{ }{3:│}{1: }{2:-----------------}|
+ {1: }{4: if(n > 1) }{3:│}{1: }{2:-----------------}|
+ {1: }{4: { }{3:│}{1: }{2:-----------------}|
+ {1: }{4: return fac}{3:│}{1: }{2:-----------------}|
+ {1: }{4: } }{3:│}{1: }{2:-----------------}|
+ {1: }{4: return 1; }{3:│}{1: }{2:-----------------}|
+ {1: }{4:} }{3:│}{1: }{2:-----------------}|
+ {1: }{4: }{3:│}{1: }{2:-----------------}|
+ {1: }int main(int argc,{3:│}{1: }int main(int argc|
+ {1: }{ {3:│}{1: }{ |
+ {1: }{9: frobnitz(f}{8:act}{9:(}{3:│}{1: }{9: frobnitz(f}{8:ib}{9:(}|
+ {1: }^} {3:│}{1: }} |
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ |
+ ]])
+ end)
+ end)
+
+ describe('diffopt+=indent-heuristic', function()
+ setup(function()
+ local f1 = [[
+ def finalize(values)
+
+ values.each do |v|
+ v.finalize
+ end]]
+ write_file(fname, f1, false)
+ local f2 = [[
+ def finalize(values)
+
+ values.each do |v|
+ v.prepare
+ end
+
+ values.each do |v|
+ v.finalize
+ end]]
+ write_file(fname_2, f2, false)
+ feed(':diffupdate!<cr>')
+ end)
+
+ it('internal', function()
+ reread()
+ feed(":set diffopt=internal,filler<cr>")
+ screen:expect([[
+ {1: }^def finalize(value{3:│}{1: }def finalize(valu|
+ {1: } {3:│}{1: } |
+ {1: } values.each do |{3:│}{1: } values.each do |
+ {1: }{2:------------------}{3:│}{1: }{4: v.prepare }|
+ {1: }{2:------------------}{3:│}{1: }{4: end }|
+ {1: }{2:------------------}{3:│}{1: }{4: }|
+ {1: }{2:------------------}{3:│}{1: }{4: values.each do }|
+ {1: } v.finalize {3:│}{1: } v.finalize |
+ {1: } end {3:│}{1: } end |
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=internal,filler |
+ ]])
+ end)
+
+ it('indent-heuristic', function()
+ reread()
+ feed(':set diffopt=internal,filler,indent-heuristic<cr>')
+ screen:expect([[
+ {1: }^def finalize(value{3:│}{1: }def finalize(valu|
+ {1: } {3:│}{1: } |
+ {1: }{2:------------------}{3:│}{1: }{4: values.each do }|
+ {1: }{2:------------------}{3:│}{1: }{4: v.prepare }|
+ {1: }{2:------------------}{3:│}{1: }{4: end }|
+ {1: }{2:------------------}{3:│}{1: }{4: }|
+ {1: } values.each do |{3:│}{1: } values.each do |
+ {1: } v.finalize {3:│}{1: } v.finalize |
+ {1: } end {3:│}{1: } end |
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ |
+ ]])
+ end)
+
+ it('indent-heuristic random order', function()
+ reread()
+ feed(':set diffopt=internal,filler,indent-heuristic,algorithm:patience<cr>')
+ feed(':<cr>')
+ screen:expect([[
+ {1: }^def finalize(value{3:│}{1: }def finalize(valu|
+ {1: } {3:│}{1: } |
+ {1: }{2:------------------}{3:│}{1: }{4: values.each do }|
+ {1: }{2:------------------}{3:│}{1: }{4: v.prepare }|
+ {1: }{2:------------------}{3:│}{1: }{4: end }|
+ {1: }{2:------------------}{3:│}{1: }{4: }|
+ {1: } values.each do |{3:│}{1: } values.each do |
+ {1: } v.finalize {3:│}{1: } v.finalize |
+ {1: } end {3:│}{1: } end |
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ : |
+ ]])
+ end)
+ end)
+
+ it('Diff the same file', function()
+ write_file(fname, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", false)
+ write_file(fname_2, "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n", false)
+ reread()
+
+ feed(':set diffopt=filler<cr>')
+ screen:expect([[
+ {1:+ }{5:^+-- 10 lines: 1···}{3:│}{1:+ }{5:+-- 10 lines: 1··}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler |
+ ]])
+
+ feed(':set diffopt+=internal<cr>')
+ screen:expect([[
+ {1:+ }{5:^+-- 10 lines: 1···}{3:│}{1:+ }{5:+-- 10 lines: 1··}|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=internal |
+ ]])
+ end)
+
+ it('Diff an empty file', function()
+ write_file(fname, "", false)
+ write_file(fname_2, "", false)
+ reread()
+
+ feed(':set diffopt=filler<cr>')
+ screen:expect([[
+ {1:- }^ {3:│}{1:- } |
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler |
+ ]])
+
+ feed(':set diffopt+=internal<cr>')
+ screen:expect([[
+ {1:- }^ {3:│}{1:- } |
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=internal |
+ ]])
+ end)
+
+ it('diffopt+=icase', function()
+ write_file(fname, "a\nb\ncd\n", false)
+ write_file(fname_2, "A\nb\ncDe\n", false)
+ reread()
+
+ feed(':set diffopt=filler,icase<cr>')
+ screen:expect([[
+ {1: }^a {3:│}{1: }A |
+ {1: }b {3:│}{1: }b |
+ {1: }{9:cd }{3:│}{1: }{9:cD}{8:e}{9: }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler,icase |
+ ]])
+
+ feed(':set diffopt+=internal<cr>')
+ screen:expect([[
+ {1: }^a {3:│}{1: }A |
+ {1: }b {3:│}{1: }b |
+ {1: }{9:cd }{3:│}{1: }{9:cD}{8:e}{9: }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt+=internal |
+ ]])
+ end)
+
+ describe('diffopt+=iwhite', function()
+ setup(function()
+ local f1 = 'int main()\n{\n printf("Hello, World!");\n return 0;\n}\n'
+ write_file(fname, f1, false)
+ local f2 = 'int main()\n{\n if (0)\n {\n printf("Hello, World!");\n return 0;\n }\n}\n'
+ write_file(fname_2, f2, false)
+ feed(':diffupdate!<cr>')
+ end)
+
+ it('external', function()
+ reread()
+ feed(':set diffopt=filler,iwhite<cr>')
+ screen:expect([[
+ {1: }^int main() {3:│}{1: }int main() |
+ {1: }{ {3:│}{1: }{ |
+ {1: }{2:------------------}{3:│}{1: }{4: if (0) }|
+ {1: }{2:------------------}{3:│}{1: }{4: { }|
+ {1: } printf("Hello, {3:│}{1: } printf("Hel|
+ {1: } return 0; {3:│}{1: } return 0; |
+ {1: }{2:------------------}{3:│}{1: }{4: } }|
+ {1: }} {3:│}{1: }} |
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler,iwhite |
+ ]])
+ end)
+
+ it('internal', function()
+ reread()
+ feed(':set diffopt=filler,iwhite,internal<cr>')
+ screen:expect([[
+ {1: }^int main() {3:│}{1: }int main() |
+ {1: }{ {3:│}{1: }{ |
+ {1: }{2:------------------}{3:│}{1: }{4: if (0) }|
+ {1: }{2:------------------}{3:│}{1: }{4: { }|
+ {1: } printf("Hello, {3:│}{1: } printf("Hel|
+ {1: } return 0; {3:│}{1: } return 0; |
+ {1: }{2:------------------}{3:│}{1: }{4: } }|
+ {1: }} {3:│}{1: }} |
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=filler,iwhite,internal |
+ ]])
+ end)
+ end)
+
+ describe('diffopt+=iblank', function()
+ setup(function()
+ write_file(fname, 'a\n\n \ncd\nef\nxxx\n', false)
+ write_file(fname_2, 'a\ncd\n\nef\nyyy\n', false)
+ feed(':diffupdate!<cr>')
+ end)
+
+ it('generic', function()
+ reread()
+ feed(':set diffopt=internal,filler,iblank<cr>')
+ screen:expect([[
+ {1: }^a {3:│}{1: }a |
+ {1: }{4: }{3:│}{1: }{2:-----------------}|
+ {1: }{4: }{3:│}{1: }{2:-----------------}|
+ {1: }cd {3:│}{1: }cd |
+ {1: }ef {3:│}{1: } |
+ {1: }{8:xxx}{9: }{3:│}{1: }ef |
+ {1: }{6:~ }{3:│}{1: }{8:yyy}{9: }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ :set diffopt=internal,filler,iblank |
+ ]])
+ end)
+
+ it('diffopt+=iwhite', function()
+ reread()
+ feed(':set diffopt=internal,filler,iblank,iwhite<cr>')
+ feed(':<cr>')
+ screen:expect([[
+ {1: }^a {3:│}{1: }a |
+ {1: } {3:│}{1: }cd |
+ {1: } {3:│}{1: } |
+ {1: }cd {3:│}{1: }ef |
+ {1: }ef {3:│}{1: }{8:yyy}{9: }|
+ {1: }{8:xxx}{9: }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ : |
+ ]])
+ end)
+
+ it('diffopt+=iwhiteall', function()
+ reread()
+ feed(':set diffopt=internal,filler,iblank,iwhiteall<cr>')
+ feed(':<cr>')
+ screen:expect([[
+ {1: }^a {3:│}{1: }a |
+ {1: } {3:│}{1: }cd |
+ {1: } {3:│}{1: } |
+ {1: }cd {3:│}{1: }ef |
+ {1: }ef {3:│}{1: }{8:yyy}{9: }|
+ {1: }{8:xxx}{9: }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ : |
+ ]])
+ end)
+
+ it('diffopt+=iwhiteeol', function()
+ reread()
+ feed(':set diffopt=internal,filler,iblank,iwhiteeol<cr>')
+ feed(':<cr>')
+ screen:expect([[
+ {1: }^a {3:│}{1: }a |
+ {1: } {3:│}{1: }cd |
+ {1: } {3:│}{1: } |
+ {1: }cd {3:│}{1: }ef |
+ {1: }ef {3:│}{1: }{8:yyy}{9: }|
+ {1: }{8:xxx}{9: }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ : |
+ ]])
+ end)
+ end)
+
+ describe('diffopt+=iwhite{eol,all}', function()
+ setup(function()
+ write_file(fname, 'a \nx\ncd\nef\nxx xx\nfoo\nbar\n', false)
+ write_file(fname_2, 'a\nx\nc d\n ef\nxx xx\nfoo\n\nbar\n', false)
+ feed(':diffupdate!<cr>')
+ end)
+
+ it('diffopt+=iwhiteeol', function()
+ reread()
+ feed(':set diffopt=internal,filler,iwhiteeol<cr>')
+ feed(':<cr>')
+ screen:expect([[
+ {1: }^a {3:│}{1: }a |
+ {1: }x {3:│}{1: }x |
+ {1: }{9:cd }{3:│}{1: }{9:c}{8: }{9:d }|
+ {1: }{9:ef }{3:│}{1: }{8: }{9:ef }|
+ {1: }{9:xx }{8: }{9:xx }{3:│}{1: }{9:xx xx }|
+ {1: }foo {3:│}{1: }foo |
+ {1: }{2:------------------}{3:│}{1: }{4: }|
+ {1: }bar {3:│}{1: }bar |
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ : |
+ ]])
+ end)
+
+ it('diffopt+=iwhiteall', function()
+ reread()
+ feed(':set diffopt=internal,filler,iwhiteall<cr>')
+ feed(':<cr>')
+ screen:expect([[
+ {1: }^a {3:│}{1: }a |
+ {1: }x {3:│}{1: }x |
+ {1: }cd {3:│}{1: }c d |
+ {1: }ef {3:│}{1: } ef |
+ {1: }xx xx {3:│}{1: }xx xx |
+ {1: }foo {3:│}{1: }foo |
+ {1: }{2:------------------}{3:│}{1: }{4: }|
+ {1: }bar {3:│}{1: }bar |
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {1: }{6:~ }{3:│}{1: }{6:~ }|
+ {7:<onal-diff-screen-1 }{3:<l-diff-screen-1.2 }|
+ : |
+ ]])
+ end)
+ end)
+end)
diff --git a/test/functional/ui/embed_spec.lua b/test/functional/ui/embed_spec.lua
new file mode 100644
index 0000000000..10dbc68672
--- /dev/null
+++ b/test/functional/ui/embed_spec.lua
@@ -0,0 +1,82 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local feed = helpers.feed
+local eq = helpers.eq
+local clear = helpers.clear
+
+local function test_embed(ext_linegrid)
+ local screen
+ local function startup(...)
+ clear{headless=false, args={...}}
+
+ -- attach immediately after startup, for early UI
+ screen = Screen.new(60, 8)
+ screen:attach{ext_linegrid=ext_linegrid}
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [2] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [3] = {bold = true, foreground = Screen.colors.Blue1},
+ [4] = {bold = true, foreground = Screen.colors.Green},
+ })
+ end
+
+ it('can display errors', function()
+ startup('--cmd', 'echoerr invalid+')
+ screen:expect([[
+ |
+ |
+ |
+ |
+ |
+ Error detected while processing pre-vimrc command line: |
+ E121: Undefined variable: invalid |
+ Press ENTER or type command to continue^ |
+ ]])
+
+ feed('<cr>')
+ screen:expect([[
+ ^ |
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ {3:~ }|
+ |
+ ]])
+ end)
+
+ it("doesn't erase output when setting color scheme", function()
+ startup('--cmd', 'echoerr "foo"', '--cmd', 'color default', '--cmd', 'echoerr "bar"')
+ screen:expect([[
+ |
+ |
+ |
+ |
+ Error detected while processing pre-vimrc command line: |
+ foo |
+ {1:bar} |
+ {4:Press ENTER or type command to continue}^ |
+ ]])
+ end)
+
+ it("doesn't erase output when setting Normal colors", function()
+ startup('--cmd', 'echoerr "foo"', '--cmd', 'hi Normal guibg=Green', '--cmd', 'echoerr "bar"')
+ screen:expect{grid=[[
+ |
+ |
+ |
+ |
+ Error detected while processing pre-vimrc command line: |
+ foo |
+ bar |
+ Press ENTER or type command to continue^ |
+ ]], condition=function()
+ eq(Screen.colors.Green, screen.default_colors.rgb_bg)
+ end}
+ end)
+end
+
+describe('--embed UI on startup (ext_linegrid=true)', function() test_embed(true) end)
+describe('--embed UI on startup (ext_linegrid=false)', function() test_embed(false) end)
diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua
new file mode 100644
index 0000000000..943cbcef56
--- /dev/null
+++ b/test/functional/ui/fold_spec.lua
@@ -0,0 +1,235 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, feed, eq = helpers.clear, helpers.feed, helpers.eq
+local command = helpers.command
+local feed_command = helpers.feed_command
+local insert = helpers.insert
+local funcs = helpers.funcs
+local meths = helpers.meths
+
+describe("folded lines", function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(45, 8)
+ screen:attach({rgb=true})
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {reverse = true},
+ [3] = {bold = true, reverse = true},
+ [4] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [5] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
+ [6] = {background = Screen.colors.Yellow},
+ [7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
+ })
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it("highlighting with relative line numbers", function()
+ command("set relativenumber foldmethod=marker")
+ feed_command("set foldcolumn=2")
+ funcs.setline(1, '{{{1')
+ funcs.setline(2, 'line 1')
+ funcs.setline(3, '{{{1')
+ funcs.setline(4, 'line 2')
+ feed("j")
+ screen:expect([[
+ {7:+ }{5: 1 +-- 2 lines: ·························}|
+ {7:+ }{5: 0 ^+-- 2 lines: ·························}|
+ {7: }{1:~ }|
+ {7: }{1:~ }|
+ {7: }{1:~ }|
+ {7: }{1:~ }|
+ {7: }{1:~ }|
+ :set foldcolumn=2 |
+ ]])
+ end)
+
+ it("works with multibyte text", function()
+ -- Currently the only allowed value of 'maxcombine'
+ eq(6, meths.get_option('maxcombine'))
+ eq(true, meths.get_option('arabicshape'))
+ insert([[
+ å 语 x̨̣̘̫̲͚͎̎͂̀̂͛͛̾͢͟ العَرَبِيَّة
+ möre text]])
+ screen:expect([[
+ å 语 x̎͂̀̂͛͛ ﺎﻠﻋَﺮَﺒِﻳَّﺓ |
+ möre tex^t |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed('vkzf')
+ screen:expect([[
+ {5:^+-- 2 lines: å 语 x̎͂̀̂͛͛ ﺎﻠﻋَﺮَﺒِﻳَّﺓ·················}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed_command("set noarabicshape")
+ screen:expect([[
+ {5:^+-- 2 lines: å 语 x̎͂̀̂͛͛ العَرَبِيَّة·················}|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :set noarabicshape |
+ ]])
+
+ feed_command("set number foldcolumn=2")
+ screen:expect([[
+ {7:+ }{5: 1 ^+-- 2 lines: å 语 x̎͂̀̂͛͛ العَرَبِيَّة···········}|
+ {7: }{1:~ }|
+ {7: }{1:~ }|
+ {7: }{1:~ }|
+ {7: }{1:~ }|
+ {7: }{1:~ }|
+ {7: }{1:~ }|
+ :set number foldcolumn=2 |
+ ]])
+
+ -- Note: too much of the folded line gets cut off.This is a vim bug.
+ feed_command("set rightleft")
+ screen:expect([[
+ {5:+-- 2 lines: å ······················^· 1 }{7: +}|
+ {1: ~}{7: }|
+ {1: ~}{7: }|
+ {1: ~}{7: }|
+ {1: ~}{7: }|
+ {1: ~}{7: }|
+ {1: ~}{7: }|
+ :set rightleft |
+ ]])
+
+ feed_command("set nonumber foldcolumn=0")
+ screen:expect([[
+ {5:+-- 2 lines: å 语 x̎͂̀̂͛͛ ال·····················^·}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ :set nonumber foldcolumn=0 |
+ ]])
+
+ feed_command("set arabicshape")
+ screen:expect([[
+ {5:+-- 2 lines: å 语 x̎͂̀̂͛͛ ﺍﻟ·····················^·}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ :set arabicshape |
+ ]])
+
+ feed('zo')
+ screen:expect([[
+ ﺔﻴَّﺑِﺮَﻌَ^ﻟﺍ x̎͂̀̂͛͛ 语 å|
+ txet eröm|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ :set arabicshape |
+ ]])
+
+ feed_command('set noarabicshape')
+ screen:expect([[
+ ةيَّبِرَعَ^لا x̎͂̀̂͛͛ 语 å|
+ txet eröm|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ {1: ~}|
+ :set noarabicshape |
+ ]])
+
+ end)
+
+ it("work in cmdline window", function()
+ feed_command("set foldmethod=manual")
+ feed_command("let x = 1")
+ feed_command("/alpha")
+ feed_command("/omega")
+
+ feed("<cr>q:")
+ screen:expect([[
+ |
+ {2:[No Name] }|
+ {1::}set foldmethod=manual |
+ {1::}let x = 1 |
+ {1::}^ |
+ {1::~ }|
+ {3:[Command Line] }|
+ : |
+ ]])
+
+ feed("kzfk")
+ screen:expect([[
+ |
+ {2:[No Name] }|
+ {1::}{5:^+-- 2 lines: set foldmethod=manual·········}|
+ {1::} |
+ {1::~ }|
+ {1::~ }|
+ {3:[Command Line] }|
+ : |
+ ]])
+
+ feed("<cr>")
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ : |
+ ]])
+
+ feed("/<c-f>")
+ screen:expect([[
+ |
+ {2:[No Name] }|
+ {1:/}alpha |
+ {1:/}{6:omega} |
+ {1:/}^ |
+ {1:/~ }|
+ {3:[Command Line] }|
+ / |
+ ]])
+
+ feed("ggzfG")
+ screen:expect([[
+ |
+ {2:[No Name] }|
+ {1:/}{5:^+-- 3 lines: alpha·························}|
+ {1:/~ }|
+ {1:/~ }|
+ {1:/~ }|
+ {3:[Command Line] }|
+ / |
+ ]])
+
+ end)
+end)
diff --git a/test/functional/ui/highlight_spec.lua b/test/functional/ui/highlight_spec.lua
index 6ef40fff62..39170337d7 100644
--- a/test/functional/ui/highlight_spec.lua
+++ b/test/functional/ui/highlight_spec.lua
@@ -2,22 +2,22 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local os = require('os')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
-local execute, request, eq = helpers.execute, helpers.request, helpers.eq
+local command = helpers.command
+local eval, exc_exec = helpers.eval, helpers.exc_exec
+local feed_command, eq = helpers.feed_command, helpers.eq
+local curbufmeths = helpers.curbufmeths
-
-describe('color scheme compatibility', function()
+describe('colorscheme compatibility', function()
before_each(function()
clear()
end)
it('t_Co is set to 256 by default', function()
- eq('256', request('vim_eval', '&t_Co'))
- request('vim_set_option', 't_Co', '88')
- eq('88', request('vim_eval', '&t_Co'))
+ eq('256', eval('&t_Co'))
end)
end)
-describe('manual syntax highlight', function()
+describe('highlight: `:syntax manual`', function()
-- When using manual syntax highlighting, it should be preserved even when
-- switching buffers... bug did only occur without :set hidden
-- Ref: vim patch 7.4.1236
@@ -27,10 +27,11 @@ describe('manual syntax highlight', function()
clear()
screen = Screen.new(20,5)
screen:attach()
- --ignore highligting of ~-lines
- screen:set_default_attr_ignore( {{bold=true, foreground=Screen.colors.Blue}} )
--syntax highlight for vimcscripts "echo"
- screen:set_default_attr_ids( {[1] = {bold=true, foreground=Screen.colors.Brown}} )
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {bold=true, foreground=Screen.colors.Brown}
+ } )
end)
after_each(function()
@@ -39,67 +40,61 @@ describe('manual syntax highlight', function()
end)
it("works with buffer switch and 'hidden'", function()
- execute('e tmp1.vim')
- execute('e Xtest-functional-ui-highlight.tmp.vim')
- execute('filetype on')
- execute('syntax manual')
- execute('set ft=vim')
- execute('set syntax=ON')
+ command('e tmp1.vim')
+ command('e Xtest-functional-ui-highlight.tmp.vim')
+ command('filetype on')
+ command('syntax manual')
+ command('set ft=vim')
+ command('set syntax=ON')
feed('iecho 1<esc>0')
- execute('set hidden')
- execute('w')
- execute('bn')
- execute('bp')
+ command('set hidden')
+ command('w')
+ command('bn')
+ feed_command('bp')
screen:expect([[
{1:^echo} 1 |
- ~ |
- ~ |
- ~ |
- <f 1 --100%-- col 1 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ :bp |
]])
end)
it("works with buffer switch and 'nohidden'", function()
- execute('e tmp1.vim')
- execute('e Xtest-functional-ui-highlight.tmp.vim')
- execute('filetype on')
- execute('syntax manual')
- execute('set ft=vim')
- execute('set syntax=ON')
+ command('e tmp1.vim')
+ command('e Xtest-functional-ui-highlight.tmp.vim')
+ command('filetype on')
+ command('syntax manual')
+ command('set filetype=vim fileformat=unix')
+ command('set syntax=ON')
feed('iecho 1<esc>0')
- execute('set nohidden')
- execute('w')
- execute('bn')
- execute('bp')
+ command('set nohidden')
+ command('w')
+ command('silent bn')
+ eq("tmp1.vim", eval("fnamemodify(bufname('%'), ':t')"))
+ feed_command('silent bp')
+ eq("Xtest-functional-ui-highlight.tmp.vim", eval("fnamemodify(bufname('%'), ':t')"))
screen:expect([[
{1:^echo} 1 |
- ~ |
- ~ |
- ~ |
- <ht.tmp.vim" 1L, 7C |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ :silent bp |
]])
end)
end)
-describe('Default highlight groups', function()
- -- Test the default attributes for highlight groups shown by the :highlight
- -- command
+describe('highlight defaults', function()
local screen
- local hlgroup_colors = {
- NonText = Screen.colors.Blue,
- Question = Screen.colors.SeaGreen
- }
-
before_each(function()
clear()
screen = Screen.new()
screen:attach()
- --ignore highligting of ~-lines
- screen:set_default_attr_ignore( {{bold=true, foreground=hlgroup_colors.NonText}} )
+ command("set display-=msgsep")
end)
after_each(function()
@@ -108,269 +103,297 @@ describe('Default highlight groups', function()
it('window status bar', function()
screen:set_default_attr_ids({
+ [0] = {bold=true, foreground=Screen.colors.Blue},
[1] = {reverse = true, bold = true}, -- StatusLine
[2] = {reverse = true} -- StatusLineNC
})
- execute('sp', 'vsp', 'vsp')
- screen:expect([[
- ^ {2:|} {2:|} |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
+ feed_command('sp', 'vsp', 'vsp')
+ screen:expect([[
+ ^ {2:│} {2:│} |
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
{1:[No Name] }{2:[No Name] [No Name] }|
|
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
{2:[No Name] }|
- |
+ :vsp |
]])
-- navigate to verify that the attributes are properly moved
feed('<c-w>j')
screen:expect([[
- {2:|} {2:|} |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
+ {2:│} {2:│} |
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
{2:[No Name] [No Name] [No Name] }|
^ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
{1:[No Name] }|
- |
+ :vsp |
]])
-- note that when moving to a window with small width nvim will increase
-- the width of the new active window at the expense of a inactive window
-- (upstream vim has the same behavior)
feed('<c-w>k<c-w>l')
screen:expect([[
- {2:|}^ {2:|} |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
+ {2:│}^ {2:│} |
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
{2:[No Name] }{1:[No Name] }{2:[No Name] }|
|
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
{2:[No Name] }|
- |
+ :vsp |
]])
feed('<c-w>l')
screen:expect([[
- {2:|} {2:|}^ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
+ {2:│} {2:│}^ |
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
{2:[No Name] [No Name] }{1:[No Name] }|
|
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
{2:[No Name] }|
- |
+ :vsp |
]])
feed('<c-w>h<c-w>h')
screen:expect([[
- ^ {2:|} {2:|} |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
- ~ {2:|}~ {2:|}~ |
+ ^ {2:│} {2:│} |
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
+ {0:~ }{2:│}{0:~ }{2:│}{0:~ }|
{1:[No Name] }{2:[No Name] [No Name] }|
|
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
{2:[No Name] }|
- |
+ :vsp |
]])
end)
it('insert mode text', function()
feed('i')
+ screen:try_resize(53, 4)
screen:expect([[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
{1:-- INSERT --} |
- ]], {[1] = {bold = true}})
+ ]], {[0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {bold = true}})
end)
it('end of file markers', function()
+ screen:try_resize(53, 4)
screen:expect([[
^ |
{1:~ }|
{1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
|
- ]], {[1] = {bold = true, foreground = hlgroup_colors.NonText}})
+ ]], {[1] = {bold = true, foreground = Screen.colors.Blue}})
end)
it('"wait return" text', function()
+ screen:try_resize(53, 4)
feed(':ls<cr>')
screen:expect([[
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
:ls |
1 %a "[No Name]" line 1 |
{1:Press ENTER or type command to continue}^ |
- ]], {[1] = {bold = true, foreground = hlgroup_colors.Question}})
+ ]], {[0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {bold = true, foreground = Screen.colors.SeaGreen}})
feed('<cr>') -- skip the "Press ENTER..." state or tests will hang
end)
+
it('can be cleared and linked to other highlight groups', function()
- execute('highlight clear ModeMsg')
+ screen:try_resize(53, 4)
+ feed_command('highlight clear ModeMsg')
feed('i')
screen:expect([[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
-- INSERT -- |
- ]], {})
+ ]], {[0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {bold=true}})
feed('<esc>')
- execute('highlight CustomHLGroup guifg=red guibg=green')
- execute('highlight link ModeMsg CustomHLGroup')
+ feed_command('highlight CustomHLGroup guifg=red guibg=green')
+ feed_command('highlight link ModeMsg CustomHLGroup')
feed('i')
screen:expect([[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
{1:-- INSERT --} |
- ]], {[1] = {foreground = Screen.colors.Red, background = Screen.colors.Green}})
+ ]], {[0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {foreground = Screen.colors.Red, background = Screen.colors.Green}})
end)
+
it('can be cleared by assigning NONE', function()
- execute('syn keyword TmpKeyword neovim')
- execute('hi link TmpKeyword ErrorMsg')
+ screen:try_resize(53, 4)
+ feed_command('syn keyword TmpKeyword neovim')
+ feed_command('hi link TmpKeyword ErrorMsg')
insert('neovim')
screen:expect([[
{1:neovi^m} |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
|
]], {
+ [0] = {bold=true, foreground=Screen.colors.Blue},
[1] = {foreground = Screen.colors.White, background = Screen.colors.Red}
})
- execute("hi ErrorMsg term=NONE cterm=NONE ctermfg=NONE ctermbg=NONE"
+ feed_command("hi ErrorMsg term=NONE cterm=NONE ctermfg=NONE ctermbg=NONE"
.. " gui=NONE guifg=NONE guibg=NONE guisp=NONE")
screen:expect([[
neovi^m |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], {[0] = {bold=true, foreground=Screen.colors.Blue}})
+ end)
+
+ it('Cursor after `:hi clear|syntax reset` #6508', function()
+ command('highlight clear|syntax reset')
+ eq('guifg=bg guibg=fg', eval([[matchstr(execute('hi Cursor'), '\v(gui|cterm).*$')]]))
+ end)
+
+ it('Whitespace highlight', function()
+ screen:try_resize(53, 4)
+ feed_command('highlight NonText gui=NONE guifg=#FF0000')
+ feed_command('set listchars=space:.,tab:>-,trail:*,eol:¬ list')
+ insert(' ne \t o\tv im ')
+ screen:expect([[
+ ne{0:.>----.}o{0:>-----}v{0:..}im{0:*^*¬} |
+ {0:~ }|
+ {0:~ }|
|
- ]], {})
+ ]], {
+ [0] = {foreground=Screen.colors.Red},
+ [1] = {foreground=Screen.colors.Blue},
+ })
+ feed_command('highlight Whitespace gui=NONE guifg=#0000FF')
+ screen:expect([[
+ ne{1:.>----.}o{1:>-----}v{1:..}im{1:*^*}{0:¬} |
+ {0:~ }|
+ {0:~ }|
+ :highlight Whitespace gui=NONE guifg=#0000FF |
+ ]], {
+ [0] = {foreground=Screen.colors.Red},
+ [1] = {foreground=Screen.colors.Blue},
+ })
end)
end)
-describe('guisp (special/undercurl)', function()
+describe('highlight', function()
local screen
before_each(function()
clear()
screen = Screen.new(25,10)
screen:attach()
- screen:set_default_attr_ignore({
- [1] = {bold = true, foreground = Screen.colors.Blue},
- [2] = {bold = true}
+ end)
+
+ it('visual', function()
+ screen:detach()
+ screen = Screen.new(20,4)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {background = Screen.colors.LightGrey},
+ [2] = {bold = true, foreground = Screen.colors.Blue1},
+ [3] = {bold = true},
})
+ insert([[
+ line1 foo bar
+ ]])
+
+ -- Non-blinking block cursor: does NOT highlight char-at-cursor.
+ command('set guicursor=a:block-blinkon0')
+ feed('gg$vhhh')
+ screen:expect([[
+ line1 foo^ {1:bar} |
+ |
+ {2:~ }|
+ {3:-- VISUAL --} |
+ ]])
+
+ -- Vertical cursor: highlights char-at-cursor. #8983
+ command('set guicursor=a:block-blinkon175')
+ feed('<esc>gg$vhhh')
+ screen:expect([[
+ line1 foo{1:^ bar} |
+ |
+ {2:~ }|
+ {3:-- VISUAL --} |
+ ]])
end)
- it('can be set and is applied like foreground or background', function()
- execute('syntax on')
- execute('syn keyword TmpKeyword neovim')
- execute('syn keyword TmpKeyword1 special')
- execute('syn keyword TmpKeyword2 specialwithbg')
- execute('syn keyword TmpKeyword3 specialwithfg')
- execute('hi! Awesome guifg=red guibg=yellow guisp=red')
- execute('hi! Awesome1 guisp=red')
- execute('hi! Awesome2 guibg=yellow guisp=red')
- execute('hi! Awesome3 guifg=red guisp=red')
- execute('hi link TmpKeyword Awesome')
- execute('hi link TmpKeyword1 Awesome1')
- execute('hi link TmpKeyword2 Awesome2')
- execute('hi link TmpKeyword3 Awesome3')
+ it('cterm=standout gui=standout', function()
+ screen:detach()
+ screen = Screen.new(20,5)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {standout = true, bold = true, underline = true,
+ background = Screen.colors.Gray90, foreground = Screen.colors.Blue1},
+ [3] = {standout = true, underline = true,
+ background = Screen.colors.Gray90}
+ })
+ feed_command('hi CursorLine cterm=standout,underline gui=standout,underline')
+ feed_command('set cursorline')
+ feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list')
+ feed('i\t abcd <cr>\t abcd <cr><esc>k')
+ screen:expect([[
+ {1:>-------.}abcd{1:*¬} |
+ {2:^>-------.}{3:abcd}{2:*¬}{3: }|
+ {1:¬} |
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('guisp (special/undercurl)', function()
+ feed_command('syntax on')
+ feed_command('syn keyword TmpKeyword neovim')
+ feed_command('syn keyword TmpKeyword1 special')
+ feed_command('syn keyword TmpKeyword2 specialwithbg')
+ feed_command('syn keyword TmpKeyword3 specialwithfg')
+ feed_command('hi! Awesome guifg=red guibg=yellow guisp=red')
+ feed_command('hi! Awesome1 guisp=red')
+ feed_command('hi! Awesome2 guibg=yellow guisp=red')
+ feed_command('hi! Awesome3 guifg=red guisp=red')
+ feed_command('hi link TmpKeyword Awesome')
+ feed_command('hi link TmpKeyword1 Awesome1')
+ feed_command('hi link TmpKeyword2 Awesome2')
+ feed_command('hi link TmpKeyword3 Awesome3')
insert([[
neovim
awesome neovim
@@ -389,29 +412,24 @@ describe('guisp (special/undercurl)', function()
{4:specialwithfg} |
|
{1:neovim} tabbed^ |
- ~ |
- -- INSERT -- |
+ {0:~ }|
+ {5:-- INSERT --} |
]],{
+ [0] = {bold=true, foreground=Screen.colors.Blue},
[1] = {background = Screen.colors.Yellow, foreground = Screen.colors.Red,
special = Screen.colors.Red},
[2] = {special = Screen.colors.Red},
[3] = {special = Screen.colors.Red, background = Screen.colors.Yellow},
[4] = {foreground = Screen.colors.Red, special = Screen.colors.Red},
+ [5] = {bold=true},
})
end)
end)
-describe("'cursorline' with 'listchars'", function()
+describe("'listchars' highlight", function()
local screen
- local hlgroup_colors = {
- NonText = Screen.colors.Blue,
- Cursorline = Screen.colors.Grey90,
- SpecialKey = Screen.colors.Red,
- Visual = Screen.colors.LightGrey,
- }
-
before_each(function()
clear()
screen = Screen.new(20,5)
@@ -423,48 +441,50 @@ describe("'cursorline' with 'listchars'", function()
end)
it("'cursorline' and 'cursorcolumn'", function()
- screen:set_default_attr_ids({[1] = {background=hlgroup_colors.Cursorline}})
- screen:set_default_attr_ignore( {{bold=true, foreground=hlgroup_colors.NonText}} )
- execute('highlight clear ModeMsg')
- execute('set cursorline')
+ screen:set_default_attr_ids({
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {background=Screen.colors.Grey90}
+ })
+ feed_command('highlight clear ModeMsg')
+ feed_command('set cursorline')
feed('i')
screen:expect([[
{1:^ }|
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
-- INSERT -- |
]])
feed('abcdefg<cr>kkasdf')
screen:expect([[
abcdefg |
{1:kkasdf^ }|
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
-- INSERT -- |
]])
feed('<esc>')
screen:expect([[
abcdefg |
{1:kkasd^f }|
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
|
]])
- execute('set nocursorline')
+ feed_command('set nocursorline')
screen:expect([[
abcdefg |
kkasd^f |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
:set nocursorline |
]])
feed('k')
screen:expect([[
abcde^fg |
kkasdf |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
:set nocursorline |
]])
feed('jjji<cr><cr><cr><esc>')
@@ -475,8 +495,8 @@ describe("'cursorline' with 'listchars'", function()
^f |
|
]])
- execute('set cursorline')
- execute('set cursorcolumn')
+ feed_command('set cursorline')
+ feed_command('set cursorcolumn')
feed('kkiabcdefghijk<esc>hh')
screen:expect([[
kkasd {1: } |
@@ -497,29 +517,29 @@ describe("'cursorline' with 'listchars'", function()
it("'cursorline' and with 'listchar' option: space, eol, tab, and trail", function()
screen:set_default_attr_ids({
- [1] = {background=hlgroup_colors.Cursorline},
+ [1] = {background=Screen.colors.Grey90},
[2] = {
- foreground=hlgroup_colors.SpecialKey,
- background=hlgroup_colors.Cursorline,
+ foreground=Screen.colors.Red,
+ background=Screen.colors.Grey90,
},
[3] = {
- background=hlgroup_colors.Cursorline,
- foreground=hlgroup_colors.NonText,
+ background=Screen.colors.Grey90,
+ foreground=Screen.colors.Blue,
bold=true,
},
[4] = {
- foreground=hlgroup_colors.NonText,
+ foreground=Screen.colors.Blue,
bold=true,
},
[5] = {
- foreground=hlgroup_colors.SpecialKey,
+ foreground=Screen.colors.Red,
},
})
- execute('highlight clear ModeMsg')
- execute('highlight SpecialKey guifg=#FF0000')
- execute('set cursorline')
- execute('set tabstop=8')
- execute('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list')
+ feed_command('highlight clear ModeMsg')
+ feed_command('highlight Whitespace guifg=#FF0000')
+ feed_command('set cursorline')
+ feed_command('set tabstop=8')
+ feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list')
feed('i\t abcd <cr>\t abcd <cr><esc>k')
screen:expect([[
{5:>-------.}abcd{5:*}{4:¬} |
@@ -536,7 +556,7 @@ describe("'cursorline' with 'listchars'", function()
{4:~ }|
|
]])
- execute('set nocursorline')
+ feed_command('set nocursorline')
screen:expect([[
{5:^>-------.}abcd{5:*}{4:¬} |
{5:>-------.}abcd{5:*}{4:¬} |
@@ -544,7 +564,7 @@ describe("'cursorline' with 'listchars'", function()
{4:~ }|
:set nocursorline |
]])
- execute('set nowrap')
+ feed_command('set nowrap')
feed('ALorem ipsum dolor sit amet<ESC>0')
screen:expect([[
{5:^>-------.}abcd{5:.}Lorem{4:>}|
@@ -553,9 +573,9 @@ describe("'cursorline' with 'listchars'", function()
{4:~ }|
|
]])
- execute('set cursorline')
+ feed_command('set cursorline')
screen:expect([[
- {2:^>-------.}{1:abcd}{2:.}{1:Lorem}{4:>}|
+ {2:^>-------.}{1:abcd}{2:.}{1:Lorem}{3:>}|
{5:>-------.}abcd{5:*}{4:¬} |
{4:¬} |
{4:~ }|
@@ -563,7 +583,7 @@ describe("'cursorline' with 'listchars'", function()
]])
feed('$')
screen:expect([[
- {4:<}{1:r}{2:.}{1:sit}{2:.}{1:ame^t}{3:¬}{1: }|
+ {3:<}{1:r}{2:.}{1:sit}{2:.}{1:ame^t}{3:¬}{1: }|
{4:<} |
{4:<} |
{4:~ }|
@@ -581,42 +601,42 @@ describe("'cursorline' with 'listchars'", function()
it("'listchar' in visual mode", function()
screen:set_default_attr_ids({
- [1] = {background=hlgroup_colors.Cursorline},
+ [1] = {background=Screen.colors.Grey90},
[2] = {
- foreground=hlgroup_colors.SpecialKey,
- background=hlgroup_colors.Cursorline,
+ foreground=Screen.colors.Red,
+ background=Screen.colors.Grey90,
},
[3] = {
- background=hlgroup_colors.Cursorline,
- foreground=hlgroup_colors.NonText,
+ background=Screen.colors.Grey90,
+ foreground=Screen.colors.Blue,
bold=true,
},
[4] = {
- foreground=hlgroup_colors.NonText,
+ foreground=Screen.colors.Blue,
bold=true,
},
[5] = {
- foreground=hlgroup_colors.SpecialKey,
+ foreground=Screen.colors.Red,
},
[6] = {
- background=hlgroup_colors.Visual,
+ background=Screen.colors.LightGrey,
},
[7] = {
- background=hlgroup_colors.Visual,
- foreground=hlgroup_colors.SpecialKey,
+ background=Screen.colors.LightGrey,
+ foreground=Screen.colors.Red,
},
[8] = {
- background=hlgroup_colors.Visual,
- foreground=hlgroup_colors.NonText,
+ background=Screen.colors.LightGrey,
+ foreground=Screen.colors.Blue,
bold=true,
},
})
- execute('highlight clear ModeMsg')
- execute('highlight SpecialKey guifg=#FF0000')
- execute('set cursorline')
- execute('set tabstop=8')
- execute('set nowrap')
- execute('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list')
+ feed_command('highlight clear ModeMsg')
+ feed_command('highlight Whitespace guifg=#FF0000')
+ feed_command('set cursorline')
+ feed_command('set tabstop=8')
+ feed_command('set nowrap')
+ feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list')
feed('i\t abcd <cr>\t abcd Lorem ipsum dolor sit amet<cr><esc>kkk0')
screen:expect([[
{2:^>-------.}{1:abcd}{2:*}{3:¬}{1: }|
@@ -644,10 +664,697 @@ describe("'cursorline' with 'listchars'", function()
feed('<esc>$')
screen:expect([[
{4:<} |
- {4:<}{1:r}{2:.}{1:sit}{2:.}{1:ame^t}{3:¬}{1: }|
+ {3:<}{1:r}{2:.}{1:sit}{2:.}{1:ame^t}{3:¬}{1: }|
{4:<} |
{4:~ }|
|
]])
end)
+
+ it("'cursorline' with :match", function()
+ screen:set_default_attr_ids({
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {background=Screen.colors.Grey90},
+ [2] = {foreground=Screen.colors.Red},
+ [3] = {foreground=Screen.colors.Green1},
+ })
+ feed_command('highlight clear ModeMsg')
+ feed_command('highlight Whitespace guifg=#FF0000')
+ feed_command('highlight Error guifg=#00FF00')
+ feed_command('set nowrap')
+ feed('ia \t bc \t <esc>')
+ screen:expect([[
+ a bc ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed_command('set listchars=space:.,eol:¬,tab:>-,extends:>,precedes:<,trail:* list')
+ screen:expect([[
+ a{2:.>-----.}bc{2:*>---*^*}{0:¬} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed_command('match Error /\\s\\+$/')
+ screen:expect([[
+ a{2:.>-----.}bc{3:*>---*^*}{0:¬} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+end)
+
+describe('CursorLine highlight', function()
+ before_each(clear)
+
+ it('overridden by Error, ColorColumn if fg not set', function()
+ local screen = Screen.new(50,5)
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.SlateBlue},
+ [2] = {bold = true, foreground = Screen.colors.Brown},
+ [3] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [4] = {foreground = Screen.colors.SlateBlue, background = Screen.colors.Gray90},
+ [5] = {background = Screen.colors.Gray90},
+ [6] = {bold = true, foreground = Screen.colors.Blue1},
+ [7] = {background = Screen.colors.LightRed},
+ })
+ screen:attach()
+
+ command('filetype on')
+ command('syntax on')
+ command('set cursorline ft=json')
+ feed('i{<cr>"a" : abc // 10;<cr>}<cr><esc>')
+ screen:expect([[
+ {1:{} |
+ "{2:a}" : {3:abc} {3:// 10;} |
+ {1:}} |
+ {5:^ }|
+ |
+ ]])
+
+ command('set colorcolumn=3')
+ feed('i <esc>')
+ screen:expect([[
+ {1:{} {7: } |
+ "{2:a}{7:"} : {3:abc} {3:// 10;} |
+ {1:}} {7: } |
+ {5: ^ }{7: }{5: }|
+ |
+ ]])
+ end)
+
+ it('with split-windows in diff-mode', function()
+ local screen = Screen.new(50,12)
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
+ [2] = {bold = true, background = Screen.colors.Red},
+ [3] = {background = Screen.colors.LightMagenta},
+ [4] = {reverse = true},
+ [5] = {background = Screen.colors.LightBlue},
+ [6] = {background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1},
+ [7] = {background = Screen.colors.Red, foreground = Screen.colors.White},
+ [8] = {bold = true, foreground = Screen.colors.Blue1},
+ [9] = {bold = true, reverse = true},
+ [10] = {bold = true},
+ })
+ screen:attach()
+
+ command('hi CursorLine ctermbg=red ctermfg=white guibg=red guifg=white')
+ command('set cursorline')
+ feed('iline 1 some text<cr>line 2 more text<cr>extra line!<cr>extra line!<cr>last line ...<cr>')
+ feed('<esc>gg')
+ command('vsplit')
+ command('enew')
+ feed('iline 1 some text<cr>line 2 moRe text!<cr>extra line!<cr>extra line!<cr>extra line!<cr>last line ...<cr>')
+ feed('<esc>gg')
+ command('windo diffthis')
+ screen:expect([[
+ {1: }{7:line 1 some text }{4:│}{1: }{7:^line 1 some text }|
+ {1: }{3:line 2 mo}{2:Re text!}{3: }{4:│}{1: }{3:line 2 mo}{2:re text}{3: }|
+ {1: }{5:extra line! }{4:│}{1: }{6:----------------------}|
+ {1: }extra line! {4:│}{1: }extra line! |
+ {1: }extra line! {4:│}{1: }extra line! |
+ {1: }last line ... {4:│}{1: }last line ... |
+ {1: } {4:│}{1: } |
+ {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {4:[No Name] [+] }{9:[No Name] [+] }|
+ |
+ ]])
+ feed('jjjjj')
+ screen:expect([[
+ {1: }line 1 some text {4:│}{1: }line 1 some text |
+ {1: }{3:line 2 mo}{2:Re text!}{3: }{4:│}{1: }{3:line 2 mo}{2:re text}{3: }|
+ {1: }{5:extra line! }{4:│}{1: }{6:----------------------}|
+ {1: }extra line! {4:│}{1: }extra line! |
+ {1: }extra line! {4:│}{1: }extra line! |
+ {1: }last line ... {4:│}{1: }last line ... |
+ {1: }{7: }{4:│}{1: }{7:^ }|
+ {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {4:[No Name] [+] }{9:[No Name] [+] }|
+ |
+ ]])
+
+ -- CursorLine with fg=NONE is "low-priority".
+ -- Rendered as underline in a diff-line. #9028
+ command('hi CursorLine ctermbg=red ctermfg=NONE guibg=red guifg=NONE')
+ feed('kkkk')
+ screen:expect([[
+ {1: }line 1 some text {4:│}{1: }line 1 some text |
+ {1: }{11:line 2 mo}{12:Re text!}{11: }{4:│}{1: }{11:^line 2 mo}{12:re text}{11: }|
+ {1: }{5:extra line! }{4:│}{1: }{6:----------------------}|
+ {1: }extra line! {4:│}{1: }extra line! |
+ {1: }extra line! {4:│}{1: }extra line! |
+ {1: }last line ... {4:│}{1: }last line ... |
+ {1: } {4:│}{1: } |
+ {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {1: }{8:~ }{4:│}{1: }{8:~ }|
+ {4:[No Name] [+] }{9:[No Name] [+] }|
+ |
+ ]], {
+ [1] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
+ [2] = {bold = true, background = Screen.colors.Red},
+ [3] = {background = Screen.colors.LightMagenta},
+ [4] = {reverse = true},
+ [5] = {background = Screen.colors.LightBlue},
+ [6] = {background = Screen.colors.LightCyan1, bold = true, foreground = Screen.colors.Blue1},
+ [7] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [8] = {bold = true, foreground = Screen.colors.Blue1},
+ [9] = {bold = true, reverse = true},
+ [10] = {bold = true},
+ [11] = {underline = true,
+ background = Screen.colors.LightMagenta},
+ [12] = {bold = true, underline = true,
+ background = Screen.colors.Red},
+ })
+ end)
+end)
+
+
+describe("MsgSeparator highlight and msgsep fillchar", function()
+ before_each(clear)
+ it("works", function()
+ local screen = Screen.new(50,5)
+ screen:set_default_attr_ids({
+ [1] = {bold=true, foreground=Screen.colors.Blue},
+ [2] = {bold=true, reverse=true},
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [4] = {background = Screen.colors.Cyan, bold = true, reverse = true},
+ [5] = {bold = true, background = Screen.colors.Magenta}
+ })
+ screen:attach()
+
+ -- defaults
+ feed_command("ls")
+ screen:expect([[
+ |
+ {2: }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>')
+
+ feed_command("set fillchars+=msgsep:-")
+ feed_command("ls")
+ screen:expect([[
+ |
+ {2:--------------------------------------------------}|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+
+ -- linked to StatusLine per default
+ feed_command("hi StatusLine guibg=Cyan")
+ feed_command("ls")
+ screen:expect([[
+ |
+ {4:--------------------------------------------------}|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+
+ -- but can be unlinked
+ feed_command("hi clear MsgSeparator")
+ feed_command("hi MsgSeparator guibg=Magenta gui=bold")
+ feed_command("ls")
+ screen:expect([[
+ |
+ {5:--------------------------------------------------}|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+
+ -- when display doesn't contain msgsep, these options have no effect
+ feed_command("set display-=msgsep")
+ feed_command("ls")
+ screen:expect([[
+ {1:~ }|
+ {1:~ }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ end)
+end)
+
+describe("'winhighlight' highlight", function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(20,8)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {background = Screen.colors.DarkBlue},
+ [2] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Blue1},
+ [3] = {bold = true, reverse = true},
+ [4] = {reverse = true},
+ [5] = {background = Screen.colors.DarkGreen},
+ [6] = {background = Screen.colors.DarkGreen, bold = true, foreground = Screen.colors.Blue1},
+ [7] = {background = Screen.colors.DarkMagenta},
+ [8] = {background = Screen.colors.DarkMagenta, bold = true, foreground = Screen.colors.Blue1},
+ [9] = {foreground = Screen.colors.Brown},
+ [10] = {foreground = Screen.colors.Brown, background = Screen.colors.DarkBlue},
+ [11] = {background = Screen.colors.DarkBlue, bold = true, reverse = true},
+ [12] = {background = Screen.colors.DarkGreen, reverse = true},
+ [13] = {background = Screen.colors.Magenta4, reverse = true},
+ [14] = {background = Screen.colors.DarkBlue, reverse = true},
+ [15] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [16] = {foreground = Screen.colors.Blue1},
+ [17] = {background = Screen.colors.LightRed},
+ [18] = {background = Screen.colors.Gray90},
+ [19] = {foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray},
+ [20] = {background = Screen.colors.LightGrey, underline = true},
+ [21] = {bold = true},
+ [22] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [23] = {background = Screen.colors.LightMagenta},
+ [24] = {background = Screen.colors.WebGray},
+ [25] = {bold = true, foreground = Screen.colors.Green1},
+ [26] = {background = Screen.colors.Red},
+ [27] = {background = Screen.colors.DarkBlue, bold = true, foreground = Screen.colors.Green1},
+ })
+ command("hi Background1 guibg=DarkBlue")
+ command("hi Background2 guibg=DarkGreen")
+ end)
+
+ it('works for background color', function()
+ insert("aa")
+ command("split")
+ command("set winhl=Normal:Background1")
+ screen:expect([[
+ {1:a^a }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] [+] }|
+ aa |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+
+ command("enew")
+ screen:expect([[
+ {1:^ }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] }|
+ aa |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end)
+
+ it('handles invalid values', function()
+ command("set winhl=Normal:Background1")
+ screen:expect([[
+ {1:^ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]])
+
+ eq('Vim(set):E474: Invalid argument: winhl=xxx:yyy',
+ exc_exec("set winhl=xxx:yyy"))
+ eq('Normal:Background1', eval('&winhl'))
+ screen:expect{grid=[[
+ {1:^ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]], unchanged=true}
+ end)
+
+
+ it('works local to the buffer', function()
+ insert("aa")
+ command("split")
+ command("setlocal winhl=Normal:Background1")
+ screen:expect([[
+ {1:a^a }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] [+] }|
+ aa |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+
+ command("enew")
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ aa |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+
+ command("bnext")
+ screen:expect([[
+ {1:^aa }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] [+] }|
+ aa |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end)
+
+ it('for inactive window background works', function()
+ command("set winhl=Normal:Background1,NormalNC:Background2")
+ -- tests global value is copied across split
+ command("split")
+ screen:expect([[
+ {1:^ }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] }|
+ {5: }|
+ {6:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+
+ feed("<c-w><c-w>")
+ screen:expect([[
+ {5: }|
+ {6:~ }|
+ {6:~ }|
+ {4:[No Name] }|
+ {1:^ }|
+ {2:~ }|
+ {3:[No Name] }|
+ |
+ ]])
+
+ feed("<c-w><c-w>")
+ screen:expect([[
+ {1:^ }|
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] }|
+ {5: }|
+ {6:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+ end)
+
+ it('works with NormalNC', function()
+ command("hi NormalNC guibg=DarkMagenta")
+ -- tests global value is copied across split
+ command("split")
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ {7: }|
+ {8:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+
+ command("wincmd w")
+ screen:expect([[
+ {7: }|
+ {8:~ }|
+ {8:~ }|
+ {4:[No Name] }|
+ ^ |
+ {0:~ }|
+ {3:[No Name] }|
+ |
+ ]])
+
+
+ -- winbg=Normal:... overrides global NormalNC
+ command("set winhl=Normal:Background1")
+ screen:expect([[
+ {7: }|
+ {8:~ }|
+ {8:~ }|
+ {4:[No Name] }|
+ {1:^ }|
+ {2:~ }|
+ {3:[No Name] }|
+ |
+ ]])
+
+ command("wincmd w")
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ {1: }|
+ {2:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+
+ command("wincmd w")
+ command("set winhl=Normal:Background1,NormalNC:Background2")
+ screen:expect([[
+ {7: }|
+ {8:~ }|
+ {8:~ }|
+ {4:[No Name] }|
+ {1:^ }|
+ {2:~ }|
+ {3:[No Name] }|
+ |
+ ]])
+
+ command("wincmd w")
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ {5: }|
+ {6:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+ end)
+
+ it('background applies also to non-text', function()
+ command('set sidescroll=0')
+ insert('Lorem ipsum dolor sit amet ')
+ command('set shiftwidth=2')
+ feed('>>')
+ command('set number')
+ command('set breakindent')
+ command('set briopt=shift:5,min:0')
+ command('set list')
+ command('set showbreak=↪')
+ screen:expect([[
+ {9: 1 } ^Lorem ipsum do|
+ {9: } {0:↪}lor sit |
+ {9: } {0:↪}amet{0:-} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ command('set winhl=Normal:Background1')
+ screen:expect([[
+ {10: 1 }{1: ^Lorem ipsum do}|
+ {10: }{1: }{2:↪}{1:lor sit }|
+ {10: }{1: }{2:↪}{1:amet}{2:-}{1: }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]])
+
+ command('set nowrap')
+ command('set listchars+=extends:❯,precedes:❮')
+ feed('3w')
+ screen:expect([[
+ {10: 1 }{2:❮}{1: dolor ^sit ame}{2:❯}|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]])
+ end)
+
+ it("background doesn't override syntax background", function()
+ command('syntax on')
+ command('syntax keyword Foobar foobar')
+ command('syntax keyword Article the')
+ command('hi Foobar guibg=#FF0000')
+ command('hi Article guifg=#00FF00 gui=bold')
+ insert('the foobar was foobar')
+ screen:expect([[
+ {25:the} {26:foobar} was {26:fooba}|
+ {26:^r} |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ -- winhl=Normal:Group with background doesn't override syntax background,
+ -- but does combine with syntax foreground.
+ command('set winhl=Normal:Background1')
+ screen:expect([[
+ {27:the}{1: }{26:foobar}{1: was }{26:fooba}|
+ {26:^r}{1: }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]])
+ end)
+
+ it('can override NonText, Conceal and EndOfBuffer', function()
+ curbufmeths.set_lines(0,-1,true, {"raa\000"})
+ command('call matchaddpos("Conceal", [[1,2]], 0, -1, {"conceal": "#"})')
+ command('set cole=2 cocu=nvic')
+ command('split')
+ command('call matchaddpos("Conceal", [[1,2]], 0, -1, {"conceal": "#"})')
+ command('set winhl=SpecialKey:ErrorMsg,EndOfBuffer:Background1,'
+ ..'Conceal:Background2')
+
+ screen:expect([[
+ ^r{5:#}a{15:^@} |
+ {1:~ }|
+ {1:~ }|
+ {3:[No Name] [+] }|
+ r{19:#}a{16:^@} |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end)
+
+ it('can override LineNr, CursorColumn and ColorColumn', function()
+ insert('very text\nmore text')
+ command('set number')
+ command('set colorcolumn=2')
+ command('set cursorcolumn')
+
+ command('split')
+ command('set winhl=LineNr:Background1,CursorColumn:Background2,'
+ ..'ColorColumn:ErrorMsg')
+ screen:expect([[
+ {1: 1 }v{15:e}ry tex{5:t} |
+ {1: 2 }m{15:o}re tex^t |
+ {0:~ }|
+ {3:[No Name] [+] }|
+ {9: 1 }v{17:e}ry tex{18:t} |
+ {9: 2 }m{17:o}re text |
+ {4:[No Name] [+] }|
+ |
+ ]])
+ end)
+
+ it('can override Tabline', function()
+ command('tabnew')
+ command('set winhl=TabLine:Background1,TabLineSel:ErrorMsg')
+
+ screen:expect([[
+ {20: No Name] }{15: No Name]}{20:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ command("tabnext")
+ screen:expect([[
+ {21: No Name] }{1: No Name]}{20:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('can override popupmenu', function()
+ insert('word wording wordy')
+ command('split')
+ command('set winhl=Pmenu:Background1,PmenuSel:Background2,'
+ ..'PmenuSbar:ErrorMsg,PmenuThumb:Normal')
+ screen:expect([[
+ word wording word^y |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] [+] }|
+ word wording wordy |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ |
+ ]])
+
+ feed('oword<c-x><c-p>')
+ screen:expect([[
+ word wording wordy |
+ wordy^ |
+ {1:word }{0: }|
+ {1:wording }{3: }|
+ {5:wordy }rdy |
+ wordy |
+ {4:[No Name] [+] }|
+ {21:-- }{22:match 1 of 3} |
+ ]])
+
+ feed('<esc>u<c-w><c-w>oword<c-x><c-p>')
+ screen:expect([[
+ word wording wordy |
+ wordy |
+ {23:word }{0: }|
+ {23:wording }{4: }|
+ {24:wordy }rdy |
+ wordy^ |
+ {3:[No Name] [+] }|
+ {21:-- }{22:match 1 of 3} |
+ ]])
+ end)
end)
diff --git a/test/functional/ui/hlstate_spec.lua b/test/functional/ui/hlstate_spec.lua
new file mode 100644
index 0000000000..775b701438
--- /dev/null
+++ b/test/functional/ui/hlstate_spec.lua
@@ -0,0 +1,287 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local clear, insert = helpers.clear, helpers.insert
+local command = helpers.command
+local meths = helpers.meths
+local iswin = helpers.iswin
+local nvim_dir = helpers.nvim_dir
+local thelpers = require('test.functional.terminal.helpers')
+
+describe('ext_hlstate detailed highlights', function()
+ local screen
+
+ before_each(function()
+ clear()
+ command('syntax on')
+ screen = Screen.new(40, 8)
+ screen:attach({ext_hlstate=true})
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+
+ it('work with combined UI and syntax highlights', function()
+ insert([[
+ these are some lines
+ with colorful text]])
+ meths.buf_add_highlight(0, -1, "String", 0 , 10, 14)
+ meths.buf_add_highlight(0, -1, "Statement", 1 , 5, -1)
+ command("/th co")
+
+ screen:expect([[
+ these are {1:some} lines |
+ ^wi{2:th }{4:co}{3:lorful text} |
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {5:~ }|
+ {6:search hit BOTTOM, continuing at TOP} |
+ ]], {
+ [1] = {{foreground = Screen.colors.Magenta},
+ {{hi_name = "Constant", kind = "syntax"}}},
+ [2] = {{background = Screen.colors.Yellow},
+ {{hi_name = "Search", ui_name = "Search", kind = "ui"}}},
+ [3] = {{bold = true, foreground = Screen.colors.Brown},
+ {{hi_name = "Statement", kind = "syntax"}}},
+ [4] = {{bold = true, background = Screen.colors.Yellow, foreground = Screen.colors.Brown}, {3, 2}},
+ [5] = {{bold = true, foreground = Screen.colors.Blue1},
+ {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
+ [6] = {{foreground = Screen.colors.Red},
+ {{hi_name = "WarningMsg", ui_name = "WarningMsg", kind = "ui"}}},
+ })
+ end)
+
+ it('work with cleared UI highlights', function()
+ screen:set_default_attr_ids({
+ [1] = {{}, {{hi_name = "VertSplit", ui_name = "VertSplit", kind = "ui"}}},
+ [2] = {{bold = true, foreground = Screen.colors.Blue1},
+ {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
+ [3] = {{bold = true, reverse = true},
+ {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}} ,
+ [4] = {{reverse = true},
+ {{hi_name = "StatusLineNC", ui_name = "StatusLineNC" , kind = "ui"}}},
+ [5] = {{}, {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}},
+ [6] = {{}, {{hi_name = "StatusLineNC", ui_name = "StatusLineNC", kind = "ui"}}},
+ })
+ command("hi clear VertSplit")
+ command("vsplit")
+
+ screen:expect([[
+ ^ {1:│} |
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {3:[No Name] }{4:[No Name] }|
+ |
+ ]])
+
+ command("hi clear StatusLine | hi clear StatuslineNC")
+ screen:expect([[
+ ^ {1:│} |
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {5:[No Name] }{6:[No Name] }|
+ |
+ ]])
+
+ -- redrawing is done even if visible highlights didn't change
+ command("wincmd w")
+ screen:expect([[
+ {1:│}^ |
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {2:~ }{1:│}{2:~ }|
+ {6:[No Name] }{5:[No Name] }|
+ |
+ ]])
+
+ end)
+
+ it("work with window-local highlights", function()
+ screen:set_default_attr_ids({
+ [1] = {{foreground = Screen.colors.Brown}, {{hi_name = "LineNr", ui_name = "LineNr", kind = "ui"}}},
+ [2] = {{bold = true, foreground = Screen.colors.Blue1}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
+ [3] = {{bold = true, reverse = true}, {{hi_name = "StatusLine", ui_name = "StatusLine", kind = "ui"}}},
+ [4] = {{reverse = true}, {{hi_name = "StatusLineNC", ui_name = "StatusLineNC", kind = "ui"}}},
+ [5] = {{background = Screen.colors.Red, foreground = Screen.colors.Grey100}, {{hi_name = "ErrorMsg", ui_name = "LineNr", kind = "ui"}}},
+ [6] = {{bold = true, reverse = true}, {{hi_name = "MsgSeparator", ui_name = "Normal", kind = "ui"}}},
+ [7] = {{foreground = Screen.colors.Brown, bold = true, reverse = true}, {6, 1}},
+ [8] = {{foreground = Screen.colors.Blue1, bold = true, reverse = true}, {6, 2}},
+ [9] = {{bold = true, foreground = Screen.colors.Brown}, {{hi_name = "Statement", ui_name = "NormalNC", kind = "ui"}}},
+ [10] = {{bold = true, foreground = Screen.colors.Brown}, {9, 1}},
+ [11] = {{bold = true, foreground = Screen.colors.Blue1}, {9, 2}}
+ })
+
+ command("set number")
+ command("split")
+ -- NormalNC is not applied if not set, to avoid spurious redraws
+ screen:expect([[
+ {1: 1 }^ |
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] }|
+ {1: 1 } |
+ {2:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+
+ command("set winhl=LineNr:ErrorMsg")
+ screen:expect([[
+ {5: 1 }^ |
+ {2:~ }|
+ {2:~ }|
+ {3:[No Name] }|
+ {1: 1 } |
+ {2:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+
+ command("set winhl=Normal:MsgSeparator,NormalNC:Statement")
+ screen:expect([[
+ {7: 1 }{6:^ }|
+ {8:~ }|
+ {8:~ }|
+ {3:[No Name] }|
+ {1: 1 } |
+ {2:~ }|
+ {4:[No Name] }|
+ |
+ ]])
+
+ command("wincmd w")
+ screen:expect([[
+ {10: 1 }{9: }|
+ {11:~ }|
+ {11:~ }|
+ {4:[No Name] }|
+ {1: 1 }^ |
+ {2:~ }|
+ {3:[No Name] }|
+ |
+ ]])
+ end)
+
+ it("work with :terminal", function()
+ screen:set_default_attr_ids({
+ [1] = {{}, {{hi_name = "TermCursorNC", ui_name = "TermCursorNC", kind = "ui"}}},
+ [2] = {{foreground = 52479}, {{kind = "term"}}},
+ [3] = {{bold = true, foreground = 52479}, {{kind = "term"}}},
+ [4] = {{foreground = 52479}, {2, 1}},
+ [5] = {{foreground = 4259839}, {{kind = "term"}}},
+ [6] = {{foreground = 4259839}, {5, 1}},
+ })
+ command('enew | call termopen(["'..nvim_dir..'/tty-test"])')
+ screen:expect([[
+ ^tty ready |
+ {1: } |
+ |
+ |
+ |
+ |
+ |
+ |
+ ]])
+
+ thelpers.feed_data('x ')
+ thelpers.set_fg(45)
+ thelpers.feed_data('y ')
+ thelpers.set_bold()
+ thelpers.feed_data('z\n')
+ -- TODO(bfredl): check if this distinction makes sense
+ if iswin() then
+ screen:expect([[
+ ^tty ready |
+ x {5:y z} |
+ {1: } |
+ |
+ |
+ |
+ |
+ |
+ ]])
+ else
+ screen:expect([[
+ ^tty ready |
+ x {2:y }{3:z} |
+ {1: } |
+ |
+ |
+ |
+ |
+ |
+ ]])
+ end
+
+ thelpers.feed_termcode("[A")
+ thelpers.feed_termcode("[2C")
+ if iswin() then
+ screen:expect([[
+ ^tty ready |
+ x {6:y}{5: z} |
+ |
+ |
+ |
+ |
+ |
+ |
+ ]])
+ else
+ screen:expect([[
+ ^tty ready |
+ x {4:y}{2: }{3:z} |
+ |
+ |
+ |
+ |
+ |
+ |
+ ]])
+ end
+ end)
+
+ it("can use independent cterm and rgb colors", function()
+ -- tell test module to save all attributes (doesn't change nvim options)
+ screen:set_hlstate_cterm(true)
+
+ screen:set_default_attr_ids({
+ [1] = {{bold = true, foreground = Screen.colors.Blue1}, {foreground = 12}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
+ [2] = {{reverse = true, foreground = Screen.colors.Red}, {foreground = 10, italic=true}, {{hi_name = "NonText", ui_name = "EndOfBuffer", kind = "ui"}}},
+ })
+ screen:expect([[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ command("hi NonText guifg=Red gui=reverse ctermfg=Green cterm=italic")
+ screen:expect([[
+ ^ |
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ {2:~ }|
+ |
+ ]])
+
+ end)
+end)
diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua
new file mode 100644
index 0000000000..3228d6b7fc
--- /dev/null
+++ b/test/functional/ui/inccommand_spec.lua
@@ -0,0 +1,2574 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local curbufmeths = helpers.curbufmeths
+local eq = helpers.eq
+local eval = helpers.eval
+local feed_command = helpers.feed_command
+local expect = helpers.expect
+local feed = helpers.feed
+local insert = helpers.insert
+local meths = helpers.meths
+local neq = helpers.neq
+local ok = helpers.ok
+local retry = helpers.retry
+local source = helpers.source
+local wait = helpers.wait
+local nvim = helpers.nvim
+local iswin = helpers.iswin
+local sleep = helpers.sleep
+
+local default_text = [[
+ Inc substitution on
+ two lines
+]]
+
+local multiline_text = [[
+ 1 2 3
+ A B C
+ 4 5 6
+ X Y Z
+ 7 8 9
+]]
+
+local multimatch_text = [[
+ a bdc eae a fgl lzia r
+ x
+]]
+
+local multibyte_text = [[
+ £ ¥ ѫѫ PEPPERS
+£ ¥ ѫfѫ
+ a£ ѫ¥KOL
+£ ¥ libm
+£ ¥
+]]
+
+local long_multiline_text = [[
+ 1 2 3
+ A B C
+ 4 5 6
+ X Y Z
+ 7 8 9
+ K L M
+ a b c
+ d e f
+ q r s
+ x y z
+ £ m n
+ t œ ¥
+]]
+
+local function common_setup(screen, inccommand, text)
+ if screen then
+ command("syntax on")
+ command("set nohlsearch")
+ command("hi Substitute guifg=red guibg=yellow")
+ command("set display-=msgsep")
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = {foreground = Screen.colors.Fuchsia},
+ [2] = {foreground = Screen.colors.Brown, bold = true},
+ [3] = {foreground = Screen.colors.SlateBlue},
+ [4] = {bold = true, foreground = Screen.colors.SlateBlue},
+ [5] = {foreground = Screen.colors.DarkCyan},
+ [6] = {bold = true},
+ [7] = {underline = true, bold = true, foreground = Screen.colors.SlateBlue},
+ [8] = {foreground = Screen.colors.Slateblue, underline = true},
+ [9] = {background = Screen.colors.Yellow},
+ [10] = {reverse = true},
+ [11] = {reverse = true, bold=true},
+ [12] = {foreground = Screen.colors.Red, background = Screen.colors.Yellow},
+ [13] = {bold = true, foreground = Screen.colors.SeaGreen},
+ [14] = {foreground = Screen.colors.White, background = Screen.colors.Red},
+ [15] = {bold=true, foreground=Screen.colors.Blue},
+ [16] = {background=Screen.colors.Grey90}, -- cursorline
+ vis = {background=Screen.colors.LightGrey}
+ })
+ end
+
+ command("set inccommand=" .. (inccommand and inccommand or ""))
+
+ if text then
+ insert(text)
+ end
+end
+
+describe(":substitute, inccommand=split", function()
+ before_each(function()
+ clear()
+ common_setup(nil, "split", default_text)
+ end)
+
+ -- Test the tests: verify that the `1==bufnr('$')` assertion
+ -- in the "no preview" tests (below) actually means something.
+ it("previews interactive cmdline", function()
+ feed(':%s/tw/MO/g')
+ retry(nil, 1000, function()
+ eq(2, eval("bufnr('$')"))
+ end)
+ end)
+
+ it("no preview if invoked by a script", function()
+ source('%s/tw/MO/g')
+ wait()
+ eq(1, eval("bufnr('$')"))
+ -- sanity check: assert the buffer state
+ expect(default_text:gsub("tw", "MO"))
+ end)
+
+ it("no preview if invoked by feedkeys()", function()
+ -- in a script...
+ source([[:call feedkeys(":%s/tw/MO/g\<CR>")]])
+ wait()
+ -- or interactively...
+ feed([[:call feedkeys(":%s/tw/MO/g\<CR>")<CR>]])
+ wait()
+ eq(1, eval("bufnr('$')"))
+ -- sanity check: assert the buffer state
+ expect(default_text:gsub("tw", "MO"))
+ end)
+end)
+
+describe(":substitute, 'inccommand' preserves", function()
+ before_each(clear)
+
+ it('listed buffers (:ls)', function()
+ local screen = Screen.new(30,10)
+ common_setup(screen, "split", "ABC")
+
+ feed_command("%s/AB/BA/")
+ feed_command("ls")
+
+ screen:expect([[
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :ls |
+ 1 %a + "[No Name]" |
+ line 1 |
+ {13:Press ENTER or type command to}|
+ {13: continue}^ |
+ ]])
+ end)
+
+ for _, case in pairs{"", "split", "nosplit"} do
+ it("various delimiters (inccommand="..case..")", function()
+ insert(default_text)
+ feed_command("set inccommand=" .. case)
+
+ local delims = { '/', '#', ';', '%', ',', '@', '!', ''}
+ for _,delim in pairs(delims) do
+ feed_command("%s"..delim.."lines"..delim.."LINES"..delim.."g")
+ expect([[
+ Inc substitution on
+ two LINES
+ ]])
+ feed_command("undo")
+ end
+ end)
+ end
+
+ for _, case in pairs{"", "split", "nosplit"} do
+ it("'undolevels' (inccommand="..case..")", function()
+ feed_command("set undolevels=139")
+ feed_command("setlocal undolevels=34")
+ feed_command("set inccommand=" .. case)
+ insert("as")
+ feed(":%s/as/glork/<enter>")
+ eq(meths.get_option('undolevels'), 139)
+ eq(curbufmeths.get_option('undolevels'), 34)
+ end)
+ end
+
+ for _, case in ipairs({"", "split", "nosplit"}) do
+ it("empty undotree() (inccommand="..case..")", function()
+ feed_command("set undolevels=1000")
+ feed_command("set inccommand=" .. case)
+ local expected_undotree = eval("undotree()")
+
+ -- Start typing an incomplete :substitute command.
+ feed([[:%s/e/YYYY/g]])
+ wait()
+ -- Cancel the :substitute.
+ feed([[<C-\><C-N>]])
+
+ -- The undo tree should be unchanged.
+ eq(expected_undotree, eval("undotree()"))
+ eq({}, eval("undotree()")["entries"])
+ end)
+ end
+
+ for _, case in ipairs({"", "split", "nosplit"}) do
+ it("undotree() with branches (inccommand="..case..")", function()
+ feed_command("set undolevels=1000")
+ feed_command("set inccommand=" .. case)
+ -- Make some changes.
+ feed([[isome text 1<C-\><C-N>]])
+ feed([[osome text 2<C-\><C-N>]])
+ -- Add an undo branch.
+ feed([[u]])
+ -- More changes, more undo branches.
+ feed([[osome text 3<C-\><C-N>]])
+ feed([[AX<C-\><C-N>]])
+ feed([[...]])
+ feed([[uu]])
+ feed([[osome text 4<C-\><C-N>]])
+ feed([[u<C-R>u]])
+ feed([[osome text 5<C-\><C-N>]])
+ expect([[
+ some text 1
+ some text 3XX
+ some text 5]])
+ local expected_undotree = eval("undotree()")
+ eq(5, #expected_undotree["entries"]) -- sanity
+
+ -- Start typing an incomplete :substitute command.
+ feed([[:%s/e/YYYY/g]])
+ wait()
+ -- Cancel the :substitute.
+ feed([[<C-\><C-N>]])
+
+ -- The undo tree should be unchanged.
+ eq(expected_undotree, eval("undotree()"))
+ end)
+ end
+
+ for _, case in pairs{"", "split", "nosplit"} do
+ it("b:changedtick (inccommand="..case..")", function()
+ feed_command("set inccommand=" .. case)
+ feed([[isome text 1<C-\><C-N>]])
+ feed([[osome text 2<C-\><C-N>]])
+ local expected_tick = eval("b:changedtick")
+ ok(expected_tick > 0)
+
+ expect([[
+ some text 1
+ some text 2]])
+ feed(":%s/e/XXX/")
+ wait()
+
+ eq(expected_tick, eval("b:changedtick"))
+ end)
+ end
+
+ for _, case in pairs{"", "split", "nosplit"} do
+ it("visual selection for non-previewable command (inccommand="..case..") #5888", function()
+ local screen = Screen.new(30,10)
+ common_setup(screen, case, default_text)
+ feed('1G2V')
+
+ feed(':s')
+ screen:expect([[
+ {vis:Inc substitution on} |
+ t{vis:wo lines} |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :'<,'>s^ |
+ ]])
+
+ feed('o')
+ screen:expect([[
+ {vis:Inc substitution on} |
+ t{vis:wo lines} |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :'<,'>so^ |
+ ]])
+ end)
+ end
+
+end)
+
+describe(":substitute, 'inccommand' preserves undo", function()
+ local cases = { "", "split", "nosplit" }
+
+ local substrings = {
+ ":%s/1",
+ ":%s/1/",
+ ":%s/1/<bs>",
+ ":%s/1/a",
+ ":%s/1/a<bs>",
+ ":%s/1/ax",
+ ":%s/1/ax<bs>",
+ ":%s/1/ax<bs><bs>",
+ ":%s/1/ax<bs><bs><bs>",
+ ":%s/1/ax/",
+ ":%s/1/ax/<bs>",
+ ":%s/1/ax/<bs>/",
+ ":%s/1/ax/g",
+ ":%s/1/ax/g<bs>",
+ ":%s/1/ax/g<bs><bs>"
+ }
+
+ local function test_sub(substring, split, redoable)
+ clear()
+ feed_command("set inccommand=" .. split)
+
+ insert("1")
+ feed("o2<esc>")
+ feed_command("undo")
+ feed("o3<esc>")
+ if redoable then
+ feed("o4<esc>")
+ feed_command("undo")
+ end
+ feed(substring.. "<enter>")
+ feed_command("undo")
+
+ feed("g-")
+ expect([[
+ 1
+ 2]])
+
+ feed("g+")
+ expect([[
+ 1
+ 3]])
+ end
+
+ local function test_notsub(substring, split, redoable)
+ clear()
+ feed_command("set inccommand=" .. split)
+
+ insert("1")
+ feed("o2<esc>")
+ feed_command("undo")
+ feed("o3<esc>")
+ if redoable then
+ feed("o4<esc>")
+ feed_command("undo")
+ end
+ feed(substring .. "<esc>")
+
+ feed("g-")
+ expect([[
+ 1
+ 2]])
+
+ feed("g+")
+ expect([[
+ 1
+ 3]])
+
+ if redoable then
+ feed("<c-r>")
+ expect([[
+ 1
+ 3
+ 4]])
+ end
+ end
+
+
+ local function test_threetree(substring, split)
+ clear()
+ feed_command("set inccommand=" .. split)
+
+ insert("1")
+ feed("o2<esc>")
+ feed("o3<esc>")
+ feed("uu")
+ feed("oa<esc>")
+ feed("ob<esc>")
+ feed("uu")
+ feed("oA<esc>")
+ feed("oB<esc>")
+
+ -- This is the undo tree (x-Axis is timeline), we're at B now
+ -- ----------------A - B
+ -- /
+ -- | --------a - b
+ -- |/
+ -- 1 - 2 - 3
+
+ feed("2u")
+ feed(substring .. "<esc>")
+ expect([[
+ 1]])
+ feed("g-")
+ expect([[
+ ]])
+ feed("g+")
+ expect([[
+ 1]])
+ feed("<c-r>")
+ expect([[
+ 1
+ A]])
+
+ feed("g-") -- go to b
+ feed("2u")
+ feed(substring .. "<esc>")
+ feed("<c-r>")
+ expect([[
+ 1
+ a]])
+
+ feed("g-") -- go to 3
+ feed("2u")
+ feed(substring .. "<esc>")
+ feed("<c-r>")
+ expect([[
+ 1
+ 2]])
+ end
+
+ it("at a non-leaf of the undo tree", function()
+ for _, case in pairs(cases) do
+ for _, str in pairs(substrings) do
+ for _, redoable in pairs({true}) do
+ test_sub(str, case, redoable)
+ end
+ end
+ end
+ end)
+
+ it("at a leaf of the undo tree", function()
+ for _, case in pairs(cases) do
+ for _, str in pairs(substrings) do
+ for _, redoable in pairs({false}) do
+ test_sub(str, case, redoable)
+ end
+ end
+ end
+ end)
+
+ it("when interrupting substitution", function()
+ for _, case in pairs(cases) do
+ for _, str in pairs(substrings) do
+ for _, redoable in pairs({true,false}) do
+ test_notsub(str, case, redoable)
+ end
+ end
+ end
+ end)
+
+ it("in a complex undo scenario", function()
+ for _, case in pairs(cases) do
+ for _, str in pairs(substrings) do
+ test_threetree(str, case)
+ end
+ end
+ end)
+
+ it('with undolevels=0', function()
+ for _, case in pairs(cases) do
+ clear()
+ common_setup(nil, case, default_text)
+ feed_command("set undolevels=0")
+
+ feed("1G0")
+ insert("X")
+ feed(":%s/tw/MO/<esc>")
+ feed_command("undo")
+ expect(default_text)
+ feed_command("undo")
+ expect(default_text:gsub("Inc", "XInc"))
+ feed_command("undo")
+
+ feed_command("%s/tw/MO/g")
+ expect(default_text:gsub("tw", "MO"))
+ feed_command("undo")
+ expect(default_text)
+ feed_command("undo")
+ expect(default_text:gsub("tw", "MO"))
+ end
+ end)
+
+ it('with undolevels=1', function()
+ local screen = Screen.new(20,10)
+
+ for _, case in pairs(cases) do
+ clear()
+ common_setup(screen, case, default_text)
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ ^ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ |
+ ]])
+ feed_command("set undolevels=1")
+
+ feed("1G0")
+ insert("X")
+ feed("IY<esc>")
+ feed(":%s/tw/MO/<esc>")
+ -- feed_command("undo") here would cause "Press ENTER".
+ feed("u")
+ expect(default_text:gsub("Inc", "XInc"))
+ feed("u")
+ expect(default_text)
+
+ feed(":%s/tw/MO/g<enter>")
+ feed(":%s/MO/GO/g<enter>")
+ feed(":%s/GO/NO/g<enter>")
+ feed("u")
+ expect(default_text:gsub("tw", "GO"))
+ feed("u")
+ expect(default_text:gsub("tw", "MO"))
+ feed("u")
+
+ if case == "split" then
+ screen:expect([[
+ Inc substitution on |
+ ^MOo lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ Already ...t change |
+ ]])
+ else
+ screen:expect([[
+ Inc substitution on |
+ ^MOo lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ Already ...t change |
+ ]])
+ end
+ end
+ screen:detach()
+ end)
+
+ it('with undolevels=2', function()
+ local screen = Screen.new(20,10)
+
+ for _, case in pairs(cases) do
+ clear()
+ common_setup(screen, case, default_text)
+ feed_command("set undolevels=2")
+
+ feed("2GAx<esc>")
+ feed("Ay<esc>")
+ feed("Az<esc>")
+ feed(":%s/tw/AR<esc>")
+ -- feed_command("undo") here would cause "Press ENTER".
+ feed("u")
+ expect(default_text:gsub("lines", "linesxy"))
+ feed("u")
+ expect(default_text:gsub("lines", "linesx"))
+ feed("u")
+ expect(default_text)
+ feed("u")
+
+ if case == "split" then
+ screen:expect([[
+ Inc substitution on |
+ two line^s |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ Already ...t change |
+ ]])
+ else
+ screen:expect([[
+ Inc substitution on |
+ two line^s |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ Already ...t change |
+ ]])
+ end
+
+ feed(":%s/tw/MO/g<enter>")
+ feed(":%s/MO/GO/g<enter>")
+ feed(":%s/GO/NO/g<enter>")
+ feed(":%s/NO/LO/g<enter>")
+ feed("u")
+ expect(default_text:gsub("tw", "NO"))
+ feed("u")
+ expect(default_text:gsub("tw", "GO"))
+ feed("u")
+ expect(default_text:gsub("tw", "MO"))
+ feed("u")
+
+ if case == "split" then
+ screen:expect([[
+ Inc substitution on |
+ ^MOo lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ Already ...t change |
+ ]])
+ else
+ screen:expect([[
+ Inc substitution on |
+ ^MOo lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ Already ...t change |
+ ]])
+ end
+ screen:detach()
+ end
+ end)
+
+ it('with undolevels=-1', function()
+ local screen = Screen.new(20,10)
+
+ for _, case in pairs(cases) do
+ clear()
+ common_setup(screen, case, default_text)
+
+ feed_command("set undolevels=-1")
+ feed(":%s/tw/MO/g<enter>")
+ -- feed_command("undo") here will result in a "Press ENTER" prompt
+ feed("u")
+ if case == "split" then
+ screen:expect([[
+ Inc substitution on |
+ ^MOo lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ Already ...t change |
+ ]])
+ else
+ screen:expect([[
+ Inc substitution on |
+ ^MOo lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ Already ...t change |
+ ]])
+ end
+
+ -- repeat with an interrupted substitution
+ clear()
+ common_setup(screen, case, default_text)
+
+ feed_command("set undolevels=-1")
+ feed("1G")
+ feed("IL<esc>")
+ feed(":%s/tw/MO/g<esc>")
+ feed("u")
+
+ screen:expect([[
+ ^LInc substitution on|
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ Already ...t change |
+ ]])
+ end
+ screen:detach()
+ end)
+
+end)
+
+describe(":substitute, inccommand=split", function()
+ local screen = Screen.new(30,15)
+
+ before_each(function()
+ clear()
+ common_setup(screen, "split", default_text .. default_text)
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it("preserves 'modified' buffer flag", function()
+ feed_command("set nomodified")
+ feed(":%s/tw")
+ screen:expect([[
+ Inc substitution on |
+ {12:tw}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] }|
+ |2| {12:tw}o lines |
+ |4| {12:tw}o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw^ |
+ ]])
+ feed([[<C-\><C-N>]]) -- Cancel the :substitute command.
+ eq(0, eval("&modified"))
+ end)
+
+ it("shows preview when cmd modifiers are present", function()
+ -- one modifier
+ feed(':keeppatterns %s/tw/to')
+ screen:expect{any=[[{12:to}o lines]]}
+ feed('<Esc>')
+ screen:expect{any=[[two lines]]}
+
+ -- multiple modifiers
+ feed(':keeppatterns silent %s/tw/to')
+ screen:expect{any=[[{12:to}o lines]]}
+ feed('<Esc>')
+ screen:expect{any=[[two lines]]}
+
+ -- non-modifier prefix
+ feed(':silent tabedit %s/tw/to')
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :silent tabedit %s/tw/to^ |
+ ]])
+ end)
+
+ it('shows split window when typing the pattern', function()
+ feed(":%s/tw")
+ screen:expect([[
+ Inc substitution on |
+ {12:tw}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |2| {12:tw}o lines |
+ |4| {12:tw}o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw^ |
+ ]])
+ end)
+
+ it('shows preview with empty replacement', function()
+ feed(":%s/tw/")
+ screen:expect([[
+ Inc substitution on |
+ o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |2| o lines |
+ |4| o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw/^ |
+ ]])
+
+ feed("x")
+ screen:expect([[
+ Inc substitution on |
+ {12:x}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |2| {12:x}o lines |
+ |4| {12:x}o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw/x^ |
+ ]])
+
+ feed("<bs>")
+ screen:expect([[
+ Inc substitution on |
+ o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |2| o lines |
+ |4| o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw/^ |
+ ]])
+
+ end)
+
+ it('shows split window when typing replacement', function()
+ feed(":%s/tw/XX")
+ screen:expect([[
+ Inc substitution on |
+ {12:XX}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |2| {12:XX}o lines |
+ |4| {12:XX}o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw/XX^ |
+ ]])
+ end)
+
+ it('does not show split window for :s/', function()
+ feed("2gg")
+ feed(":s/tw")
+ screen:expect([[
+ Inc substitution on |
+ {12:tw}o lines |
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :s/tw^ |
+ ]])
+ end)
+
+ it("'hlsearch' is active, 'cursorline' is not", function()
+ feed_command("set hlsearch cursorline")
+ feed("gg")
+
+ -- Assert that 'cursorline' is active.
+ screen:expect([[
+ {16:^Inc substitution on }|
+ two lines |
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :set hlsearch cursorline |
+ ]])
+
+ feed(":%s/tw")
+ -- 'cursorline' is NOT active during preview.
+ screen:expect([[
+ Inc substitution on |
+ {12:tw}o lines |
+ Inc substitution on |
+ {12:tw}o lines |
+ |
+ {11:[No Name] [+] }|
+ |2| {12:tw}o lines |
+ |4| {12:tw}o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw^ |
+ ]])
+ end)
+
+ it('highlights the replacement text', function()
+ feed('ggO')
+ feed('M M M<esc>')
+ feed(':%s/M/123/g')
+ screen:expect([[
+ {12:123} {12:123} {12:123} |
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ {11:[No Name] [+] }|
+ |1| {12:123} {12:123} {12:123} |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/M/123/g^ |
+ ]])
+ end)
+
+ it("highlights nothing when there's no match", function()
+ feed('gg')
+ feed(':%s/Inx')
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ |
+ {11:[No Name] [+] }|
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/Inx^ |
+ ]])
+ end)
+
+ it('previews correctly when previewhight is small', function()
+ feed_command('set cwh=3')
+ feed_command('set hls')
+ feed('ggdG')
+ insert(string.rep('abc abc abc\n', 20))
+ feed(':%s/abc/MMM/g')
+ screen:expect([[
+ {12:MMM} {12:MMM} {12:MMM} |
+ {12:MMM} {12:MMM} {12:MMM} |
+ {12:MMM} {12:MMM} {12:MMM} |
+ {12:MMM} {12:MMM} {12:MMM} |
+ {12:MMM} {12:MMM} {12:MMM} |
+ {12:MMM} {12:MMM} {12:MMM} |
+ {12:MMM} {12:MMM} {12:MMM} |
+ {12:MMM} {12:MMM} {12:MMM} |
+ {12:MMM} {12:MMM} {12:MMM} |
+ {11:[No Name] [+] }|
+ | 1| {12:MMM} {12:MMM} {12:MMM} |
+ | 2| {12:MMM} {12:MMM} {12:MMM} |
+ | 3| {12:MMM} {12:MMM} {12:MMM} |
+ {10:[Preview] }|
+ :%s/abc/MMM/g^ |
+ ]])
+ end)
+
+ it('actually replaces text', function()
+ feed(":%s/tw/XX/g<Enter>")
+
+ screen:expect([[
+ Inc substitution on |
+ XXo lines |
+ Inc substitution on |
+ ^XXo lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/tw/XX/g |
+ ]])
+ end)
+
+ it('shows correct line numbers with many lines', function()
+ feed("gg")
+ feed("2yy")
+ feed("2000p")
+ feed_command("1,1000s/tw/BB/g")
+
+ feed(":%s/tw/X")
+ screen:expect([[
+ BBo lines |
+ Inc substitution on |
+ {12:X}o lines |
+ Inc substitution on |
+ {12:X}o lines |
+ {11:[No Name] [+] }|
+ |1001| {12:X}o lines |
+ |1003| {12:X}o lines |
+ |1005| {12:X}o lines |
+ |1007| {12:X}o lines |
+ |1009| {12:X}o lines |
+ |1011| {12:X}o lines |
+ |1013| {12:X}o lines |
+ {10:[Preview] }|
+ :%s/tw/X^ |
+ ]])
+ end)
+
+ it('does not spam the buffer numbers', function()
+ -- The preview buffer is re-used (unless user deleted it), so buffer numbers
+ -- will not increase on each keystroke.
+ feed(":%s/tw/Xo/g")
+ -- Delete and re-type the g a few times.
+ feed("<BS>")
+ wait()
+ feed("g")
+ wait()
+ feed("<BS>")
+ wait()
+ feed("g")
+ wait()
+ feed("<CR>")
+ wait()
+ feed(":vs tmp<enter>")
+ eq(3, helpers.call('bufnr', '$'))
+ end)
+
+ it('works with the n flag', function()
+ feed(":%s/tw/Mix/n<Enter>")
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ ^ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ 2 matches on 2 lines |
+ ]])
+ end)
+
+ it("deactivates if 'redrawtime' is exceeded #5602", function()
+ -- Assert that 'inccommand' is ENABLED initially.
+ eq("split", eval("&inccommand"))
+ -- Set 'redrawtime' to minimal value, to ensure timeout is triggered.
+ feed_command("set redrawtime=1 nowrap")
+ -- Load a big file.
+ feed_command("silent edit! test/functional/fixtures/bigfile_oneline.txt")
+ -- Start :substitute with a slow pattern.
+ feed([[:%s/B.*N/x]])
+ wait()
+
+ -- Assert that 'inccommand' is DISABLED in cmdline mode.
+ eq("", eval("&inccommand"))
+ -- Assert that preview cleared (or never manifested).
+ screen:expect([[
+ 0000;<control>;Cc;0;BN;;;;;N;N|
+ 2F923;CJK COMPATIBILITY IDEOGR|
+ 2F924;CJK COMPATIBILITY IDEOGR|
+ 2F925;CJK COMPATIBILITY IDEOGR|
+ 2F926;CJK COMPATIBILITY IDEOGR|
+ 2F927;CJK COMPATIBILITY IDEOGR|
+ 2F928;CJK COMPATIBILITY IDEOGR|
+ 2F929;CJK COMPATIBILITY IDEOGR|
+ 2F92A;CJK COMPATIBILITY IDEOGR|
+ 2F92B;CJK COMPATIBILITY IDEOGR|
+ 2F92C;CJK COMPATIBILITY IDEOGR|
+ 2F92D;CJK COMPATIBILITY IDEOGR|
+ 2F92E;CJK COMPATIBILITY IDEOGR|
+ 2F92F;CJK COMPATIBILITY IDEOGR|
+ :%s/B.*N/x^ |
+ ]])
+
+ -- Assert that 'inccommand' is again ENABLED after leaving cmdline mode.
+ feed([[<C-\><C-N>]])
+ eq("split", eval("&inccommand"))
+ end)
+
+ it("clears preview if non-previewable command is edited #5585", function()
+ -- Put a non-previewable command in history.
+ feed_command("echo 'foo'")
+ -- Start an incomplete :substitute command.
+ feed(":1,2s/t/X")
+
+ screen:expect([[
+ Inc subs{12:X}itution on |
+ {12:X}wo lines |
+ Inc substitution on |
+ two lines |
+ |
+ {11:[No Name] [+] }|
+ |1| Inc subs{12:X}itution on |
+ |2| {12:X}wo lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :1,2s/t/X^ |
+ ]])
+
+ -- Select the previous command.
+ feed("<C-P>")
+ -- Assert that preview was cleared.
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :echo 'foo'^ |
+ ]])
+ end)
+
+end)
+
+describe("inccommand=nosplit", function()
+ local screen = Screen.new(20,10)
+
+ before_each(function()
+ clear()
+ common_setup(screen, "nosplit", default_text .. default_text)
+ end)
+
+ after_each(function()
+ if screen then screen:detach() end
+ end)
+
+ it("works with :smagic, :snomagic", function()
+ feed_command("set hlsearch")
+ insert("Line *.3.* here")
+
+ feed(":%smagic/3.*/X") -- start :smagic command
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ Line *.{12:X} |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%smagic/3.*/X^ |
+ ]])
+
+ feed([[<C-\><C-N>]]) -- cancel
+ feed(":%snomagic/3.*/X") -- start :snomagic command
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ Line *.{12:X} here |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%snomagic/3.*/X^ |
+ ]])
+ end)
+
+ it("shows preview when cmd modifiers are present", function()
+ -- one modifier
+ feed(':keeppatterns %s/tw/to')
+ screen:expect{any=[[{12:to}o lines]]}
+ feed('<Esc>')
+ screen:expect{any=[[two lines]]}
+
+ -- multiple modifiers
+ feed(':keeppatterns silent %s/tw/to')
+ screen:expect{any=[[{12:to}o lines]]}
+ feed('<Esc>')
+ screen:expect{any=[[two lines]]}
+
+ -- non-modifier prefix
+ feed(':silent tabedit %s/tw/to')
+ screen:expect([[
+ two lines |
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :silent tabedit %s/t|
+ w/to^ |
+ ]])
+ end)
+
+ it("does not show window after toggling :set inccommand", function()
+ feed(":%s/tw/OKOK")
+ feed("<Esc>")
+ command("set icm=split")
+ feed(":%s/tw/OKOK")
+ feed("<Esc>")
+ command("set icm=nosplit")
+ feed(":%s/tw/OKOK")
+ wait()
+ screen:expect([[
+ Inc substitution on |
+ {12:OKOK}o lines |
+ Inc substitution on |
+ {12:OKOK}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/tw/OKOK^ |
+ ]])
+ end)
+
+ it('never shows preview buffer', function()
+ feed_command("set hlsearch")
+
+ feed(":%s/tw")
+ screen:expect([[
+ Inc substitution on |
+ {12:tw}o lines |
+ Inc substitution on |
+ {12:tw}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/tw^ |
+ ]])
+
+ feed("/BM")
+ screen:expect([[
+ Inc substitution on |
+ {12:BM}o lines |
+ Inc substitution on |
+ {12:BM}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/tw/BM^ |
+ ]])
+
+ feed("/")
+ screen:expect([[
+ Inc substitution on |
+ {12:BM}o lines |
+ Inc substitution on |
+ {12:BM}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/tw/BM/^ |
+ ]])
+
+ feed("<enter>")
+ screen:expect([[
+ Inc substitution on |
+ BMo lines |
+ Inc substitution on |
+ ^BMo lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/tw/BM/ |
+ ]])
+ end)
+
+ it("clears preview if non-previewable command is edited", function()
+ -- Put a non-previewable command in history.
+ feed_command("echo 'foo'")
+ -- Start an incomplete :substitute command.
+ feed(":1,2s/t/X")
+
+ screen:expect([[
+ Inc subs{12:X}itution on |
+ {12:X}wo lines |
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :1,2s/t/X^ |
+ ]])
+
+ -- Select the previous command.
+ feed("<C-P>")
+ -- Assert that preview was cleared.
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :echo 'foo'^ |
+ ]])
+ end)
+
+ it("does not execute trailing bar-separated commands #7494", function()
+ feed(':%s/two/three/g|q!')
+ screen:expect([[
+ Inc substitution on |
+ {12:three} lines |
+ Inc substitution on |
+ {12:three} lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/two/three/g|q!^ |
+ ]])
+ eq(eval('v:null'), eval('v:exiting'))
+ end)
+end)
+
+describe(":substitute, 'inccommand' with a failing expression", function()
+ local screen = Screen.new(20,10)
+ local cases = { "", "split", "nosplit" }
+
+ local function refresh(case)
+ clear()
+ common_setup(screen, case, default_text)
+ end
+
+ it('in the pattern does nothing', function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ feed_command("set inccommand=" .. case)
+ feed(":silent! %s/tw\\(/LARD/<enter>")
+ expect(default_text)
+ end
+ end)
+
+ it('in the replacement deletes the matches', function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ local replacements = { "\\='LARD", "\\=xx_novar__xx" }
+
+ for _, repl in pairs(replacements) do
+ feed_command("set inccommand=" .. case)
+ feed(":silent! %s/tw/" .. repl .. "/<enter>")
+ expect(default_text:gsub("tw", ""))
+ feed_command("undo")
+ end
+ end
+ end)
+
+ it('in the range does not error #5912', function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ feed(':100s/')
+
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :100s/^ |
+ ]])
+
+ feed('<enter>')
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ ^ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {14:E16: Invalid range} |
+ ]])
+ end
+ end)
+
+end)
+
+describe("'inccommand' and :cnoremap", function()
+ local cases = { "", "split", "nosplit" }
+
+ local function refresh(case)
+ clear()
+ common_setup(nil, case, default_text)
+ end
+
+ it('work with remapped characters', function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ local cmd = "%s/lines/LINES/g"
+
+ for i = 1, string.len(cmd) do
+ local c = string.sub(cmd, i, i)
+ feed_command("cnoremap ".. c .. " " .. c)
+ end
+
+ feed_command(cmd)
+ expect([[
+ Inc substitution on
+ two LINES
+ ]])
+ end
+ end)
+
+ it('work when mappings move the cursor', function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ feed_command("cnoremap ,S LINES/<left><left><left><left><left><left>")
+
+ feed(":%s/lines/,Sor three <enter>")
+ expect([[
+ Inc substitution on
+ two or three LINES
+ ]])
+
+ feed_command("cnoremap ;S /X/<left><left><left>")
+ feed(":%s/;SI<enter>")
+ expect([[
+ Xnc substitution on
+ two or three LXNES
+ ]])
+
+ feed_command("cnoremap ,T //Y/<left><left><left>")
+ feed(":%s,TX<enter>")
+ expect([[
+ Ync substitution on
+ two or three LYNES
+ ]])
+
+ feed_command("cnoremap ;T s//Z/<left><left><left>")
+ feed(":%;TY<enter>")
+ expect([[
+ Znc substitution on
+ two or three LZNES
+ ]])
+ end
+ end)
+
+ it('still works with a broken mapping', function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ feed_command("cnoremap <expr> x execute('bwipeout!')[-1].'x'")
+
+ feed(":%s/tw/tox<enter>")
+
+ -- error thrown b/c of the mapping
+ neq(nil, eval('v:errmsg'):find('^E523:'))
+ expect([[
+ Inc substitution on
+ toxo lines
+ ]])
+ end
+ end)
+
+ it('work when temporarily moving the cursor', function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ feed_command("cnoremap <expr> x cursor(1, 1)[-1].'x'")
+
+ feed(":%s/tw/tox/g<enter>")
+ expect(default_text:gsub("tw", "tox"))
+ end
+ end)
+
+ it("work when a mapping disables 'inccommand'", function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ feed_command("cnoremap <expr> x execute('set inccommand=')[-1]")
+
+ feed(":%s/tw/toxa/g<enter>")
+ expect(default_text:gsub("tw", "toa"))
+ end
+ end)
+
+ it('work with a complex mapping', function()
+ for _, case in pairs(cases) do
+ refresh(case)
+ source([[cnoremap x <C-\>eextend(g:, {'fo': getcmdline()})
+ \.fo<CR><C-c>:new<CR>:bw!<CR>:<C-r>=remove(g:, 'fo')<CR>x]])
+
+ feed(":%s/tw/tox")
+ feed("/<enter>")
+ expect(default_text:gsub("tw", "tox"))
+ end
+ end)
+
+end)
+
+describe("'inccommand' autocommands", function()
+ before_each(clear)
+
+ -- keys are events to be tested
+ -- values are arrays like
+ -- { open = { 1 }, close = { 2, 3} }
+ -- which would mean that during the test below the event fires for
+ -- buffer 1 when opening the preview window, and for buffers 2 and 3
+ -- when closing the preview window
+ local eventsExpected = {
+ BufAdd = {},
+ BufDelete = {},
+ BufEnter = {},
+ BufFilePost = {},
+ BufFilePre = {},
+ BufHidden = {},
+ BufLeave = {},
+ BufNew = {},
+ BufNewFile = {},
+ BufRead = {},
+ BufReadCmd = {},
+ BufReadPre = {},
+ BufUnload = {},
+ BufWinEnter = {},
+ BufWinLeave = {},
+ BufWipeout = {},
+ BufWrite = {},
+ BufWriteCmd = {},
+ BufWritePost = {},
+ Syntax = {},
+ FileType = {},
+ WinEnter = {},
+ WinLeave = {},
+ CmdwinEnter = {},
+ CmdwinLeave = {},
+ }
+
+ local function bufferlist(t)
+ local s = ""
+ for _, buffer in pairs(t) do
+ s = s .. ", " .. tostring(buffer)
+ end
+ return s
+ end
+
+ -- fill the table with default values
+ for event, _ in pairs(eventsExpected) do
+ eventsExpected[event].open = eventsExpected[event].open or {}
+ eventsExpected[event].close = eventsExpected[event].close or {}
+ end
+
+ local function register_autocmd(event)
+ meths.set_var(event .. "_fired", {})
+ feed_command("autocmd " .. event .. " * call add(g:" .. event .. "_fired, expand('<abuf>'))")
+ end
+
+ it('are not fired when splitting', function()
+ common_setup(nil, "split", default_text)
+
+ local eventsObserved = {}
+ for event, _ in pairs(eventsExpected) do
+ eventsObserved[event] = {}
+ register_autocmd(event)
+ end
+
+ feed(":%s/tw")
+
+ for event, _ in pairs(eventsExpected) do
+ eventsObserved[event].open = meths.get_var(event .. "_fired")
+ meths.set_var(event .. "_fired", {})
+ end
+
+ feed("/<enter>")
+
+ for event, _ in pairs(eventsExpected) do
+ eventsObserved[event].close = meths.get_var(event .. "_fired")
+ end
+
+ for event, _ in pairs(eventsExpected) do
+ eq(event .. bufferlist(eventsExpected[event].open),
+ event .. bufferlist(eventsObserved[event].open))
+ eq(event .. bufferlist(eventsExpected[event].close),
+ event .. bufferlist(eventsObserved[event].close))
+ end
+ end)
+
+end)
+
+describe("'inccommand' split windows", function()
+ local screen
+ local function refresh()
+ clear()
+ screen = Screen.new(40,30)
+ common_setup(screen, "split", default_text)
+ end
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('work after more splits', function()
+ refresh()
+
+ feed("gg")
+ feed_command("vsplit")
+ feed_command("split")
+ feed(":%s/tw")
+ screen:expect([[
+ Inc substitution on {10:│}Inc substitution on|
+ {12:tw}o lines {10:│}{12:tw}o lines |
+ {10:│} |
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {11:[No Name] [+] }{10:│}{15:~ }|
+ Inc substitution on {10:│}{15:~ }|
+ {12:tw}o lines {10:│}{15:~ }|
+ {10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {10:[No Name] [+] [No Name] [+] }|
+ |2| {12:tw}o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw^ |
+ ]])
+
+ feed("<esc>")
+ feed_command("only")
+ feed_command("split")
+ feed_command("vsplit")
+
+ feed(":%s/tw")
+ screen:expect([[
+ Inc substitution on {10:│}Inc substitution on|
+ {12:tw}o lines {10:│}{12:tw}o lines |
+ {10:│} |
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {15:~ }{10:│}{15:~ }|
+ {11:[No Name] [+] }{10:[No Name] [+] }|
+ Inc substitution on |
+ {12:tw}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {10:[No Name] [+] }|
+ |2| {12:tw}o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw^ |
+ ]])
+ end)
+
+ local settings = {
+ "splitbelow",
+ "splitright",
+ "noequalalways",
+ "equalalways eadirection=ver",
+ "equalalways eadirection=hor",
+ "equalalways eadirection=both",
+ }
+
+ it("are not affected by various settings", function()
+ for _, setting in pairs(settings) do
+ refresh()
+ feed_command("set " .. setting)
+
+ feed(":%s/tw")
+
+ screen:expect([[
+ Inc substitution on |
+ {12:tw}o lines |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |2| {12:tw}o lines |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/tw^ |
+ ]])
+ end
+ end)
+
+end)
+
+describe("'inccommand' with 'gdefault'", function()
+ before_each(function()
+ clear()
+ end)
+
+ it("does not lock up #7244", function()
+ common_setup(nil, "nosplit", "{")
+ command("set gdefault")
+ feed(":s/{\\n")
+ eq({mode='c', blocking=false}, nvim("get_mode"))
+ feed("/A<Enter>")
+ expect("A")
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
+
+ it("with multiline text and range, does not lock up #7244", function()
+ common_setup(nil, "nosplit", "{\n\n{")
+ command("set gdefault")
+ feed(":%s/{\\n")
+ eq({mode='c', blocking=false}, nvim("get_mode"))
+ feed("/A<Enter>")
+ expect("A\nA")
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
+
+ it("does not crash on zero-width matches #7485", function()
+ common_setup(nil, "split", default_text)
+ command("set gdefault")
+ feed("gg")
+ feed("Vj")
+ feed(":s/\\%V")
+ eq({mode='c', blocking=false}, nvim("get_mode"))
+ feed("<Esc>")
+ eq({mode='n', blocking=false}, nvim("get_mode"))
+ end)
+
+ it("removes highlights after abort for a zero-width match", function()
+ local screen = Screen.new(30,5)
+ common_setup(screen, "nosplit", default_text)
+ command("set gdefault")
+
+ feed(":%s/\\%1c/a/")
+ screen:expect([[
+ {12:a}Inc substitution on |
+ {12:a}two lines |
+ {12:a} |
+ {15:~ }|
+ :%s/\%1c/a/^ |
+ ]])
+
+ feed("<Esc>")
+ screen:expect([[
+ Inc substitution on |
+ two lines |
+ ^ |
+ {15:~ }|
+ |
+ ]])
+ end)
+
+end)
+
+describe(":substitute", function()
+ local screen = Screen.new(30,15)
+ before_each(function()
+ clear()
+ end)
+
+ it("inccommand=split, highlights multiline substitutions", function()
+ common_setup(screen, "split", multiline_text)
+ feed("gg")
+
+ feed(":%s/2\\_.*X")
+ screen:expect([[
+ 1 {12:2 3} |
+ {12:A B C} |
+ {12:4 5 6} |
+ {12:X} Y Z |
+ 7 8 9 |
+ {11:[No Name] [+] }|
+ |1| 1 {12:2 3} |
+ |2|{12: A B C} |
+ |3|{12: 4 5 6} |
+ |4|{12: X} Y Z |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/2\_.*X^ |
+ ]])
+
+ feed("/MMM")
+ screen:expect([[
+ 1 {12:MMM} Y Z |
+ 7 8 9 |
+ |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |1| 1 {12:MMM} Y Z |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/2\_.*X/MMM^ |
+ ]])
+
+ feed("\\rK\\rLLL")
+ screen:expect([[
+ 1 {12:MMM} |
+ {12:K} |
+ {12:LLL} Y Z |
+ 7 8 9 |
+ |
+ {11:[No Name] [+] }|
+ |1| 1 {12:MMM} |
+ |2|{12: K} |
+ |3|{12: LLL} Y Z |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/2\_.*X/MMM\rK\rLLL^ |
+ ]])
+ end)
+
+ it("inccommand=nosplit, highlights multiline substitutions", function()
+ common_setup(screen, "nosplit", multiline_text)
+ feed("gg")
+
+ feed(":%s/2\\_.*X/MMM")
+ screen:expect([[
+ 1 {12:MMM} Y Z |
+ 7 8 9 |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/2\_.*X/MMM^ |
+ ]])
+
+ feed("\\rK\\rLLL")
+ screen:expect([[
+ 1 {12:MMM} |
+ {12:K} |
+ {12:LLL} Y Z |
+ 7 8 9 |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/2\_.*X/MMM\rK\rLLL^ |
+ ]])
+ end)
+
+ it("inccommand=split, highlights multiple matches on a line", function()
+ common_setup(screen, "split", multimatch_text)
+ command("set gdefault")
+ feed("gg")
+
+ feed(":%s/a/XLK")
+ screen:expect([[
+ {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r|
+ x |
+ |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |1| {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:X}|
+ {12:LK} r |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/a/XLK^ |
+ ]])
+ end)
+
+ it("inccommand=nosplit, highlights multiple matches on a line", function()
+ common_setup(screen, "nosplit", multimatch_text)
+ command("set gdefault")
+ feed("gg")
+
+ feed(":%s/a/XLK")
+ screen:expect([[
+ {12:XLK} bdc e{12:XLK}e {12:XLK} fgl lzi{12:XLK} r|
+ x |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/a/XLK^ |
+ ]])
+ end)
+
+ it("inccommand=split, with \\zs", function()
+ common_setup(screen, "split", multiline_text)
+ feed("gg")
+
+ feed(":%s/[0-9]\\n\\zs[A-Z]/OKO")
+ screen:expect([[
+ 1 2 3 |
+ {12:OKO} B C |
+ 4 5 6 |
+ {12:OKO} Y Z |
+ 7 8 9 |
+ {11:[No Name] [+] }|
+ |1| 1 2 3 |
+ |2| {12:OKO} B C |
+ |3| 4 5 6 |
+ |4| {12:OKO} Y Z |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/[0-9]\n\zs[A-Z]/OKO^ |
+ ]])
+ end)
+
+ it("inccommand=nosplit, with \\zs", function()
+ common_setup(screen, "nosplit", multiline_text)
+ feed("gg")
+
+ feed(":%s/[0-9]\\n\\zs[A-Z]/OKO")
+ screen:expect([[
+ 1 2 3 |
+ {12:OKO} B C |
+ 4 5 6 |
+ {12:OKO} Y Z |
+ 7 8 9 |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/[0-9]\n\zs[A-Z]/OKO^ |
+ ]])
+ end)
+
+ it("inccommand=split, substitutions of different length",
+ function()
+ common_setup(screen, "split", "T T123 T2T TTT T090804\nx")
+
+ feed(":%s/T\\([0-9]\\+\\)/\\1\\1/g")
+ screen:expect([[
+ T {12:123123} {12:22}T TTT {12:090804090804} |
+ x |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |1| T {12:123123} {12:22}T TTT {12:090804090}|
+ {12:804} |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/T\([0-9]\+\)/\1\1/g^ |
+ ]])
+ end)
+
+ it("inccommand=nosplit, substitutions of different length", function()
+ common_setup(screen, "nosplit", "T T123 T2T TTT T090804\nx")
+
+ feed(":%s/T\\([0-9]\\+\\)/\\1\\1/g")
+ screen:expect([[
+ T {12:123123} {12:22}T TTT {12:090804090804} |
+ x |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/T\([0-9]\+\)/\1\1/g^ |
+ ]])
+ end)
+
+ it("inccommand=split, contraction of lines", function()
+ local text = [[
+ T T123 T T123 T2T TT T23423424
+ x
+ afa Q
+ adf la;lkd R
+ alx
+ ]]
+
+ common_setup(screen, "split", text)
+ feed(":%s/[QR]\\n")
+ screen:expect([[
+ afa {12:Q} |
+ adf la;lkd {12:R} |
+ alx |
+ |
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |3| afa {12:Q} |
+ |4|{12: }adf la;lkd {12:R} |
+ |5|{12: }alx |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/[QR]\n^ |
+ ]])
+
+ feed("/KKK")
+ screen:expect([[
+ x |
+ afa {12:KKK}adf la;lkd {12:KKK}alx |
+ |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |3| afa {12:KKK}adf la;lkd {12:KKK}alx |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/[QR]\n/KKK^ |
+ ]])
+ end)
+
+ it("inccommand=nosplit, contraction of lines", function()
+ local text = [[
+ T T123 T T123 T2T TT T23423424
+ x
+ afa Q
+ adf la;lkd R
+ alx
+ ]]
+
+ common_setup(screen, "nosplit", text)
+ feed(":%s/[QR]\\n/KKK")
+ screen:expect([[
+ T T123 T T123 T2T TT T23423424|
+ x |
+ afa {12:KKK}adf la;lkd {12:KKK}alx |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/[QR]\n/KKK^ |
+ ]])
+ end)
+
+ it("inccommand=split, multibyte text", function()
+ common_setup(screen, "split", multibyte_text)
+ feed(":%s/£.*ѫ/X¥¥")
+ screen:expect([[
+ {12:X¥¥} |
+ a{12:X¥¥}¥KOL |
+ £ ¥ libm |
+ £ ¥ |
+ |
+ {11:[No Name] [+] }|
+ |1| {12:X¥¥} PEPPERS |
+ |2| {12:X¥¥} |
+ |3| a{12:X¥¥}¥KOL |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/£.*ѫ/X¥¥^ |
+ ]])
+
+ feed("\\ra££ ¥")
+ screen:expect([[
+ {12:a££ ¥} |
+ a{12:X¥¥} |
+ {12:a££ ¥}¥KOL |
+ £ ¥ libm |
+ £ ¥ |
+ {11:[No Name] [+] }|
+ |1| {12:X¥¥} |
+ |2|{12: a££ ¥} PEPPERS |
+ |3| {12:X¥¥} |
+ |4|{12: a££ ¥} |
+ |5| a{12:X¥¥} |
+ |6|{12: a££ ¥}¥KOL |
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/£.*ѫ/X¥¥\ra££ ¥^ |
+ ]])
+ end)
+
+ it("inccommand=nosplit, multibyte text", function()
+ common_setup(screen, "nosplit", multibyte_text)
+ feed(":%s/£.*ѫ/X¥¥")
+ screen:expect([[
+ {12:X¥¥} PEPPERS |
+ {12:X¥¥} |
+ a{12:X¥¥}¥KOL |
+ £ ¥ libm |
+ £ ¥ |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/£.*ѫ/X¥¥^ |
+ ]])
+
+ feed("\\ra££ ¥")
+ screen:expect([[
+ {12:X¥¥} |
+ {12:a££ ¥} PEPPERS |
+ {12:X¥¥} |
+ {12:a££ ¥} |
+ a{12:X¥¥} |
+ {12:a££ ¥}¥KOL |
+ £ ¥ libm |
+ £ ¥ |
+ |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ :%s/£.*ѫ/X¥¥\ra££ ¥^ |
+ ]])
+ end)
+
+ it("inccommand=split, small cmdwinheight", function()
+ common_setup(screen, "split", long_multiline_text)
+ command("set cmdwinheight=2")
+
+ feed(":%s/[a-z]")
+ screen:expect([[
+ X Y Z |
+ 7 8 9 |
+ K L M |
+ {12:a} b c |
+ {12:d} e f |
+ {12:q} r s |
+ {12:x} y z |
+ £ {12:m} n |
+ {12:t} œ ¥ |
+ |
+ {11:[No Name] [+] }|
+ | 7| {12:a} b c |
+ | 8| {12:d} e f |
+ {10:[Preview] }|
+ :%s/[a-z]^ |
+ ]])
+
+ feed("/JLKR £")
+ screen:expect([[
+ X Y Z |
+ 7 8 9 |
+ K L M |
+ {12:JLKR £} b c |
+ {12:JLKR £} e f |
+ {12:JLKR £} r s |
+ {12:JLKR £} y z |
+ £ {12:JLKR £} n |
+ {12:JLKR £} œ ¥ |
+ |
+ {11:[No Name] [+] }|
+ | 7| {12:JLKR £} b c |
+ | 8| {12:JLKR £} e f |
+ {10:[Preview] }|
+ :%s/[a-z]/JLKR £^ |
+ ]])
+
+ feed("\\rѫ ab \\rXXXX")
+ screen:expect([[
+ 7 8 9 |
+ K L M |
+ {12:JLKR £} |
+ {12:ѫ ab } |
+ {12:XXXX} b c |
+ {12:JLKR £} |
+ {12:ѫ ab } |
+ {12:XXXX} e f |
+ {12:JLKR £} |
+ {11:[No Name] [+] }|
+ | 7| {12:JLKR £} |
+ | 8|{12: ѫ ab } |
+ {10:[Preview] }|
+ :%s/[a-z]/JLKR £\rѫ ab \rXXX|
+ X^ |
+ ]])
+ end)
+
+ it("inccommand=split, large cmdwinheight", function()
+ common_setup(screen, "split", long_multiline_text)
+ command("set cmdwinheight=11")
+
+ feed(":%s/. .$")
+ screen:expect([[
+ t {12:œ ¥} |
+ {11:[No Name] [+] }|
+ | 1| 1 {12:2 3} |
+ | 2| A {12:B C} |
+ | 3| 4 {12:5 6} |
+ | 4| X {12:Y Z} |
+ | 5| 7 {12:8 9} |
+ | 6| K {12:L M} |
+ | 7| a {12:b c} |
+ | 8| d {12:e f} |
+ | 9| q {12:r s} |
+ |10| x {12:y z} |
+ |11| £ {12:m n} |
+ {10:[Preview] }|
+ :%s/. .$^ |
+ ]])
+
+ feed("/ YYY")
+ screen:expect([[
+ t {12: YYY} |
+ {11:[No Name] [+] }|
+ | 1| 1 {12: YYY} |
+ | 2| A {12: YYY} |
+ | 3| 4 {12: YYY} |
+ | 4| X {12: YYY} |
+ | 5| 7 {12: YYY} |
+ | 6| K {12: YYY} |
+ | 7| a {12: YYY} |
+ | 8| d {12: YYY} |
+ | 9| q {12: YYY} |
+ |10| x {12: YYY} |
+ |11| £ {12: YYY} |
+ {10:[Preview] }|
+ :%s/. .$/ YYY^ |
+ ]])
+
+ feed("\\r KKK")
+ screen:expect([[
+ a {12: YYY} |
+ {11:[No Name] [+] }|
+ | 1| 1 {12: YYY} |
+ | 2|{12: KKK} |
+ | 3| A {12: YYY} |
+ | 4|{12: KKK} |
+ | 5| 4 {12: YYY} |
+ | 6|{12: KKK} |
+ | 7| X {12: YYY} |
+ | 8|{12: KKK} |
+ | 9| 7 {12: YYY} |
+ |10|{12: KKK} |
+ |11| K {12: YYY} |
+ {10:[Preview] }|
+ :%s/. .$/ YYY\r KKK^ |
+ ]])
+ end)
+
+ it("inccommand=split, lookaround", function()
+ common_setup(screen, "split", "something\neverything\nsomeone")
+ feed([[:%s/\(some\)\@<lt>=thing/one/]])
+ screen:expect([[
+ some{12:one} |
+ everything |
+ someone |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |1| some{12:one} |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/\(some\)\@<=thing/one/^ |
+ ]])
+
+ feed("<C-c>")
+ wait()
+ feed([[:%s/\(some\)\@<lt>!thing/one/]])
+ screen:expect([[
+ something |
+ every{12:one} |
+ someone |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |2| every{12:one} |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/\(some\)\@<!thing/one/^ |
+ ]])
+
+ feed([[<C-c>]])
+ wait()
+ feed([[:%s/some\(thing\)\@=/every/]])
+ screen:expect([[
+ {12:every}thing |
+ everything |
+ someone |
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |1| {12:every}thing |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/some\(thing\)\@=/every/^ |
+ ]])
+
+ feed([[<C-c>]])
+ wait()
+ feed([[:%s/some\(thing\)\@!/every/]])
+ screen:expect([[
+ everything |
+ {12:every}one |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ |3| {12:every}one |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/some\(thing\)\@!/every/^ |
+ ]])
+ end)
+end)
+
+it(':substitute with inccommand during :terminal activity', function()
+ if helpers.skip_fragile(pending) then
+ return
+ end
+ retry(2, 40000, function()
+ local screen = Screen.new(30,15)
+ clear()
+
+ command("set cmdwinheight=3")
+ if iswin() then
+ feed([[:terminal for /L \%I in (1,1,5000) do @(echo xxx & echo xxx & echo xxx)<cr>]])
+ else
+ feed([[:terminal for i in $(seq 1 5000); do printf 'xxx\nxxx\nxxx\n'; done<cr>]])
+ end
+ command('file term')
+ command('new')
+ common_setup(screen, 'split', 'foo bar baz\nbar baz fox\nbar foo baz')
+ command('wincmd =')
+
+ -- Wait for terminal output.
+ screen:expect([[
+ bar baz fox |
+ bar foo ba^z |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ xxx |
+ xxx |
+ xxx |
+ xxx |
+ xxx |
+ xxx |
+ {10:term }|
+ |
+ ]])
+
+ feed('gg')
+ feed(':%s/foo/ZZZ')
+ sleep(20) -- Allow some terminal activity.
+ screen:expect([[
+ {12:ZZZ} bar baz |
+ bar baz fox |
+ bar {12:ZZZ} baz |
+ {15:~ }|
+ {15:~ }|
+ {15:~ }|
+ {11:[No Name] [+] }|
+ xxx |
+ xxx |
+ {10:term }|
+ |1| {12:ZZZ} bar baz |
+ |3| bar {12:ZZZ} baz |
+ {15:~ }|
+ {10:[Preview] }|
+ :%s/foo/ZZZ^ |
+ ]])
+
+ end)
+end)
diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua
index 407c576002..850efed282 100644
--- a/test/functional/ui/input_spec.lua
+++ b/test/functional/ui/input_spec.lua
@@ -1,7 +1,9 @@
local helpers = require('test.functional.helpers')(after_each)
-local clear, execute, nvim = helpers.clear, helpers.execute, helpers.nvim
-local feed, next_message, eq = helpers.feed, helpers.next_message, helpers.eq
+local clear, feed_command, nvim = helpers.clear, helpers.feed_command, helpers.nvim
+local feed, next_msg, eq = helpers.feed, helpers.next_msg, helpers.eq
+local command = helpers.command
local expect = helpers.expect
+local write_file = helpers.write_file
local Screen = require('test.functional.ui.screen')
describe('mappings', function()
@@ -10,12 +12,12 @@ describe('mappings', function()
local add_mapping = function(mapping, send)
local cmd = "nnoremap "..mapping.." :call rpcnotify("..cid..", 'mapped', '"
..send:gsub('<', '<lt>').."')<cr>"
- execute(cmd)
+ feed_command(cmd)
end
local check_mapping = function(mapping, expected)
feed(mapping)
- eq({'notification', 'mapped', {expected}}, next_message())
+ eq({'notification', 'mapped', {expected}}, next_msg())
end
before_each(function()
@@ -55,10 +57,13 @@ describe('feeding large chunks of input with <Paste>', function()
clear()
screen = Screen.new()
screen:attach()
- execute('set ruler')
+ feed_command('set ruler')
end)
it('ok', function()
+ if helpers.skip_fragile(pending) then
+ return
+ end
local t = {}
for i = 1, 20000 do
t[i] = 'item ' .. tostring(i)
@@ -124,3 +129,97 @@ describe('input utf sequences that contain CSI/K_SPECIAL', function()
expect('…')
end)
end)
+
+describe('input non-printable chars', function()
+ after_each(function()
+ os.remove('Xtest-overwrite')
+ end)
+
+ it("doesn't crash when echoing them back", function()
+ write_file("Xtest-overwrite", [[foobar]])
+ clear()
+ local screen = Screen.new(60,8)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen4}
+ })
+ screen:attach()
+ command("set display-=msgsep shortmess-=F")
+
+ feed_command("e Xtest-overwrite")
+ screen:expect([[
+ ^foobar |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "Xtest-overwrite" [noeol] 1L, 6C |
+ ]])
+
+ -- The timestamp is in second resolution, wait two seconds to be sure.
+ screen:sleep(2000)
+ write_file("Xtest-overwrite", [[smurf]])
+ feed_command("w")
+ screen:expect([[
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "Xtest-overwrite" |
+ {2:WARNING: The file has been changed since reading it!!!} |
+ {3:Do you really want to write to it (y/n)?}^ |
+ ]])
+
+ feed("u")
+ screen:expect([[
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "Xtest-overwrite" |
+ {2:WARNING: The file has been changed since reading it!!!} |
+ {3:Do you really want to write to it (y/n)?}u |
+ {3:Do you really want to write to it (y/n)?}^ |
+ ]])
+
+ feed("\005")
+ screen:expect([[
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ "Xtest-overwrite" |
+ {2:WARNING: The file has been changed since reading it!!!} |
+ {3:Do you really want to write to it (y/n)?}u |
+ {3:Do you really want to write to it (y/n)?} |
+ {3:Do you really want to write to it (y/n)?}^ |
+ ]])
+
+ feed("n")
+ screen:expect([[
+ {1:~ }|
+ {1:~ }|
+ "Xtest-overwrite" |
+ {2:WARNING: The file has been changed since reading it!!!} |
+ {3:Do you really want to write to it (y/n)?}u |
+ {3:Do you really want to write to it (y/n)?} |
+ {3:Do you really want to write to it (y/n)?}n |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+
+ feed("<cr>")
+ screen:expect([[
+ ^foobar |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/mode_spec.lua b/test/functional/ui/mode_spec.lua
new file mode 100644
index 0000000000..f6b3c1c3c9
--- /dev/null
+++ b/test/functional/ui/mode_spec.lua
@@ -0,0 +1,189 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+
+local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
+local command, eval = helpers.command, helpers.eval
+local eq = helpers.eq
+
+describe('ui mode_change event', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(25, 4)
+ screen:attach({rgb= true})
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=255},
+ [1] = {bold=true, reverse=true},
+ [2] = {bold=true},
+ [3] = {reverse=true},
+ })
+ end)
+
+ it('works in normal mode', function()
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], mode="normal"}
+
+ feed('d')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], mode="operator"}
+
+ feed('<esc>')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], mode="normal"}
+ end)
+
+ it('works in insert mode', function()
+ feed('i')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT --} |
+ ]], mode="insert"}
+
+ feed('word<esc>')
+ screen:expect{grid=[[
+ wor^d |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], mode="normal"}
+
+ command("set showmatch")
+ eq(eval('&matchtime'), 5) -- tenths of seconds
+ feed('a(stuff')
+ screen:expect{grid=[[
+ word(stuff^ |
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT --} |
+ ]], mode="insert"}
+
+ feed(')')
+ screen:expect{grid=[[
+ word^(stuff) |
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT --} |
+ ]], mode="showmatch"}
+
+ screen:sleep(400)
+ screen:expect{grid=[[
+ word(stuff)^ |
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT --} |
+ ]], mode="insert"}
+ end)
+
+ it('works in replace mode', function()
+ feed('R')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {2:-- REPLACE --} |
+ ]], mode="replace"}
+
+ feed('word<esc>')
+ screen:expect{grid=[[
+ wor^d |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], mode="normal"}
+ end)
+
+ it('works in cmdline mode', function()
+ feed(':')
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {0:~ }|
+ :^ |
+ ]], mode="cmdline_normal"}
+
+ feed('x<left>')
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {0:~ }|
+ :^x |
+ ]], mode="cmdline_insert"}
+
+ feed('<insert>')
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {0:~ }|
+ :^x |
+ ]], mode="cmdline_replace"}
+
+
+ feed('<right>')
+ screen:expect{grid=[[
+ |
+ {0:~ }|
+ {0:~ }|
+ :x^ |
+ ]], mode="cmdline_normal"}
+
+ feed('<esc>')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], mode="normal"}
+ end)
+
+ it('works in visual mode', function()
+ insert("text")
+ feed('v')
+ screen:expect{grid=[[
+ tex^t |
+ {0:~ }|
+ {0:~ }|
+ {2:-- VISUAL --} |
+ ]], mode="visual"}
+
+ feed('<esc>')
+ screen:expect{grid=[[
+ tex^t |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], mode="normal"}
+
+ command('set selection=exclusive')
+ feed('v')
+ screen:expect{grid=[[
+ tex^t |
+ {0:~ }|
+ {0:~ }|
+ {2:-- VISUAL --} |
+ ]], mode="visual_select"}
+
+ feed('<esc>')
+ screen:expect{grid=[[
+ tex^t |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], mode="normal"}
+ end)
+end)
+
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index a433143266..8d35df6f48 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -1,42 +1,38 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths
-local insert, execute = helpers.insert, helpers.execute
+local insert, feed_command = helpers.insert, helpers.feed_command
local eq, funcs = helpers.eq, helpers.funcs
+local command = helpers.command
-describe('Mouse input', function()
+describe('ui/mouse/input', function()
local screen
- local hlgroup_colors = {
- NonText = Screen.colors.Blue,
- Visual = Screen.colors.LightGrey
- }
-
before_each(function()
clear()
meths.set_option('mouse', 'a')
meths.set_option('listchars', 'eol:$')
- -- set mouset to very high value to ensure that even in valgrind/travis,
- -- nvim will still pick multiple clicks
- meths.set_option('mouset', 5000)
screen = Screen.new(25, 5)
screen:attach()
screen:set_default_attr_ids({
- [1] = {background = hlgroup_colors.Visual},
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {background = Screen.colors.LightGrey},
[2] = {bold = true},
[3] = {
- foreground = hlgroup_colors.NonText,
- background = hlgroup_colors.Visual,
+ foreground = Screen.colors.Blue,
+ background = Screen.colors.LightGrey,
bold = true,
},
+ [4] = {reverse = true},
+ [5] = {bold = true, reverse = true},
})
- screen:set_default_attr_ignore( {{bold=true, foreground=hlgroup_colors.NonText}} )
+ command("set display-=msgsep")
feed('itesting<cr>mouse<cr>support and selection<esc>')
screen:expect([[
testing |
mouse |
support and selectio^n |
- ~ |
+ {0:~ }|
|
]])
end)
@@ -45,13 +41,13 @@ describe('Mouse input', function()
screen:detach()
end)
- it('left click moves cursor', function()
+ it('single left click moves cursor', function()
feed('<LeftMouse><2,1>')
screen:expect([[
testing |
mo^use |
support and selection |
- ~ |
+ {0:~ }|
|
]])
feed('<LeftMouse><0,0>')
@@ -59,95 +55,368 @@ describe('Mouse input', function()
^testing |
mouse |
support and selection |
- ~ |
+ {0:~ }|
|
]])
end)
+ it('double left click enters visual mode', function()
+ feed('<LeftMouse><0,0>')
+ feed('<LeftRelease><0,0>')
+ feed('<LeftMouse><0,0>')
+ feed('<LeftRelease><0,0>')
+ screen:expect([[
+ {1:testin}^g |
+ mouse |
+ support and selection |
+ {0:~ }|
+ {2:-- VISUAL --} |
+ ]])
+ end)
+
+ it('triple left click enters visual line mode', function()
+ feed('<LeftMouse><0,0>')
+ feed('<LeftRelease><0,0>')
+ feed('<LeftMouse><0,0>')
+ feed('<LeftRelease><0,0>')
+ feed('<LeftMouse><0,0>')
+ feed('<LeftRelease><0,0>')
+ screen:expect([[
+ ^t{1:esting}{3: } |
+ mouse |
+ support and selection |
+ {0:~ }|
+ {2:-- VISUAL LINE --} |
+ ]])
+ end)
+
+ it('quadruple left click enters visual block mode', function()
+ feed('<LeftMouse><0,0>')
+ feed('<LeftRelease><0,0>')
+ feed('<LeftMouse><0,0>')
+ feed('<LeftRelease><0,0>')
+ feed('<LeftMouse><0,0>')
+ feed('<LeftRelease><0,0>')
+ feed('<LeftMouse><0,0>')
+ feed('<LeftRelease><0,0>')
+ screen:expect([[
+ ^testing |
+ mouse |
+ support and selection |
+ {0:~ }|
+ {2:-- VISUAL BLOCK --} |
+ ]])
+ end)
+
+ describe('tab drag', function()
+ before_each(function()
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ tab = { background=Screen.colors.LightGrey, underline=true },
+ sel = { bold=true },
+ fill = { reverse=true }
+ })
+ end)
+
+ it('in tabline on filler space moves tab to the end', function()
+ feed_command('%delete')
+ insert('this is foo')
+ feed_command('silent file foo | tabnew | file bar')
+ insert('this is bar')
+ screen:expect([[
+ {tab: + foo }{sel: + bar }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftMouse><4,0>')
+ screen:expect([[
+ {sel: + foo }{tab: + bar }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftDrag><14,0>')
+ screen:expect([[
+ {tab: + bar }{sel: + foo }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('in tabline to the left moves tab left', function()
+ if helpers.skip_fragile(pending,
+ os.getenv("TRAVIS") and (helpers.os_name() == "osx"
+ or os.getenv("CLANG_SANITIZER") == "ASAN_UBSAN")) -- #4874
+ then
+ return
+ end
+
+ feed_command('%delete')
+ insert('this is foo')
+ feed_command('silent file foo | tabnew | file bar')
+ insert('this is bar')
+ screen:expect([[
+ {tab: + foo }{sel: + bar }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftMouse><11,0>')
+ screen:expect{grid=[[
+ {tab: + foo }{sel: + bar }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], unchanged=true}
+ feed('<LeftDrag><6,0>')
+ screen:expect([[
+ {sel: + bar }{tab: + foo }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('in tabline to the right moves tab right', function()
+ feed_command('%delete')
+ insert('this is foo')
+ feed_command('silent file foo | tabnew | file bar')
+ insert('this is bar')
+ screen:expect([[
+ {tab: + foo }{sel: + bar }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftMouse><4,0>')
+ screen:expect([[
+ {sel: + foo }{tab: + bar }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftDrag><7,0>')
+ screen:expect([[
+ {tab: + bar }{sel: + foo }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('out of tabline under filler space moves tab to the end', function()
+ feed_command('%delete')
+ insert('this is foo')
+ feed_command('silent file foo | tabnew | file bar')
+ insert('this is bar')
+ screen:expect([[
+ {tab: + foo }{sel: + bar }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftMouse><4,0>')
+ screen:expect([[
+ {sel: + foo }{tab: + bar }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftDrag><4,1>')
+ screen:expect{grid=[[
+ {sel: + foo }{tab: + bar }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], unchanged=true}
+ feed('<LeftDrag><14,1>')
+ screen:expect([[
+ {tab: + bar }{sel: + foo }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('out of tabline to the left moves tab left', function()
+ feed_command('%delete')
+ insert('this is foo')
+ feed_command('silent file foo | tabnew | file bar')
+ insert('this is bar')
+ screen:expect([[
+ {tab: + foo }{sel: + bar }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftMouse><11,0>')
+ screen:expect{grid=[[
+ {tab: + foo }{sel: + bar }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], unchanged=true}
+ feed('<LeftDrag><11,1>')
+ screen:expect{grid=[[
+ {tab: + foo }{sel: + bar }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], unchanged=true}
+ feed('<LeftDrag><6,1>')
+ screen:expect([[
+ {sel: + bar }{tab: + foo }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('out of tabline to the right moves tab right', function()
+ feed_command('%delete')
+ insert('this is foo')
+ feed_command('silent file foo | tabnew | file bar')
+ insert('this is bar')
+ screen:expect([[
+ {tab: + foo }{sel: + bar }{fill: }{tab:X}|
+ this is ba^r |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftMouse><4,0>')
+ screen:expect([[
+ {sel: + foo }{tab: + bar }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ feed('<LeftDrag><4,1>')
+ screen:expect{grid=[[
+ {sel: + foo }{tab: + bar }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]], unchanged=true}
+ feed('<LeftDrag><7,1>')
+ screen:expect([[
+ {tab: + bar }{sel: + foo }{fill: }{tab:X}|
+ this is fo^o |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+ end)
+
describe('tabline', function()
- local tab_attrs = {
- tab = { background=Screen.colors.LightGrey, underline=true },
- sel = { bold=true },
- fill = { reverse=true }
- }
+ before_each(function()
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ tab = { background=Screen.colors.LightGrey, underline=true },
+ sel = { bold=true },
+ fill = { reverse=true }
+ })
+ end)
it('left click in default tabline (position 4) switches to tab', function()
- execute('%delete')
+ feed_command('%delete')
insert('this is foo')
- execute('silent file foo | tabnew | file bar')
+ feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
this is ba^r |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
|
- ]], tab_attrs)
+ ]])
feed('<LeftMouse><4,0>')
screen:expect([[
{sel: + foo }{tab: + bar }{fill: }{tab:X}|
this is fo^o |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
|
- ]], tab_attrs)
+ ]])
end)
it('left click in default tabline (position 24) closes tab', function()
meths.set_option('hidden', true)
- execute('%delete')
+ feed_command('%delete')
insert('this is foo')
- execute('silent file foo | tabnew | file bar')
+ feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
this is ba^r |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
|
- ]], tab_attrs)
+ ]])
feed('<LeftMouse><24,0>')
screen:expect([[
this is fo^o |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
- ]], tab_attrs)
+ ]])
end)
it('double click in default tabline (position 4) opens new tab', function()
meths.set_option('hidden', true)
- execute('%delete')
+ feed_command('%delete')
insert('this is foo')
- execute('silent file foo | tabnew | file bar')
+ feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
this is ba^r |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
|
- ]], tab_attrs)
+ ]])
feed('<2-LeftMouse><4,0>')
screen:expect([[
{sel: Name] }{tab: + foo + bar }{fill: }{tab:X}|
^ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
|
- ]], tab_attrs)
+ ]])
end)
describe('%@ label', function()
before_each(function()
- execute([[
+ feed_command([[
function Test(...)
let g:reply = a:000
return copy(a:000) " Check for memory leaks: return should be freed
endfunction
]])
- execute([[
+ feed_command([[
function Test2(...)
return call('Test', a:000 + [2])
endfunction
@@ -158,9 +427,9 @@ describe('Mouse input', function()
{fill:test-test2 }|
mouse |
support and selectio^n |
- ~ |
+ {0:~ }|
|
- ]], tab_attrs)
+ ]])
meths.set_var('reply', {})
end)
@@ -216,7 +485,7 @@ describe('Mouse input', function()
testing |
mo^use |
support and selection |
- ~ |
+ {0:~ }|
|
]])
feed('<LeftDrag><4,1>')
@@ -224,7 +493,7 @@ describe('Mouse input', function()
testing |
mo{1:us}^e |
support and selection |
- ~ |
+ {0:~ }|
{2:-- VISUAL --} |
]])
feed('<LeftDrag><2,2>')
@@ -232,7 +501,7 @@ describe('Mouse input', function()
testing |
mo{1:use}{3: } |
{1:su}^pport and selection |
- ~ |
+ {0:~ }|
{2:-- VISUAL --} |
]])
feed('<LeftDrag><0,0>')
@@ -240,46 +509,47 @@ describe('Mouse input', function()
^t{1:esting}{3: } |
{1:mou}se |
support and selection |
- ~ |
+ {0:~ }|
{2:-- VISUAL --} |
]])
end)
it('left drag changes visual selection after tab click', function()
- local tab_attrs = {
+ screen:set_default_attr_ids({
+ [0] = {bold=true, foreground=Screen.colors.Blue},
tab = { background=Screen.colors.LightGrey, underline=true },
sel = { bold=true },
fill = { reverse=true },
vis = { background=Screen.colors.LightGrey }
- }
- execute('silent file foo | tabnew | file bar')
+ })
+ feed_command('silent file foo | tabnew | file bar')
insert('this is bar')
- execute('tabprevious') -- go to first tab
+ feed_command('tabprevious') -- go to first tab
screen:expect([[
{sel: + foo }{tab: + bar }{fill: }{tab:X}|
mouse |
support and selectio^n |
- ~ |
- |
- ]], tab_attrs)
+ {0:~ }|
+ :tabprevious |
+ ]])
feed('<LeftMouse><10,0><LeftRelease>') -- go to second tab
helpers.wait()
feed('<LeftMouse><0,1>')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
^this is bar |
- ~ |
- ~ |
- |
- ]], tab_attrs)
+ {0:~ }|
+ {0:~ }|
+ :tabprevious |
+ ]])
feed('<LeftDrag><4,1>')
screen:expect([[
{tab: + foo }{sel: + bar }{fill: }{tab:X}|
{vis:this}^ is bar |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
{sel:-- VISUAL --} |
- ]], tab_attrs)
+ ]])
end)
it('two clicks will select the word and enter VISUAL', function()
@@ -288,7 +558,7 @@ describe('Mouse input', function()
testing |
mouse |
{1:suppor}^t and selection |
- ~ |
+ {0:~ }|
{2:-- VISUAL --} |
]])
end)
@@ -299,7 +569,7 @@ describe('Mouse input', function()
testing |
mouse |
{1:su}^p{1:port and selection}{3: } |
- ~ |
+ {0:~ }|
{2:-- VISUAL LINE --} |
]])
end)
@@ -310,7 +580,7 @@ describe('Mouse input', function()
testing |
mouse |
su^pport and selection |
- ~ |
+ {0:~ }|
{2:-- VISUAL BLOCK --} |
]])
end)
@@ -321,7 +591,7 @@ describe('Mouse input', function()
^testing |
mouse |
support and selection |
- ~ |
+ {0:~ }|
|
]])
feed('<RightMouse><2,2>')
@@ -329,7 +599,7 @@ describe('Mouse input', function()
{1:testing}{3: } |
{1:mouse}{3: } |
{1:su}^pport and selection |
- ~ |
+ {0:~ }|
{2:-- VISUAL --} |
]])
end)
@@ -360,79 +630,78 @@ describe('Mouse input', function()
mouse scrolling
]])
screen:try_resize(53, 14)
- execute('sp', 'vsp')
- screen:set_default_attr_ignore( {{bold=true, foreground=hlgroup_colors.NonText},
- {reverse=true}, {bold=true, reverse=true}} )
+ feed_command('sp', 'vsp')
screen:expect([[
- lines |lines |
- to |to |
- test |test |
- mouse scrolling |mouse scrolling |
- ^ | |
- ~ |~ |
- [No Name] [+] [No Name] [+] |
+ lines {4:│}lines |
+ to {4:│}to |
+ test {4:│}test |
+ mouse scrolling {4:│}mouse scrolling |
+ ^ {4:│} |
+ {0:~ }{4:│}{0:~ }|
+ {5:[No Name] [+] }{4:[No Name] [+] }|
to |
test |
mouse scrolling |
|
- ~ |
- [No Name] [+] |
+ {0:~ }|
+ {4:[No Name] [+] }|
:vsp |
]])
- feed('<MouseUp><0,0>')
+ feed('<ScrollWheelDown><0,0>')
screen:expect([[
- mouse scrolling |lines |
- ^ |to |
- ~ |test |
- ~ |mouse scrolling |
- ~ | |
- ~ |~ |
- [No Name] [+] [No Name] [+] |
+ mouse scrolling {4:│}lines |
+ ^ {4:│}to |
+ {0:~ }{4:│}test |
+ {0:~ }{4:│}mouse scrolling |
+ {0:~ }{4:│} |
+ {0:~ }{4:│}{0:~ }|
+ {5:[No Name] [+] }{4:[No Name] [+] }|
to |
test |
mouse scrolling |
|
- ~ |
- [No Name] [+] |
- |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ :vsp |
]])
- feed('<MouseDown><27,0>')
+ feed('<ScrollWheelUp><27,0>')
screen:expect([[
- mouse scrolling |text |
- ^ |with |
- ~ |many |
- ~ |lines |
- ~ |to |
- ~ |test |
- [No Name] [+] [No Name] [+] |
+ mouse scrolling {4:│}text |
+ ^ {4:│}with |
+ {0:~ }{4:│}many |
+ {0:~ }{4:│}lines |
+ {0:~ }{4:│}to |
+ {0:~ }{4:│}test |
+ {5:[No Name] [+] }{4:[No Name] [+] }|
to |
test |
mouse scrolling |
|
- ~ |
- [No Name] [+] |
- |
+ {0:~ }|
+ {4:[No Name] [+] }|
+ :vsp |
]])
- feed('<MouseDown><27,7><MouseDown>')
+ feed('<ScrollWheelUp><27,7><ScrollWheelUp>')
screen:expect([[
- mouse scrolling |text |
- ^ |with |
- ~ |many |
- ~ |lines |
- ~ |to |
- ~ |test |
- [No Name] [+] [No Name] [+] |
+ mouse scrolling {4:│}text |
+ ^ {4:│}with |
+ {0:~ }{4:│}many |
+ {0:~ }{4:│}lines |
+ {0:~ }{4:│}to |
+ {0:~ }{4:│}test |
+ {5:[No Name] [+] }{4:[No Name] [+] }|
Inserting |
text |
with |
many |
lines |
- [No Name] [+] |
- |
+ {4:[No Name] [+] }|
+ :vsp |
]])
end)
it('horizontal scrolling', function()
+ command('set sidescroll=0')
feed("<esc>:set nowrap<cr>")
feed("a <esc>20Ab<esc>")
@@ -440,7 +709,7 @@ describe('Mouse input', function()
|
|
bbbbbbbbbbbbbbb^b |
- ~ |
+ {0:~ }|
|
]])
@@ -449,7 +718,7 @@ describe('Mouse input', function()
|
|
n bbbbbbbbbbbbbbbbbbb^b |
- ~ |
+ {0:~ }|
|
]])
@@ -458,8 +727,572 @@ describe('Mouse input', function()
g |
|
^t and selection bbbbbbbbb|
- ~ |
+ {0:~ }|
|
]])
end)
+
+ describe('on concealed text', function()
+ -- Helpful for reading the test expectations:
+ -- :match Error /\^/
+
+ before_each(function()
+ screen:try_resize(25, 7)
+ screen:set_default_attr_ids({
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ c = { foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray },
+ sm = {bold = true},
+ })
+ feed('ggdG')
+
+ feed_command('set concealcursor=ni')
+ feed_command('set nowrap')
+ feed_command('set shiftwidth=2 tabstop=4 list listchars=tab:>-')
+ feed_command('syntax match NonText "\\*" conceal')
+ feed_command('syntax match NonText "cats" conceal cchar=X')
+ feed_command('syntax match NonText "x" conceal cchar=>')
+
+ -- First column is there to retain the tabs.
+ insert([[
+ |Section *t1*
+ | *t2* *t3* *t4*
+ |x 私は猫が大好き *cats* ✨🐈✨
+ ]])
+
+ feed('gg<c-v>Gxgg')
+ end)
+
+ it('(level 1) click on non-wrapped lines', function()
+ feed_command('let &conceallevel=1', 'echo')
+
+ feed('<esc><LeftMouse><0,0>')
+ screen:expect([[
+ ^Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><1,0>')
+ screen:expect([[
+ S^ection{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><21,0>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }^t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><21,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t^3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: X } {0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><0,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:^>} 私は猫が大好き{0:>---}{c: X } {0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><7,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は^猫が大好き{0:>---}{c: X } {0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><21,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ {c:>} 私は猫が大好き{0:>---}{c: ^X } {0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ end) -- level 1 - non wrapped
+
+ it('(level 1) click on wrapped lines', function()
+ feed_command('let &conceallevel=1', 'let &wrap=1', 'echo')
+
+ feed('<esc><LeftMouse><24,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c:^ }|
+ t4{c: } |
+ {c:>} 私は猫が大好き{0:>---}{c: X} |
+ {c: } ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><0,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ ^t4{c: } |
+ {c:>} 私は猫が大好き{0:>---}{c: X} |
+ {c: } ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><8,3>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ t4{c: } |
+ {c:>} 私は猫^が大好き{0:>---}{c: X} |
+ {c: } ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><21,3>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ t4{c: } |
+ {c:>} 私は猫が大好き{0:>---}{c: ^X} |
+ {c: } ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><4,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}{c: }t1{c: } |
+ {0:>--->--->---} {c: }t2{c: } {c: }t3{c: } {c: }|
+ t4{c: } |
+ {c:>} 私は猫が大好き{0:>---}{c: X} |
+ {c: } ✨^🐈✨ |
+ |
+ |
+ ]])
+ end) -- level 1 - wrapped
+
+
+ it('(level 2) click on non-wrapped lines', function()
+ feed_command('let &conceallevel=2', 'echo')
+
+ feed('<esc><LeftMouse><20,0>')
+ screen:expect([[
+ Section{0:>>--->--->---}^t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><14,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} ^t2 t3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><18,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t^3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><0,2>') -- Weirdness
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:^>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><8,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:>} 私は猫^が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><20,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:^X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end) -- level 2 - non wrapped
+
+ it('(level 2) click on non-wrapped lines (insert mode)', function()
+ feed_command('let &conceallevel=2', 'echo')
+
+ feed('<esc>i<LeftMouse><20,0>')
+ screen:expect([[
+ Section{0:>>--->--->---}^t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ {sm:-- INSERT --} |
+ ]])
+
+ feed('<LeftMouse><14,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} ^t2 t3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ {sm:-- INSERT --} |
+ ]])
+
+ feed('<LeftMouse><18,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t^3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ {sm:-- INSERT --} |
+ ]])
+
+ feed('<LeftMouse><0,2>') -- Weirdness
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:^>} 私は猫が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ {sm:-- INSERT --} |
+ ]])
+
+ feed('<LeftMouse><8,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:>} 私は猫^が大好き{0:>---}{c:X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ {sm:-- INSERT --} |
+ ]])
+
+ feed('<LeftMouse><20,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:^X} ✨{0:>}|
+ |
+ {0:~ }|
+ {0:~ }|
+ {sm:-- INSERT --} |
+ ]])
+ end) -- level 2 - non wrapped (insert mode)
+
+ it('(level 2) click on wrapped lines', function()
+ feed_command('let &conceallevel=2', 'let &wrap=1', 'echo')
+
+ feed('<esc><LeftMouse><20,0>')
+ screen:expect([[
+ Section{0:>>--->--->---}^t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><14,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} ^t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><18,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t^3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ -- NOTE: The click would ideally be on the 't' in 't4', but wrapping
+ -- caused the invisible '*' right before 't4' to remain on the previous
+ -- screen line. This is being treated as expected because fixing this is
+ -- out of scope for mouse clicks. Should the wrapping behavior of
+ -- concealed characters change in the future, this case should be
+ -- reevaluated.
+ feed('<esc><LeftMouse><0,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 ^ |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><1,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t^4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><0,3>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:^>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><20,3>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:^X} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><1,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ^✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><5,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ {c:>} 私は猫が大好き{0:>---}{c:X} |
+ ✨🐈^✨ |
+ |
+ |
+ ]])
+ end) -- level 2 - wrapped
+
+
+ it('(level 3) click on non-wrapped lines', function()
+ feed_command('let &conceallevel=3', 'echo')
+
+ feed('<esc><LeftMouse><0,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ ^ 私は猫が大好き{0:>----} ✨🐈|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><1,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ ^私は猫が大好き{0:>----} ✨🐈|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><13,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ 私は猫が大好^き{0:>----} ✨🐈|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('<esc><LeftMouse><20,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 t4 |
+ 私は猫が大好き{0:>----}^ ✨🐈|
+ |
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end) -- level 3 - non wrapped
+
+ it('(level 3) click on wrapped lines', function()
+ feed_command('let &conceallevel=3', 'let &wrap=1', 'echo')
+
+ feed('<esc><LeftMouse><14,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} ^t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><18,1>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t^3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><1,2>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t^4 |
+ 私は猫が大好き{0:>----} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><0,3>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ ^ 私は猫が大好き{0:>----} |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><20,3>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----}^ |
+ ✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><1,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ^✨🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><3,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ✨^🐈✨ |
+ |
+ |
+ ]])
+
+ feed('<esc><LeftMouse><5,4>')
+ screen:expect([[
+ Section{0:>>--->--->---}t1 |
+ {0:>--->--->---} t2 t3 |
+ t4 |
+ 私は猫が大好き{0:>----} |
+ ✨🐈^✨ |
+ |
+ |
+ ]])
+
+ end) -- level 3 - wrapped
+ end)
end)
diff --git a/test/functional/ui/multibyte_spec.lua b/test/functional/ui/multibyte_spec.lua
new file mode 100644
index 0000000000..3e63353ad2
--- /dev/null
+++ b/test/functional/ui/multibyte_spec.lua
@@ -0,0 +1,185 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local feed = helpers.feed
+local feed_command = helpers.feed_command
+local insert = helpers.insert
+local funcs = helpers.funcs
+
+describe("multibyte rendering", function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(60, 6)
+ screen:attach({rgb=true})
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {background = Screen.colors.WebGray},
+ [3] = {background = Screen.colors.LightMagenta},
+ [4] = {bold = true},
+ })
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it("works with composed char at start of line", function()
+ insert([[
+ ̊
+ x]])
+ feed("gg")
+ -- verify the modifier infact is alone
+ feed_command("ascii")
+ screen:expect([[
+ ^ ̊ |
+ x |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ < ̊> 778, Hex 030a, Octal 1412 |
+ ]])
+
+ -- a char inserted before will spontaneously merge with it
+ feed("ia<esc>")
+ feed_command("ascii")
+ screen:expect([[
+ ^å |
+ x |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ <a> 97, Hex 61, Octal 141 < ̊> 778, Hex 030a, Octal 1412 |
+ ]])
+ end)
+
+ it('works with doublewidth char at end of line', function()
+ feed('58a <esc>a馬<esc>')
+ screen:expect([[
+ ^馬|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed('i <esc>')
+ screen:expect([[
+ ^ {1:>}|
+ 馬 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ feed('l')
+ screen:expect([[
+ {1:>}|
+ ^馬 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('clears left half of double-width char when right half is overdrawn', function()
+ feed('o-馬<esc>ggiab ')
+ screen:expect([[
+ ab ^ |
+ -馬 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT --} |
+ ]])
+
+ -- check double-with char is temporarily hidden when overlapped
+ funcs.complete(4, {'xx', 'yy'})
+ screen:expect([[
+ ab xx^ |
+ - {2: xx } |
+ {1:~ }{3: yy }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT --} |
+ ]])
+
+ -- check it is properly restored
+ feed('z')
+ screen:expect([[
+ ab xxz^ |
+ -馬 |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:-- INSERT --} |
+ ]])
+ end)
+end)
+
+describe('multibyte rendering: statusline', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(40, 4)
+ screen:attach()
+ command('set laststatus=2')
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('last char shows (multibyte)', function()
+ command('set statusline=你好')
+ screen:expect([[
+ ^ |
+ ~ |
+ 你好 |
+ |
+ ]])
+ end)
+ it('last char shows (single byte)', function()
+ command('set statusline=abc')
+ screen:expect([[
+ ^ |
+ ~ |
+ abc |
+ |
+ ]])
+ end)
+ it('unicode control points', function()
+ command('set statusline=Ÿ')
+ screen:expect([[
+ ^ |
+ ~ |
+ <9f> |
+ |
+ ]])
+ end)
+ it('MAX_MCO (6) unicode combination points', function()
+ command('set statusline=o̸⃯ᷰ⃐⃧⃝')
+ -- o + U+1DF0 + U+20EF + U+0338 + U+20D0 + U+20E7 + U+20DD
+ screen:expect([[
+ ^ |
+ ~ |
+ o̸⃯ᷰ⃐⃧⃝ |
+ |
+ ]])
+ end)
+ it('non-printable followed by MAX_MCO unicode combination points', function()
+ command('set statusline=Ÿ̸⃯ᷰ⃐⃧⃝')
+ -- U+9F + U+1DF0 + U+20EF + U+0338 + U+20D0 + U+20E7 + U+20DD
+ screen:expect([[
+ ^ |
+ ~ |
+ <9f><1df0><20ef><0338><20d0><20e7><20dd>|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua
new file mode 100644
index 0000000000..a5d4e34000
--- /dev/null
+++ b/test/functional/ui/multigrid_spec.lua
@@ -0,0 +1,1524 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local feed, command, insert = helpers.feed, helpers.command, helpers.insert
+local eq = helpers.eq
+
+
+describe('multigrid screen', function()
+ local screen
+
+ before_each(function()
+ clear{headless=false, args={'--cmd', 'set laststatus=2'}}
+ screen = Screen.new(53,14)
+ screen:attach({ext_multigrid=true})
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Magenta},
+ [3] = {foreground = Screen.colors.Brown, bold = true},
+ [4] = {foreground = Screen.colors.SlateBlue},
+ [5] = {bold = true, foreground = Screen.colors.SlateBlue},
+ [6] = {foreground = Screen.colors.Cyan4},
+ [7] = {bold = true},
+ [8] = {underline = true, bold = true, foreground = Screen.colors.SlateBlue},
+ [9] = {foreground = Screen.colors.SlateBlue, underline = true},
+ [10] = {foreground = Screen.colors.Red},
+ [11] = {bold = true, reverse = true},
+ [12] = {reverse = true},
+ [13] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
+ [14] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [15] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [16] = {background = Screen.colors.LightGrey, underline = true},
+ [17] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta},
+ [18] = {bold = true, foreground = Screen.colors.Magenta},
+ [19] = {foreground = Screen.colors.Brown},
+ })
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('default initial screen', function()
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] }|
+ |
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+
+ it('positions windows correctly', function()
+ command('vsplit')
+ screen:expect([[
+ ## grid 1
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], nil, nil, function()
+ eq({
+ [2] = { win = 1000, startrow = 0, startcol = 27, width = 26, height = 12 },
+ [3] = { win = 1001, startrow = 0, startcol = 0, width = 26, height = 12 }
+ }, screen.win_position)
+ end)
+ command('wincmd l')
+ command('split')
+ screen:expect([[
+ ## grid 1
+ [3:--------------------------]{12:│}[4:--------------------------]|
+ [3:--------------------------]{12:│}[4:--------------------------]|
+ [3:--------------------------]{12:│}[4:--------------------------]|
+ [3:--------------------------]{12:│}[4:--------------------------]|
+ [3:--------------------------]{12:│}[4:--------------------------]|
+ [3:--------------------------]{12:│}[4:--------------------------]|
+ [3:--------------------------]{12:│}{11:[No Name] }|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ {12:[No Name] [No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], nil, nil, function()
+ eq({
+ [2] = { win = 1000, startrow = 7, startcol = 27, width = 26, height = 5 },
+ [3] = { win = 1001, startrow = 0, startcol = 0, width = 26, height = 12 },
+ [4] = { win = 1002, startrow = 0, startcol = 27, width = 26, height = 6 }
+ }, screen.win_position)
+ end)
+ command('wincmd h')
+ command('q')
+ screen:expect([[
+ ## grid 1
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]], nil, nil, function()
+ eq({
+ [2] = { win = 1000, startrow = 7, startcol = 0, width = 53, height = 5 },
+ [4] = { win = 1002, startrow = 0, startcol = 0, width = 53, height = 6 }
+ }, screen.win_position)
+ end)
+ end)
+
+ describe('split', function ()
+ describe('horizontally', function ()
+ it('allocates grids', function ()
+ command('sp')
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+
+ it('resizes grids', function ()
+ command('sp')
+ command('resize 8')
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+
+ it('splits vertically', function()
+ command('sp')
+ command('vsp')
+ command('vsp')
+ screen:expect([[
+ ## grid 1
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ {11:[No Name] }{12:[No Name] [No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 5
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ insert('hello')
+ screen:expect([[
+ ## grid 1
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ [5:--------------------]{12:│}[4:----------------]{12:│}[3:---------------]|
+ {11:[No Name] [+] }{12:[No Name] [+] [No Name] [+] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] [+] }|
+ |
+ ## grid 2
+ hello |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ hello |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ hello |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 5
+ hell^o |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+ it('closes splits', function ()
+ command('sp')
+ screen:expect([[
+ ## grid 1
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ [3:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ command('q')
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] }|
+ |
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+ end)
+
+ describe('vertically', function ()
+ it('allocates grids', function ()
+ command('vsp')
+ screen:expect([[
+ ## grid 1
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+ it('resizes grids', function ()
+ command('vsp')
+ command('vertical resize 10')
+ -- see "Note 1" for info about why there are two vseps
+ screen:expect([[
+ ## grid 1
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ [3:----------]{12:│}[2:------------------------------------------]|
+ {11:<No Name] }{12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+ it('splits horizontally', function ()
+ command('vsp')
+ command('sp')
+ screen:expect([[
+ ## grid 1
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] }{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ {12:[No Name] [No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ insert('hello')
+ screen:expect([[
+ ## grid 1
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ [4:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] [+] }{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ {12:[No Name] [+] [No Name] [+] }|
+ |
+ ## grid 2
+ hello |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ hello |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ hell^o |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+ it('closes splits', function ()
+ command('vsp')
+ screen:expect([[
+ ## grid 1
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ command('q')
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] }|
+ |
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+ end)
+ end)
+
+ describe('on resize', function ()
+ it('rebuilds all grids', function ()
+ screen:try_resize(25, 6)
+ screen:expect([[
+ ## grid 1
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ [2:-------------------------]|
+ {11:[No Name] }|
+ |
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+
+ it('has minimum width/height values', function()
+ screen:try_resize(1, 1)
+ screen:expect([[
+ ## grid 1
+ [2:------------]|
+ {11:[No Name] }|
+ |
+ ## grid 2
+ ^ |
+ ]])
+
+ feed('<esc>:ls')
+ screen:expect([[
+ ## grid 1
+ [2:------------]|
+ {11:[No Name] }|
+ :ls^ |
+ ## grid 2
+ |
+ ]])
+ end)
+ end)
+
+ describe('grid of smaller inner size', function()
+ it('is rendered correctly', function()
+ screen:try_resize_grid(2, 8, 5)
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] }|
+ |
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+ end)
+
+ describe('grid of bigger inner size', function()
+ it('is rendered correctly', function()
+ screen:try_resize_grid(2, 80, 20)
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] }|
+ |
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+ end)
+
+
+ describe('with resized grid', function()
+ before_each(function()
+ screen:try_resize_grid(2, 60, 20)
+ end)
+ it('gets written till grid width', function()
+ insert(('a'):rep(60).."\n")
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ |
+ ## grid 2
+ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa|
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+
+ it('wraps with grid width', function()
+ insert(('b'):rep(80).."\n")
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ |
+ ## grid 2
+ bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb|
+ bbbbbbbbbbbbbbbbbbbb |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+
+ it('displays messages with default grid width', function()
+ command('echomsg "this is a very very very very very very very very'..
+ ' long message"')
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] }|
+ this is a very very very...ry very very long message |
+ ## grid 2
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+
+ it('creates folds with grid width', function()
+ insert('this is a fold\nthis is inside fold\nthis is outside fold')
+ feed('kzfgg')
+ screen:expect([[
+ ## grid 1
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11:[No Name] [+] }|
+ |
+ ## grid 2
+ {13:^+-- 2 lines: this is a fold································}|
+ this is outside fold |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+ end)
+
+ it('multiline messages scroll over windows', function()
+ command('sp')
+ command('vsp')
+ screen:expect([[
+ ## grid 1
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ feed(":echoerr 'very' | echoerr 'much' | echoerr 'fail'<cr>")
+ screen:expect([[
+ ## grid 1
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {11: }|
+ {14:very} |
+ {14:much} |
+ {14:fail} |
+ {15:Press ENTER or type command to continue}^ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ feed('<cr>')
+ screen:expect([[
+ ## grid 1
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ command([[
+ func! ErrMsg()
+ for i in range(11)
+ echoerr "error ".i
+ endfor
+ endfunc]])
+ feed(":call ErrMsg()<cr>")
+ screen:expect([[
+ ## grid 1
+ {14:Error detected while processing function ErrMsg:} |
+ {19:line 2:} |
+ {14:error 0} |
+ {14:error 1} |
+ {14:error 2} |
+ {14:error 3} |
+ {14:error 4} |
+ {14:error 5} |
+ {14:error 6} |
+ {14:error 7} |
+ {14:error 8} |
+ {14:error 9} |
+ {14:error 10} |
+ {15:Press ENTER or type command to continue}^ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ feed("<c-c>")
+ screen:expect([[
+ ## grid 1
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ [4:--------------------------]{12:│}[3:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ [2:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+
+ it('handles switich tabs', function()
+ command('vsp')
+ screen:expect([[
+ ## grid 1
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+
+ command('tabnew')
+ -- note the old grids aren't resized yet
+ screen:expect([[
+ ## grid 1
+ {16: }{17:2}{16: [No Name] }{7: [No Name] }{12: }{16:X}|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {11:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ command('sp')
+ screen:expect([[
+ ## grid 1
+ {16: }{17:2}{16: [No Name] }{7: }{18:2}{7: [No Name] }{12: }{16:X}|
+ [5:-----------------------------------------------------]|
+ [5:-----------------------------------------------------]|
+ [5:-----------------------------------------------------]|
+ [5:-----------------------------------------------------]|
+ [5:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 5
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ command('tabnext')
+ screen:expect([[
+ ## grid 1
+ {7: }{18:2}{7: [No Name] }{16: }{17:2}{16: [No Name] }{12: }{16:X}|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 5
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ command('tabnext')
+ screen:expect([[
+ ## grid 1
+ {16: }{17:2}{16: [No Name] }{7: }{18:2}{7: [No Name] }{12: }{16:X}|
+ [5:-----------------------------------------------------]|
+ [5:-----------------------------------------------------]|
+ [5:-----------------------------------------------------]|
+ [5:-----------------------------------------------------]|
+ [5:-----------------------------------------------------]|
+ {11:[No Name] }|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ [4:-----------------------------------------------------]|
+ {12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 4
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 5
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+
+ command('tabclose')
+ screen:expect([[
+ ## grid 1
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ [3:--------------------------]{12:│}[2:--------------------------]|
+ {11:[No Name] }{12:[No Name] }|
+ |
+ ## grid 2
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ## grid 3
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/options_spec.lua b/test/functional/ui/options_spec.lua
new file mode 100644
index 0000000000..c26fa5e29b
--- /dev/null
+++ b/test/functional/ui/options_spec.lua
@@ -0,0 +1,124 @@
+local global_helpers = require('test.helpers')
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local command = helpers.command
+local eq = helpers.eq
+local shallowcopy = global_helpers.shallowcopy
+
+describe('ui receives option updates', function()
+ local screen
+
+ local function reset(opts, ...)
+ local defaults = {
+ ambiwidth='single',
+ arabicshape=true,
+ emoji=true,
+ guifont='',
+ guifontset='',
+ guifontwide='',
+ linespace=0,
+ showtabline=1,
+ termguicolors=false,
+ ext_cmdline=false,
+ ext_popupmenu=false,
+ ext_tabline=false,
+ ext_wildmenu=false,
+ ext_linegrid=false,
+ ext_hlstate=false,
+ ext_multigrid=false,
+ }
+
+ clear(...)
+ screen = Screen.new(20,5)
+ screen:attach(opts)
+ -- NB: UI test suite can be run in both "linegrid" and legacy grid mode.
+ -- In both cases check that the received value is the one requested.
+ defaults.ext_linegrid = screen._options.ext_linegrid or false
+ return defaults
+ end
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it("for defaults", function()
+ local expected = reset()
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+ end)
+
+ it("when setting options", function()
+ local expected = reset()
+ local defaults = shallowcopy(expected)
+
+ command("set termguicolors")
+ expected.termguicolors = true
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
+ command("set guifont=Comic\\ Sans")
+ expected.guifont = "Comic Sans"
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
+ command("set showtabline=0")
+ expected.showtabline = 0
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
+ command("set linespace=13")
+ expected.linespace = 13
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
+ command("set linespace=-11")
+ expected.linespace = -11
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
+ command("set all&")
+ screen:expect(function()
+ eq(defaults, screen.options)
+ end)
+ end)
+
+ it('with UI extensions', function()
+ local expected = reset({ext_cmdline=true, ext_wildmenu=true})
+
+ expected.ext_cmdline = true
+ expected.ext_wildmenu = true
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
+ screen:set_option('ext_popupmenu', true)
+ expected.ext_popupmenu = true
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+
+ screen:set_option('ext_wildmenu', false)
+ expected.ext_wildmenu = false
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+ end)
+
+ local function startup_test(headless)
+ local expected = reset(nil,{headless=headless,args={'--cmd', 'set guifont=Comic\\ Sans\\ 12'}})
+ expected.guifont = "Comic Sans 12"
+ screen:expect(function()
+ eq(expected, screen.options)
+ end)
+ end
+
+ it('from startup options with --headless', function() startup_test(true) end)
+ it('from startup options with --embed', function() startup_test(false) end)
+end)
diff --git a/test/functional/ui/output_spec.lua b/test/functional/ui/output_spec.lua
new file mode 100644
index 0000000000..87b489fd71
--- /dev/null
+++ b/test/functional/ui/output_spec.lua
@@ -0,0 +1,234 @@
+local Screen = require('test.functional.ui.screen')
+local helpers = require('test.functional.helpers')(after_each)
+local child_session = require('test.functional.terminal.helpers')
+local mkdir, write_file, rmdir = helpers.mkdir, helpers.write_file, helpers.rmdir
+local eq = helpers.eq
+local eval = helpers.eval
+local feed = helpers.feed
+local feed_command = helpers.feed_command
+local iswin = helpers.iswin
+local clear = helpers.clear
+local command = helpers.command
+local nvim_dir = helpers.nvim_dir
+
+describe("shell command :!", function()
+ if helpers.pending_win32(pending) then return end
+
+ local screen
+ before_each(function()
+ clear()
+ screen = child_session.screen_setup(0, '["'..helpers.nvim_prog..
+ '", "-u", "NONE", "-i", "NONE", "--cmd", "'..helpers.nvim_set..'"]')
+ screen:expect([[
+ {1: } |
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ {4:~ }|
+ |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+
+ after_each(function()
+ child_session.feed_data("\3") -- Ctrl-C
+ screen:detach()
+ end)
+
+ it("displays output without LF/EOF. #4646 #4569 #3772", function()
+ -- NOTE: We use a child nvim (within a :term buffer)
+ -- to avoid triggering a UI flush.
+ child_session.feed_data(":!printf foo; sleep 200\n")
+ screen:expect([[
+ |
+ {4:~ }|
+ {4:~ }|
+ {5: }|
+ :!printf foo; sleep 200 |
+ foo |
+ {3:-- TERMINAL --} |
+ ]])
+ end)
+
+ it("throttles shell-command output greater than ~10KB", function()
+ if helpers.skip_fragile(pending) then
+ return
+ end
+ child_session.feed_data(
+ ":!for i in $(seq 2 30000); do echo XXXXXXXXXX $i; done\n")
+
+ -- If we observe any line starting with a dot, then throttling occurred.
+ -- Avoid false failure on slow systems.
+ screen:expect{any="\n%.", timeout=20000}
+
+ -- Final chunk of output should always be displayed, never skipped.
+ -- (Throttling is non-deterministic, this test is merely a sanity check.)
+ screen:expect([[
+ XXXXXXXXXX 29997 |
+ XXXXXXXXXX 29998 |
+ XXXXXXXXXX 29999 |
+ XXXXXXXXXX 30000 |
+ |
+ {10:Press ENTER or type command to continue}{1: } |
+ {3:-- TERMINAL --} |
+ ]], {
+ -- test/functional/helpers.lua defaults to background=light.
+ [1] = {reverse = true},
+ [3] = {bold = true},
+ [10] = {foreground = 2},
+ })
+ end)
+end)
+
+describe("shell command :!", function()
+ before_each(function()
+ clear()
+ end)
+
+ it("cat a binary file #4142", function()
+ feed(":exe 'silent !cat '.shellescape(v:progpath)<CR>")
+ eq(2, eval('1+1')) -- Still alive?
+ end)
+
+ it([[display \x08 char #4142]], function()
+ feed(":silent !echo \08<CR>")
+ eq(2, eval('1+1')) -- Still alive?
+ end)
+
+ it('handles control codes', function()
+ if iswin() then
+ pending('missing printf', function() end)
+ return
+ end
+ local screen = Screen.new(50, 4)
+ screen:attach()
+ command("set display-=msgsep")
+ -- Print TAB chars. #2958
+ feed([[:!printf '1\t2\t3'<CR>]])
+ screen:expect([[
+ ~ |
+ :!printf '1\t2\t3' |
+ 1 2 3 |
+ Press ENTER or type command to continue^ |
+ ]])
+ feed([[<CR>]])
+ -- Print BELL control code. #4338
+ screen.bell = false
+ feed([[:!printf '\007\007\007\007text'<CR>]])
+ screen:expect{grid=[[
+ ~ |
+ :!printf '\007\007\007\007text' |
+ text |
+ Press ENTER or type command to continue^ |
+ ]], condition=function()
+ eq(true, screen.bell)
+ end}
+ feed([[<CR>]])
+ -- Print BS control code.
+ feed([[:echo system('printf ''\010\n''')<CR>]])
+ screen:expect([[
+ ~ |
+ ^H |
+ |
+ Press ENTER or type command to continue^ |
+ ]])
+ feed([[<CR>]])
+ -- Print LF control code.
+ feed([[:!printf '\n'<CR>]])
+ screen:expect([[
+ :!printf '\n' |
+ |
+ |
+ Press ENTER or type command to continue^ |
+ ]])
+ feed([[<CR>]])
+ end)
+
+ describe('', function()
+ local screen
+ before_each(function()
+ rmdir('bang_filter_spec')
+ mkdir('bang_filter_spec')
+ write_file('bang_filter_spec/f1', 'f1')
+ write_file('bang_filter_spec/f2', 'f2')
+ write_file('bang_filter_spec/f3', 'f3')
+ screen = Screen.new(53,10)
+ screen:set_default_attr_ids({
+ [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {foreground = Screen.colors.Blue1},
+ [3] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ [4] = {bold = true, reverse = true},
+ })
+ screen:attach()
+ end)
+
+ after_each(function()
+ rmdir('bang_filter_spec')
+ end)
+
+ it("doesn't truncate Last line of shell output #3269", function()
+ command(helpers.iswin()
+ and [[nnoremap <silent>\l :!dir /b bang_filter_spec<cr>]]
+ or [[nnoremap <silent>\l :!ls bang_filter_spec<cr>]])
+ local result = (helpers.iswin()
+ and [[:!dir /b bang_filter_spec]]
+ or [[:!ls bang_filter_spec ]])
+ feed([[\l]])
+ screen:expect([[
+ |
+ {1:~ }|
+ {1:~ }|
+ {4: }|
+ ]]..result..[[ |
+ f1 |
+ f2 |
+ f3 |
+ |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ end)
+
+ it('handles binary and multibyte data', function()
+ feed_command('!cat test/functional/fixtures/shell_data.txt')
+ screen.bell = false
+ screen:expect{grid=[[
+ |
+ {1:~ }|
+ {4: }|
+ :!cat test/functional/fixtures/shell_data.txt |
+ {2:^@^A^B^C^D^E^F^H} |
+ {2:^N^O^P^Q^R^S^T^U^V^W^X^Y^Z^[^\^]^^^_} |
+ ö 한글 {2:<a5><c3>} |
+ t {2:<ff>} |
+ |
+ {3:Press ENTER or type command to continue}^ |
+ ]], condition=function()
+ eq(true, screen.bell)
+ end}
+ end)
+
+ it('handles multibyte sequences split over buffer boundaries', function()
+ command('cd '..nvim_dir)
+ local cmd
+ if iswin() then
+ cmd = '!shell-test UTF-8 '
+ else
+ cmd = '!./shell-test UTF-8'
+ end
+ feed_command(cmd)
+ -- Note: only the first example of split composed char works
+ screen:expect([[
+ |
+ {4: }|
+ :]]..cmd..[[ |
+ å |
+ ref: å̲ |
+ 1: å̲ |
+ 2: å ̲ |
+ 3: å ̲ |
+ |
+ {3:Press ENTER or type command to continue}^ |
+ ]])
+ end)
+ end)
+end)
diff --git a/test/functional/ui/popupmenu_spec.lua b/test/functional/ui/popupmenu_spec.lua
new file mode 100644
index 0000000000..9424931de4
--- /dev/null
+++ b/test/functional/ui/popupmenu_spec.lua
@@ -0,0 +1,610 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, feed = helpers.clear, helpers.feed
+local source = helpers.source
+local insert = helpers.insert
+local meths = helpers.meths
+local command = helpers.command
+
+describe('ui/ext_popupmenu', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(60, 8)
+ screen:attach({rgb=true, ext_popupmenu=true})
+ screen:set_default_attr_ids({
+ [1] = {bold=true, foreground=Screen.colors.Blue},
+ [2] = {bold = true},
+ [3] = {reverse = true},
+ [4] = {bold = true, reverse = true},
+ [5] = {bold = true, foreground = Screen.colors.SeaGreen},
+ [6] = {background = Screen.colors.WebGray},
+ [7] = {background = Screen.colors.LightMagenta},
+ })
+ source([[
+ function! TestComplete() abort
+ call complete(1, [{'word':'foo', 'abbr':'fo', 'menu':'the foo', 'info':'foo-y', 'kind':'x'}, 'bar', 'spam'])
+ return ''
+ endfunction
+ ]])
+ end)
+
+ local expected = {
+ {'fo', 'x', 'the foo', 'foo-y'},
+ {'bar', '', '', ''},
+ {'spam', '', '', ''},
+ }
+
+ it('works', function()
+ feed('o<C-r>=TestComplete()<CR>')
+ screen:expect{grid=[[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=0,
+ anchor={1,0},
+ }}
+
+ feed('<c-p>')
+ screen:expect{grid=[[
+ |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=-1,
+ anchor={1,0},
+ }}
+
+ -- down moves the selection in the menu, but does not insert anything
+ feed('<down><down>')
+ screen:expect{grid=[[
+ |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=1,
+ anchor={1,0},
+ }}
+
+ feed('<cr>')
+ screen:expect{grid=[[
+ |
+ bar^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]]}
+ end)
+
+ it('can be controlled by API', function()
+ feed('o<C-r>=TestComplete()<CR>')
+ screen:expect{grid=[[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=0,
+ anchor={1,0},
+ }}
+
+ meths.select_popupmenu_item(1,false,false,{})
+ screen:expect{grid=[[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=1,
+ anchor={1,0},
+ }}
+
+ meths.select_popupmenu_item(2,true,false,{})
+ screen:expect{grid=[[
+ |
+ spam^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=2,
+ anchor={1,0},
+ }}
+
+ meths.select_popupmenu_item(0,true,true,{})
+ screen:expect([[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+
+ feed('<c-w><C-r>=TestComplete()<CR>')
+ screen:expect{grid=[[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=0,
+ anchor={1,0},
+ }}
+
+ meths.select_popupmenu_item(-1,false,false,{})
+ screen:expect{grid=[[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=-1,
+ anchor={1,0},
+ }}
+
+ meths.select_popupmenu_item(1,true,false,{})
+ screen:expect{grid=[[
+ |
+ bar^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=1,
+ anchor={1,0},
+ }}
+
+ meths.select_popupmenu_item(-1,true,false,{})
+ screen:expect{grid=[[
+ |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=-1,
+ anchor={1,0},
+ }}
+
+ meths.select_popupmenu_item(0,true,false,{})
+ screen:expect{grid=[[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=0,
+ anchor={1,0},
+ }}
+
+ meths.select_popupmenu_item(-1,true,true,{})
+ screen:expect([[
+ |
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ command('imap <f1> <cmd>call nvim_select_popupmenu_item(2,v:true,v:false,{})<cr>')
+ command('imap <f2> <cmd>call nvim_select_popupmenu_item(-1,v:false,v:false,{})<cr>')
+ command('imap <f3> <cmd>call nvim_select_popupmenu_item(1,v:false,v:true,{})<cr>')
+ feed('<C-r>=TestComplete()<CR>')
+ screen:expect{grid=[[
+ |
+ foo^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=0,
+ anchor={1,0},
+ }}
+
+ feed('<f1>')
+ screen:expect{grid=[[
+ |
+ spam^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=2,
+ anchor={1,0},
+ }}
+
+ feed('<f2>')
+ screen:expect{grid=[[
+ |
+ spam^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]], popupmenu={
+ items=expected,
+ pos=-1,
+ anchor={1,0},
+ }}
+
+ feed('<f3>')
+ screen:expect([[
+ |
+ bar^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ -- also should work for builtin popupmenu
+ screen:set_option('ext_popupmenu', false)
+ feed('<C-r>=TestComplete()<CR>')
+ screen:expect([[
+ |
+ foo^ |
+ {6:fo x the foo }{1: }|
+ {7:bar }{1: }|
+ {7:spam }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<f1>')
+ screen:expect([[
+ |
+ spam^ |
+ {7:fo x the foo }{1: }|
+ {7:bar }{1: }|
+ {6:spam }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<f2>')
+ screen:expect([[
+ |
+ spam^ |
+ {7:fo x the foo }{1: }|
+ {7:bar }{1: }|
+ {7:spam }{1: }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+
+ feed('<f3>')
+ screen:expect([[
+ |
+ bar^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:-- INSERT --} |
+ ]])
+ end)
+end)
+
+
+describe('popup placement', function()
+ local screen
+ before_each(function()
+ clear()
+ screen = Screen.new(32, 20)
+ screen:attach()
+ screen:set_default_attr_ids({
+ -- popup selected item / scrollbar track
+ ['s'] = {background = Screen.colors.WebGray},
+ -- popup non-selected item
+ ['n'] = {background = Screen.colors.LightMagenta},
+ -- popup scrollbar knob
+ ['c'] = {background = Screen.colors.Grey0},
+ [1] = {bold = true, foreground = Screen.colors.Blue},
+ [2] = {bold = true},
+ [3] = {reverse = true},
+ [4] = {bold = true, reverse = true},
+ [5] = {bold = true, foreground = Screen.colors.SeaGreen}
+ })
+ end)
+
+ it('works with preview-window above', function()
+ feed(':ped<CR><c-w>4+')
+ feed('iaa bb cc dd ee ff gg hh ii jj<cr>')
+ feed('<c-x><c-n>')
+ screen:expect([[
+ aa bb cc dd ee ff gg hh ii jj |
+ aa |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3:[No Name] [Preview][+] }|
+ aa bb cc dd ee ff gg hh ii jj |
+ aa^ |
+ {s:aa }{c: }{1: }|
+ {n:bb }{c: }{1: }|
+ {n:cc }{c: }{1: }|
+ {n:dd }{c: }{1: }|
+ {n:ee }{c: }{1: }|
+ {n:ff }{c: }{1: }|
+ {n:gg }{s: }{1: }|
+ {n:hh }{s: }{4: }|
+ {2:-- }{5:match 1 of 10} |
+ ]])
+ end)
+
+ it('works with preview-window below', function()
+ feed(':ped<CR><c-w>4+<c-w>r')
+ feed('iaa bb cc dd ee ff gg hh ii jj<cr>')
+ feed('<c-x><c-n>')
+ screen:expect([[
+ aa bb cc dd ee ff gg hh ii jj |
+ aa^ |
+ {s:aa }{c: }{1: }|
+ {n:bb }{c: }{1: }|
+ {n:cc }{c: }{1: }|
+ {n:dd }{c: }{1: }|
+ {n:ee }{c: }{1: }|
+ {n:ff }{c: }{1: }|
+ {n:gg }{s: }{1: }|
+ {n:hh }{s: }{4: }|
+ aa bb cc dd ee ff gg hh ii jj |
+ aa |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {3:[No Name] [Preview][+] }|
+ {2:-- }{5:match 1 of 10} |
+ ]])
+ end)
+
+ it('works with preview-window above and tall and inverted', function()
+ feed(':ped<CR><c-w>8+')
+ feed('iaa<cr>bb<cr>cc<cr>dd<cr>ee<cr>')
+ feed('ff<cr>gg<cr>hh<cr>ii<cr>jj<cr>')
+ feed('kk<cr>ll<cr>mm<cr>nn<cr>oo<cr>')
+ feed('<c-x><c-n>')
+ screen:expect([[
+ aa |
+ bb |
+ cc |
+ dd |
+ {s:aa }{c: }{3:ew][+] }|
+ {n:bb }{c: } |
+ {n:cc }{c: } |
+ {n:dd }{c: } |
+ {n:ee }{c: } |
+ {n:ff }{c: } |
+ {n:gg }{c: } |
+ {n:hh }{c: } |
+ {n:ii }{c: } |
+ {n:jj }{c: } |
+ {n:kk }{c: } |
+ {n:ll }{s: } |
+ {n:mm }{s: } |
+ aa^ |
+ {4:[No Name] [+] }|
+ {2:-- }{5:match 1 of 15} |
+ ]])
+ end)
+
+ it('works with preview-window above and short and inverted', function()
+ feed(':ped<CR><c-w>4+')
+ feed('iaa<cr>bb<cr>cc<cr>dd<cr>ee<cr>')
+ feed('ff<cr>gg<cr>hh<cr>ii<cr>jj<cr>')
+ feed('<c-x><c-n>')
+ screen:expect([[
+ aa |
+ bb |
+ cc |
+ dd |
+ ee |
+ ff |
+ gg |
+ {s:aa } |
+ {n:bb }{3:iew][+] }|
+ {n:cc } |
+ {n:dd } |
+ {n:ee } |
+ {n:ff } |
+ {n:gg } |
+ {n:hh } |
+ {n:ii } |
+ {n:jj } |
+ aa^ |
+ {4:[No Name] [+] }|
+ {2:-- }{5:match 1 of 10} |
+ ]])
+ end)
+
+ it('works with preview-window below and inverted', function()
+ feed(':ped<CR><c-w>4+<c-w>r')
+ feed('iaa<cr>bb<cr>cc<cr>dd<cr>ee<cr>')
+ feed('ff<cr>gg<cr>hh<cr>ii<cr>jj<cr>')
+ feed('<c-x><c-n>')
+ screen:expect([[
+ {s:aa }{c: } |
+ {n:bb }{c: } |
+ {n:cc }{c: } |
+ {n:dd }{c: } |
+ {n:ee }{c: } |
+ {n:ff }{c: } |
+ {n:gg }{s: } |
+ {n:hh }{s: } |
+ aa^ |
+ {4:[No Name] [+] }|
+ aa |
+ bb |
+ cc |
+ dd |
+ ee |
+ ff |
+ gg |
+ hh |
+ {3:[No Name] [Preview][+] }|
+ {2:-- }{5:match 1 of 10} |
+ ]])
+ end)
+
+ it('works with vsplits', function()
+ insert('aaa aab aac\n')
+ feed(':vsplit<cr>')
+ screen:expect([[
+ aaa aab aac {3:│}aaa aab aac|
+ ^ {3:│} |
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {4:[No Name] [+] }{3:<Name] [+] }|
+ :vsplit |
+ ]])
+
+ feed('ibbb a<c-x><c-n>')
+ screen:expect([[
+ aaa aab aac {3:│}aaa aab aac|
+ bbb aaa^ {3:│}bbb aaa |
+ {1:~ }{s: aaa }{1: }{3:│}{1:~ }|
+ {1:~ }{n: aab }{1: }{3:│}{1:~ }|
+ {1:~ }{n: aac }{1: }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {4:[No Name] [+] }{3:<Name] [+] }|
+ {2:-- }{5:match 1 of 3} |
+ ]])
+
+ feed('<esc><c-w><c-w>oc a<c-x><c-n>')
+ screen:expect([[
+ aaa aab aac{3:│}aaa aab aac |
+ bbb aaa {3:│}bbb aaa |
+ c aaa {3:│}c aaa^ |
+ {1:~ }{3:│}{1:~}{s: aaa }{1: }|
+ {1:~ }{3:│}{1:~}{n: aab }{1: }|
+ {1:~ }{3:│}{1:~}{n: aac }{1: }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {1:~ }{3:│}{1:~ }|
+ {3:<Name] [+] }{4:[No Name] [+] }|
+ {2:-- }{5:match 1 of 3} |
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/quickfix_spec.lua b/test/functional/ui/quickfix_spec.lua
new file mode 100644
index 0000000000..b0d89ee3b6
--- /dev/null
+++ b/test/functional/ui/quickfix_spec.lua
@@ -0,0 +1,196 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, feed, meths = helpers.clear, helpers.feed, helpers.meths
+local insert, command = helpers.insert, helpers.command
+
+
+describe('quickfix selection highlight', function()
+ local screen
+
+ before_each(function()
+ clear()
+
+ screen = Screen.new(25, 10)
+ screen:attach()
+ screen:set_default_attr_ids({
+ [1] = { bold = true, foreground = Screen.colors.Blue },
+ [2] = {reverse = true},
+ [3] = {foreground = Screen.colors.Brown},
+ [4] = {bold = true, reverse = true},
+ [5] = {background = Screen.colors.Green},
+ [6] = {foreground = Screen.colors.Brown, background = Screen.colors.Green},
+ [7] = {background = Screen.colors.Red},
+ [8] = {foreground = Screen.colors.Brown, background = Screen.colors.Red},
+ [9] = {background = Screen.colors.Fuchsia},
+ [10] = {foreground = Screen.colors.Red, background = Screen.colors.Fuchsia},
+ [11] = {foreground = Screen.colors.Red},
+ [12] = {foreground = Screen.colors.Brown, background = Screen.colors.Fuchsia},
+ })
+
+ meths.set_option('errorformat', '%m %l')
+ command('syntax on')
+ command('highlight Search guibg=Green')
+
+ insert([[
+ Line 1
+ Line 2
+ Line 3
+ Line 4
+ Line 5
+ ]])
+
+ command('cad')
+ feed('gg')
+
+ screen:expect([[
+ ^Line 1 |
+ Line 2 |
+ Line 3 |
+ Line 4 |
+ Line 5 |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ end)
+
+ it('using default Search highlight group', function()
+ command('copen')
+
+ screen:expect([[
+ Line 1 |
+ {2:[No Name] [+] }|
+ {5:^|}{6:1}{5:| Line }|
+ |{3:2}| Line |
+ |{3:3}| Line |
+ |{3:4}| Line |
+ |{3:5}| Line |
+ || |
+ {4:[Quickfix List] }|
+ |
+ ]])
+
+ command('cnext')
+
+ screen:expect([[
+ Line 1 |
+ {2:[No Name] [+] }|
+ |{3:1}| Line |
+ {5:^|}{6:2}{5:| Line }|
+ |{3:3}| Line |
+ |{3:4}| Line |
+ |{3:5}| Line |
+ || |
+ {4:[Quickfix List] }|
+ |
+ ]])
+ end)
+
+ it('using QuickFixLine highlight group', function()
+ command('highlight QuickFixLine guibg=Red')
+
+ command('copen')
+
+ screen:expect([[
+ Line 1 |
+ {2:[No Name] [+] }|
+ {7:^|}{8:1}{7:| Line }|
+ |{3:2}| Line |
+ |{3:3}| Line |
+ |{3:4}| Line |
+ |{3:5}| Line |
+ || |
+ {4:[Quickfix List] }|
+ |
+ ]])
+
+ command('cnext')
+
+ screen:expect([[
+ Line 1 |
+ {2:[No Name] [+] }|
+ |{3:1}| Line |
+ {7:^|}{8:2}{7:| Line }|
+ |{3:3}| Line |
+ |{3:4}| Line |
+ |{3:5}| Line |
+ || |
+ {4:[Quickfix List] }|
+ |
+ ]])
+ end)
+
+ it('combines with CursorLine', function()
+ command('set cursorline')
+ command('highlight QuickFixLine guifg=Red')
+ command('highlight CursorLine guibg=Fuchsia')
+
+ command('copen')
+
+ screen:expect([[
+ {9:Line 1 }|
+ {2:[No Name] [+] }|
+ {10:^|1| Line }|
+ |{3:2}| Line |
+ |{3:3}| Line |
+ |{3:4}| Line |
+ |{3:5}| Line |
+ || |
+ {4:[Quickfix List] }|
+ |
+ ]])
+
+ feed('j')
+
+ screen:expect([[
+ {9:Line 1 }|
+ {2:[No Name] [+] }|
+ {11:|1| Line }|
+ {9:^|}{12:2}{9:| Line }|
+ |{3:3}| Line |
+ |{3:4}| Line |
+ |{3:5}| Line |
+ || |
+ {4:[Quickfix List] }|
+ |
+ ]])
+ end)
+
+ it('QuickFixLine background takes precedence over CursorLine', function()
+ command('set cursorline')
+ command('highlight QuickFixLine guibg=Red')
+ command('highlight CursorLine guibg=Fuchsia')
+
+ command('copen')
+
+ screen:expect([[
+ {9:Line 1 }|
+ {2:[No Name] [+] }|
+ {7:^|}{8:1}{7:| Line }|
+ |{3:2}| Line |
+ |{3:3}| Line |
+ |{3:4}| Line |
+ |{3:5}| Line |
+ || |
+ {4:[Quickfix List] }|
+ |
+ ]])
+
+ feed('j')
+
+ screen:expect([[
+ {9:Line 1 }|
+ {2:[No Name] [+] }|
+ {7:|}{8:1}{7:| Line }|
+ {9:^|}{12:2}{9:| Line }|
+ |{3:3}| Line |
+ |{3:4}| Line |
+ |{3:5}| Line |
+ || |
+ {4:[Quickfix List] }|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/screen.lua b/test/functional/ui/screen.lua
index d7af2a4fce..69f4a44dd8 100644
--- a/test/functional/ui/screen.lua
+++ b/test/functional/ui/screen.lua
@@ -1,31 +1,17 @@
--- This module contains the Screen class, a complete Nvim screen implementation
--- designed for functional testing. The goal is to provide a simple and
--- intuitive API for verifying screen state after a set of actions.
+-- This module contains the Screen class, a complete Nvim UI implementation
+-- designed for functional testing (verifying screen state, in particular).
--
--- The screen class exposes a single assertion method, "Screen:expect". This
--- method takes a string representing the expected screen state and an optional
--- set of attribute identifiers for checking highlighted characters(more on
--- this later).
---
--- The string passed to "expect" will be processed according to these rules:
---
--- - Each line of the string represents and is matched individually against
--- a screen row.
--- - The entire string is stripped of common indentation
--- - Expected screen rows are stripped of the last character. The last
--- character should be used to write pipes(|) that make clear where the
--- screen ends
--- - The last line is stripped, so the string must have (row count + 1)
--- lines.
+-- Screen:expect() takes a string representing the expected screen state and an
+-- optional set of attribute identifiers for checking highlighted characters.
--
-- Example usage:
--
-- local screen = Screen.new(25, 10)
--- -- attach the screen to the current Nvim instance
+-- -- Attach the screen to the current Nvim instance.
-- screen:attach()
--- --enter insert mode and type some text
+-- -- Enter insert-mode and type some text.
-- feed('ihello screen')
--- -- declare an expectation for the eventual screen state
+-- -- Assert the expected screen state.
-- screen:expect([[
-- hello screen |
-- ~ |
@@ -39,31 +25,19 @@
-- -- INSERT -- |
-- ]]) -- <- Last line is stripped
--
--- Since screen updates are received asynchronously, "expect" is actually
--- specifying the eventual screen state. This is how "expect" works: It will
--- start the event loop with a timeout of 5 seconds. Each time it receives an
--- update the expected state will be checked against the updated state.
---
--- If the expected state matches the current state, the event loop will be
--- stopped and "expect" will return. If the timeout expires, the last match
--- error will be reported and the test will fail.
+-- Since screen updates are received asynchronously, expect() actually specifies
+-- the _eventual_ screen state.
--
--- If the second argument is passed to "expect", the screen rows will be
--- transformed before being matched against the string lines. The
--- transformation rule is simple: Each substring "S" composed with characters
--- having the exact same set of attributes will be substituted by "{K:S}",
--- where K is a key associated the attribute set via the second argument of
--- "expect".
--- If a transformation table is present, unexpected attribute sets in the final
--- state is considered an error. To make testing simpler, a list of attribute
--- sets that should be ignored can be passed as a third argument. Alternatively,
--- this third argument can be "true" to indicate that all unexpected attribute
--- sets should be ignored.
+-- This is how expect() works:
+-- * It starts the event loop with a timeout.
+-- * Each time it receives an update it checks that against the expected state.
+-- * If the expected state matches the current state, the event loop will be
+-- stopped and expect() will return.
+-- * If the timeout expires, the last match error will be reported and the
+-- test will fail.
--
--- To illustrate how this works, let's say that in the above example we wanted
--- to assert that the "-- INSERT --" string is highlighted with the bold
--- attribute(which normally is), here's how the call to "expect" should look
--- like:
+-- Continuing the above example, say we want to assert that "-- INSERT --" is
+-- highlighted with the bold attribute. The expect() call should look like this:
--
-- NonText = Screen.colors.Blue
-- screen:expect([[
@@ -81,52 +55,58 @@
--
-- In this case "b" is a string associated with the set composed of one
-- attribute: bold. Note that since the {b:} markup is not a real part of the
--- screen, the delimiter(|) had to be moved right. Also, the highlighting of the
--- NonText markers (~) is ignored in this test.
+-- screen, the delimiter "|" moved to the right. Also, the highlighting of the
+-- NonText markers "~" is ignored in this test.
+--
+-- Tests will often share a group of attribute sets to expect(). Those can be
+-- defined at the beginning of a test:
--
--- Multiple expect:s will likely share a group of attribute sets to test.
--- Therefore these could be specified at the beginning of a test like this:
-- NonText = Screen.colors.Blue
-- screen:set_default_attr_ids( {
-- [1] = {reverse = true, bold = true},
-- [2] = {reverse = true}
-- })
-- screen:set_default_attr_ignore( {{}, {bold=true, foreground=NonText}} )
--- These can be overridden for a specific expect expression, by passing
--- different sets as parameters.
--
--- To help writing screen tests, there is a utility function
--- "screen:snapshot_util()", that can be placed in a test file at any point an
--- "expect(...)" should be. It will wait a short amount of time and then dump
--- the current state of the screen, in the form of an "expect(..)" expression
--- that would match it exactly. "snapshot_util" optionally also take the
--- transformation and ignore set as parameters, like expect, or uses the default
--- set. It will generate a larger attribute transformation set, if needed.
--- To generate a text-only test without highlight checks,
--- use `screen:snapshot_util({},true)`
+-- To help write screen tests, see Screen:snapshot_util().
+-- To debug screen tests, see Screen:redraw_debug().
+local global_helpers = require('test.helpers')
+local deepcopy = global_helpers.deepcopy
+local shallowcopy = global_helpers.shallowcopy
local helpers = require('test.functional.helpers')(nil)
-local request, run = helpers.request, helpers.run
+local request, run_session = helpers.request, helpers.run_session
+local eq = helpers.eq
local dedent = helpers.dedent
+local get_session = helpers.get_session
+local create_callindex = helpers.create_callindex
+
+local inspect = require('inspect')
+
+local function isempty(v)
+ return type(v) == 'table' and next(v) == nil
+end
local Screen = {}
Screen.__index = Screen
local debug_screen
-local default_screen_timeout = 3500
+local default_timeout_factor = 1
if os.getenv('VALGRIND') then
- default_screen_timeout = default_screen_timeout * 3
+ default_timeout_factor = default_timeout_factor * 3
end
if os.getenv('CI') then
- default_screen_timeout = default_screen_timeout * 3
+ default_timeout_factor = default_timeout_factor * 3
end
+local default_screen_timeout = default_timeout_factor * 3500
+
do
local spawn, nvim_prog = helpers.spawn, helpers.nvim_prog
local session = spawn({nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N', '--embed'})
- local status, rv = session:request('vim_get_color_map')
+ local status, rv = session:request('nvim_get_color_map')
if not status then
print('failed to get color map')
os.exit(1)
@@ -170,17 +150,38 @@ function Screen.new(width, height)
update_menu = false,
visual_bell = false,
suspended = false,
+ mode = 'normal',
+ options = {},
+ popupmenu = nil,
+ cmdline = {},
+ cmdline_block = {},
+ wildmenu_items = nil,
+ wildmenu_selected = nil,
+ win_position = {},
+ _session = nil,
_default_attr_ids = nil,
_default_attr_ignore = nil,
- _mode = 'normal',
_mouse_enabled = true,
_attrs = {},
+ _hl_info = {},
+ _attr_table = {[0]={{},{}}},
+ _clear_attrs = {},
+ _new_attrs = false,
+ _width = width,
+ _height = height,
+ _grids = {},
_cursor = {
- row = 1, col = 1
+ grid = 1, row = 1, col = 1
},
- _busy = false
+ _busy = false,
}, Screen)
- self:_handle_resize(width, height)
+ local function ui(method, ...)
+ local status, rv = self._session:request('nvim_ui_'..method, ...)
+ if not status then
+ error(rv[2])
+ end
+ end
+ self.uimeths = create_callindex(ui)
return self
end
@@ -188,71 +189,307 @@ function Screen:set_default_attr_ids(attr_ids)
self._default_attr_ids = attr_ids
end
+function Screen:get_default_attr_ids()
+ return deepcopy(self._default_attr_ids)
+end
+
function Screen:set_default_attr_ignore(attr_ignore)
self._default_attr_ignore = attr_ignore
end
-function Screen:attach(rgb)
- if rgb == nil then
- rgb = true
+function Screen:set_hlstate_cterm(val)
+ self._hlstate_cterm = val
+end
+
+function Screen:attach(options, session)
+ if session == nil then
+ session = get_session()
+ end
+ if options == nil then
+ options = {}
+ end
+ if options.ext_linegrid == nil then
+ options.ext_linegrid = true
+ end
+
+ self._session = session
+ self._options = options
+ self._clear_attrs = (options.ext_linegrid and {{},{}}) or {}
+ self:_handle_resize(self._width, self._height)
+ self.uimeths.attach(self._width, self._height, options)
+ if self._options.rgb == nil then
+ -- nvim defaults to rgb=true internally,
+ -- simplify test code by doing the same.
+ self._options.rgb = true
end
- request('ui_attach', self._width, self._height, rgb)
+ if self._options.ext_multigrid then
+ self._options.ext_linegrid = true
+ end
+ self._session = session
end
function Screen:detach()
- request('ui_detach')
+ self.uimeths.detach()
+ self._session = nil
end
function Screen:try_resize(columns, rows)
- request('ui_try_resize', columns, rows)
+ self._width = columns
+ self._height = rows
+ self.uimeths.try_resize(columns, rows)
+end
+
+function Screen:try_resize_grid(grid, columns, rows)
+ self.uimeths.try_resize_grid(grid, columns, rows)
+end
+
+function Screen:set_option(option, value)
+ self.uimeths.set_option(option, value)
+ self._options[option] = value
end
+-- canonical order of ext keys, used to generate asserts
+local ext_keys = {
+ 'popupmenu', 'cmdline', 'cmdline_block', 'wildmenu_items', 'wildmenu_pos'
+}
+
+-- Asserts that the screen state eventually matches an expected state
+--
+-- This function can either be called with the positional forms
+--
+-- screen:expect(grid, [attr_ids, attr_ignore])
+-- screen:expect(condition)
+--
+-- or to use additional arguments (or grid and condition at the same time)
+-- the keyword form has to be used:
+--
+-- screen:expect{grid=[[...]], cmdline={...}, condition=function() ... end}
+--
+--
+-- grid: Expected screen state (string). Each line represents a screen
+-- row. Last character of each row (typically "|") is stripped.
+-- Common indentation is stripped.
+-- attr_ids: Expected text attributes. Screen rows are transformed according
+-- to this table, as follows: each substring S composed of
+-- characters having the same attributes will be substituted by
+-- "{K:S}", where K is a key in `attr_ids`. Any unexpected
+-- attributes in the final state are an error.
+-- Use screen:set_default_attr_ids() to define attributes for many
+-- expect() calls.
+-- attr_ignore: Ignored text attributes, or `true` to ignore all. By default
+-- nothing is ignored.
+-- condition: Function asserting some arbitrary condition. Return value is
+-- ignored, throw an error (use eq() or similar) to signal failure.
+-- any: Lua pattern string expected to match a screen line. NB: the
+-- following chars are magic characters
+-- ( ) . % + - * ? [ ^ $
+-- and must be escaped with a preceding % for a literal match.
+-- mode: Expected mode as signaled by "mode_change" event
+-- unchanged: Test that the screen state is unchanged since the previous
+-- expect(...). Any flush event resulting in a different state is
+-- considered an error. Not observing any events until timeout
+-- is acceptable.
+-- intermediate:Test that the final state is the same as the previous expect,
+-- but expect an intermediate state that is different. If possible
+-- it is better to use an explicit screen:expect(...) for this
+-- intermediate state.
+-- reset: Reset the state internal to the test Screen before starting to
+-- receive updates. This should be used after command("redraw!")
+-- or some other mechanism that will invoke "redraw!", to check
+-- that all screen state is transmitted again. This includes
+-- state related to ext_ features as mentioned below.
+-- timeout: maximum time that will be waited until the expected state is
+-- seen (or maximum time to observe an incorrect change when
+-- `unchanged` flag is used)
+--
+-- The following keys should be used to expect the state of various ext_
+-- features. Note that an absent key will assert that the item is currently
+-- NOT present on the screen, also when positional form is used.
+--
+-- popupmenu: Expected ext_popupmenu state,
+-- cmdline: Expected ext_cmdline state, as an array of cmdlines of
+-- different level.
+-- cmdline_block: Expected ext_cmdline block (for function definitions)
+-- wildmenu_items: Expected items for ext_wildmenu
+-- wildmenu_pos: Expected position for ext_wildmenu
function Screen:expect(expected, attr_ids, attr_ignore)
- -- remove the last line and dedent
- expected = dedent(expected:gsub('\n[ ]+$', ''))
+ local grid, condition = nil, nil
local expected_rows = {}
- for row in expected:gmatch('[^\n]+') do
- -- the last character should be the screen delimiter
- row = row:sub(1, #row - 1)
- table.insert(expected_rows, row)
- end
- local ids = attr_ids or self._default_attr_ids
- local ignore = attr_ignore or self._default_attr_ignore
- self:wait(function()
- local actual_rows = {}
- for i = 1, self._height do
- actual_rows[i] = self:_row_repr(self._rows[i], ids, ignore)
- end
- for i = 1, self._height do
- if expected_rows[i] ~= actual_rows[i] then
- local msg_expected_rows = {}
- for j = 1, #expected_rows do
- msg_expected_rows[j] = expected_rows[j]
- end
- msg_expected_rows[i] = '*' .. msg_expected_rows[i]
- actual_rows[i] = '*' .. actual_rows[i]
+ if type(expected) == "table" then
+ assert(not (attr_ids ~= nil or attr_ignore ~= nil))
+ local is_key = {grid=true, attr_ids=true, attr_ignore=true, condition=true,
+ any=true, mode=true, unchanged=true, intermediate=true,
+ reset=true, timeout=true}
+ for _, v in ipairs(ext_keys) do
+ is_key[v] = true
+ end
+ for k, _ in pairs(expected) do
+ if not is_key[k] then
+ error("Screen:expect: Unknown keyword argument '"..k.."'")
+ end
+ end
+ grid = expected.grid
+ attr_ids = expected.attr_ids
+ attr_ignore = expected.attr_ignore
+ condition = expected.condition
+ assert(not (expected.any ~= nil and grid ~= nil))
+ elseif type(expected) == "string" then
+ grid = expected
+ expected = {}
+ elseif type(expected) == "function" then
+ assert(not (attr_ids ~= nil or attr_ignore ~= nil))
+ condition = expected
+ expected = {}
+ else
+ assert(false)
+ end
+
+ if grid ~= nil then
+ -- Remove the last line and dedent. Note that gsub returns more then one
+ -- value.
+ grid = dedent(grid:gsub('\n[ ]+$', ''), 0)
+ for row in grid:gmatch('[^\n]+') do
+ table.insert(expected_rows, row)
+ end
+ end
+ local attr_state = {
+ ids = attr_ids or self._default_attr_ids,
+ ignore = attr_ignore or self._default_attr_ignore,
+ }
+ if self._options.ext_hlstate then
+ attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {})
+ end
+ self._new_attrs = false
+ self:_wait(function()
+ if condition ~= nil then
+ local status, res = pcall(condition)
+ if not status then
+ return tostring(res)
+ end
+ end
+
+ if self._options.ext_hlstate and self._new_attrs then
+ attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids or {})
+ end
+
+ local actual_rows = self:render(not expected.any, attr_state)
+
+ if expected.any ~= nil then
+ -- Search for `any` anywhere in the screen lines.
+ local actual_screen_str = table.concat(actual_rows, '\n')
+ if nil == string.find(actual_screen_str, expected.any) then
return (
- 'Row ' .. tostring(i) .. ' didn\'t match.\n'
- .. 'Expected:\n|' .. table.concat(msg_expected_rows, '|\n|') .. '|\n'
- .. 'Actual:\n|' .. table.concat(actual_rows, '|\n|') .. '|'
- )
+ 'Failed to match any screen lines.\n'
+ .. 'Expected (anywhere): "' .. expected.any .. '"\n'
+ .. 'Actual:\n |' .. table.concat(actual_rows, '\n |') .. '\n\n')
end
end
- end)
+
+ if grid ~= nil then
+ -- `expected` must match the screen lines exactly.
+ if #actual_rows ~= #expected_rows then
+ return "Expected screen state's row count(" .. #expected_rows
+ .. ') differs from configured height(' .. #actual_rows .. ') of Screen.'
+ end
+ for i = 1, #actual_rows do
+ if expected_rows[i] ~= actual_rows[i] then
+ local msg_expected_rows = {}
+ for j = 1, #expected_rows do
+ msg_expected_rows[j] = expected_rows[j]
+ end
+ msg_expected_rows[i] = '*' .. msg_expected_rows[i]
+ actual_rows[i] = '*' .. actual_rows[i]
+ return (
+ 'Row ' .. tostring(i) .. ' did not match.\n'
+ ..'Expected:\n |'..table.concat(msg_expected_rows, '\n |')..'\n'
+ ..'Actual:\n |'..table.concat(actual_rows, '\n |')..'\n\n'..[[
+To print the expect() call that would assert the current screen state, use
+screen:snapshot_util(). In case of non-deterministic failures, use
+screen:redraw_debug() to show all intermediate screen states. ]])
+ end
+ end
+ end
+
+ -- Extension features. The default expectations should cover the case of
+ -- the ext_ feature being disabled, or the feature currently not activated
+ -- (for instance no external cmdline visible). Some extensions require
+ -- preprocessing to represent highlights in a reproducible way.
+ local extstate = self:_extstate_repr(attr_state)
+
+ -- convert assertion errors into invalid screen state descriptions
+ local status, res = pcall(function()
+ for _, k in ipairs(ext_keys) do
+ -- Empty states is considered the default and need not be mentioned
+ if not (expected[k] == nil and isempty(extstate[k])) then
+ eq(expected[k], extstate[k], k)
+ end
+ end
+ if expected.mode ~= nil then
+ eq(expected.mode, self.mode, "mode")
+ end
+ end)
+ if not status then
+ return tostring(res)
+ end
+ end, expected)
end
-function Screen:wait(check, timeout)
- local err, checked = false
+function Screen:_wait(check, flags)
+ local err, checked = false, false
local success_seen = false
local failure_after_success = false
+ local did_flush = true
+ local warn_immediate = not (flags.unchanged or flags.intermediate)
+
+ if flags.intermediate and flags.unchanged then
+ error("Choose only one of 'intermediate' and 'unchanged', not both")
+ end
+
+ if flags.reset then
+ -- throw away all state, we expect it to be retransmitted
+ self:_reset()
+ end
+
+ -- Maximum timeout, after which a incorrect state will be regarded as a
+ -- failure
+ local timeout = flags.timeout or self.timeout
+
+ -- Minimal timeout before the loop is allowed to be stopped so we
+ -- always do some check for failure after success.
+ local minimal_timeout = default_timeout_factor * 2
+
+ local immediate_seen, intermediate_seen = false, false
+ if not check() then
+ minimal_timeout = default_timeout_factor * 20
+ immediate_seen = true
+ end
+
+ -- for an unchanged test, flags.timeout means the time during the state is
+ -- expected to be unchanged, so always wait this full time.
+ if (flags.unchanged or flags.intermediate) and flags.timeout ~= nil then
+ minimal_timeout = timeout
+ end
+
+ assert(timeout >= minimal_timeout)
+ local did_miminal_timeout = false
+
local function notification_cb(method, args)
assert(method == 'redraw')
- self:_redraw(args)
+ did_flush = self:_redraw(args)
+ if not did_flush then
+ return
+ end
err = check()
checked = true
+ if err and immediate_seen then
+ intermediate_seen = true
+ end
+
if not err then
success_seen = true
- helpers.stop()
+ if did_miminal_timeout then
+ self._session:stop()
+ end
elseif success_seen and #args > 0 then
failure_after_success = true
--print(require('inspect')(args))
@@ -260,80 +497,224 @@ function Screen:wait(check, timeout)
return true
end
- run(nil, notification_cb, nil, timeout or self.timeout)
- if not checked then
+ run_session(self._session, nil, notification_cb, nil, minimal_timeout)
+ if not did_flush then
+ err = "no flush received"
+ elseif not checked then
err = check()
+ if not err and flags.unchanged then
+ -- expecting NO screen change: use a shorter timout
+ success_seen = true
+ end
+ end
+
+ if not success_seen then
+ did_miminal_timeout = true
+ run_session(self._session, nil, notification_cb, nil, timeout-minimal_timeout)
+ end
+
+ local did_warn = false
+ if warn_immediate and immediate_seen then
+ print([[
+
+warning: Screen test succeeded immediately. Try to avoid this unless the
+purpose of the test really requires it.]])
+ if intermediate_seen then
+ print([[
+There are intermediate states between the two identical expects.
+Use screen:snapshot_util() or screen:redraw_debug() to find them, and add them
+to the test if they make sense.
+]])
+ else
+ print([[If necessary, silence this warning with 'unchanged' argument of screen:expect.]])
+ end
+ did_warn = true
end
if failure_after_success then
print([[
-Warning: Screen changes have been received after the expected state was seen.
-This is probably due to an indeterminism in the test. Try adding
-`wait()` (or even a separate `screen:expect(...)`) at a point of possible
-indeterminism, typically in between a `feed()` or `execute()` which is non-
-synchronous, and a synchronous api call.
-
-Note that sometimes a `wait` can trigger redraws and consequently generate more
-indeterminism. If adding `wait` calls seems to increase the frequency of these
-messages, try removing every `wait` call in the test.
-
-If everything else fails, use Screen:redraw_debug to help investigate what is
- causing the problem.
+
+warning: Screen changes were received after the expected state. This indicates
+indeterminism in the test. Try adding screen:expect(...) (or wait()) between
+asynchronous (feed(), nvim_input()) and synchronous API calls.
+ - Use screen:redraw_debug() to investigate; it may find relevant intermediate
+ states that should be added to the test to make it more robust.
+ - If the purpose of the test is to assert state after some user input sent
+ with feed(), adding screen:expect() before the feed() will help to ensure
+ the input is sent when Nvim is in a predictable state. This is preferable
+ to wait(), for being closer to real user interaction.
+ - wait() can trigger redraws and consequently generate more indeterminism.
+ Try removing wait().
]])
+ did_warn = true
+ end
+
+
+ if err then
+ assert(false, err)
+ elseif did_warn then
local tb = debug.traceback()
local index = string.find(tb, '\n%s*%[C]')
print(string.sub(tb,1,index))
end
- if err then
- assert(false, err)
+ if flags.intermediate then
+ assert(intermediate_seen, "expected intermediate screen state before final screen state")
+ elseif flags.unchanged then
+ assert(not intermediate_seen, "expected screen state to be unchanged")
end
end
function Screen:sleep(ms)
- pcall(function() self:wait(function() return "error" end, ms) end)
+ local function notification_cb(method, args)
+ assert(method == 'redraw')
+ self:_redraw(args)
+ end
+ run_session(self._session, nil, notification_cb, nil, ms)
end
function Screen:_redraw(updates)
- for _, update in ipairs(updates) do
+ local did_flush = false
+ for k, update in ipairs(updates) do
-- print('--')
-- print(require('inspect')(update))
local method = update[1]
for i = 2, #update do
- local handler = self['_handle_'..method]
- handler(self, unpack(update[i]))
+ local handler_name = '_handle_'..method
+ local handler = self[handler_name]
+ if handler ~= nil then
+ handler(self, unpack(update[i]))
+ else
+ assert(self._on_event,
+ "Add Screen:"..handler_name.." or call Screen:set_on_event_handler")
+ self._on_event(method, update[i])
+ end
+ end
+ if k == #updates and method == "flush" then
+ did_flush = true
end
- -- print(self:_current_screen())
end
+ return did_flush
+end
+
+function Screen:set_on_event_handler(callback)
+ self._on_event = callback
end
function Screen:_handle_resize(width, height)
+ self:_handle_grid_resize(1, width, height)
+ self._scroll_region = {
+ top = 1, bot = height, left = 1, right = width
+ }
+ self._grid = self._grids[1]
+end
+
+local function min(x,y)
+ if x < y then
+ return x
+ else
+ return y
+ end
+end
+
+function Screen:_handle_grid_resize(grid, width, height)
local rows = {}
for _ = 1, height do
local cols = {}
for _ = 1, width do
- table.insert(cols, {text = ' ', attrs = {}})
+ table.insert(cols, {text = ' ', attrs = self._clear_attrs, hl_id = 0})
end
table.insert(rows, cols)
end
- self._cursor.row = 1
- self._cursor.col = 1
- self._rows = rows
- self._width = width
- self._height = height
- self._scroll_region = {
- top = 1, bot = height, left = 1, right = width
+ if grid > 1 and self._grids[grid] ~= nil then
+ local old = self._grids[grid]
+ for i = 1, min(height,old.height) do
+ for j = 1, min(width,old.width) do
+ rows[i][j] = old.rows[i][j]
+ end
+ end
+ end
+
+ if self._cursor.grid == grid then
+ self._cursor.row = 1
+ self._cursor.col = 1
+ end
+ self._grids[grid] = {
+ rows=rows,
+ width=width,
+ height=height,
}
end
+function Screen:_handle_win_scroll_over_start()
+ self.scroll_over = true
+ self.scroll_over_pos = self._grids[1].height
+end
+
+function Screen:_handle_win_scroll_over_reset()
+ self.scroll_over = false
+end
+
+function Screen:_handle_flush()
+end
+
+function Screen:_reset()
+ -- TODO: generalize to multigrid later
+ self:_handle_grid_clear(1)
+
+ -- TODO: share with initialization, so it generalizes?
+ self.popupmenu = nil
+ self.cmdline = {}
+ self.cmdline_block = {}
+ self.wildmenu_items = nil
+ self.wildmenu_pos = nil
+end
+
+
+function Screen:_handle_mode_info_set(cursor_style_enabled, mode_info)
+ self._cursor_style_enabled = cursor_style_enabled
+ for _, item in pairs(mode_info) do
+ -- attr IDs are not stable, but their value should be
+ if item.attr_id ~= nil then
+ item.attr = self._attr_table[item.attr_id][1]
+ item.attr_id = nil
+ end
+ if item.attr_id_lm ~= nil then
+ item.attr_lm = self._attr_table[item.attr_id_lm][1]
+ item.attr_id_lm = nil
+ end
+ end
+ self._mode_info = mode_info
+end
+
function Screen:_handle_clear()
- self:_clear_block(self._scroll_region.top, self._scroll_region.bot,
- self._scroll_region.left, self._scroll_region.right)
+ -- the first implemented UI protocol clients (python-gui and builitin TUI)
+ -- allowed the cleared region to be restricted by setting the scroll region.
+ -- this was never used by nvim tough, and not documented and implemented by
+ -- newer clients, to check we remain compatible with both kind of clients,
+ -- ensure the scroll region is in a reset state.
+ local expected_region = {
+ top = 1, bot = self._grid.height, left = 1, right = self._grid.width
+ }
+ eq(expected_region, self._scroll_region)
+ self:_handle_grid_clear(1)
+end
+
+function Screen:_handle_grid_clear(grid)
+ self:_clear_block(self._grids[grid], 1, self._grids[grid].height, 1, self._grids[grid].width)
+end
+
+function Screen:_handle_grid_destroy(grid)
+ self._grids[grid] = nil
+ if self._options.ext_multigrid then
+ assert(self.win_position[grid])
+ self.win_position[grid] = nil
+ end
end
function Screen:_handle_eol_clear()
local row, col = self._cursor.row, self._cursor.col
- self:_clear_block(row, row, col, self._scroll_region.right)
+ self:_clear_block(self._grid, row, row, col, self._grid.width)
end
function Screen:_handle_cursor_goto(row, col)
@@ -341,6 +722,12 @@ function Screen:_handle_cursor_goto(row, col)
self._cursor.col = col + 1
end
+function Screen:_handle_grid_cursor_goto(grid, row, col)
+ self._cursor.grid = grid
+ self._cursor.row = row + 1
+ self._cursor.col = col + 1
+end
+
function Screen:_handle_busy_start()
self._busy = true
end
@@ -357,9 +744,9 @@ function Screen:_handle_mouse_off()
self._mouse_enabled = false
end
-function Screen:_handle_mode_change(mode)
- assert(mode == 'insert' or mode == 'replace' or mode == 'normal')
- self._mode = mode
+function Screen:_handle_mode_change(mode, idx)
+ assert(mode == self._mode_info[idx+1].name)
+ self.mode = mode
end
function Screen:_handle_set_scroll_region(top, bot, left, right)
@@ -374,31 +761,73 @@ function Screen:_handle_scroll(count)
local bot = self._scroll_region.bot
local left = self._scroll_region.left
local right = self._scroll_region.right
+ self:_handle_grid_scroll(1, top-1, bot, left-1, right, count, 0)
+end
+
+function Screen:_handle_grid_scroll(g, top, bot, left, right, rows, cols)
+ if self.scroll_over and g == 1 and top < self.scroll_over_pos then
+ self.scroll_over_pos = top
+ end
+
+ top = top+1
+ left = left+1
+ assert(cols == 0)
+ local grid = self._grids[g]
local start, stop, step
- if count > 0 then
+
+ if rows > 0 then
start = top
- stop = bot - count
+ stop = bot - rows
step = 1
else
start = bot
- stop = top - count
+ stop = top - rows
step = -1
end
-- shift scroll region
for i = start, stop, step do
- local target = self._rows[i]
- local source = self._rows[i + count]
+ local target = grid.rows[i]
+ local source = grid.rows[i + rows]
for j = left, right do
target[j].text = source[j].text
target[j].attrs = source[j].attrs
+ target[j].hl_id = source[j].hl_id
end
end
-- clear invalid rows
- for i = stop + step, stop + count, step do
- self:_clear_row_section(i, left, right)
+ for i = stop + step, stop + rows, step do
+ self:_clear_row_section(grid, i, left, right)
+ end
+end
+
+function Screen:_handle_hl_attr_define(id, rgb_attrs, cterm_attrs, info)
+ self._attr_table[id] = {rgb_attrs, cterm_attrs}
+ self._hl_info[id] = info
+ self._new_attrs = true
+end
+
+function Screen:_handle_win_pos(grid, win, startrow, startcol, width, height)
+ self.win_position[grid] = {
+ win = win,
+ startrow = startrow,
+ startcol = startcol,
+ width = width,
+ height = height
+ }
+end
+
+function Screen:_handle_win_hide(grid)
+ self.win_position[grid] = nil
+end
+
+function Screen:get_hl(val)
+ if self._options.ext_newgrid then
+ return self._attr_table[val][1]
+ else
+ return val
end
end
@@ -407,12 +836,36 @@ function Screen:_handle_highlight_set(attrs)
end
function Screen:_handle_put(str)
- local cell = self._rows[self._cursor.row][self._cursor.col]
+ assert(not self._options.ext_linegrid)
+ local cell = self._grid.rows[self._cursor.row][self._cursor.col]
cell.text = str
cell.attrs = self._attrs
+ cell.hl_id = -1
self._cursor.col = self._cursor.col + 1
end
+function Screen:_handle_grid_line(grid, row, col, items)
+ assert(self._options.ext_linegrid)
+ local line = self._grids[grid].rows[row+1]
+ local colpos = col+1
+ local hl = self._clear_attrs
+ local hl_id = 0
+ for _,item in ipairs(items) do
+ local text, hl_id_cell, count = unpack(item)
+ if hl_id_cell ~= nil then
+ hl_id = hl_id_cell
+ hl = self._attr_table[hl_id]
+ end
+ for _ = 1, (count or 1) do
+ local cell = line[colpos]
+ cell.text = text
+ cell.hl_id = hl_id
+ cell.attrs = hl
+ colpos = colpos+1
+ end
+ end
+end
+
function Screen:_handle_bell()
self.bell = true
end
@@ -421,6 +874,16 @@ function Screen:_handle_visual_bell()
self.visual_bell = true
end
+function Screen:_handle_default_colors_set(rgb_fg, rgb_bg, rgb_sp, cterm_fg, cterm_bg)
+ self.default_colors = {
+ rgb_fg=rgb_fg,
+ rgb_bg=rgb_bg,
+ rgb_sp=rgb_sp,
+ cterm_fg=cterm_fg,
+ cterm_bg=cterm_bg
+ }
+end
+
function Screen:_handle_update_fg(fg)
self._fg = fg
end
@@ -449,41 +912,129 @@ function Screen:_handle_set_icon(icon)
self.icon = icon
end
-function Screen:_clear_block(top, bot, left, right)
+function Screen:_handle_option_set(name, value)
+ self.options[name] = value
+end
+
+function Screen:_handle_popupmenu_show(items, selected, row, col)
+ self.popupmenu = {items=items,pos=selected, anchor={row, col}}
+end
+
+function Screen:_handle_popupmenu_select(selected)
+ self.popupmenu.pos = selected
+end
+
+function Screen:_handle_popupmenu_hide()
+ self.popupmenu = nil
+end
+
+function Screen:_handle_cmdline_show(content, pos, firstc, prompt, indent, level)
+ if firstc == '' then firstc = nil end
+ if prompt == '' then prompt = nil end
+ if indent == 0 then indent = nil end
+ self.cmdline[level] = {content=content, pos=pos, firstc=firstc,
+ prompt=prompt, indent=indent}
+end
+
+function Screen:_handle_cmdline_hide(level)
+ self.cmdline[level] = nil
+end
+
+function Screen:_handle_cmdline_special_char(char, shift, level)
+ -- cleared by next cmdline_show on the same level
+ self.cmdline[level].special = {char, shift}
+end
+
+function Screen:_handle_cmdline_pos(pos, level)
+ self.cmdline[level].pos = pos
+end
+
+function Screen:_handle_cmdline_block_show(block)
+ self.cmdline_block = block
+end
+
+function Screen:_handle_cmdline_block_append(item)
+ self.cmdline_block[#self.cmdline_block+1] = item
+end
+
+function Screen:_handle_cmdline_block_hide()
+ self.cmdline_block = {}
+end
+
+function Screen:_handle_wildmenu_show(items)
+ self.wildmenu_items = items
+end
+
+function Screen:_handle_wildmenu_select(pos)
+ self.wildmenu_pos = pos
+end
+
+function Screen:_handle_wildmenu_hide()
+ self.wildmenu_items, self.wildmenu_pos = nil, nil
+end
+
+function Screen:_clear_block(grid, top, bot, left, right)
for i = top, bot do
- self:_clear_row_section(i, left, right)
+ self:_clear_row_section(grid, i, left, right)
end
end
-function Screen:_clear_row_section(rownum, startcol, stopcol)
- local row = self._rows[rownum]
+function Screen:_clear_row_section(grid, rownum, startcol, stopcol)
+ local row = grid.rows[rownum]
for i = startcol, stopcol do
row[i].text = ' '
- row[i].attrs = {}
+ row[i].attrs = self._clear_attrs
end
end
-function Screen:_row_repr(row, attr_ids, attr_ignore)
+function Screen:_row_repr(gridnr, rownr, attr_state, cursor)
local rv = {}
local current_attr_id
- for i = 1, self._width do
- local attr_id = self:_get_attr_id(attr_ids, attr_ignore, row[i].attrs)
- if current_attr_id and attr_id ~= current_attr_id then
- -- close current attribute bracket, add it before any whitespace
- -- up to the current cell
- -- table.insert(rv, backward_find_meaningful(rv, i), '}')
- table.insert(rv, '}')
- current_attr_id = nil
- end
- if not current_attr_id and attr_id then
- -- open a new attribute bracket
- table.insert(rv, '{' .. attr_id .. ':')
- current_attr_id = attr_id
+ local i = 1
+ local has_windows = self._options.ext_multigrid and gridnr == 1
+ if self.scroll_over and self.scroll_over_pos < rownr then
+ has_windows = false
+ end
+ local row = self._grids[gridnr].rows[rownr]
+ while i <= #row do
+ local did_window = false
+ if has_windows then
+ for id,pos in pairs(self.win_position) do
+ if i-1 == pos.startcol and pos.startrow <= rownr-1 and rownr-1 < pos.startrow + pos.height then
+ if current_attr_id then
+ -- close current attribute bracket
+ table.insert(rv, '}')
+ current_attr_id = nil
+ end
+ table.insert(rv, '['..id..':'..string.rep('-',pos.width)..']')
+ i = i + pos.width
+ did_window = true
+ end
+ end
end
- if not self._busy and self._rows[self._cursor.row] == row and self._cursor.col == i then
- table.insert(rv, '^')
+
+ if not did_window then
+ local attrs = row[i].attrs
+ if self._options.ext_linegrid then
+ attrs = attrs[(self._options.rgb and 1) or 2]
+ end
+ local attr_id = self:_get_attr_id(attr_state, attrs, row[i].hl_id)
+ if current_attr_id and attr_id ~= current_attr_id then
+ -- close current attribute bracket
+ table.insert(rv, '}')
+ current_attr_id = nil
+ end
+ if not current_attr_id and attr_id then
+ -- open a new attribute bracket
+ table.insert(rv, '{' .. attr_id .. ':')
+ current_attr_id = attr_id
+ end
+ if not self._busy and cursor and self._cursor.col == i then
+ table.insert(rv, '^')
+ end
+ table.insert(rv, row[i].text)
+ i = i + 1
end
- table.insert(rv, row[i].text)
end
if current_attr_id then
table.insert(rv, '}')
@@ -493,18 +1044,50 @@ function Screen:_row_repr(row, attr_ids, attr_ignore)
return table.concat(rv, '')--:gsub('%s+$', '')
end
+function Screen:_extstate_repr(attr_state)
+ local cmdline = {}
+ for i, entry in pairs(self.cmdline) do
+ entry = shallowcopy(entry)
+ entry.content = self:_chunks_repr(entry.content, attr_state)
+ cmdline[i] = entry
+ end
-function Screen:_current_screen()
- -- get a string that represents the current screen state(debugging helper)
- local rv = {}
- for i = 1, self._height do
- table.insert(rv, "'"..self:_row_repr(self._rows[i]).."'")
+ local cmdline_block = {}
+ for i, entry in ipairs(self.cmdline_block) do
+ cmdline_block[i] = self:_chunks_repr(entry, attr_state)
end
- return table.concat(rv, '\n')
+
+ return {
+ popupmenu=self.popupmenu,
+ cmdline=cmdline,
+ cmdline_block=cmdline_block,
+ wildmenu_items=self.wildmenu_items,
+ wildmenu_pos=self.wildmenu_pos,
+ }
end
+function Screen:_chunks_repr(chunks, attr_state)
+ local repr_chunks = {}
+ for i, chunk in ipairs(chunks) do
+ local hl, text = unpack(chunk)
+ local attrs
+ if self._options.ext_linegrid then
+ attrs = self._attr_table[hl][1]
+ else
+ attrs = hl
+ end
+ local attr_id = self:_get_attr_id(attr_state, attrs, hl)
+ repr_chunks[i] = {text, attr_id}
+ end
+ return repr_chunks
+end
+
+-- Generates tests. Call it where Screen:expect() would be. Waits briefly, then
+-- dumps the current screen state in the form of Screen:expect().
+-- Use snapshot_util({},true) to generate a text-only (no attributes) test.
+--
+-- @see Screen:redraw_debug()
function Screen:snapshot_util(attrs, ignore)
- -- util to generate screen test
self:sleep(250)
self:print_snapshot(attrs, ignore)
end
@@ -523,60 +1106,196 @@ function Screen:redraw_debug(attrs, ignore, timeout)
if timeout == nil then
timeout = 250
end
- run(nil, notification_cb, nil, timeout)
+ run_session(self._session, nil, notification_cb, nil, timeout)
+end
+
+function Screen:render(headers, attr_state, preview)
+ headers = headers and self._options.ext_multigrid
+ local rv = {}
+ for igrid,grid in pairs(self._grids) do
+ if headers then
+ table.insert(rv, "## grid "..igrid)
+ end
+ for i = 1, grid.height do
+ local cursor = self._cursor.grid == igrid and self._cursor.row == i
+ local prefix = (headers or preview) and " " or ""
+ table.insert(rv, prefix..self:_row_repr(igrid, i, attr_state, cursor).."|")
+ end
+ end
+ return rv
end
function Screen:print_snapshot(attrs, ignore)
+ attrs = attrs or self._default_attr_ids
if ignore == nil then
ignore = self._default_attr_ignore
end
- if attrs == nil then
- attrs = {}
- if self._default_attr_ids ~= nil then
- for i, a in ipairs(self._default_attr_ids) do
- attrs[i] = a
+ local attr_state = {
+ ids = {},
+ ignore = ignore,
+ mutable = true, -- allow _row_repr to add missing highlights
+ }
+
+ if attrs ~= nil then
+ for i, a in pairs(attrs) do
+ attr_state.ids[i] = a
+ end
+ end
+ if self._options.ext_hlstate then
+ attr_state.id_to_index = self:hlstate_check_attrs(attr_state.ids)
+ end
+
+ local lines = self:render(true, attr_state, true)
+
+ local ext_state = self:_extstate_repr(attr_state)
+ local keys = false
+ for k, v in pairs(ext_state) do
+ if isempty(v) then
+ ext_state[k] = nil -- deleting keys while iterating is ok
+ else
+ keys = true
+ end
+ end
+
+ local attrstr = ""
+ if attr_state.modified then
+ local attrstrs = {}
+ for i, a in pairs(attr_state.ids) do
+ local dict
+ if self._options.ext_hlstate then
+ dict = self:_pprint_hlstate(a)
+ else
+ dict = "{"..self:_pprint_attrs(a).."}"
end
+ local keyval = (type(i) == "number") and "["..tostring(i).."]" or i
+ table.insert(attrstrs, " "..keyval.." = "..dict..",")
end
+ attrstr = (", "..(keys and "attr_ids=" or "")
+ .."{\n"..table.concat(attrstrs, "\n").."\n}")
+ end
+ print( "\nscreen:expect"..(keys and "{grid=" or "(").."[[")
+ print( table.concat(lines, '\n'))
+ io.stdout:write( "]]"..attrstr)
+ for _, k in ipairs(ext_keys) do
+ if ext_state[k] ~= nil then
+ io.stdout:write(", "..k.."="..inspect(ext_state[k]))
+ end
+ end
+ print((keys and "}" or ")").."\n")
+ io.stdout:flush()
+end
+
+function Screen:_insert_hl_id(attr_state, hl_id)
+ if attr_state.id_to_index[hl_id] ~= nil then
+ return attr_state.id_to_index[hl_id]
+ end
+ local raw_info = self._hl_info[hl_id]
+ local info = {}
+ if #raw_info > 1 then
+ for i, item in ipairs(raw_info) do
+ info[i] = self:_insert_hl_id(attr_state, item.id)
+ end
+ else
+ info[1] = {}
+ for k, v in pairs(raw_info[1]) do
+ if k ~= "id" then
+ info[1][k] = v
+ end
+ end
+ end
+
+ local entry = self._attr_table[hl_id]
+ local attrval
+ if self._hlstate_cterm then
+ attrval = {entry[1], entry[2], info} -- unpack() doesn't work
+ else
+ attrval = {entry[1], info}
+ end
- if ignore ~= true then
- for i = 1, self._height do
- local row = self._rows[i]
- for j = 1, self._width do
- local attr = row[j].attrs
- if self:_attr_index(attrs, attr) == nil and self:_attr_index(ignore, attr) == nil then
- if not self:_equal_attrs(attr, {}) then
- table.insert(attrs, attr)
+
+ table.insert(attr_state.ids, attrval)
+ attr_state.id_to_index[hl_id] = #attr_state.ids
+ return #attr_state.ids
+end
+
+function Screen:hlstate_check_attrs(attrs)
+ local id_to_index = {}
+ for i = 1,#self._attr_table do
+ local iinfo = self._hl_info[i]
+ local matchinfo = {}
+ if #iinfo > 1 then
+ for k,item in ipairs(iinfo) do
+ matchinfo[k] = id_to_index[item.id]
+ end
+ else
+ matchinfo = iinfo
+ end
+ for k,v in pairs(attrs) do
+ local attr, info, attr_rgb, attr_cterm
+ if self._hlstate_cterm then
+ attr_rgb, attr_cterm, info = unpack(v)
+ attr = {attr_rgb, attr_cterm}
+ else
+ attr, info = unpack(v)
+ end
+ if self:_equal_attr_def(attr, self._attr_table[i]) then
+ if #info == #matchinfo then
+ local match = false
+ if #info == 1 then
+ if self:_equal_info(info[1],matchinfo[1]) then
+ match = true
+ end
+ else
+ match = true
+ for j = 1,#info do
+ if info[j] ~= matchinfo[j] then
+ match = false
+ end
end
end
+ if match then
+ id_to_index[i] = k
+ end
end
end
end
end
+ return id_to_index
+end
- local rv = {}
- for i = 1, self._height do
- table.insert(rv, " "..self:_row_repr(self._rows[i],attrs, ignore).."|")
- end
- local attrstrs = {}
- local alldefault = true
- for i, a in ipairs(attrs) do
- if self._default_attr_ids == nil or self._default_attr_ids[i] ~= a then
- alldefault = false
- end
- local dict = "{"..self:_pprint_attrs(a).."}"
- table.insert(attrstrs, "["..tostring(i).."] = "..dict)
- end
- local attrstr = "{"..table.concat(attrstrs, ", ").."}"
- print( "\nscreen:expect([[")
- print( table.concat(rv, '\n'))
- if alldefault then
- print( "]])\n")
+
+function Screen:_pprint_hlstate(item)
+ --print(require('inspect')(item))
+ local attrdict = "{"..self:_pprint_attrs(item[1]).."}, "
+ local attrdict2, hlinfo
+ if self._hlstate_cterm then
+ attrdict2 = "{"..self:_pprint_attrs(item[2]).."}, "
+ hlinfo = item[3]
+ else
+ attrdict2 = ""
+ hlinfo = item[2]
+ end
+ local descdict = "{"..self:_pprint_hlinfo(hlinfo).."}"
+ return "{"..attrdict..attrdict2..descdict.."}"
+end
+
+function Screen:_pprint_hlinfo(states)
+ if #states == 1 then
+ local items = {}
+ for f, v in pairs(states[1]) do
+ local desc = tostring(v)
+ if type(v) == type("") then
+ desc = '"'..desc..'"'
+ end
+ table.insert(items, f.." = "..desc)
+ end
+ return "{"..table.concat(items, ", ").."}"
else
- print( "]], "..attrstr..")\n")
+ return table.concat(states, ", ")
end
- io.stdout:flush()
end
+
function Screen:_pprint_attrs(attrs)
local items = {}
for f, v in pairs(attrs) do
@@ -591,7 +1310,7 @@ function Screen:_pprint_attrs(attrs)
return table.concat(items, ", ")
end
-function backward_find_meaningful(tbl, from) -- luacheck: ignore
+local function backward_find_meaningful(tbl, from) -- luacheck: no unused
for i = from or #tbl, 1, -1 do
if tbl[i] ~= ' ' then
return i + 1
@@ -600,32 +1319,64 @@ function backward_find_meaningful(tbl, from) -- luacheck: ignore
return from
end
-function Screen:_get_attr_id(attr_ids, ignore, attrs)
- if not attr_ids then
+function Screen:_get_attr_id(attr_state, attrs, hl_id)
+ if not attr_state.ids then
return
end
- for id, a in pairs(attr_ids) do
- if self:_equal_attrs(a, attrs) then
- return id
- end
+
+ if self._options.ext_hlstate then
+ local id = attr_state.id_to_index[hl_id]
+ if id ~= nil or hl_id == 0 then
+ return id
+ end
+ if attr_state.mutable then
+ id = self:_insert_hl_id(attr_state, hl_id)
+ attr_state.modified = true
+ return id
+ end
+ return "UNEXPECTED "..self:_pprint_attrs(self._attr_table[hl_id][1])
+ else
+ for id, a in pairs(attr_state.ids) do
+ if self:_equal_attrs(a, attrs) then
+ return id
+ end
+ end
+ if self:_equal_attrs(attrs, {}) or
+ attr_state.ignore == true or
+ self:_attr_index(attr_state.ignore, attrs) ~= nil then
+ -- ignore this attrs
+ return nil
+ end
+ if attr_state.mutable then
+ table.insert(attr_state.ids, attrs)
+ attr_state.modified = true
+ return #attr_state.ids
+ end
+ return "UNEXPECTED "..self:_pprint_attrs(attrs)
end
- if self:_equal_attrs(attrs, {}) or
- ignore == true or self:_attr_index(ignore, attrs) ~= nil then
- -- ignore this attrs
- return nil
+end
+
+function Screen:_equal_attr_def(a, b)
+ if self._hlstate_cterm then
+ return self:_equal_attrs(a[1],b[1]) and self:_equal_attrs(a[2],b[2])
+ else
+ return self:_equal_attrs(a,b[1])
end
- return "UNEXPECTED "..self:_pprint_attrs(attrs)
end
function Screen:_equal_attrs(a, b)
return a.bold == b.bold and a.standout == b.standout and
a.underline == b.underline and a.undercurl == b.undercurl and
a.italic == b.italic and a.reverse == b.reverse and
- a.foreground == b.foreground and
- a.background == b.background and
+ a.foreground == b.foreground and a.background == b.background and
a.special == b.special
end
+function Screen:_equal_info(a, b)
+ return a.kind == b.kind and a.hi_name == b.hi_name and
+ a.ui_name == b.ui_name
+end
+
function Screen:_attr_index(attrs, attr)
if not attrs then
return nil
diff --git a/test/functional/ui/screen_basic_spec.lua b/test/functional/ui/screen_basic_spec.lua
index 9249be4aec..04d532f6e1 100644
--- a/test/functional/ui/screen_basic_spec.lua
+++ b/test/functional/ui/screen_basic_spec.lua
@@ -1,13 +1,16 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local spawn, set_session, clear = helpers.spawn, helpers.set_session, helpers.clear
-local feed, execute = helpers.feed, helpers.execute
+local feed, command = helpers.feed, helpers.command
local insert = helpers.insert
+local eq = helpers.eq
+local eval = helpers.eval
+local iswin = helpers.iswin
-describe('Initial screen', function()
+describe('screen', function()
local screen
local nvim_argv = {helpers.nvim_prog, '-u', 'NONE', '-i', 'NONE', '-N',
- '--cmd', 'set shortmess+=I background=light noswapfile',
+ '--cmd', 'set shortmess+=I background=light noswapfile belloff= noshowcmd noruler',
'--embed'}
before_each(function()
@@ -15,41 +18,53 @@ describe('Initial screen', function()
set_session(screen_nvim)
screen = Screen.new()
screen:attach()
- screen:set_default_attr_ignore( {{bold=true, foreground=255}} )
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=255},
+ [1] = {bold=true, reverse=true},
+ } )
end)
after_each(function()
screen:detach()
end)
- it('is the default initial screen', function()
+ it('default initial screen', function()
screen:expect([[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- [No Name] |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
|
]])
end)
end)
-describe('Screen', function()
+local function screen_tests(linegrid)
local screen
before_each(function()
clear()
screen = Screen.new()
- screen:attach()
- screen:set_default_attr_ignore( {{bold=true, foreground=255}} )
+ screen:attach({rgb=true,ext_linegrid=linegrid})
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=255},
+ [1] = {bold=true, reverse=true},
+ [2] = {bold=true},
+ [3] = {reverse=true},
+ [4] = {background = Screen.colors.LightGrey, underline = true},
+ [5] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Fuchsia},
+ [6] = {bold = true, foreground = Screen.colors.Fuchsia},
+ [7] = {bold = true, foreground = Screen.colors.SeaGreen},
+ } )
end)
after_each(function()
@@ -59,33 +74,44 @@ describe('Screen', function()
describe(':suspend', function()
it('is forwarded to the UI', function()
local function check()
- if not screen.suspended then
- return 'Screen was not suspended'
- end
+ eq(true, screen.suspended)
end
- execute('suspend')
- screen:wait(check)
+
+ command('let g:ev = []')
+ command('autocmd VimResume * :call add(g:ev, "r")')
+ command('autocmd VimSuspend * :call add(g:ev, "s")')
+
+ eq(false, screen.suspended)
+ command('suspend')
+ eq({ 's', 'r' }, eval('g:ev'))
+
+ screen:expect(check)
screen.suspended = false
+
feed('<c-z>')
- screen:wait(check)
+ eq({ 's', 'r', 's', 'r' }, eval('g:ev'))
+
+ screen:expect(check)
+ screen.suspended = false
+
+ command('suspend')
+ eq({ 's', 'r', 's', 'r', 's', 'r' }, eval('g:ev'))
end)
end)
describe('bell/visual bell', function()
it('is forwarded to the UI', function()
feed('<left>')
- screen:wait(function()
- if not screen.bell or screen.visual_bell then
- return 'Bell was not sent'
- end
+ screen:expect(function()
+ eq(true, screen.bell)
+ eq(false, screen.visual_bell)
end)
screen.bell = false
- execute('set visualbell')
+ command('set visualbell')
feed('<left>')
- screen:wait(function()
- if not screen.visual_bell or screen.bell then
- return 'Visual bell was not sent'
- end
+ screen:expect(function()
+ eq(true, screen.visual_bell)
+ eq(false, screen.bell)
end)
end)
end)
@@ -93,36 +119,28 @@ describe('Screen', function()
describe(':set title', function()
it('is forwarded to the UI', function()
local expected = 'test-title'
- execute('set titlestring='..expected)
- execute('set title')
- screen:wait(function()
- local actual = screen.title
- if actual ~= expected then
- return 'Expected title to be "'..expected..'" but was "'..actual..'"'
- end
+ command('set titlestring='..expected)
+ command('set title')
+ screen:expect(function()
+ eq(expected, screen.title)
end)
end)
it('has correct default title with unnamed file', function()
local expected = '[No Name] - NVIM'
- execute('set title')
- screen:wait(function()
- local actual = screen.title
- if actual ~= expected then
- return 'Expected title to be "'..expected..'" but was "'..actual..'"'
- end
+ command('set title')
+ screen:expect(function()
+ eq(expected, screen.title)
end)
end)
it('has correct default title with named file', function()
- local expected = 'myfile (/mydir) - NVIM'
- execute('set title')
- execute('file /mydir/myfile')
- screen:wait(function()
- local actual = screen.title
- if actual ~= expected then
- return 'Expected title to be "'..expected..'" but was "'..actual..'"'
- end
+ local expected = (iswin() and 'myfile (C:\\mydir) - NVIM'
+ or 'myfile (/mydir) - NVIM')
+ command('set title')
+ command(iswin() and 'file C:\\mydir\\myfile' or 'file /mydir/myfile')
+ screen:expect(function()
+ eq(expected, screen.title)
end)
end)
end)
@@ -130,153 +148,420 @@ describe('Screen', function()
describe(':set icon', function()
it('is forwarded to the UI', function()
local expected = 'test-icon'
- execute('set iconstring='..expected)
- execute('set icon')
- screen:wait(function()
- local actual = screen.icon
- if actual ~= expected then
- return 'Expected title to be "'..expected..'" but was "'..actual..'"'
- end
+ command('set iconstring='..expected)
+ command('set icon')
+ screen:expect(function()
+ eq(expected, screen.icon)
end)
end)
end)
+ describe('statusline', function()
+ it('is redrawn after <c-l>', function()
+ command('set laststatus=2')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ |
+ ]])
+
+ feed('<c-l>')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ |
+ ]], reset=true}
+
+ command('split')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ |
+ ]])
+
+ feed('<c-l>')
+ screen:expect{grid=[[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
+ |
+ ]], reset=true}
+ end)
+ end)
+
describe('window', function()
describe('split', function()
it('horizontal', function()
- execute('sp')
+ command('sp')
screen:expect([[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- [No Name] |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
|
- ~ |
- ~ |
- ~ |
- ~ |
- [No Name] |
- :sp |
]])
end)
it('horizontal and resize', function()
- execute('sp')
- execute('resize 8')
+ command('sp')
+ command('resize 8')
screen:expect([[
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- [No Name] |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1:[No Name] }|
+ |
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
|
- ~ |
- ~ |
- [No Name] |
- :resize 8 |
]])
end)
it('horizontal and vertical', function()
- execute('sp', 'vsp', 'vsp')
+ command('sp')
+ command('vsp')
+ command('vsp')
screen:expect([[
- ^ | | |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- [No Name] [No Name] [No Name] |
+ ^ {3:│} {3:│} |
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {1:[No Name] }{3:[No Name] [No Name] }|
|
- ~ |
- ~ |
- ~ |
- ~ |
- [No Name] |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] }|
|
]])
insert('hello')
screen:expect([[
- hell^o |hello |hello |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- [No Name] [+] [No Name] [+] [No Name] [+] |
+ hell^o {3:│}hello {3:│}hello |
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
hello |
- ~ |
- ~ |
- ~ |
- ~ |
- [No Name] [+] |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] [+] }|
|
]])
end)
end)
end)
- describe('tabnew', function()
- it('creates a new buffer', function()
- execute('sp', 'vsp', 'vsp')
+ describe('tabs', function()
+ it('tabnew creates a new buffer', function()
+ command('sp')
+ command('vsp')
+ command('vsp')
insert('hello')
screen:expect([[
- hell^o |hello |hello |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- [No Name] [+] [No Name] [+] [No Name] [+] |
+ hell^o {3:│}hello {3:│}hello |
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
hello |
- ~ |
- ~ |
- ~ |
- ~ |
- [No Name] [+] |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] [+] }|
|
]])
- execute('tabnew')
+ command('tabnew')
insert('hello2')
feed('h')
screen:expect([[
- 4+ [No Name] + [No Name] X|
+ {4: }{5:4}{4:+ [No Name] }{2: + [No Name] }{3: }{4:X}|
hell^o2 |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- |
- ]])
- execute('tabprevious')
- screen:expect([[
- 4+ [No Name] + [No Name] X|
- hell^o |hello |hello |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- ~ |~ |~ |
- [No Name] [+] [No Name] [+] [No Name] [+] |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ command('tabprevious')
+ screen:expect([[
+ {2: }{6:4}{2:+ [No Name] }{4: + [No Name] }{3: }{4:X}|
+ hell^o {3:│}hello {3:│}hello |
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {0:~ }{3:│}{0:~ }{3:│}{0:~ }|
+ {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
hello |
- ~ |
- ~ |
- ~ |
- [No Name] [+] |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {3:[No Name] [+] }|
+ |
+ ]])
+ end)
+
+ it('tabline is redrawn after messages', function()
+ command('tabnew')
+ screen:expect([[
+ {4: [No Name] }{2: [No Name] }{3: }{4:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed(':echo "'..string.rep('x\\n', 11)..'"<cr>')
+ screen:expect([[
+ {1: }|
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ |
+ {7:Press ENTER or type command to continue}^ |
+ ]])
+
+ feed('<cr>')
+ screen:expect([[
+ {4: [No Name] }{2: [No Name] }{3: }{4:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed(':echo "'..string.rep('x\\n', 12)..'"<cr>')
+ screen:expect([[
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ x |
+ |
+ {7:Press ENTER or type command to continue}^ |
+ ]])
+
+ feed('<cr>')
+ screen:expect([[
+ {4: [No Name] }{2: [No Name] }{3: }{4:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ end)
+
+ it('redraws properly with :tab split right after scroll', function()
+ feed('15Ofoo<esc>15Obar<esc>gg')
+
+ command('vsplit')
+ screen:expect([[
+ ^foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ {1:[No Name] [+] }{3:[No Name] [+] }|
+ |
+ ]])
+
+ feed('<PageDown>')
+ screen:expect([[
+ ^foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ foo {3:│}foo |
+ bar {3:│}foo |
+ bar {3:│}foo |
+ bar {3:│}foo |
+ bar {3:│}foo |
+ bar {3:│}foo |
+ bar {3:│}foo |
+ bar {3:│}foo |
+ bar {3:│}foo |
+ {1:[No Name] [+] }{3:[No Name] [+] }|
+ |
+ ]])
+ command('tab split')
+ screen:expect([[
+ {4: }{5:2}{4:+ [No Name] }{2: + [No Name] }{3: }{4:X}|
+ ^foo |
+ foo |
+ foo |
+ foo |
+ bar |
+ bar |
+ bar |
+ bar |
+ bar |
+ bar |
+ bar |
+ bar |
+ |
+ ]])
+ end)
+
+ it('redraws unvisited tab #9152', function()
+ insert('hello')
+ -- create a tab without visiting it
+ command('tabnew|tabnext')
+ screen:expect([[
+ {2: + [No Name] }{4: [No Name] }{3: }{4:X}|
+ hell^o |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('gT')
+ screen:expect([[
+ {4: + [No Name] }{2: [No Name] }{3: }{4:X}|
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
]])
end)
@@ -289,17 +574,17 @@ describe('Screen', function()
line 1 |
line 2 |
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- -- INSERT -- |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT --} |
]])
end)
end)
@@ -307,25 +592,25 @@ describe('Screen', function()
describe('normal mode', function()
-- https://code.google.com/p/vim/issues/detail?id=339
it("setting 'ruler' doesn't reset the preferred column", function()
- execute('set virtualedit=')
+ command('set virtualedit=')
feed('i0123456<cr>789<esc>kllj')
- execute('set ruler')
+ command('set ruler')
feed('k')
screen:expect([[
0123^456 |
789 |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- :set ruler 1,5 All |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ 1,5 All |
]])
end)
end)
@@ -335,39 +620,62 @@ describe('Screen', function()
feed(':ls')
screen:expect([[
|
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
:ls^ |
]])
end)
- it('execute command with multi-line output', function()
+ it('execute command with multi-line output without msgsep', function()
+ command("set display-=msgsep")
feed(':ls<cr>')
screen:expect([[
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
:ls |
1 %a "[No Name]" line 1 |
- Press ENTER or type command to continue^ |
+ {7:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<cr>') -- skip the "Press ENTER..." state or tests will hang
+ end)
+
+ it('execute command with multi-line output and with msgsep', function()
+ command("set display+=msgsep")
+ feed(':ls<cr>')
+ screen:expect([[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1: }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {7:Press ENTER or type command to continue}^ |
]])
feed('<cr>') -- skip the "Press ENTER..." state or tests will hang
end)
@@ -390,21 +698,23 @@ describe('Screen', function()
split
windows
]])
- execute('sp', 'vsp', 'vsp')
+ command('sp')
+ command('vsp')
+ command('vsp')
screen:expect([[
- and |and |and |
- clearing |clearing |clearing |
- in |in |in |
- split |split |split |
- windows |windows |windows |
- ^ | | |
- [No Name] [+] [No Name] [+] [No Name] [+] |
+ and {3:│}and {3:│}and |
+ clearing {3:│}clearing {3:│}clearing |
+ in {3:│}in {3:│}in |
+ split {3:│}split {3:│}split |
+ windows {3:│}windows {3:│}windows |
+ ^ {3:│} {3:│} |
+ {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
clearing |
in |
split |
windows |
|
- [No Name] [+] |
+ {3:[No Name] [+] }|
|
]])
end)
@@ -412,121 +722,121 @@ describe('Screen', function()
it('only affects the current scroll region', function()
feed('6k')
screen:expect([[
- ^scrolling |and |and |
- and |clearing |clearing |
- clearing |in |in |
- in |split |split |
- split |windows |windows |
- windows | | |
- [No Name] [+] [No Name] [+] [No Name] [+] |
+ ^scrolling {3:│}and {3:│}and |
+ and {3:│}clearing {3:│}clearing |
+ clearing {3:│}in {3:│}in |
+ in {3:│}split {3:│}split |
+ split {3:│}windows {3:│}windows |
+ windows {3:│} {3:│} |
+ {1:[No Name] [+] }{3:[No Name] [+] [No Name] [+] }|
clearing |
in |
split |
windows |
|
- [No Name] [+] |
+ {3:[No Name] [+] }|
|
]])
feed('<c-w>l')
screen:expect([[
- scrolling |and |and |
- and |clearing |clearing |
- clearing |in |in |
- in |split |split |
- split |windows |windows |
- windows |^ | |
- [No Name] [+] [No Name] [+] <Name] [+] |
+ scrolling {3:│}and {3:│}and |
+ and {3:│}clearing {3:│}clearing |
+ clearing {3:│}in {3:│}in |
+ in {3:│}split {3:│}split |
+ split {3:│}windows {3:│}windows |
+ windows {3:│}^ {3:│} |
+ {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
split |
windows |
|
- [No Name] [+] |
+ {3:[No Name] [+] }|
|
]])
feed('gg')
screen:expect([[
- scrolling |^Inserting |and |
- and |text |clearing |
- clearing |with |in |
- in |many |split |
- split |lines |windows |
- windows |to | |
- [No Name] [+] [No Name] [+] <Name] [+] |
+ scrolling {3:│}^Inserting {3:│}and |
+ and {3:│}text {3:│}clearing |
+ clearing {3:│}with {3:│}in |
+ in {3:│}many {3:│}split |
+ split {3:│}lines {3:│}windows |
+ windows {3:│}to {3:│} |
+ {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
split |
windows |
|
- [No Name] [+] |
+ {3:[No Name] [+] }|
|
]])
feed('7j')
screen:expect([[
- scrolling |with |and |
- and |many |clearing |
- clearing |lines |in |
- in |to |split |
- split |test |windows |
- windows |^scrolling | |
- [No Name] [+] [No Name] [+] <Name] [+] |
+ scrolling {3:│}with {3:│}and |
+ and {3:│}many {3:│}clearing |
+ clearing {3:│}lines {3:│}in |
+ in {3:│}to {3:│}split |
+ split {3:│}test {3:│}windows |
+ windows {3:│}^scrolling {3:│} |
+ {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
split |
windows |
|
- [No Name] [+] |
+ {3:[No Name] [+] }|
|
]])
feed('2j')
screen:expect([[
- scrolling |lines |and |
- and |to |clearing |
- clearing |test |in |
- in |scrolling |split |
- split |and |windows |
- windows |^clearing | |
- [No Name] [+] [No Name] [+] <Name] [+] |
+ scrolling {3:│}lines {3:│}and |
+ and {3:│}to {3:│}clearing |
+ clearing {3:│}test {3:│}in |
+ in {3:│}scrolling {3:│}split |
+ split {3:│}and {3:│}windows |
+ windows {3:│}^clearing {3:│} |
+ {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
split |
windows |
|
- [No Name] [+] |
+ {3:[No Name] [+] }|
|
]])
feed('5k')
screen:expect([[
- scrolling |^lines |and |
- and |to |clearing |
- clearing |test |in |
- in |scrolling |split |
- split |and |windows |
- windows |clearing | |
- [No Name] [+] [No Name] [+] <Name] [+] |
+ scrolling {3:│}^lines {3:│}and |
+ and {3:│}to {3:│}clearing |
+ clearing {3:│}test {3:│}in |
+ in {3:│}scrolling {3:│}split |
+ split {3:│}and {3:│}windows |
+ windows {3:│}clearing {3:│} |
+ {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
split |
windows |
|
- [No Name] [+] |
+ {3:[No Name] [+] }|
|
]])
feed('k')
screen:expect([[
- scrolling |^many |and |
- and |lines |clearing |
- clearing |to |in |
- in |test |split |
- split |scrolling |windows |
- windows |and | |
- [No Name] [+] [No Name] [+] <Name] [+] |
+ scrolling {3:│}^many {3:│}and |
+ and {3:│}lines {3:│}clearing |
+ clearing {3:│}to {3:│}in |
+ in {3:│}test {3:│}split |
+ split {3:│}scrolling {3:│}windows |
+ windows {3:│}and {3:│} |
+ {3:[No Name] [+] }{1:[No Name] [+] }{3:<Name] [+] }|
clearing |
in |
split |
windows |
|
- [No Name] [+] |
+ {3:[No Name] [+] }|
|
]])
end)
@@ -541,20 +851,20 @@ describe('Screen', function()
it('rebuilds the whole screen', function()
screen:expect([[
resize^ |
- ~ |
- ~ |
- ~ |
- -- INSERT -- |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT --} |
]])
end)
- -- FIXME this has some race conditions that cause it to fail periodically
- pending('has minimum width/height values', function()
+ it('has minimum width/height values', function()
screen:try_resize(1, 1)
screen:expect([[
- -- INS^ERT --|
- |
+ resize^ |
+ {2:-- INSERT -} |
]])
+
feed('<esc>:ls')
screen:expect([[
resize |
@@ -562,4 +872,91 @@ describe('Screen', function()
]])
end)
end)
+
+ describe('press enter', function()
+ it('does not crash on <F1> at “Press ENTER”', function()
+ command('nnoremap <F1> :echo "TEST"<CR>')
+ feed(':ls<CR>')
+ screen:expect([[
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {1: }|
+ :ls |
+ 1 %a "[No Name]" line 1 |
+ {7:Press ENTER or type command to continue}^ |
+ ]])
+ feed('<F1>')
+ screen:expect([[
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ TEST |
+ ]])
+ end)
+ end)
+
+ -- Regression test for #8357
+ it('does not have artifacts after temporary chars in insert mode', function()
+ command('inoremap jk <esc>')
+ feed('ifooj')
+ screen:expect([[
+ foo^j |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {2:-- INSERT --} |
+ ]])
+ feed('k')
+ screen:expect([[
+ fo^o |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+end
+
+describe("Screen (char-based)", function()
+ screen_tests(false)
+end)
+
+describe("Screen (line-based)", function()
+ screen_tests(true)
end)
diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua
index 894a75d355..a46670d8a2 100644
--- a/test/functional/ui/searchhl_spec.lua
+++ b/test/functional/ui/searchhl_spec.lua
@@ -1,41 +1,40 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
-local execute = helpers.execute
+local command = helpers.command
+local feed_command = helpers.feed_command
+local eq = helpers.eq
+local eval = helpers.eval
+local iswin = helpers.iswin
+local sleep = helpers.sleep
describe('search highlighting', function()
local screen
local colors = Screen.colors
- local hl_colors = {
- NonText = colors.Blue,
- Search = colors.Yellow,
- Message = colors.Red,
- }
before_each(function()
clear()
screen = Screen.new(40, 7)
screen:attach()
- --ignore highligting of ~-lines
screen:set_default_attr_ids( {
- [1] = {background = hl_colors.Search},
- [2] = {reverse = true},
- [3] = {foreground = hl_colors.Message},
+ [1] = {bold=true, foreground=Screen.colors.Blue},
+ [2] = {background = colors.Yellow}, -- Search
+ [3] = {reverse = true},
+ [4] = {foreground = colors.Red}, -- Message
})
- screen:set_default_attr_ignore( {{bold=true, foreground=hl_colors.NonText}} )
end)
it('is disabled by ":set nohlsearch"', function()
- execute('set nohlsearch')
+ feed_command('set nohlsearch')
insert("some text\nmore text")
feed("gg/text<cr>")
screen:expect([[
some ^text |
more text |
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
/text |
]])
end)
@@ -51,98 +50,206 @@ describe('search highlighting', function()
-- 'hlsearch' is enabled by default. #2859
feed("gg/text<cr>")
screen:expect([[
- some {1:^text} |
- more {1:text}stuff |
- stupid{1:texttext}stuff |
- a {1:text} word |
+ some {2:^text} |
+ more {2:text}stuff |
+ stupid{2:texttext}stuff |
+ a {2:text} word |
|
- ~ |
+ {1:~ }|
/text |
]])
-- overlapping matches not allowed
feed("3nx")
screen:expect([[
- some {1:text} |
- more {1:text}stuff |
- stupid{1:text}^extstuff |
- a {1:text} word |
+ some {2:text} |
+ more {2:text}stuff |
+ stupid{2:text}^extstuff |
+ a {2:text} word |
|
- ~ |
+ {1:~ }|
/text |
]])
feed("ggn*") -- search for entire word
screen:expect([[
- some {1:text} |
+ some {2:text} |
more textstuff |
stupidtextextstuff |
- a {1:^text} word |
+ a {2:^text} word |
|
- ~ |
+ {1:~ }|
/\<text\> |
]])
- execute("nohlsearch")
+ feed_command("nohlsearch")
screen:expect([[
some text |
more textstuff |
stupidtextextstuff |
a ^text word |
|
- ~ |
+ {1:~ }|
:nohlsearch |
]])
end)
+ it('highlights after EOL', function()
+ insert("\n\n\n\n\n\n")
+
+ feed("gg/^<cr>")
+ screen:expect([[
+ {2: } |
+ {2:^ } |
+ {2: } |
+ {2: } |
+ {2: } |
+ {2: } |
+ /^ |
+ ]])
+
+ -- Test that highlights are preserved after moving the cursor.
+ feed("j")
+ screen:expect([[
+ {2: } |
+ {2: } |
+ {2:^ } |
+ {2: } |
+ {2: } |
+ {2: } |
+ /^ |
+ ]])
+
+ -- Repeat the test in rightleft mode.
+ command("nohlsearch")
+ command("set rightleft")
+ feed("gg/^<cr>")
+
+ screen:expect([[
+ {2: }|
+ {2:^ }|
+ {2: }|
+ {2: }|
+ {2: }|
+ {2: }|
+ ^/ |
+ ]])
+
+ feed("j")
+ screen:expect([[
+ {2: }|
+ {2: }|
+ {2:^ }|
+ {2: }|
+ {2: }|
+ {2: }|
+ ^/ |
+ ]])
+ end)
+
+ it('is preserved during :terminal activity', function()
+ if iswin() then
+ feed([[:terminal for /L \%I in (1,1,5000) do @(echo xxx & echo xxx & echo xxx)<cr>]])
+ else
+ feed([[:terminal for i in $(seq 1 5000); do printf 'xxx\nxxx\nxxx\n'; done<cr>]])
+ end
+
+ feed(':file term<CR>')
+ feed(':vnew<CR>')
+ insert([[
+ foo bar baz
+ bar baz foo
+ bar foo baz
+ ]])
+ feed('/foo')
+ sleep(50) -- Allow some terminal activity.
+ screen:expect([[
+ {3:foo} bar baz {3:│}xxx |
+ bar baz {2:foo} {3:│}xxx |
+ bar {2:foo} baz {3:│}xxx |
+ {3:│}xxx |
+ {1:~ }{3:│}xxx |
+ {5:[No Name] [+] }{3:term }|
+ /foo^ |
+ ]], { [1] = {bold = true, foreground = Screen.colors.Blue1},
+ [2] = {background = Screen.colors.Yellow},
+ [3] = {reverse = true},
+ [4] = {foreground = Screen.colors.Red},
+ [5] = {bold = true, reverse = true},
+ })
+ end)
+
it('works with incsearch', function()
- execute('set hlsearch')
- execute('set incsearch')
+ feed_command('set hlsearch')
+ feed_command('set incsearch')
insert([[
the first line
in a little file
]])
feed("gg/li")
screen:expect([[
+ the first {3:li}ne |
+ in a {2:li}ttle file |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /li^ |
+ ]])
+
+ -- check that consecutive matches are caught by C-g/C-t
+ feed("<C-g>")
+ screen:expect([[
the first {2:li}ne |
- in a little file |
+ in a {3:li}ttle file |
|
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /li^ |
+ ]])
+
+ feed("<C-t>")
+ screen:expect([[
+ the first {3:li}ne |
+ in a {2:li}ttle file |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
/li^ |
]])
feed("t")
screen:expect([[
the first line |
- in a {2:lit}tle file |
+ in a {3:lit}tle file |
|
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
/lit^ |
]])
feed("<cr>")
screen:expect([[
the first line |
- in a {1:^lit}tle file |
+ in a {2:^lit}tle file |
|
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
/lit |
]])
feed("/fir")
screen:expect([[
- the {2:fir}st line |
- in a {1:lit}tle file |
+ the {3:fir}st line |
+ in a little file |
|
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
/fir^ |
]])
@@ -150,135 +257,244 @@ describe('search highlighting', function()
feed("<esc>/ttle")
screen:expect([[
the first line |
- in a {1:li}{2:ttle} file |
+ in a li{3:ttle} file |
|
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
/ttle^ |
]])
+
+ -- cancelling search resets to the old search term
+ feed('<esc>')
+ screen:expect([[
+ the first line |
+ in a {2:^lit}tle file |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ eq('lit', eval('@/'))
+
+ -- cancelling inc search restores the hl state
+ feed(':noh<cr>')
+ screen:expect([[
+ the first line |
+ in a ^little file |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ :noh |
+ ]])
+
+ feed('/first')
+ screen:expect([[
+ the {3:first} line |
+ in a little file |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /first^ |
+ ]])
+ feed('<esc>')
+ screen:expect([[
+ the first line |
+ in a ^little file |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+
+ -- test that pressing C-g in an empty command line does not move the cursor
+ feed('/<C-g>')
+ screen:expect([[
+ the first line |
+ in a little file |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /^ |
+ ]])
+
+ -- same, for C-t
+ feed('<ESC>')
+ screen:expect([[
+ the first line |
+ in a ^little file |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ |
+ ]])
+ feed('/<C-t>')
+ screen:expect([[
+ the first line |
+ in a little file |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ /^ |
+ ]])
+
+ -- 8.0.1304, test that C-g and C-t works with incsearch and empty pattern
+ feed('<esc>/fi<CR>')
+ feed('//')
+ screen:expect([[
+ the {3:fi}rst line |
+ in a little {2:fi}le |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ //^ |
+ ]])
+
+ feed('<C-g>')
+ screen:expect([[
+ the {2:fi}rst line |
+ in a little {3:fi}le |
+ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ //^ |
+ ]])
end)
it('works with incsearch and offset', function()
- execute('set hlsearch')
- execute('set incsearch')
+ feed_command('set hlsearch')
+ feed_command('set incsearch')
insert([[
not the match you're looking for
the match is here]])
feed("gg/mat/e")
screen:expect([[
- not the {2:mat}ch you're looking for |
- the match is here |
- ~ |
- ~ |
- ~ |
- ~ |
+ not the {3:mat}ch you're looking for |
+ the {2:mat}ch is here |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
/mat/e^ |
]])
-- Search with count and /e offset fixed in Vim patch 7.4.532.
feed("<esc>2/mat/e")
screen:expect([[
- not the match you're looking for |
- the {2:mat}ch is here |
- ~ |
- ~ |
- ~ |
- ~ |
+ not the {2:mat}ch you're looking for |
+ the {3:mat}ch is here |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
/mat/e^ |
]])
feed("<cr>")
screen:expect([[
- not the {1:mat}ch you're looking for |
- the {1:ma^t}ch is here |
- ~ |
- ~ |
- ~ |
- ~ |
+ not the {2:mat}ch you're looking for |
+ the {2:ma^t}ch is here |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
/mat/e |
]])
end)
it('works with multiline regexps', function()
- execute('set hlsearch')
+ feed_command('set hlsearch')
feed('4oa repeated line<esc>')
feed('/line\\na<cr>')
screen:expect([[
|
- a repeated {1:^line} |
- {1:a} repeated {1:line} |
- {1:a} repeated {1:line} |
- {1:a} repeated line |
- ~ |
- {3:search hit BOTTOM, continuing at TOP} |
+ a repeated {2:^line} |
+ {2:a} repeated {2:line} |
+ {2:a} repeated {2:line} |
+ {2:a} repeated line |
+ {1:~ }|
+ {4:search hit BOTTOM, continuing at TOP} |
]])
-- it redraws rows above the changed one
feed('4Grb')
screen:expect([[
|
- a repeated {1:line} |
- {1:a} repeated line |
- ^b repeated {1:line} |
- {1:a} repeated line |
- ~ |
- {3:search hit BOTTOM, continuing at TOP} |
+ a repeated {2:line} |
+ {2:a} repeated line |
+ ^b repeated {2:line} |
+ {2:a} repeated line |
+ {1:~ }|
+ {4:search hit BOTTOM, continuing at TOP} |
]])
end)
it('works with matchadd and syntax', function()
- execute('set hlsearch')
+ screen:set_default_attr_ids( {
+ [1] = {bold=true, foreground=Screen.colors.Blue},
+ [2] = {background = colors.Yellow},
+ [3] = {reverse = true},
+ [4] = {foreground = colors.Red},
+ [5] = {bold = true, background = colors.Green},
+ [6] = {italic = true, background = colors.Magenta},
+ [7] = {bold = true, background = colors.Yellow},
+ } )
+ feed_command('set hlsearch')
insert([[
very special text
]])
- execute("syntax on")
- execute("highlight MyGroup guibg=Green gui=bold")
- execute("highlight MyGroup2 guibg=Magenta gui=italic")
- execute("call matchadd('MyGroup', 'special')")
- execute("call matchadd('MyGroup2', 'text', 0)")
+ feed_command("syntax on")
+ feed_command("highlight MyGroup guibg=Green gui=bold")
+ feed_command("highlight MyGroup2 guibg=Magenta gui=italic")
+ feed_command("call matchadd('MyGroup', 'special')")
+ feed_command("call matchadd('MyGroup2', 'text', 0)")
-- searchhl and matchadd matches are exclusive, only the higest priority
-- is used (and matches with lower priorities are not combined)
- execute("/ial te")
+ feed_command("/ial te")
screen:expect([[
- very {4:spec^ial}{1: te}{5:xt} |
+ very {5:spec^ial}{2: te}{6:xt} |
|
- ~ |
- ~ |
- ~ |
- ~ |
- {3:search hit BOTTOM, continuing at TOP} |
- ]], {[1] = {background = hl_colors.Search}, [2] = {reverse = true},
- [3] = {foreground = hl_colors.Message}, [4] = {bold = true, background =
- colors.Green}, [5] = {italic = true, background = colors.Magenta}})
-
- execute("call clearmatches()")
- screen:expect([[
- very spec{1:^ial te}xt |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {4:search hit BOTTOM, continuing at TOP} |
+ ]])
+
+ feed_command("call clearmatches()")
+ screen:expect([[
+ very spec{2:^ial te}xt |
|
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
:call clearmatches() |
]])
-- searchhl has priority over syntax, but in this case
-- nonconflicting attributes are combined
- execute("syntax keyword MyGroup special")
+ feed_command("syntax keyword MyGroup special")
screen:expect([[
- very {4:spec}{5:^ial}{1: te}xt |
+ very {5:spec}{7:^ial}{2: te}xt |
|
- ~ |
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
:syntax keyword MyGroup special |
- ]], {[1] = {background = hl_colors.Search}, [2] = {reverse = true},
- [3] = {foreground = hl_colors.Message}, [4] = {bold = true,
- background = colors.Green}, [5] = {bold = true, background = hl_colors.Search}})
+ ]])
end)
end)
diff --git a/test/functional/ui/sign_spec.lua b/test/functional/ui/sign_spec.lua
index 2b6e294627..bc0e2e3799 100644
--- a/test/functional/ui/sign_spec.lua
+++ b/test/functional/ui/sign_spec.lua
@@ -1,6 +1,7 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
-local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute
+local clear, feed, command = helpers.clear, helpers.feed, helpers.command
+local source = helpers.source
describe('Signs', function()
local screen
@@ -9,7 +10,20 @@ describe('Signs', function()
clear()
screen = Screen.new()
screen:attach()
- screen:set_default_attr_ignore( {{}, {bold=true, foreground=255}} )
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=255},
+ [1] = {background = Screen.colors.Yellow},
+ [2] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.Grey},
+ [3] = {background = Screen.colors.Gray90},
+ [4] = {bold = true, reverse = true},
+ [5] = {reverse = true},
+ [6] = {foreground = Screen.colors.Brown},
+ [7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
+ [8] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [9] = {bold = true, foreground = Screen.colors.Magenta},
+ [10] = {foreground = Screen.colors.Blue1},
+ [11] = {bold = true, foreground = Screen.colors.SeaGreen4},
+ } )
end)
after_each(function()
@@ -19,26 +33,125 @@ describe('Signs', function()
describe(':sign place', function()
it('shadows previously placed signs', function()
feed('ia<cr>b<cr>c<cr><esc>')
- execute('sign define piet text=>> texthl=Search')
- execute('sign define pietx text=>! texthl=Search')
- execute('sign place 1 line=1 name=piet buffer=1')
- execute('sign place 2 line=3 name=piet buffer=1')
- execute('sign place 3 line=1 name=pietx buffer=1')
+ command('sign define piet text=>> texthl=Search')
+ command('sign define pietx text=>! texthl=Search')
+ command('sign place 1 line=1 name=piet buffer=1')
+ command('sign place 2 line=3 name=piet buffer=1')
+ command('sign place 3 line=1 name=pietx buffer=1')
screen:expect([[
- >!a |
- b |
- >>c |
- ^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- :sign place 3 line=1 name=pietx buffer=1 |
+ {1:>!}a |
+ {2: }b |
+ {1:>>}c |
+ {2: }^ |
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ |
+ ]])
+ end)
+
+ it('can be called right after :split', function()
+ feed('ia<cr>b<cr>c<cr><esc>gg')
+ -- This used to cause a crash due to :sign using a special redraw
+ -- (not updating nvim's specific highlight data structures)
+ -- without proper redraw first, as split just flags for redraw later.
+ source([[
+ set cursorline
+ sign define piet text=>> texthl=Search
+ split
+ sign place 3 line=2 name=piet buffer=1
+ ]])
+ screen:expect([[
+ {2: }{3:^a }|
+ {1:>>}b |
+ {2: }c |
+ {2: } |
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {4:[No Name] [+] }|
+ {2: }{3:a }|
+ {1:>>}b |
+ {2: }c |
+ {2: } |
+ {2: }{0:~ }|
+ {5:[No Name] [+] }|
+ |
+ ]])
+ end)
+
+ it('can combine text, linehl and numhl', function()
+ feed('ia<cr>b<cr>c<cr><esc>')
+ command('set number')
+ command('sign define piet text=>> texthl=Search')
+ command('sign define pietx linehl=ErrorMsg')
+ command('sign define pietxx numhl=Folded')
+ command('sign place 1 line=1 name=piet buffer=1')
+ command('sign place 2 line=2 name=pietx buffer=1')
+ command('sign place 3 line=3 name=pietxx buffer=1')
+ command('sign place 4 line=4 name=piet buffer=1')
+ command('sign place 5 line=4 name=pietx buffer=1')
+ command('sign place 6 line=4 name=pietxx buffer=1')
+ screen:expect([[
+ {1:>>}{6: 1 }a |
+ {2: }{6: 2 }{8:b }|
+ {2: }{7: 3 }c |
+ {1:>>}{7: 4 }{8:^ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ |
+ ]])
+ end)
+
+ it('can have 32bit sign IDs', function()
+ command('sign define piet text=>> texthl=Search')
+ command('sign place 100000 line=1 name=piet buffer=1')
+ feed(':sign place<cr>')
+ screen:expect([[
+ {1:>>} |
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {4: }|
+ :sign place |
+ {9:--- Signs ---} |
+ {10:Signs for [NULL]:} |
+ line=1 id=100000 name=piet |
+ |
+ {11:Press ENTER or type command to continue}^ |
+ ]])
+
+ feed('<cr>')
+ screen:expect([[
+ {1:>>}^ |
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ {2: }{0:~ }|
+ |
]])
end)
end)
diff --git a/test/functional/ui/spell_spec.lua b/test/functional/ui/spell_spec.lua
new file mode 100644
index 0000000000..913f1b9bed
--- /dev/null
+++ b/test/functional/ui/spell_spec.lua
@@ -0,0 +1,49 @@
+-- Test for scenarios involving 'spell'
+
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear = helpers.clear
+local feed = helpers.feed
+local feed_command = helpers.feed_command
+local insert = helpers.insert
+
+describe("'spell'", function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(80, 8)
+ screen:attach()
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {special = Screen.colors.Red, undercurl = true}
+ })
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('joins long lines #7937', function()
+ feed_command('set spell')
+ insert([[
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
+ tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
+ quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
+ consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
+ cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat
+ non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+ ]])
+ feed('ggJJJJJJ0')
+ screen:expect([[
+ {1:^Lorem} {1:ipsum} dolor sit {1:amet}, {1:consectetur} {1:adipiscing} {1:elit}, {1:sed} do {1:eiusmod} {1:tempor} {1:i}|
+ {1:ncididunt} {1:ut} {1:labore} {1:et} {1:dolore} {1:magna} {1:aliqua}. {1:Ut} {1:enim} ad minim {1:veniam}, {1:quis} {1:nostru}|
+ {1:d} {1:exercitation} {1:ullamco} {1:laboris} {1:nisi} {1:ut} {1:aliquip} ex ea {1:commodo} {1:consequat}. {1:Duis} {1:aut}|
+ {1:e} {1:irure} dolor in {1:reprehenderit} in {1:voluptate} {1:velit} {1:esse} {1:cillum} {1:dolore} {1:eu} {1:fugiat} {1:n}|
+ {1:ulla} {1:pariatur}. {1:Excepteur} {1:sint} {1:occaecat} {1:cupidatat} non {1:proident}, {1:sunt} in culpa {1:qui}|
+ {1:officia} {1:deserunt} {1:mollit} {1:anim} id est {1:laborum}. |
+ {0:~ }|
+ |
+ ]])
+ end)
+end)
diff --git a/test/functional/ui/syntax_conceal_spec.lua b/test/functional/ui/syntax_conceal_spec.lua
index c2ab0711c0..d678784dc9 100644
--- a/test/functional/ui/syntax_conceal_spec.lua
+++ b/test/functional/ui/syntax_conceal_spec.lua
@@ -1,27 +1,33 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
-local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute
+local clear, feed, command = helpers.clear, helpers.feed, helpers.command
local insert = helpers.insert
describe('Screen', function()
- local screen
+ local screen
- before_each(function()
+ before_each(function()
clear()
screen = Screen.new(nil,10)
screen:attach()
- screen:set_default_attr_ignore( {{bold=true, foreground=255}} )
- screen:set_default_attr_ids( {{foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray}} )
+ screen:set_default_attr_ids( {
+ [0] = {bold=true, foreground=Screen.colors.Blue},
+ [1] = {foreground = Screen.colors.LightGrey, background = Screen.colors.DarkGray},
+ [2] = {bold = true, reverse = true},
+ [3] = {reverse = true},
+ [4] = {bold = true},
+ [5] = {background = Screen.colors.Yellow},
+ } )
end)
-
+
after_each(function()
screen:detach()
- end)
+ end)
describe("match and conceal", function()
before_each(function()
- execute("let &conceallevel=1")
+ command("let &conceallevel=1")
end)
describe("multiple", function()
@@ -34,7 +40,7 @@ describe('Screen', function()
&&
&&
]])
- execute("syn match dAmpersand '[&][&]' conceal cchar=∧")
+ command("syn match dAmpersand '[&][&]' conceal cchar=∧")
end)
it("double characters.", function()
@@ -46,13 +52,13 @@ describe('Screen', function()
{1:∧} |
{1:∧} |
^ |
- ~ |
- ~ |
- :syn match dAmpersand '[&][&]' conceal cchar=∧ |
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
- it('double characters and move the cursor one line up.', function()
+ it('double characters and move the cursor one line up.', function()
feed("k")
screen:expect([[
{1:∧} |
@@ -62,9 +68,9 @@ describe('Screen', function()
{1:∧} |
^&& |
|
- ~ |
- ~ |
- :syn match dAmpersand '[&][&]' conceal cchar=∧ |
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
@@ -78,13 +84,13 @@ describe('Screen', function()
{1:∧} |
{1:∧} |
|
- ~ |
- ~ |
- :syn match dAmpersand '[&][&]' conceal cchar=∧ |
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
- it('double characters and move the cursor to the second line in the file.', function()
+ it('double characters and move the cursor to the second line in the file.', function()
feed("ggj")
screen:expect([[
{1:∧} |
@@ -94,13 +100,13 @@ describe('Screen', function()
{1:∧} |
{1:∧} |
|
- ~ |
- ~ |
- :syn match dAmpersand '[&][&]' conceal cchar=∧ |
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
- it('double characters and then move the cursor to the beginning of the file and back to the end of the file.', function()
+ it('double characters and then move the cursor to the beginning of the file and back to the end of the file.', function()
feed("ggG")
screen:expect([[
{1:∧} |
@@ -110,51 +116,51 @@ describe('Screen', function()
{1:∧} |
{1:∧} |
^ |
- ~ |
- ~ |
- :syn match dAmpersand '[&][&]' conceal cchar=∧ |
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
- end) -- multiple
-
+ end) -- multiple
+
it("keyword instances in initially in the document.", function()
feed("2ilambda<cr><ESC>")
- execute("let &conceallevel=1")
- execute("syn keyword kLambda lambda conceal cchar=λ")
+ command("let &conceallevel=1")
+ command("syn keyword kLambda lambda conceal cchar=λ")
screen:expect([[
{1:λ} |
{1:λ} |
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- :syn keyword kLambda lambda conceal cchar=λ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
]])
end) -- Keyword
describe("regions in the document", function()
- before_each(function()
+ before_each(function()
feed("2")
insert("<r> a region of text </r>\n")
- execute("let &conceallevel=1")
+ command("let &conceallevel=1")
end)
-
- it('initially and conceal it.', function()
- execute("syn region rText start='<r>' end='</r>' conceal cchar=R")
+
+ it('initially and conceal it.', function()
+ command("syn region rText start='<r>' end='</r>' conceal cchar=R")
screen:expect([[
{1:R} |
{1:R} |
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
]])
end)
@@ -162,23 +168,23 @@ describe('Screen', function()
it('initially and conceal its start tag and end tag.', function()
-- concealends has a known bug (todo.txt) where the first match won't
-- be replaced with cchar.
- execute("syn region rText matchgroup=rMatch start='<r>' end='</r>' concealends cchar=-")
+ command("syn region rText matchgroup=rMatch start='<r>' end='</r>' concealends cchar=-")
screen:expect([[
{1: } a region of text {1:-} |
{1: } a region of text {1:-} |
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
]])
end)
-
+
it('that are nested and conceal the nested region\'s start and end tags.', function()
- execute("syn region rText contains=rText matchgroup=rMatch start='<r>' end='</r>' concealends cchar=-")
+ command("syn region rText contains=rText matchgroup=rMatch start='<r>' end='</r>' concealends cchar=-")
insert("<r> A region with <r> a nested <r> nested region.</r> </r> </r>\n")
screen:expect([[
{1: } a region of text {1:-} |
@@ -186,21 +192,21 @@ describe('Screen', function()
{1: } A region with {1: } a nested {1: } nested region.{1:-} |
{1:-} {1:-} |
^ |
- ~ |
- ~ |
- ~ |
- ~ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
|
]])
end)
end) -- regions in the document
describe("a region of text", function()
- before_each(function()
- execute("syntax conceal on")
+ before_each(function()
+ command("syntax conceal on")
feed("2")
insert("<r> a region of text </r>\n")
- execute("syn region rText start='<r>' end='</r>' cchar=-")
+ command("syn region rText start='<r>' end='</r>' cchar=-")
end)
it("and turn on implicit concealing", function()
@@ -208,123 +214,613 @@ describe('Screen', function()
{1:-} |
{1:-} |
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- ~ |
- :syn region rText start='<r>' end='</r>' cchar=- |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
it("and then turn on, then off, and then back on implicit concealing.", function()
- execute("syntax conceal off")
+ command("syntax conceal off")
feed("2")
insert("<i> italian text </i>\n")
- execute("syn region iText start='<i>' end='</i>' cchar=*")
+ command("syn region iText start='<i>' end='</i>' cchar=*")
screen:expect([[
{1:-} |
{1:-} |
<i> italian text </i> |
<i> italian text </i> |
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- :syn region iText start='<i>' end='</i>' cchar=* |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
]])
- execute("syntax conceal on")
- execute("syn region iText start='<i>' end='</i>' cchar=*")
+ command("syntax conceal on")
+ command("syn region iText start='<i>' end='</i>' cchar=*")
screen:expect([[
{1:-} |
{1:-} |
{1:*} |
{1:*} |
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- :syn region iText start='<i>' end='</i>' cchar=* |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
end) -- a region of text (implicit concealing)
end) -- match and conceal
- describe("let the conceal level be", function()
+ describe("let the conceal level be", function()
before_each(function()
- insert("// No Conceal\n")
- insert('"Conceal without a cchar"\n')
- insert("+ With cchar\n\n")
- execute("syn match noConceal '^//.*$'")
- execute("syn match concealNoCchar '\".\\{-}\"$' conceal")
- execute("syn match concealWCchar '^+.\\{-}$' conceal cchar=C")
+ insert("// No Conceal\n")
+ insert('"Conceal without a cchar"\n')
+ insert("+ With cchar\n\n")
+ command("syn match noConceal '^//.*$'")
+ command("syn match concealNoCchar '\".\\{-}\"$' conceal")
+ command("syn match concealWCchar '^+.\\{-}$' conceal cchar=C")
end)
-
- it("0. No concealing.", function()
- execute("let &conceallevel=0")
+
+ it("0. No concealing.", function()
+ command("let &conceallevel=0")
screen:expect([[
// No Conceal |
"Conceal without a cchar" |
+ With cchar |
|
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- :let &conceallevel=0 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
-
+
it("1. Conceal using cchar or reference listchars.", function()
- execute("let &conceallevel=1")
+ command("let &conceallevel=1")
screen:expect([[
// No Conceal |
{1: } |
{1:C} |
|
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- :let &conceallevel=1 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
-
+
it("2. Hidden unless cchar is set.", function()
- execute("let &conceallevel=2")
+ command("let &conceallevel=2")
screen:expect([[
// No Conceal |
|
{1:C} |
|
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- :let &conceallevel=2 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
-
- it("3. Hide all concealed text.", function()
- execute("let &conceallevel=3")
+
+ it("3. Hide all concealed text.", function()
+ command("let &conceallevel=3")
screen:expect([[
// No Conceal |
|
|
|
^ |
- ~ |
- ~ |
- ~ |
- ~ |
- :let &conceallevel=3 |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
]])
end)
end) -- conceallevel
+
+
+ describe("cursor movement", function()
+ before_each(function()
+ command("syn keyword concealy barf conceal cchar=b")
+ command("set cole=2")
+ feed('5Ofoo barf bar barf eggs<esc>')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo barf bar barf egg^s |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ end)
+
+ it('between windows', function()
+ command("split")
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo barf bar barf egg^s |
+ |
+ {2:[No Name] [+] }|
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {3:[No Name] [+] }|
+ |
+ ]])
+ feed('<c-w>w')
+
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {3:[No Name] [+] }|
+ foo {1:b} bar {1:b} eggs |
+ foo barf bar barf egg^s |
+ |
+ {2:[No Name] [+] }|
+ |
+ ]])
+ end)
+
+ it('in insert mode', function()
+ feed('i')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo barf bar barf egg^s |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:-- INSERT --} |
+ ]])
+
+ feed('<up>')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo barf bar barf egg^s |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:-- INSERT --} |
+ ]])
+ end)
+
+ it('between modes cocu=iv', function()
+ command('set cocu=iv')
+ feed('gg')
+ screen:expect([[
+ ^foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('i')
+ screen:expect([[
+ ^foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:-- INSERT --} |
+ ]])
+
+ feed('<esc>')
+ screen:expect([[
+ ^foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('v')
+ screen:expect([[
+ ^foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:-- VISUAL --} |
+ ]])
+
+ feed('<esc>')
+ screen:expect([[
+ ^foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ end)
+
+ it('between modes cocu=n', function()
+ command('set cocu=n')
+ feed('gg')
+ screen:expect([[
+ ^foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('i')
+ screen:expect([[
+ ^foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:-- INSERT --} |
+ ]])
+
+ feed('<esc>')
+ screen:expect([[
+ ^foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+
+ feed('v')
+ screen:expect([[
+ ^foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {4:-- VISUAL --} |
+ ]])
+
+ feed('<esc>')
+ screen:expect([[
+ ^foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('and open line', function()
+ feed('o')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ ^ |
+ |
+ {0:~ }|
+ {0:~ }|
+ {4:-- INSERT --} |
+ ]])
+ end)
+
+ it('and open line cocu=i', function()
+ command('set cocu=i')
+ feed('o')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ ^ |
+ |
+ {0:~ }|
+ {0:~ }|
+ {4:-- INSERT --} |
+ ]])
+ end)
+
+ describe('with incsearch', function()
+ before_each(function()
+ command('set incsearch hlsearch')
+ feed('2GA x<esc>3GA xy<esc>gg')
+ screen:expect([[
+ ^foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('cocu=', function()
+ feed('/')
+ screen:expect([[
+ foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /^ |
+ ]])
+
+ feed('x')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo barf bar barf eggs {3:x} |
+ foo {1:b} bar {1:b} eggs {5:x}y |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /x^ |
+ ]])
+
+ feed('y')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo barf bar barf eggs {3:xy} |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /xy^ |
+ ]])
+
+ feed('<c-w>')
+ screen:expect([[
+ foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /^ |
+ ]])
+ end)
+
+ it('cocu=c', function()
+ command('set cocu=c')
+
+ feed('/')
+ -- NB: we don't do this redraw. Probably best to still skip it,
+ -- to avoid annoying distraction from the cmdline
+ screen:expect([[
+ foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /^ |
+ ]])
+
+ feed('x')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs {3:x} |
+ foo {1:b} bar {1:b} eggs {5:x}y |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /x^ |
+ ]])
+
+ feed('y')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs {3:xy} |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /xy^ |
+ ]])
+
+ feed('<c-w>')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /^ |
+ ]])
+
+ feed('<esc>')
+ screen:expect([[
+ ^foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+
+ it('cocu=n', function()
+ command('set cocu=n')
+ screen:expect([[
+ ^foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+
+ feed('/')
+ -- NB: we don't do this redraw. Probably best to still skip it,
+ -- to avoid annoying distraction from the cmdline
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /^ |
+ ]])
+
+ feed('x')
+ screen:expect([[
+ foo {1:b} bar {1:b} eggs |
+ foo barf bar barf eggs {3:x} |
+ foo {1:b} bar {1:b} eggs {5:x}y |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /x^ |
+ ]])
+
+ feed('<c-w>')
+ screen:expect([[
+ foo barf bar barf eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ /^ |
+ ]])
+
+ feed('<esc>')
+ screen:expect([[
+ ^foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs x |
+ foo {1:b} bar {1:b} eggs xy |
+ foo {1:b} bar {1:b} eggs |
+ foo {1:b} bar {1:b} eggs |
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ |
+ ]])
+ end)
+ end)
+ end)
end)
diff --git a/test/functional/ui/tabline_spec.lua b/test/functional/ui/tabline_spec.lua
new file mode 100644
index 0000000000..dcab9f7ef4
--- /dev/null
+++ b/test/functional/ui/tabline_spec.lua
@@ -0,0 +1,54 @@
+local helpers = require('test.functional.helpers')(after_each)
+local Screen = require('test.functional.ui.screen')
+local clear, command, eq = helpers.clear, helpers.command, helpers.eq
+
+describe('ui/ext_tabline', function()
+ local screen
+ local event_tabs, event_curtab
+
+ before_each(function()
+ clear()
+ screen = Screen.new(25, 5)
+ screen:attach({rgb=true, ext_tabline=true})
+ screen:set_on_event_handler(function(name, data)
+ if name == "tabline_update" then
+ event_curtab, event_tabs = unpack(data)
+ end
+ end)
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('publishes UI events', function()
+ command("tabedit another-tab")
+
+ local expected_tabs = {
+ {tab = { id = 1 }, name = '[No Name]'},
+ {tab = { id = 2 }, name = 'another-tab'},
+ }
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]], condition=function()
+ eq({ id = 2 }, event_curtab)
+ eq(expected_tabs, event_tabs)
+ end}
+
+ command("tabNext")
+ screen:expect{grid=[[
+ ^ |
+ ~ |
+ ~ |
+ ~ |
+ |
+ ]], condition=function()
+ eq({ id = 1 }, event_curtab)
+ eq(expected_tabs, event_tabs)
+ end}
+ end)
+end)
diff --git a/test/functional/ui/wildmode_spec.lua b/test/functional/ui/wildmode_spec.lua
index 2a55d27567..ffe71cfadf 100644
--- a/test/functional/ui/wildmode_spec.lua
+++ b/test/functional/ui/wildmode_spec.lua
@@ -1,34 +1,171 @@
+local global_helpers = require('test.helpers')
+local shallowcopy = global_helpers.shallowcopy
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
-local clear, feed, execute = helpers.clear, helpers.feed, helpers.execute
+local clear, feed, command = helpers.clear, helpers.feed, helpers.command
+local iswin = helpers.iswin
local funcs = helpers.funcs
+local eq = helpers.eq
+local eval = helpers.eval
+local retry = helpers.retry
-describe("'wildmode'", function()
+describe("'wildmenu'", function()
local screen
-
before_each(function()
clear()
screen = Screen.new(25, 5)
screen:attach()
end)
-
after_each(function()
screen:detach()
end)
- describe("'wildmenu'", function()
- it(':sign <tab> shows wildmenu completions', function()
- execute('set wildmode=full')
- execute('set wildmenu')
- feed(':sign <tab>')
- screen:expect([[
- |
- ~ |
- ~ |
- define jump list > |
- :sign define^ |
- ]])
+ -- expect the screen stayed unchanged some time after first seen success
+ local function expect_stay_unchanged(args)
+ screen:expect(args)
+ args = shallowcopy(args)
+ args.unchanged = true
+ screen:expect(args)
+ end
+
+ it(':sign <tab> shows wildmenu completions', function()
+ command('set wildmode=full')
+ command('set wildmenu')
+ feed(':sign <tab>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ define jump list > |
+ :sign define^ |
+ ]])
+ end)
+
+ it(':sign <tab> <space> hides wildmenu #8453', function()
+ command('set wildmode=full')
+ -- only a regression if status-line open
+ command('set laststatus=2')
+ command('set wildmenu')
+ feed(':sign <tab>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ define jump list > |
+ :sign define^ |
+ ]])
+ feed('<space>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ [No Name] |
+ :sign define ^ |
+ ]])
+ end)
+
+ it('does not crash after cycling back to original text', function()
+ command('set wildmode=full')
+ feed(':j<Tab><Tab><Tab>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ join jumps |
+ :j^ |
+ ]])
+ -- This would cause nvim to crash before #6650
+ feed('<BS><Tab>')
+ screen:expect([[
+ |
+ ~ |
+ ~ |
+ ! # & < = > @ > |
+ :!^ |
+ ]])
+ end)
+
+ it('is preserved during :terminal activity', function()
+ command('set wildmenu wildmode=full')
+ command('set scrollback=4')
+ if iswin() then
+ feed([[:terminal for /L \%I in (1,1,5000) do @(echo foo & echo foo & echo foo)<cr>]])
+ else
+ feed([[:terminal for i in $(seq 1 5000); do printf 'foo\nfoo\nfoo\n'; sleep 0.1; done<cr>]])
+ end
+
+ feed([[<C-\><C-N>gg]])
+ feed([[:sign <Tab>]]) -- Invoke wildmenu.
+ expect_stay_unchanged{grid=[[
+ foo |
+ foo |
+ foo |
+ define jump list > |
+ :sign define^ |
+ ]]}
+
+ -- cmdline CTRL-D display should also be preserved.
+ feed([[<C-\><C-N>]])
+ feed([[:sign <C-D>]]) -- Invoke cmdline CTRL-D.
+ expect_stay_unchanged{grid=[[
+ :sign |
+ define place |
+ jump undefine |
+ list unplace |
+ :sign ^ |
+ ]]}
+
+ -- Exiting cmdline should show the buffer.
+ feed([[<C-\><C-N>]])
+ screen:expect([[
+ ^foo |
+ foo |
+ foo |
+ foo |
+ |
+ ]])
+ end)
+
+ it('ignores :redrawstatus called from a timer #7108', function()
+ command('set wildmenu wildmode=full')
+ command([[call timer_start(10, {->execute('redrawstatus')}, {'repeat':-1})]])
+ feed([[<C-\><C-N>]])
+ feed([[:sign <Tab>]]) -- Invoke wildmenu.
+ expect_stay_unchanged{grid=[[
+ |
+ ~ |
+ ~ |
+ define jump list > |
+ :sign define^ |
+ ]]}
+ end)
+
+ it('with laststatus=0, :vsplit, :term #2255', function()
+ -- Because this test verifies a _lack_ of activity after screen:sleep(), we
+ -- must wait the full timeout. So make it reasonable.
+ screen.timeout = 1000
+
+ if not iswin() then
+ command('set shell=sh') -- Need a predictable "$" prompt.
+ end
+ command('set laststatus=0')
+ command('vsplit')
+ command('term')
+
+ -- Check for a shell prompt to verify that the terminal loaded.
+ retry(nil, nil, function()
+ if iswin() then
+ eq('Microsoft', eval("matchstr(join(getline(1, '$')), 'Microsoft')"))
+ else
+ eq('$', eval([[matchstr(getline(1), '\$')]]))
+ end
end)
+
+ feed([[<C-\><C-N>]])
+ feed([[:<Tab>]]) -- Invoke wildmenu.
+ -- Check only the last 2 lines, because the shell output is
+ -- system-dependent.
+ expect_stay_unchanged{any='! # & < = > @ > |\n:!^'}
end)
end)
@@ -39,7 +176,7 @@ describe('command line completion', function()
clear()
screen = Screen.new(40, 5)
screen:attach()
- screen:set_default_attr_ignore({{bold=true, foreground=Screen.colors.Blue}})
+ screen:set_default_attr_ids({[1]={bold=true, foreground=Screen.colors.Blue}})
end)
after_each(function()
@@ -48,17 +185,89 @@ describe('command line completion', function()
it('lists directories with empty PATH', function()
local tmp = funcs.tempname()
- execute('e '.. tmp)
- execute('cd %:h')
- execute("call mkdir('Xtest-functional-viml-compl-dir')")
- execute('let $PATH=""')
+ command('e '.. tmp)
+ command('cd %:h')
+ command("call mkdir('Xtest-functional-viml-compl-dir')")
+ command('let $PATH=""')
feed(':!<tab><bs>')
screen:expect([[
|
- ~ |
- ~ |
- ~ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
:!Xtest-functional-viml-compl-dir^ |
]])
end)
end)
+
+describe('ui/ext_wildmenu', function()
+ local screen
+
+ before_each(function()
+ clear()
+ screen = Screen.new(25, 5)
+ screen:attach({rgb=true, ext_wildmenu=true})
+ end)
+
+ after_each(function()
+ screen:detach()
+ end)
+
+ it('works with :sign <tab>', function()
+ local expected = {
+ 'define',
+ 'jump',
+ 'list',
+ 'place',
+ 'undefine',
+ 'unplace',
+ }
+
+ command('set wildmode=full')
+ command('set wildmenu')
+ feed(':sign <tab>')
+ screen:expect{grid=[[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :sign define^ |
+ ]], wildmenu_items=expected, wildmenu_pos=0}
+
+ feed('<tab>')
+ screen:expect{grid=[[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :sign jump^ |
+ ]], wildmenu_items=expected, wildmenu_pos=1}
+
+ feed('<left><left>')
+ screen:expect{grid=[[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :sign ^ |
+ ]], wildmenu_items=expected, wildmenu_pos=-1}
+
+ feed('<right>')
+ screen:expect{grid=[[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :sign define^ |
+ ]], wildmenu_items=expected, wildmenu_pos=0}
+
+ feed('a')
+ screen:expect{grid=[[
+ |
+ ~ |
+ ~ |
+ ~ |
+ :sign definea^ |
+ ]]}
+ end)
+end)