fix(paste): don't move cursor past the end of pasted text in Normal mode

This commit is contained in:
zeertzjq 2022-03-06 06:56:24 +08:00
parent 9b1e1fbc9f
commit 2601e0873f
3 changed files with 61 additions and 17 deletions

View File

@ -171,27 +171,34 @@ do
vim.api.nvim_input(line1) vim.api.nvim_input(line1)
vim.api.nvim_set_option('paste', false) vim.api.nvim_set_option('paste', false)
elseif not is_cmdline then elseif not is_cmdline then
if phase < 2 and mode:find('^[vV\22sS\19]') then if mode:find('^i') or mode:find('^n?t') then -- Insert mode or Terminal buffer
vim.api.nvim_command([[exe "normal! \<Del>"]])
vim.api.nvim_put(lines, 'c', false, true) vim.api.nvim_put(lines, 'c', false, true)
elseif phase < 2 and not mode:find('^[iRt]') then elseif phase < 2 and mode:find('^R') and not mode:find('^Rv') then -- Replace mode
vim.api.nvim_put(lines, 'c', true, true) -- TODO: implement Replace mode streamed pasting
-- XXX: Normal-mode: workaround bad cursor-placement after first chunk. -- TODO: support Virtual Replace mode
vim.api.nvim_command('normal! a')
elseif phase < 2 and mode:find('^R') then
local nchars = 0 local nchars = 0
for _, line in ipairs(lines) do for _, line in ipairs(lines) do
nchars = nchars + line:len() nchars = nchars + line:len()
end end
local row, col = unpack(vim.api.nvim_win_get_cursor(0)) local row, col = unpack(vim.api.nvim_win_get_cursor(0))
local bufline = vim.api.nvim_buf_get_lines(0, row-1, row, true)[1] local bufline = vim.api.nvim_buf_get_lines(0, row-1, row, true)[1]
local firstline = lines[1] local firstline = lines[1]
firstline = bufline:sub(1, col)..firstline firstline = bufline:sub(1, col)..firstline
lines[1] = firstline lines[1] = firstline
-- FIXME: #lines can be 0
lines[#lines] = lines[#lines]..bufline:sub(col + nchars + 1, bufline:len()) lines[#lines] = lines[#lines]..bufline:sub(col + nchars + 1, bufline:len())
vim.api.nvim_buf_set_lines(0, row-1, row, false, lines) vim.api.nvim_buf_set_lines(0, row-1, row, false, lines)
else elseif mode:find('^[nvV\22sS\19]') then -- Normal or Visual or Select mode
vim.api.nvim_put(lines, 'c', false, true) if mode:find('^n') then -- Normal mode
vim.api.nvim_put(lines, 'c', true, false)
else -- Visual or Select mode
vim.api.nvim_command([[exe "normal! \<Del>"]])
vim.api.nvim_put(lines, 'c', false, false)
end
-- put cursor at the end of the text instead of one character after it
vim.fn.setpos('.', vim.fn.getpos("']"))
else -- Don't know what to do in other modes
return false
end end
end end
if phase ~= -1 and (now - tdots >= 100) then if phase ~= -1 and (now - tdots >= 100) then

View File

@ -665,6 +665,43 @@ describe('API', function()
feed('u') -- Undo. feed('u') -- Undo.
expect(expected1) expect(expected1)
end) end)
it('stream: Insert mode', function()
feed('i')
nvim('paste', 'aaaaaa', false, 1)
nvim('paste', 'bbbbbb', false, 2)
nvim('paste', 'cccccc', false, 2)
nvim('paste', 'dddddd', false, 3)
expect('aaaaaabbbbbbccccccdddddd')
end)
it('stream: Normal mode on empty line', function()
nvim('paste', 'aaaaaa', false, 1)
nvim('paste', 'bbbbbb', false, 2)
nvim('paste', 'cccccc', false, 2)
nvim('paste', 'dddddd', false, 3)
expect('aaaaaabbbbbbccccccdddddd')
feed('u')
expect('')
end)
it('stream: Normal mode not at the end of a line', function()
feed('i||<Esc>0')
nvim('paste', 'aaaaaa', false, 1)
nvim('paste', 'bbbbbb', false, 2)
nvim('paste', 'cccccc', false, 2)
nvim('paste', 'dddddd', false, 3)
expect('|aaaaaabbbbbbccccccdddddd|')
feed('u')
expect('||')
end)
it('stream: Normal mode at the end of a line', function()
feed('i||<Esc>')
nvim('paste', 'aaaaaa', false, 1)
nvim('paste', 'bbbbbb', false, 2)
nvim('paste', 'cccccc', false, 2)
nvim('paste', 'dddddd', false, 3)
expect('||aaaaaabbbbbbccccccdddddd')
feed('u')
expect('||')
end)
it('non-streaming', function() it('non-streaming', function()
-- With final "\n". -- With final "\n".
nvim('paste', 'line 1\nline 2\nline 3\n', true, -1) nvim('paste', 'line 1\nline 2\nline 3\n', true, -1)

View File

@ -323,7 +323,7 @@ describe('TUI', function()
feed_data('just paste it™') feed_data('just paste it™')
feed_data('\027[201~') feed_data('\027[201~')
screen:expect{grid=[[ screen:expect{grid=[[
thisjust paste it{1:3} is here | thisjust paste it{1:}3 is here |
| |
{4:~ }| {4:~ }|
{4:~ }| {4:~ }|
@ -379,7 +379,7 @@ describe('TUI', function()
end) end)
it('paste: normal-mode (+CRLF #10872)', function() it('paste: normal-mode (+CRLF #10872)', function()
feed_data(':set ruler') feed_data(':set ruler | echo')
wait_for_mode('c') wait_for_mode('c')
feed_data('\n') feed_data('\n')
wait_for_mode('n') wait_for_mode('n')
@ -423,13 +423,13 @@ describe('TUI', function()
expect_child_buf_lines(expected_crlf) expect_child_buf_lines(expected_crlf)
feed_data('u') feed_data('u')
expect_child_buf_lines({''}) expect_child_buf_lines({''})
feed_data(':echo')
wait_for_mode('c')
feed_data('\n')
wait_for_mode('n')
-- CRLF input -- CRLF input
feed_data('\027[200~'..table.concat(expected_lf,'\r\n')..'\027[201~') feed_data('\027[200~'..table.concat(expected_lf,'\r\n')..'\027[201~')
screen:expect{ screen:expect{grid=expected_grid1, attr_ids=expected_attr}
grid=expected_grid1:gsub(
':set ruler *',
'3 fewer lines; before #1 0 seconds ago '),
attr_ids=expected_attr}
expect_child_buf_lines(expected_crlf) expect_child_buf_lines(expected_crlf)
end) end)