From 21f0f7bca5a13fe847478ef27dc26bced9b3f3d1 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 18 Jun 2019 00:00:51 +0200 Subject: paste: WIP #4448 --- test/functional/terminal/helpers.lua | 3 + test/functional/terminal/paste_spec.lua | 204 ++++++++++++++++++++++++++++++++ test/functional/ui/input_spec.lua | 8 +- 3 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 test/functional/terminal/paste_spec.lua (limited to 'test/functional') diff --git a/test/functional/terminal/helpers.lua b/test/functional/terminal/helpers.lua index 18f0b9e4c1..2d99a08614 100644 --- a/test/functional/terminal/helpers.lua +++ b/test/functional/terminal/helpers.lua @@ -1,3 +1,6 @@ +-- To test tui/input.c, this module spawns `nvim` inside :terminal and sends +-- bytes via jobsend(). Note: the functional/helpers.lua test-session methods +-- operate on the _host_ session, _not_ the child session. local helpers = require('test.functional.helpers')(nil) local Screen = require('test.functional.ui.screen') local nvim_dir = helpers.nvim_dir diff --git a/test/functional/terminal/paste_spec.lua b/test/functional/terminal/paste_spec.lua new file mode 100644 index 0000000000..1c1f57246c --- /dev/null +++ b/test/functional/terminal/paste_spec.lua @@ -0,0 +1,204 @@ +-- TUI tests for "bracketed paste" mode. +-- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode +local helpers = require('test.functional.helpers') +local child_tui = require('test.functional.tui.child_session') +local Screen = require('test.functional.ui.screen') +local execute = helpers.execute +local nvim_dir = helpers.nvim_dir +local eval = helpers.eval +local eq = helpers.eq +local feed_tui = child_tui.feed_data + +describe('tui paste', function() + local screen + + before_each(function() + helpers.clear() + screen = child_tui.screen_setup(0, '["'..helpers.nvim_prog.. + '", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile"]') + + -- Pasting can be really slow in the TUI, especially in ASAN. + screen.timeout = 5000 + + screen:expect([[ + {1: } | + ~ | + ~ | + ~ | + [No Name] | + | + -- TERMINAL -- | + ]]) + end) + + after_each(function() + screen:detach() + end) + + local function setup_harness() + -- Delete the default PastePre/PastePost autocmds. + feed_tui(":autocmd! PastePre,PastePost\n") + + -- Set up test handlers. + feed_tui(":autocmd PastePre * ".. + "call feedkeys('iPastePre mode:'.mode(),'n')\n") + feed_tui(":autocmd PastePost * ".. + "call feedkeys('PastePost mode:'.mode(),'n')\n") + end + + it('handles long bursts of input', function() + execute('set ruler') + local t = {} + for i = 1, 3000 do + t[i] = 'item ' .. tostring(i) + end + feed_tui('i\027[200~') + feed_tui(table.concat(t, '\n')) + feed_tui('\027[201~') + screen:expect([[ + item 2997 | + item 2998 | + item 2999 | + item 3000{1: } | + [No Name] [+] 3000,10 Bot| + -- INSERT -- | + -- TERMINAL -- | + ]]) + end) + + it('raises PastePre, PastePost in normal-mode', function() + setup_harness() + + -- Send the "start paste" sequence. + feed_tui("\027[200~") + feed_tui("\npasted from terminal (1)\npasted from terminal (2)\n") + -- Send the "stop paste" sequence. + feed_tui("\027[201~") + + screen:expect([[ + PastePre mode:n | + pasted from terminal (1) | + pasted from terminal (2) | + PastePost mode:i{1: } | + [No Name] [+] | + -- INSERT -- | + -- TERMINAL -- | + ]]) + end) + + it('forwards spurious "start paste" sequence', function() + setup_harness() + -- If multiple "start paste" sequences are sent without a corresponding + -- "stop paste" sequence, only the first occurrence should be consumed. + + -- Send the "start paste" sequence. + feed_tui("\027[200~") + feed_tui("\npasted from terminal (1)\n") + -- Send spurious "start paste" sequence. + feed_tui("\027[200~") + feed_tui("\n") + -- Send the "stop paste" sequence. + feed_tui("\027[201~") + + screen:expect([[ + PastePre mode:n | + pasted from terminal (1) | + {1:^[}200~ | + PastePost mode:i{2: } | + [No Name] [+] | + -- INSERT -- | + -- TERMINAL -- | + ]], { + [1] = {foreground = 4}, + [2] = {reverse = true}, + }) + end) + + it('ignores spurious "stop paste" sequence', function() + setup_harness() + -- If "stop paste" sequence is received without a preceding "start paste" + -- sequence, it should be ignored. + + feed_tui("i") + -- Send "stop paste" sequence. + feed_tui("\027[201~") + + screen:expect([[ + {1: } | + ~ | + ~ | + ~ | + [No Name] | + -- INSERT -- | + -- TERMINAL -- | + ]]) + end) + + it('raises PastePre, PastePost in command-mode', function() + -- The default PastePre/PastePost handlers set the 'paste' option. To test, + -- we define a command-mode map, then assert that the mapping was ignored + -- during paste. + feed_tui(":cnoremap st XXX\n") + + feed_tui(":not pasted") + + -- Paste did not start, so the mapping _should_ apply. + screen:expect([[ + | + ~ | + ~ | + ~ | + [No Name] | + :not paXXXed{1: } | + -- TERMINAL -- | + ]]) + + feed_tui("\003") -- CTRL-C + feed_tui(":") + feed_tui("\027[200~") -- Send the "start paste" sequence. + feed_tui("pasted") + + -- Paste started, so the mapping should _not_ apply. + screen:expect([[ + | + ~ | + ~ | + ~ | + [No Name] | + :pasted{1: } | + -- TERMINAL -- | + ]]) + + feed_tui("\003") -- CTRL-C + feed_tui(":") + feed_tui("\027[201~") -- Send the "stop paste" sequence. + feed_tui("not pasted") + + -- Paste stopped, so the mapping _should_ apply. + screen:expect([[ + | + ~ | + ~ | + ~ | + [No Name] | + :not paXXXed{1: } | + -- TERMINAL -- | + ]]) + + end) + + -- TODO + it('sets undo-point after consecutive pastes', function() + end) + + -- TODO + it('handles missing "stop paste" sequence', function() + end) + + -- TODO: error when pasting into 'nomodifiable' buffer: + -- [error @ do_put:2656] 17043 - Failed to save undo information + it("handles 'nomodifiable' buffer gracefully", function() + end) + +end) + diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 0009f2c31b..7b5c6aa29d 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -127,7 +127,7 @@ describe('feeding large chunks of input with ', function() for i = 1, 20000 do t[i] = 'item ' .. tostring(i) end - feed('i') + command('doautocmd PastePre') screen:expect([[ ^ | ~ | @@ -161,7 +161,7 @@ describe('feeding large chunks of input with ', function() item 20000^ | -- INSERT (paste) -- | ]]) - feed('') + command('doautocmd PastePost') screen:expect([[ item 19988 | item 19989 | @@ -175,8 +175,8 @@ describe('feeding large chunks of input with ', function() item 19997 | item 19998 | item 19999 | - item 20000^ | - -- INSERT -- 20000,11 Bot | + item 2000^0 | + 20000,10 Bot | ]]) end) end) -- cgit From 68ea9a7c8a7a74ec6ec9782528527cf70b92a376 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 19 Aug 2019 00:18:41 +0200 Subject: TUI/paste: always flush on paste mode-change Flush input before entering, not only when leaving, paste mode. Else there could be pending input which will erroneously be sent to the paste handler. --- test/functional/terminal/tui_spec.lua | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) (limited to 'test/functional') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index af55ec1555..b990652fc0 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -146,10 +146,8 @@ describe('TUI', function() ]], attrs) end) - it('automatically sends for bracketed paste sequences', function() + it('bracketed Paste', function() -- Pasting can be really slow in the TUI, specially in ASAN. - -- This will be fixed later but for now we require a high timeout. - screen.timeout = 60000 feed_data('i\027[200~') screen:expect([[ {1: } | @@ -157,27 +155,28 @@ describe('TUI', function() {4:~ }| {4:~ }| {5:[No Name] }| - {3:-- INSERT (paste) --} | + {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) feed_data('pasted from terminal') screen:expect([[ pasted from terminal{1: } | - {4:~ }| + | {4:~ }| {4:~ }| {5:[No Name] [+] }| - {3:-- INSERT (paste) --} | + {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) - feed_data('\027[201~') + feed_data('\027[201~') -- End paste. + feed_data('\027\000') -- ESC: go to Normal mode. screen:expect([[ - pasted from terminal{1: } | - {4:~ }| + pasted from termina{1:l} | + | {4:~ }| {4:~ }| {5:[No Name] [+] }| - {3:-- INSERT --} | + | {3:-- TERMINAL --} | ]]) end) -- cgit From abd55be19a2f1443cfffb8d4953f86f32efe40aa Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 19 Aug 2019 01:01:40 +0200 Subject: paste: fixup tests --- test/functional/terminal/tui_spec.lua | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'test/functional') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index b990652fc0..127cd69975 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -146,8 +146,7 @@ describe('TUI', function() ]], attrs) end) - it('bracketed Paste', function() - -- Pasting can be really slow in the TUI, specially in ASAN. + it('paste: Insert mode', function() feed_data('i\027[200~') screen:expect([[ {1: } | @@ -160,8 +159,8 @@ describe('TUI', function() ]]) feed_data('pasted from terminal') screen:expect([[ - pasted from terminal{1: } | | + pasted from terminal{1: } | {4:~ }| {4:~ }| {5:[No Name] [+] }| @@ -171,8 +170,8 @@ describe('TUI', function() feed_data('\027[201~') -- End paste. feed_data('\027\000') -- ESC: go to Normal mode. screen:expect([[ - pasted from termina{1:l} | | + pasted from termina{1:l} | {4:~ }| {4:~ }| {5:[No Name] [+] }| @@ -181,24 +180,20 @@ describe('TUI', function() ]]) end) - it('handles pasting a specific amount of text', function() - -- Need extra time for this test, specially in ASAN. - screen.timeout = 60000 + it('pasting a specific amount of text #10311', function() feed_data('i\027[200~'..string.rep('z', 64)..'\027[201~') screen:expect([[ + | zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz| zzzzzzzzzzzzzz{1: } | {4:~ }| - {4:~ }| {5:[No Name] [+] }| {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) end) - it('can handle arbitrarily long bursts of input', function() - -- Need extra time for this test, specially in ASAN. - screen.timeout = 60000 + it('big burst of input (bracketed paste)', function() feed_command('set ruler') local t = {} for i = 1, 3000 do -- cgit From e1177be363f84f5f4f34c21b760bc47f70d5fa48 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 19 Aug 2019 23:43:19 +0200 Subject: API: nvim_put #6819 --- test/functional/api/vim_spec.lua | 43 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) (limited to 'test/functional') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 0cd81619c1..09c297940c 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -5,6 +5,7 @@ local NIL = helpers.NIL local clear, nvim, eq, neq = helpers.clear, helpers.nvim, helpers.eq, helpers.neq local command = helpers.command local eval = helpers.eval +local expect = helpers.expect local funcs = helpers.funcs local iswin = helpers.iswin local meth_pcall = helpers.meth_pcall @@ -365,6 +366,40 @@ describe('API', function() end) end) + describe('nvim_put', function() + it('inserts text', function() + -- linewise + nvim('put', {'line 1','line 2','line 3'}, 'l', false) + expect([[ + + line 1 + line 2 + line 3]]) + command('%delete _') + -- charwise + nvim('put', {'line 1','line 2','line 3'}, 'c', false) + expect([[ + line 1 + line 2 + line 3]]) + -- blockwise + nvim('put', {'AA','BB'}, 'b', false) + expect([[ + lAAine 1 + lBBine 2 + line 3]]) + command('%delete _') + -- Empty lines list. + nvim('put', {}, 'c', false) + expect([[]]) + -- Single empty line. + nvim('put', {''}, 'c', false) + expect([[ + ]]) + eq('', nvim('eval', 'v:errmsg')) + end) + end) + describe('nvim_strwidth', function() it('works', function() eq(3, nvim('strwidth', 'abc')) @@ -626,12 +661,12 @@ describe('API', function() -- Make any RPC request (can be non-async: op-pending does not block). nvim('get_current_buf') -- Buffer should not change. - helpers.expect([[ + expect([[ FIRST LINE SECOND LINE]]) -- Now send input to complete the operator. nvim('input', 'j') - helpers.expect([[ + expect([[ first line second line]]) end) @@ -664,7 +699,7 @@ describe('API', function() nvim('get_api_info') -- Send input to complete the mapping. nvim('input', 'd') - helpers.expect([[ + expect([[ FIRST LINE SECOND LINE]]) eq('it worked...', helpers.eval('g:foo')) @@ -680,7 +715,7 @@ describe('API', function() nvim('get_api_info') -- Send input to complete the mapping. nvim('input', 'x') - helpers.expect([[ + expect([[ FIRST LINE SECOND LINfooE]]) end) -- cgit From 5a2894d67753b408ada3b89c1b7fbd9152977203 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 01:21:27 +0200 Subject: paste: use nvim_put() --- test/functional/terminal/paste_spec.lua | 204 -------------------------------- test/functional/terminal/tui_spec.lua | 80 ++++++++++++- test/functional/ui/input_spec.lua | 71 ----------- 3 files changed, 77 insertions(+), 278 deletions(-) delete mode 100644 test/functional/terminal/paste_spec.lua (limited to 'test/functional') diff --git a/test/functional/terminal/paste_spec.lua b/test/functional/terminal/paste_spec.lua deleted file mode 100644 index 1c1f57246c..0000000000 --- a/test/functional/terminal/paste_spec.lua +++ /dev/null @@ -1,204 +0,0 @@ --- TUI tests for "bracketed paste" mode. --- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode -local helpers = require('test.functional.helpers') -local child_tui = require('test.functional.tui.child_session') -local Screen = require('test.functional.ui.screen') -local execute = helpers.execute -local nvim_dir = helpers.nvim_dir -local eval = helpers.eval -local eq = helpers.eq -local feed_tui = child_tui.feed_data - -describe('tui paste', function() - local screen - - before_each(function() - helpers.clear() - screen = child_tui.screen_setup(0, '["'..helpers.nvim_prog.. - '", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile"]') - - -- Pasting can be really slow in the TUI, especially in ASAN. - screen.timeout = 5000 - - screen:expect([[ - {1: } | - ~ | - ~ | - ~ | - [No Name] | - | - -- TERMINAL -- | - ]]) - end) - - after_each(function() - screen:detach() - end) - - local function setup_harness() - -- Delete the default PastePre/PastePost autocmds. - feed_tui(":autocmd! PastePre,PastePost\n") - - -- Set up test handlers. - feed_tui(":autocmd PastePre * ".. - "call feedkeys('iPastePre mode:'.mode(),'n')\n") - feed_tui(":autocmd PastePost * ".. - "call feedkeys('PastePost mode:'.mode(),'n')\n") - end - - it('handles long bursts of input', function() - execute('set ruler') - local t = {} - for i = 1, 3000 do - t[i] = 'item ' .. tostring(i) - end - feed_tui('i\027[200~') - feed_tui(table.concat(t, '\n')) - feed_tui('\027[201~') - screen:expect([[ - item 2997 | - item 2998 | - item 2999 | - item 3000{1: } | - [No Name] [+] 3000,10 Bot| - -- INSERT -- | - -- TERMINAL -- | - ]]) - end) - - it('raises PastePre, PastePost in normal-mode', function() - setup_harness() - - -- Send the "start paste" sequence. - feed_tui("\027[200~") - feed_tui("\npasted from terminal (1)\npasted from terminal (2)\n") - -- Send the "stop paste" sequence. - feed_tui("\027[201~") - - screen:expect([[ - PastePre mode:n | - pasted from terminal (1) | - pasted from terminal (2) | - PastePost mode:i{1: } | - [No Name] [+] | - -- INSERT -- | - -- TERMINAL -- | - ]]) - end) - - it('forwards spurious "start paste" sequence', function() - setup_harness() - -- If multiple "start paste" sequences are sent without a corresponding - -- "stop paste" sequence, only the first occurrence should be consumed. - - -- Send the "start paste" sequence. - feed_tui("\027[200~") - feed_tui("\npasted from terminal (1)\n") - -- Send spurious "start paste" sequence. - feed_tui("\027[200~") - feed_tui("\n") - -- Send the "stop paste" sequence. - feed_tui("\027[201~") - - screen:expect([[ - PastePre mode:n | - pasted from terminal (1) | - {1:^[}200~ | - PastePost mode:i{2: } | - [No Name] [+] | - -- INSERT -- | - -- TERMINAL -- | - ]], { - [1] = {foreground = 4}, - [2] = {reverse = true}, - }) - end) - - it('ignores spurious "stop paste" sequence', function() - setup_harness() - -- If "stop paste" sequence is received without a preceding "start paste" - -- sequence, it should be ignored. - - feed_tui("i") - -- Send "stop paste" sequence. - feed_tui("\027[201~") - - screen:expect([[ - {1: } | - ~ | - ~ | - ~ | - [No Name] | - -- INSERT -- | - -- TERMINAL -- | - ]]) - end) - - it('raises PastePre, PastePost in command-mode', function() - -- The default PastePre/PastePost handlers set the 'paste' option. To test, - -- we define a command-mode map, then assert that the mapping was ignored - -- during paste. - feed_tui(":cnoremap st XXX\n") - - feed_tui(":not pasted") - - -- Paste did not start, so the mapping _should_ apply. - screen:expect([[ - | - ~ | - ~ | - ~ | - [No Name] | - :not paXXXed{1: } | - -- TERMINAL -- | - ]]) - - feed_tui("\003") -- CTRL-C - feed_tui(":") - feed_tui("\027[200~") -- Send the "start paste" sequence. - feed_tui("pasted") - - -- Paste started, so the mapping should _not_ apply. - screen:expect([[ - | - ~ | - ~ | - ~ | - [No Name] | - :pasted{1: } | - -- TERMINAL -- | - ]]) - - feed_tui("\003") -- CTRL-C - feed_tui(":") - feed_tui("\027[201~") -- Send the "stop paste" sequence. - feed_tui("not pasted") - - -- Paste stopped, so the mapping _should_ apply. - screen:expect([[ - | - ~ | - ~ | - ~ | - [No Name] | - :not paXXXed{1: } | - -- TERMINAL -- | - ]]) - - end) - - -- TODO - it('sets undo-point after consecutive pastes', function() - end) - - -- TODO - it('handles missing "stop paste" sequence', function() - end) - - -- TODO: error when pasting into 'nomodifiable' buffer: - -- [error @ do_put:2656] 17043 - Failed to save undo information - it("handles 'nomodifiable' buffer gracefully", function() - end) - -end) - diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 127cd69975..0aa9dace0e 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -1,5 +1,9 @@ -- TUI acceptance tests. -- Uses :terminal as a way to send keys and assert screen state. +-- +-- "bracketed paste" terminal feature: +-- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode + local helpers = require('test.functional.helpers')(after_each) local uname = helpers.uname local thelpers = require('test.functional.terminal.helpers') @@ -159,10 +163,10 @@ describe('TUI', function() ]]) feed_data('pasted from terminal') screen:expect([[ - | pasted from terminal{1: } | {4:~ }| {4:~ }| + {4:~ }| {5:[No Name] [+] }| {3:-- INSERT --} | {3:-- TERMINAL --} | @@ -170,10 +174,10 @@ describe('TUI', function() feed_data('\027[201~') -- End paste. feed_data('\027\000') -- ESC: go to Normal mode. screen:expect([[ - | pasted from termina{1:l} | {4:~ }| {4:~ }| + {4:~ }| {5:[No Name] [+] }| | {3:-- TERMINAL --} | @@ -183,10 +187,10 @@ describe('TUI', function() it('pasting a specific amount of text #10311', function() feed_data('i\027[200~'..string.rep('z', 64)..'\027[201~') screen:expect([[ - | zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz| zzzzzzzzzzzzzz{1: } | {4:~ }| + {4:~ }| {5:[No Name] [+] }| {3:-- INSERT --} | {3:-- TERMINAL --} | @@ -211,6 +215,76 @@ describe('TUI', function() ]]) end) + it('forwards spurious "start paste" sequence', function() + -- If multiple "start paste" sequences are sent without a corresponding + -- "stop paste" sequence, only the first occurrence should be consumed. + + -- Send the "start paste" sequence. + feed_data('i\027[200~') + feed_data('\npasted from terminal (1)\n') + -- Send spurious "start paste" sequence. + feed_data('\027[200~') + feed_data('\n') + -- Send the "stop paste" sequence. + feed_data('\027[201~') + + screen:expect{grid=[[ + | + pasted from terminal (1) | + {6:^[}[200~{1: } | + {4:~ }| + {5:[No Name] [+] }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]], + attr_ids={ + [1] = {reverse = true}, + [2] = {background = tonumber('0x00000b')}, + [3] = {bold = true}, + [4] = {foreground = tonumber('0x00000c')}, + [5] = {bold = true, reverse = true}, + [6] = {foreground = tonumber('0x000051')}, + }} + end) + + it('ignores spurious "stop paste" sequence', function() + -- If "stop paste" sequence is received without a preceding "start paste" + -- sequence, it should be ignored. + feed_data('i') + -- Send "stop paste" sequence. + feed_data('\027[201~') + screen:expect([[ + {1: } | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] }| + {3:-- INSERT --} | + {3:-- TERMINAL --} | + ]]) + end) + + -- TODO + it('in normal-mode', function() + end) + + -- TODO + it('in command-mode', function() + end) + + -- TODO + it('sets undo-point after consecutive pastes', function() + end) + + -- TODO + it('handles missing "stop paste" sequence', function() + end) + + -- TODO: error when pasting into 'nomodifiable' buffer: + -- [error @ do_put:2656] 17043 - Failed to save undo information + it("handles 'nomodifiable' buffer gracefully", function() + end) + it('allows termguicolors to be set at runtime', function() screen:set_option('rgb', true) screen:set_default_attr_ids({ diff --git a/test/functional/ui/input_spec.lua b/test/functional/ui/input_spec.lua index 7b5c6aa29d..12d0e4f40b 100644 --- a/test/functional/ui/input_spec.lua +++ b/test/functional/ui/input_spec.lua @@ -110,77 +110,6 @@ describe('mappings', function() end) end) -describe('feeding large chunks of input with ', function() - local screen - before_each(function() - clear() - screen = Screen.new() - screen:attach() - 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) - end - command('doautocmd PastePre') - screen:expect([[ - ^ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - ~ | - -- INSERT (paste) -- | - ]]) - feed(table.concat(t, '')) - screen:expect([[ - item 19988 | - item 19989 | - item 19990 | - item 19991 | - item 19992 | - item 19993 | - item 19994 | - item 19995 | - item 19996 | - item 19997 | - item 19998 | - item 19999 | - item 20000^ | - -- INSERT (paste) -- | - ]]) - command('doautocmd PastePost') - screen:expect([[ - item 19988 | - item 19989 | - item 19990 | - item 19991 | - item 19992 | - item 19993 | - item 19994 | - item 19995 | - item 19996 | - item 19997 | - item 19998 | - item 19999 | - item 2000^0 | - 20000,10 Bot | - ]]) - end) -end) - describe('input utf sequences that contain CSI/K_SPECIAL', function() before_each(clear) it('ok', function() -- cgit From d303790ee751916a00a45ee91ff1cf3ab82928c8 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 16:55:26 +0200 Subject: paste: test --- test/functional/terminal/tui_spec.lua | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'test/functional') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 0aa9dace0e..4a450b2fb4 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -29,7 +29,8 @@ describe('TUI', function() before_each(function() clear() screen = thelpers.screen_setup(0, '["'..nvim_prog - ..'", "-u", "NONE", "-i", "NONE", "--cmd", "set noswapfile noshowcmd noruler undodir=. directory=. viewdir=. backupdir=."]') + ..'", "-u", "NONE", "-i", "NONE", "--cmd", "' + ..nvim_set..' laststatus=2 background=dark'..'"]') screen:expect([[ {1: } | {4:~ }| @@ -151,6 +152,7 @@ describe('TUI', function() end) it('paste: Insert mode', function() + -- "bracketed paste" feed_data('i\027[200~') screen:expect([[ {1: } | @@ -184,26 +186,30 @@ describe('TUI', function() ]]) end) - it('pasting a specific amount of text #10311', function() + it('paste: exactly 64 bytes #10311', function() + -- "bracketed paste" feed_data('i\027[200~'..string.rep('z', 64)..'\027[201~') + feed_data('\003') -- CTRL-C screen:expect([[ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz| - zzzzzzzzzzzzzz{1: } | + zzzzzzzzzzzzz{1:z} | {4:~ }| {4:~ }| {5:[No Name] [+] }| - {3:-- INSERT --} | + | {3:-- TERMINAL --} | ]]) end) - it('big burst of input (bracketed paste)', function() + it('paste: big burst of input', function() feed_command('set ruler') local t = {} for i = 1, 3000 do t[i] = 'item ' .. tostring(i) end - feed_data('i\027[200~'..table.concat(t, '\n')..'\027[201~') + local expected = table.concat(t, '\n') + -- "bracketed paste" + feed_data('i\027[200~'..expected..'\027[201~') screen:expect([[ item 2997 | item 2998 | @@ -231,8 +237,8 @@ describe('TUI', function() screen:expect{grid=[[ | pasted from terminal (1) | - {6:^[}[200~{1: } | - {4:~ }| + {6:^[}[200~ | + {1: } | {5:[No Name] [+] }| {3:-- INSERT --} | {3:-- TERMINAL --} | -- cgit From 0221a9220a2ec0691a7139c8362aba80d1f3b8ee Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 19:41:45 +0200 Subject: paste: edge-case: handle EOL at end-of-buffer This is "readfile()-style", see also ":help channel-lines". --- test/functional/terminal/tui_spec.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'test/functional') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 4a450b2fb4..6b12c1a889 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -189,20 +189,20 @@ describe('TUI', function() it('paste: exactly 64 bytes #10311', function() -- "bracketed paste" feed_data('i\027[200~'..string.rep('z', 64)..'\027[201~') - feed_data('\003') -- CTRL-C + feed_data(' end') screen:expect([[ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz| - zzzzzzzzzzzzz{1:z} | + zzzzzzzzzzzzzz end{1: } | {4:~ }| {4:~ }| {5:[No Name] [+] }| - | + {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) end) it('paste: big burst of input', function() - feed_command('set ruler') + feed_data(':set ruler\013') local t = {} for i = 1, 3000 do t[i] = 'item ' .. tostring(i) @@ -210,12 +210,13 @@ describe('TUI', function() local expected = table.concat(t, '\n') -- "bracketed paste" feed_data('i\027[200~'..expected..'\027[201~') + feed_data(' end') screen:expect([[ item 2997 | item 2998 | item 2999 | - item 3000{1: } | - {5:[No Name] [+] 3000,10 Bot}| + item 3000 end{1: } | + {5:[No Name] [+] 3000,14 Bot}| {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) -- cgit From 1fdae25b2b932439fdef9e70b42e82e3153b937a Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 22:04:46 +0200 Subject: test/tui_spec: connect to child session --- test/functional/terminal/tui_spec.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'test/functional') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 6b12c1a889..e63d47bbc2 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -25,12 +25,14 @@ if helpers.pending_win32(pending) then return end describe('TUI', function() local screen + local child_session before_each(function() clear() - screen = thelpers.screen_setup(0, '["'..nvim_prog - ..'", "-u", "NONE", "-i", "NONE", "--cmd", "' - ..nvim_set..' laststatus=2 background=dark'..'"]') + local child_server = helpers.new_pipename() + screen = thelpers.screen_setup(0, + string.format([=[["%s", "--listen", "%s", "-u", "NONE", "-i", "NONE", "--cmd", "%s laststatus=2 background=dark"]]=], + nvim_prog, child_server, nvim_set)) screen:expect([[ {1: } | {4:~ }| @@ -40,6 +42,7 @@ describe('TUI', function() | {3:-- TERMINAL --} | ]]) + child_session = helpers.connect(child_server) end) after_each(function() -- cgit From 613296936ba30ae73f3391c2e3c36096f3703c06 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 22:41:21 +0200 Subject: API: nvim_put: always PUT_CURSEND Fixes strange behavior where sometimes the buffer contents of a series of paste chunks (vim._paste) would be out-of-order. Now the tui_spec.lua screen-tests are much more reliable. But they still sometimes fail because of off-by-one cursor (caused by "typeahead race" resulting in wrong mode; fixed later in this patch-series). --- test/functional/api/vim_spec.lua | 1 + test/functional/terminal/tui_spec.lua | 17 ++++++++++++++--- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'test/functional') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 09c297940c..20046147b8 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -382,6 +382,7 @@ describe('API', function() line 1 line 2 line 3]]) + command('1') -- blockwise nvim('put', {'AA','BB'}, 'b', false) expect([[ diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index e63d47bbc2..3719af005c 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -190,9 +190,16 @@ describe('TUI', function() end) it('paste: exactly 64 bytes #10311', function() + local expected = string.rep('z', 64) -- "bracketed paste" - feed_data('i\027[200~'..string.rep('z', 64)..'\027[201~') + feed_data('i\027[200~'..expected..'\027[201~') feed_data(' end') + expected = expected..' end' + retry(nil, nil, function() + local _, buflines = child_session:request( + 'nvim_buf_get_lines', 0, 0, -1, false) + eq({expected}, buflines) + end) screen:expect([[ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz| zzzzzzzzzzzzzz end{1: } | @@ -210,9 +217,13 @@ describe('TUI', function() for i = 1, 3000 do t[i] = 'item ' .. tostring(i) end - local expected = table.concat(t, '\n') -- "bracketed paste" - feed_data('i\027[200~'..expected..'\027[201~') + feed_data('i\027[200~'..table.concat(t, '\n')..'\027[201~') + retry(nil, nil, function() + local _, buflines = child_session:request( + 'nvim_buf_get_lines', 0, 0, -1, false) + eq(t, buflines) + end) feed_data(' end') screen:expect([[ item 2997 | -- cgit From 93e5f0235b8e85423d0284231661ba4b0d7caa07 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 20 Aug 2019 23:53:13 +0200 Subject: API: nvim_put: "follow" parameter --- test/functional/api/vim_spec.lua | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'test/functional') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 20046147b8..3f3d9b74bb 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -369,34 +369,53 @@ describe('API', function() describe('nvim_put', function() it('inserts text', function() -- linewise - nvim('put', {'line 1','line 2','line 3'}, 'l', false) + nvim('put', {'line 1','line 2','line 3'}, 'l', true, true) expect([[ line 1 line 2 line 3]]) + eq({0,4,1,0}, funcs.getpos('.')) command('%delete _') -- charwise - nvim('put', {'line 1','line 2','line 3'}, 'c', false) + nvim('put', {'line 1','line 2','line 3'}, 'c', true, false) expect([[ line 1 line 2 line 3]]) - command('1') + eq({0,1,1,0}, funcs.getpos('.')) -- follow=false -- blockwise - nvim('put', {'AA','BB'}, 'b', false) + nvim('put', {'AA','BB'}, 'b', true, true) expect([[ lAAine 1 lBBine 2 line 3]]) + eq({0,2,4,0}, funcs.getpos('.')) command('%delete _') -- Empty lines list. - nvim('put', {}, 'c', false) + nvim('put', {}, 'c', true, true) + eq({0,1,1,0}, funcs.getpos('.')) expect([[]]) -- Single empty line. - nvim('put', {''}, 'c', false) + nvim('put', {''}, 'c', true, true) + eq({0,1,1,0}, funcs.getpos('.')) expect([[ ]]) + nvim('put', {'AB'}, 'c', true, true) + -- after=false, follow=true + nvim('put', {'line 1','line 2'}, 'c', false, true) + expect([[ + Aline 1 + line 2B]]) + eq({0,2,7,0}, funcs.getpos('.')) + command('%delete _') + nvim('put', {'AB'}, 'c', true, true) + -- after=false, follow=false + nvim('put', {'line 1','line 2'}, 'c', false, false) + expect([[ + Aline 1 + line 2B]]) + eq({0,1,2,0}, funcs.getpos('.')) eq('', nvim('eval', 'v:errmsg')) end) end) -- cgit From c95f5d166fad75ad8383f76675d06907687066a7 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Thu, 22 Aug 2019 00:19:46 +0200 Subject: paste: workaround typeahead race MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Workaround this failure: [ ERROR ] test/functional/terminal/tui_spec.lua @ 192: TUI paste: exactly 64 bytes test/functional/helpers.lua:403: retry() attempts: 478 test/functional/terminal/tui_spec.lua:201: Expected objects to be the same. Passed in: (table: 0x47cd77e8) { *[1] = 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz endz' } Expected: (table: 0x47cd7830) { *[1] = 'zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz end' } This happens because `curwin->w_cursor.col` is sometimes decremented at the end of `do_put`... because the editor is in Normal-mode instead of the expected Insert-mode. Caused by "typeahead race" (#10826): there may be queued input in the main thread not yet processed, thus the editor mode (`State` global) will be "wrong" during paste. Example: input "i" followed immediately by a paste sequence: i... ^ "i" does not get processed in time, so the editor is in Normal-mode instead of Insert-mode while handling the paste. Attempted workarounds: - vim.api.nvim_feedkeys('','x',false) in vim._paste() - exec_normal() in tinput_wait_enqueue() - LOOP_PROCESS_EVENTS(&main_loop,…,0) in tinput_wait_enqueue() ref #10826 --- test/functional/terminal/tui_spec.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'test/functional') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 3719af005c..adf968712d 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -191,8 +191,14 @@ describe('TUI', function() it('paste: exactly 64 bytes #10311', function() local expected = string.rep('z', 64) + feed_data('i') + -- Wait for Insert-mode (avoid "typeahead race" #10826). + retry(nil, nil, function() + local _, m = child_session:request('nvim_get_mode') + eq('i', m.mode) + end) -- "bracketed paste" - feed_data('i\027[200~'..expected..'\027[201~') + feed_data('\027[200~'..expected..'\027[201~') feed_data(' end') expected = expected..' end' retry(nil, nil, function() @@ -217,8 +223,14 @@ describe('TUI', function() for i = 1, 3000 do t[i] = 'item ' .. tostring(i) end + feed_data('i') + -- Wait for Insert-mode (avoid "typeahead race" #10826). + retry(nil, nil, function() + local _, m = child_session:request('nvim_get_mode') + eq('i', m.mode) + end) -- "bracketed paste" - feed_data('i\027[200~'..table.concat(t, '\n')..'\027[201~') + feed_data('\027[200~'..table.concat(t, '\n')..'\027[201~') retry(nil, nil, function() local _, buflines = child_session:request( 'nvim_buf_get_lines', 0, 0, -1, false) -- cgit From eacc70fb3ebae6d76112ab10647a42339f5f223f Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Aug 2019 13:54:27 +0200 Subject: API: nvim_paste --- test/functional/api/vim_spec.lua | 37 +++++++++++++++++++++++++++++++++++ test/functional/terminal/tui_spec.lua | 12 ++++++++---- 2 files changed, 45 insertions(+), 4 deletions(-) (limited to 'test/functional') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 3f3d9b74bb..212c4f4300 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -366,7 +366,44 @@ describe('API', function() end) end) + describe('nvim_paste', function() + it('validates args', function() + expect_err('Invalid phase: %-2', request, + 'nvim_paste', 'foo', -2) + expect_err('Invalid phase: 4', request, + 'nvim_paste', 'foo', 4) + end) + it('non-streaming', function() + -- With final "\n". + nvim('paste', 'line 1\nline 2\nline 3\n', -1) + expect([[ + line 1 + line 2 + line 3 + ]]) + -- Cursor follows the paste. + eq({0,4,1,0}, funcs.getpos('.')) + eq(false, nvim('get_option', 'paste')) + command('%delete _') + -- Without final "\n". + nvim('paste', 'line 1\nline 2\nline 3', -1) + expect([[ + line 1 + line 2 + line 3]]) + -- Cursor follows the paste. + eq({0,3,6,0}, funcs.getpos('.')) + eq(false, nvim('get_option', 'paste')) + end) + end) + describe('nvim_put', function() + it('validates args', function() + expect_err('Invalid lines %(expected array of strings%)', request, + 'nvim_put', {42}, 'l', false, false) + expect_err("Invalid type: 'x'", request, + 'nvim_put', {'foo'}, 'x', false, false) + end) it('inserts text', function() -- linewise nvim('put', {'line 1','line 2','line 3'}, 'l', true, true) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index adf968712d..414838444f 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -298,19 +298,23 @@ describe('TUI', function() end) -- TODO - it('in normal-mode', function() + it('paste: normal-mode', function() end) -- TODO - it('in command-mode', function() + it('paste: command-mode inserts 1 line', function() end) -- TODO - it('sets undo-point after consecutive pastes', function() + it('paste: sets undo-point after consecutive pastes', function() + end) + + it('paste: other modes', function() + -- Other modes act like CTRL-C + paste. end) -- TODO - it('handles missing "stop paste" sequence', function() + it('paste: handles missing "stop paste" sequence', function() end) -- TODO: error when pasting into 'nomodifiable' buffer: -- cgit From bfc5a18f4b6cb4bc2335440254c346d731063b46 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sat, 24 Aug 2019 14:01:09 +0200 Subject: paste: insert text "before" cursor in Insert-mode --- test/functional/terminal/tui_spec.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'test/functional') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 414838444f..e6d9dcddb9 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -156,19 +156,19 @@ describe('TUI', function() it('paste: Insert mode', function() -- "bracketed paste" - feed_data('i\027[200~') + feed_data('i""\027i\027[200~') screen:expect([[ - {1: } | + "{1:"} | {4:~ }| {4:~ }| {4:~ }| - {5:[No Name] }| + {5:[No Name] [+] }| {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) feed_data('pasted from terminal') screen:expect([[ - pasted from terminal{1: } | + "pasted from terminal{1:"} | {4:~ }| {4:~ }| {4:~ }| @@ -179,7 +179,7 @@ describe('TUI', function() feed_data('\027[201~') -- End paste. feed_data('\027\000') -- ESC: go to Normal mode. screen:expect([[ - pasted from termina{1:l} | + "pasted from termina{1:l}" | {4:~ }| {4:~ }| {4:~ }| -- cgit From 5b41070c639f979023178042bea8e5fcc8a898fe Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Sun, 25 Aug 2019 10:20:38 +0200 Subject: paste: implement redo (AKA dot-repeat) - Normal-mode redo idiom(?): prepend "i" and append ESC. - Insert-mode only needs AppendToRedobuffLit(). - Cmdline-mode: only paste the first line. --- test/functional/terminal/tui_spec.lua | 211 ++++++++++++++++++++++++++-------- 1 file changed, 165 insertions(+), 46 deletions(-) (limited to 'test/functional') diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index e6d9dcddb9..08ff06e0a5 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -49,6 +49,24 @@ describe('TUI', function() screen:detach() end) + -- Wait for mode in the child Nvim (avoid "typeahead race" #10826). + local function wait_for_mode(mode) + retry(nil, nil, function() + local _, m = child_session:request('nvim_get_mode') + eq(mode, m.mode) + end) + end + + -- Assert buffer contents in the child Nvim. + local function expect_child_buf_lines(expected) + assert(type({}) == type(expected)) + retry(nil, nil, function() + local _, buflines = child_session:request( + 'nvim_buf_get_lines', 0, 0, -1, false) + eq(expected, buflines) + end) + end + it('rapid resize #7572 #7628', function() -- Need buffer rows to provoke the behavior. feed_data(":edit test/functional/fixtures/bigfile.txt:") @@ -136,7 +154,7 @@ describe('TUI', function() ]]) end) - it('accepts ascii control sequences', function() + it('accepts ASCII control sequences', function() feed_data('i') feed_data('\022\007') -- ctrl+g feed_data('\022\022') -- ctrl+v @@ -187,25 +205,142 @@ describe('TUI', function() | {3:-- TERMINAL --} | ]]) + -- Dot-repeat/redo. + feed_data('2.') + screen:expect([[ + "pasted from terminapasted from terminalpasted fro| + m termina{1:l}l" | + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]) + -- Undo. + feed_data('u') + expect_child_buf_lines({'"pasted from terminal"'}) + feed_data('u') + expect_child_buf_lines({''}) + end) + + it('paste: normal-mode', function() + feed_data(':set ruler') + wait_for_mode('c') + feed_data('\n') + wait_for_mode('n') + local expected = {'line 1', ' line 2', 'ESC:\027 / CR: \013'} + local expected_attr = { + [3] = {bold = true}, + [4] = {foreground = tonumber('0x00000c')}, + [5] = {bold = true, reverse = true}, + [11] = {foreground = tonumber('0x000051')}, + [12] = {reverse = true, foreground = tonumber('0x000051')}, + } + -- "bracketed paste" + feed_data('\027[200~'..table.concat(expected,'\n')..'\027[201~') + screen:expect{ + grid=[[ + line 1 | + line 2 | + ESC:{11:^[} / CR: {12:^}{11:M} | + {4:~ }| + {5:[No Name] [+] 3,13-14 All}| + | + {3:-- TERMINAL --} | + ]], + attr_ids=expected_attr} + -- Dot-repeat/redo. + feed_data('.') + screen:expect{ + grid=[[ + line 2 | + ESC:{11:^[} / CR: {11:^M}line 1 | + line 2 | + ESC:{11:^[} / CR: {12:^}{11:M} | + {5:[No Name] [+] 5,13-14 Bot}| + | + {3:-- TERMINAL --} | + ]], + attr_ids=expected_attr} + -- Undo. + feed_data('u') + expect_child_buf_lines(expected) + feed_data('u') + expect_child_buf_lines({''}) + end) + + it('paste: cmdline-mode inserts 1 line', function() + feed_data('ifoo\n') -- Insert some text (for dot-repeat later). + feed_data('\027:""') -- Enter Cmdline-mode. + feed_data('\027[D') -- to place cursor between quotes. + wait_for_mode('c') + -- "bracketed paste" + feed_data('\027[200~line 1\nline 2\n\027[201~') + screen:expect{grid=[[ + foo | + | + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + :"line 1{1:"} | + {3:-- TERMINAL --} | + ]]} + -- Dot-repeat/redo. + feed_data('\027\000') + wait_for_mode('n') + feed_data('.') + screen:expect{grid=[[ + foo | + foo | + {1: } | + {4:~ }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]} + end) + + it('paste: cmdline-mode collects chunks of unfinished line', function() + local function expect_cmdline(expected) + retry(nil, nil, function() + local _, cmdline = child_session:request( + 'nvim_call_function', 'getcmdline', {}) + eq(expected, cmdline) + end) + end + feed_data('\027:""') -- Enter Cmdline-mode. + feed_data('\027[D') -- to place cursor between quotes. + wait_for_mode('c') + feed_data('\027[200~stuff 1 ') + expect_cmdline('"stuff 1 "') + -- Discards everything after the first line. + feed_data('more\nstuff 2\nstuff 3\n') + expect_cmdline('"stuff 1 more"') + feed_data('stuff 3') + expect_cmdline('"stuff 1 more"') + -- End the paste sequence. + feed_data('\027[201~') + feed_data(' typed') + expect_cmdline('"stuff 1 more typed"') + end) + + -- TODO + it('paste: other modes', function() + -- Other modes act like CTRL-C + paste. + end) + + it("paste: in 'nomodifiable' buffer", function() end) it('paste: exactly 64 bytes #10311', function() local expected = string.rep('z', 64) feed_data('i') - -- Wait for Insert-mode (avoid "typeahead race" #10826). - retry(nil, nil, function() - local _, m = child_session:request('nvim_get_mode') - eq('i', m.mode) - end) + wait_for_mode('i') -- "bracketed paste" feed_data('\027[200~'..expected..'\027[201~') feed_data(' end') expected = expected..' end' - retry(nil, nil, function() - local _, buflines = child_session:request( - 'nvim_buf_get_lines', 0, 0, -1, false) - eq({expected}, buflines) - end) + expect_child_buf_lines({expected}) screen:expect([[ zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz| zzzzzzzzzzzzzz end{1: } | @@ -218,24 +353,16 @@ describe('TUI', function() end) it('paste: big burst of input', function() - feed_data(':set ruler\013') + feed_data(':set ruler\n') local t = {} for i = 1, 3000 do t[i] = 'item ' .. tostring(i) end feed_data('i') - -- Wait for Insert-mode (avoid "typeahead race" #10826). - retry(nil, nil, function() - local _, m = child_session:request('nvim_get_mode') - eq('i', m.mode) - end) + wait_for_mode('i') -- "bracketed paste" feed_data('\027[200~'..table.concat(t, '\n')..'\027[201~') - retry(nil, nil, function() - local _, buflines = child_session:request( - 'nvim_buf_get_lines', 0, 0, -1, false) - eq(t, buflines) - end) + expect_child_buf_lines(t) feed_data(' end') screen:expect([[ item 2997 | @@ -246,9 +373,22 @@ describe('TUI', function() {3:-- INSERT --} | {3:-- TERMINAL --} | ]]) + feed_data('\027\000') -- ESC: go to Normal mode. + wait_for_mode('n') + -- Dot-repeat/redo. + feed_data('.') + screen:expect([[ + item 2997 | + item 2998 | + item 2999 | + item 3000 en{1:d}d | + {5:[No Name] [+] 5999,13 Bot}| + | + {3:-- TERMINAL --} | + ]]) end) - it('forwards spurious "start paste" sequence', function() + it('paste: forwards spurious "start paste" code', function() -- If multiple "start paste" sequences are sent without a corresponding -- "stop paste" sequence, only the first occurrence should be consumed. @@ -280,7 +420,7 @@ describe('TUI', function() }} end) - it('ignores spurious "stop paste" sequence', function() + it('paste: ignores spurious "stop paste" code', function() -- If "stop paste" sequence is received without a preceding "start paste" -- sequence, it should be ignored. feed_data('i') @@ -298,28 +438,7 @@ describe('TUI', function() end) -- TODO - it('paste: normal-mode', function() - end) - - -- TODO - it('paste: command-mode inserts 1 line', function() - end) - - -- TODO - it('paste: sets undo-point after consecutive pastes', function() - end) - - it('paste: other modes', function() - -- Other modes act like CTRL-C + paste. - end) - - -- TODO - it('paste: handles missing "stop paste" sequence', function() - end) - - -- TODO: error when pasting into 'nomodifiable' buffer: - -- [error @ do_put:2656] 17043 - Failed to save undo information - it("handles 'nomodifiable' buffer gracefully", function() + it('paste: handles missing "stop paste" code', function() end) it('allows termguicolors to be set at runtime', function() -- cgit From ed60015266356b3c0c42aa34698d9287f22fcba1 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Mon, 26 Aug 2019 20:57:57 +0200 Subject: paste: handle vim.paste() failure - Show error only once per "paste stream". - Drain remaining chunks until phase=3. - Lay groundwork for "cancel". - Constrain semantics of "cancel" to mean "client must stop"; it is unrelated to presence of error(s). --- test/functional/api/vim_spec.lua | 24 +++++++++++++++++ test/functional/terminal/tui_spec.lua | 51 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'test/functional') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 212c4f4300..01e4a3a1a0 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -395,6 +395,11 @@ describe('API', function() eq({0,3,6,0}, funcs.getpos('.')) eq(false, nvim('get_option', 'paste')) end) + it('vim.paste() failure', function() + nvim('execute_lua', 'vim._paste = (function(lines, phase) error("fake fail") end)', {}) + expect_err([[Error executing lua: %[string "%"]:1: fake fail]], + request, 'nvim_paste', 'line 1\nline 2\nline 3', 1) + end) end) describe('nvim_put', function() @@ -455,6 +460,25 @@ describe('API', function() eq({0,1,2,0}, funcs.getpos('.')) eq('', nvim('eval', 'v:errmsg')) end) + + it('detects charwise/linewise text (empty {type})', function() + -- linewise (final item is empty string) + nvim('put', {'line 1','line 2','line 3',''}, '', true, true) + expect([[ + + line 1 + line 2 + line 3]]) + eq({0,4,1,0}, funcs.getpos('.')) + command('%delete _') + -- charwise (final item is non-empty) + nvim('put', {'line 1','line 2','line 3'}, '', true, true) + expect([[ + line 1 + line 2 + line 3]]) + eq({0,3,6,0}, funcs.getpos('.')) + end) end) describe('nvim_strwidth', function() diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 08ff06e0a5..e7db39b5d0 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -324,6 +324,57 @@ describe('TUI', function() expect_cmdline('"stuff 1 more typed"') end) + it('paste: recovers from vim.paste() failure', function() + child_session:request('nvim_execute_lua', [[ + _G.save_paste_fn = vim._paste + vim._paste = function(lines, phase) error("fake fail") end + ]], {}) + -- Start pasting... + feed_data('\027[200~line 1\nline 2\n') + screen:expect{grid=[[ + | + {4:~ }| + {4:~ }| + {5: }| + {8:paste: Error executing lua: [string ""]:2: f}| + {10:Press ENTER or type command to continue}{1: } | + {3:-- TERMINAL --} | + ]]} + -- Remaining chunks are discarded after vim.paste() failure. + feed_data('line 3\nline 4\n') + feed_data('line 5\nline 6\n') + feed_data('line 7\nline 8\n') + -- Stop paste. + feed_data('\027[201~') + feed_data('\n') -- + -- Editor should still work after failed/drained paste. + feed_data('ityped input...\027\000') + screen:expect{grid=[[ + typed input..{1:.} | + {4:~ }| + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]} + -- Paste works if vim.paste() succeeds. + child_session:request('nvim_execute_lua', [[ + vim._paste = _G.save_paste_fn + ]], {}) + feed_data('\027[200~line A\nline B\n\027[201~') + feed_data('\n') -- + screen:expect{grid=[[ + typed input...line A | + line B | + {1: } | + {4:~ }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]} + end) + -- TODO it('paste: other modes', function() -- Other modes act like CTRL-C + paste. -- cgit From 87389c6a57cf9fa91746503c479cdbea348030b9 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 27 Aug 2019 05:19:25 +0200 Subject: paste: make vim.paste() "public" --- test/functional/api/vim_spec.lua | 2 +- test/functional/terminal/tui_spec.lua | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'test/functional') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 01e4a3a1a0..47a04795f8 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -396,7 +396,7 @@ describe('API', function() eq(false, nvim('get_option', 'paste')) end) it('vim.paste() failure', function() - nvim('execute_lua', 'vim._paste = (function(lines, phase) error("fake fail") end)', {}) + nvim('execute_lua', 'vim.paste = (function(lines, phase) error("fake fail") end)', {}) expect_err([[Error executing lua: %[string "%"]:1: fake fail]], request, 'nvim_paste', 'line 1\nline 2\nline 3', 1) end) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index e7db39b5d0..aafbbc04b1 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -326,8 +326,8 @@ describe('TUI', function() it('paste: recovers from vim.paste() failure', function() child_session:request('nvim_execute_lua', [[ - _G.save_paste_fn = vim._paste - vim._paste = function(lines, phase) error("fake fail") end + _G.save_paste_fn = vim.paste + vim.paste = function(lines, phase) error("fake fail") end ]], {}) -- Start pasting... feed_data('\027[200~line 1\nline 2\n') @@ -360,7 +360,7 @@ describe('TUI', function() ]]} -- Paste works if vim.paste() succeeds. child_session:request('nvim_execute_lua', [[ - vim._paste = _G.save_paste_fn + vim.paste = _G.save_paste_fn ]], {}) feed_data('\027[200~line A\nline B\n\027[201~') feed_data('\n') -- -- cgit From 46aa254bf30d567bd2da4fbfab33bbdcbb111a37 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 27 Aug 2019 05:19:32 +0200 Subject: paste: handle 'nomodifiable' - nvim_paste(): Marshal through luaeval() instead of nvim_execute_lua() because the latter seems to hide some errors. - Handle 'nomodifiable' in `nvim_put()` explicitly. - Require explicit `false` from `vim.paste()` in order to "cancel", otherwise assume true ("continue"). --- test/functional/api/vim_spec.lua | 5 +++ test/functional/terminal/tui_spec.lua | 61 +++++++++++++++++++++++++++++++---- 2 files changed, 59 insertions(+), 7 deletions(-) (limited to 'test/functional') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 47a04795f8..884e07e2c5 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -409,6 +409,11 @@ describe('API', function() expect_err("Invalid type: 'x'", request, 'nvim_put', {'foo'}, 'x', false, false) end) + it("fails if 'nomodifiable'", function() + command('set nomodifiable') + expect_err([[Buffer is not 'modifiable']], request, + 'nvim_put', {'a','b'}, 'l', true, true) + end) it('inserts text', function() -- linewise nvim('put', {'line 1','line 2','line 3'}, 'l', true, true) diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index aafbbc04b1..7435b293fd 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -329,14 +329,27 @@ describe('TUI', function() _G.save_paste_fn = vim.paste vim.paste = function(lines, phase) error("fake fail") end ]], {}) + -- Prepare something for dot-repeat/redo. + feed_data('ifoo\n\027\000') + wait_for_mode('n') + screen:expect{grid=[[ + foo | + {1: } | + {4:~ }| + {4:~ }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]} + wait_for_mode('n') -- Start pasting... feed_data('\027[200~line 1\nline 2\n') screen:expect{grid=[[ + foo | | {4:~ }| - {4:~ }| {5: }| - {8:paste: Error executing lua: [string ""]:2: f}| + {8:paste: Vim:E5108: Error while calling lua chunk fo}| {10:Press ENTER or type command to continue}{1: } | {3:-- TERMINAL --} | ]]} @@ -347,13 +360,24 @@ describe('TUI', function() -- Stop paste. feed_data('\027[201~') feed_data('\n') -- + --Dot-repeat/redo is not modified by failed paste. + feed_data('.') + screen:expect{grid=[[ + foo | + foo | + {1: } | + {4:~ }| + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]} -- Editor should still work after failed/drained paste. feed_data('ityped input...\027\000') screen:expect{grid=[[ + foo | + foo | typed input..{1:.} | {4:~ }| - {4:~ }| - {4:~ }| {5:[No Name] [+] }| | {3:-- TERMINAL --} | @@ -365,9 +389,35 @@ describe('TUI', function() feed_data('\027[200~line A\nline B\n\027[201~') feed_data('\n') -- screen:expect{grid=[[ + foo | typed input...line A | line B | {1: } | + {5:[No Name] [+] }| + | + {3:-- TERMINAL --} | + ]]} + end) + + it("paste: 'nomodifiable' buffer", function() + child_session:request('nvim_command', 'set nomodifiable') + feed_data('\027[200~fail 1\nfail 2\n\027[201~') + screen:expect{grid=[[ + | + {5: }| + {8:paste: Vim:E5108: Error while calling lua chunk fo}| + {8:r luaeval(): [string "-- Nvim-Lua stdlib: the `vim}| + {8:` module (:help l..."]:193: Buffer is not 'modifia}| + {10:Press ENTER or type command to continue}{1: } | + {3:-- TERMINAL --} | + ]]} + feed_data('\n') -- + child_session:request('nvim_command', 'set modifiable') + feed_data('\027[200~success 1\nsuccess 2\n\027[201~') + screen:expect{grid=[[ + success 1 | + success 2 | + {1: } | {4:~ }| {5:[No Name] [+] }| | @@ -380,9 +430,6 @@ describe('TUI', function() -- Other modes act like CTRL-C + paste. end) - it("paste: in 'nomodifiable' buffer", function() - end) - it('paste: exactly 64 bytes #10311', function() local expected = string.rep('z', 64) feed_data('i') -- cgit From 3157baed83b7e94f2ff92e6fd97e85dab41a1c94 Mon Sep 17 00:00:00 2001 From: "Justin M. Keyes" Date: Tue, 27 Aug 2019 05:19:36 +0200 Subject: API: TRY_WRAP() for "abort-causing non-exception errors" - Introduce TRY_WRAP() until we have an *architectural* solution. - TODO: bfredl idea: prepare error-handling at "top level" (nv_event). - nvim_paste(): Revert luaeval() hack (see parent commit). - With TRY_WRAP() in nvim_put(), 'nomodifiable' error now correctly "bubbles up". --- test/functional/api/vim_spec.lua | 2 +- test/functional/terminal/tui_spec.lua | 26 +++++++------------------- 2 files changed, 8 insertions(+), 20 deletions(-) (limited to 'test/functional') diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 884e07e2c5..647fab5c43 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -411,7 +411,7 @@ describe('API', function() end) it("fails if 'nomodifiable'", function() command('set nomodifiable') - expect_err([[Buffer is not 'modifiable']], request, + expect_err([[Vim:E21: Cannot make changes, 'modifiable' is off]], request, 'nvim_put', {'a','b'}, 'l', true, true) end) it('inserts text', function() diff --git a/test/functional/terminal/tui_spec.lua b/test/functional/terminal/tui_spec.lua index 7435b293fd..5445ff0127 100644 --- a/test/functional/terminal/tui_spec.lua +++ b/test/functional/terminal/tui_spec.lua @@ -185,6 +185,7 @@ describe('TUI', function() {3:-- TERMINAL --} | ]]) feed_data('pasted from terminal') + expect_child_buf_lines({'"pasted from terminal"'}) screen:expect([[ "pasted from terminal{1:"} | {4:~ }| @@ -196,6 +197,7 @@ describe('TUI', function() ]]) feed_data('\027[201~') -- End paste. feed_data('\027\000') -- ESC: go to Normal mode. + wait_for_mode('n') screen:expect([[ "pasted from termina{1:l}" | {4:~ }| @@ -207,6 +209,8 @@ describe('TUI', function() ]]) -- Dot-repeat/redo. feed_data('2.') + expect_child_buf_lines( + {'"pasted from terminapasted from terminalpasted from terminall"'}) screen:expect([[ "pasted from terminapasted from terminalpasted fro| m termina{1:l}l" | @@ -341,18 +345,10 @@ describe('TUI', function() | {3:-- TERMINAL --} | ]]} - wait_for_mode('n') -- Start pasting... feed_data('\027[200~line 1\nline 2\n') - screen:expect{grid=[[ - foo | - | - {4:~ }| - {5: }| - {8:paste: Vim:E5108: Error while calling lua chunk fo}| - {10:Press ENTER or type command to continue}{1: } | - {3:-- TERMINAL --} | - ]]} + wait_for_mode('n') + screen:expect{any='paste: Error executing lua'} -- Remaining chunks are discarded after vim.paste() failure. feed_data('line 3\nline 4\n') feed_data('line 5\nline 6\n') @@ -402,15 +398,7 @@ describe('TUI', function() it("paste: 'nomodifiable' buffer", function() child_session:request('nvim_command', 'set nomodifiable') feed_data('\027[200~fail 1\nfail 2\n\027[201~') - screen:expect{grid=[[ - | - {5: }| - {8:paste: Vim:E5108: Error while calling lua chunk fo}| - {8:r luaeval(): [string "-- Nvim-Lua stdlib: the `vim}| - {8:` module (:help l..."]:193: Buffer is not 'modifia}| - {10:Press ENTER or type command to continue}{1: } | - {3:-- TERMINAL --} | - ]]} + screen:expect{any='Vim:E21'} feed_data('\n') -- child_session:request('nvim_command', 'set modifiable') feed_data('\027[200~success 1\nsuccess 2\n\027[201~') -- cgit