neovim/test/functional/editor/mode_insert_spec.lua
bfredl e61228a214 fix(tests): needing two calls to setup a screen is cringe
Before calling "attach" a screen object is just a dummy container for
(row, col) values whose purpose is to be sent as part of the "attach"
function call anyway.

Just create the screen in an attached state directly. Keep the complete
(row, col, options) config together. It is still completely valid to
later detach and re-attach as needed, including to another session.
2024-11-14 12:40:57 +01:00

438 lines
13 KiB
Lua
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- Insert-mode tests.
local t = require('test.testutil')
local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local clear, feed, insert = n.clear, n.feed, n.insert
local expect = n.expect
local command = n.command
local eq = t.eq
local eval = n.eval
local curbuf_contents = n.curbuf_contents
local api = n.api
describe('insert-mode', function()
before_each(function()
clear()
end)
it('indents only once after "!" keys #12894', function()
command('let counter = []')
command('set indentexpr=len(add(counter,0))')
feed('i<C-F>x')
eq(' x', curbuf_contents())
end)
it('CTRL-@', function()
-- Inserts last-inserted text, leaves insert-mode.
insert('hello')
feed('i<C-@>x')
expect('hellhello')
-- C-Space is the same as C-@.
-- CTRL-SPC inserts last-inserted text, leaves insert-mode.
feed('i<C-Space>x')
expect('hellhellhello')
-- CTRL-A inserts last inserted text
feed('i<C-A>x')
expect('hellhellhellhelloxo')
end)
describe('Ctrl-R', function()
it('works', function()
command("let @@ = 'test'")
feed('i<C-r>"')
expect('test')
end)
it('works with multi-byte text', function()
command("let @@ = 'påskägg'")
feed('i<C-r>"')
expect('påskägg')
end)
it('double quote is removed after hit-enter prompt #22609', function()
local screen = Screen.new(50, 6)
feed('i<C-R>')
screen:expect([[
{18:^"} |
{1:~ }|*4
{5:-- INSERT --} |
]])
feed("=function('add')")
screen:expect([[
{18:"} |
{1:~ }|*4
={25:function}{16:(}{26:'add'}{16:)}^ |
]])
feed('<CR>')
screen:expect([[
{18:"} |
{1:~ }|
{3: }|
={25:function}{16:(}{26:'add'}{16:)} |
{9:E729: Using a Funcref as a String} |
{6:Press ENTER or type command to continue}^ |
]])
feed('<CR>')
screen:expect([[
^ |
{1:~ }|*4
{5:-- INSERT --} |
]])
end)
end)
describe('Ctrl-O', function()
it('enters command mode for one command', function()
feed('ihello world<C-o>')
feed(':let ctrlo = "test"<CR>')
feed('iii')
expect('hello worldiii')
eq(1, eval('ctrlo ==# "test"'))
end)
it('re-enters insert mode at the end of the line when running startinsert', function()
-- #6962
feed('ihello world<C-o>')
feed(':startinsert<CR>')
feed('iii')
expect('hello worldiii')
end)
it('re-enters insert mode at the beginning of the line when running startinsert', function()
insert('hello world')
feed('0<C-o>')
feed(':startinsert<CR>')
feed('aaa')
expect('aaahello world')
end)
it('re-enters insert mode in the middle of the line when running startinsert', function()
insert('hello world')
feed('bi<C-o>')
feed(':startinsert<CR>')
feed('ooo')
expect('hello oooworld')
end)
end)
describe('Ctrl-V', function()
it('supports entering the decimal value of a character', function()
feed('i<C-V>076<C-V>167')
expect('')
end)
it('supports entering the octal value of a character with "o"', function()
feed('i<C-V>o114<C-V>o247<Esc>')
expect('')
end)
it('supports entering the octal value of a character with "O"', function()
feed('i<C-V>O114<C-V>O247<Esc>')
expect('')
end)
it('supports entering the hexadecimal value of a character with "x"', function()
feed('i<C-V>x4c<C-V>xA7<Esc>')
expect('')
end)
it('supports entering the hexadecimal value of a character with "X"', function()
feed('i<C-V>X4c<C-V>XA7<Esc>')
expect('')
end)
it('supports entering the hexadecimal value of a character with "u"', function()
feed('i<C-V>u25ba<C-V>u25C7<Esc>')
expect('►◇')
end)
it('supports entering the hexadecimal value of a character with "U"', function()
feed('i<C-V>U0001f600<C-V>U0001F601<Esc>')
expect('😀😁')
end)
it('entering character by value is interrupted by invalid character', function()
feed('i<C-V>76c<C-V>76<C-F2><C-V>u3c0j<C-V>u3c0<M-F3><C-V>U1f600j<C-V>U1f600<D-F4><Esc>')
expect('LcL<C-F2>πjπ<M-F3>😀j😀<D-F4>')
end)
it('shows o, O, u, U, x, X, and digits with modifiers', function()
feed('i<C-V><M-o><C-V><D-o><C-V><M-O><C-V><D-O><Esc>')
expect('<M-o><D-o><M-O><D-O>')
feed('cc<C-V><M-u><C-V><D-u><C-V><M-U><C-V><D-U><Esc>')
expect('<M-u><D-u><M-U><D-U>')
feed('cc<C-V><M-x><C-V><D-x><C-V><M-X><C-V><D-X><Esc>')
expect('<M-x><D-x><M-X><D-X>')
feed('cc<C-V><M-1><C-V><D-2><C-V><M-7><C-V><D-8><Esc>')
expect('<M-1><D-2><M-7><D-8>')
end)
end)
it('Ctrl-Shift-V supports entering unsimplified key notations', function()
feed('i<C-S-V><C-J><C-S-V><C-@><C-S-V><C-[><C-S-V><C-S-M><C-S-V><M-C-I><C-S-V><C-D-J><Esc>')
expect('<C-J><C-@><C-[><C-S-M><M-C-I><C-D-J>')
end)
it('multi-char mapping updates screen properly #25626', function()
local screen = Screen.new(60, 6)
command('vnew')
insert('foo\nfoo\nfoo')
command('wincmd w')
command('set timeoutlen=10000')
command('inoremap jk <Esc>')
feed('i<CR>βββ<Left><Left>j')
screen:expect {
grid = [[
foo │ |
foo │β^jβ |
foo │{1:~ }|
{1:~ }│{1:~ }|
{2:[No Name] [+] }{3:[No Name] [+] }|
{5:-- INSERT --} |
]],
}
feed('k')
screen:expect {
grid = [[
foo │ |
foo │^βββ |
foo │{1:~ }|
{1:~ }│{1:~ }|
{2:[No Name] [+] }{3:[No Name] [+] }|
|
]],
}
end)
describe('backspace', function()
local function set_lines(line_b, line_e, ...)
api.nvim_buf_set_lines(0, line_b, line_e, true, { ... })
end
local function s(count)
return (' '):rep(count)
end
local function test_cols(expected_cols)
local cols = { { n.fn.col('.'), n.fn.virtcol('.') } }
for _ = 2, #expected_cols do
feed('<BS>')
table.insert(cols, { n.fn.col('.'), n.fn.virtcol('.') })
end
eq(expected_cols, cols)
end
it('works with tabs and spaces', function()
local _ = Screen.new(30, 2)
command('setl ts=4 sw=4')
set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a')
feed('$i')
test_cols({
{ 18, 26 },
{ 17, 25 },
{ 15, 21 },
{ 11, 17 },
{ 7, 13 },
{ 6, 9 },
{ 2, 5 },
{ 1, 1 },
})
end)
it('works with varsofttabstop', function()
local _ = Screen.new(30, 2)
command('setl vsts=6,2,5,3')
set_lines(0, 1, 'a\t' .. s(4) .. '\t a')
feed('$i')
test_cols({
{ 9, 18 },
{ 8, 17 },
{ 8, 14 },
{ 3, 9 },
{ 7, 7 },
{ 2, 2 },
{ 1, 1 },
})
end)
it('works with tab as ^I', function()
local _ = Screen.new(30, 2)
command('set list listchars=space:.')
command('setl ts=4 sw=4')
set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a')
feed('$i')
test_cols({
{ 18, 21 },
{ 15, 17 },
{ 11, 13 },
{ 7, 9 },
{ 4, 5 },
{ 1, 1 },
})
end)
it('works in replace mode', function()
local _ = Screen.new(50, 2)
command('setl ts=8 sw=8 sts=8')
set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a')
feed('$R')
test_cols({
{ 18, 34 },
{ 17, 33 },
{ 15, 25 },
{ 7, 17 },
{ 2, 9 },
{ 1, 8 }, -- last screen cell of first tab is at vcol 8
})
end)
it('works with breakindent', function()
local _ = Screen.new(17, 4)
command('setl ts=4 sw=4 bri briopt=min:5')
set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a')
feed('$i')
test_cols({
{ 18, 50 },
{ 17, 49 },
{ 15, 33 },
{ 11, 17 },
{ 7, 13 },
{ 6, 9 },
{ 2, 5 },
{ 1, 1 },
})
end)
it('works with inline virtual text', function()
local _ = Screen.new(50, 2)
command('setl ts=4 sw=4')
set_lines(0, 1, '\t' .. s(4) .. '\t' .. s(9) .. '\t a')
local ns = api.nvim_create_namespace('')
local vt_opts = { virt_text = { { 'text' } }, virt_text_pos = 'inline' }
api.nvim_buf_set_extmark(0, ns, 0, 2, vt_opts)
feed('$i')
test_cols({
{ 18, 30 },
{ 17, 29 },
{ 15, 25 },
{ 11, 21 },
{ 7, 17 },
{ 6, 13 },
{ 2, 9 },
{ 1, 5 },
})
end)
it("works with 'revins'", function()
local _ = Screen.new(30, 3)
command('setl ts=4 sw=4 revins')
set_lines(0, 1, ('a'):rep(16), s(3) .. '\t' .. s(4) .. '\t a')
feed('j$i')
test_cols({
{ 11, 14 },
{ 10, 13 },
{ 9, 9 },
{ 5, 5 },
{ 1, 1 },
{ 1, 1 }, -- backspace on empty line does nothing
})
eq(2, api.nvim_win_get_cursor(0)[1])
end)
end)
it('backspace after replacing multibyte chars', function()
local screen = Screen.new(30, 3)
api.nvim_buf_set_lines(0, 0, -1, true, { 'test ȧ̟̜̝̅̚m̆̉̐̐̇̈ å' })
feed('^Rabcdefghi')
screen:expect([[
abcdefghi^ |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('<bs>')
screen:expect([[
abcdefgh^å |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('<bs>')
screen:expect([[
abcdefg^ å |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('<bs>')
screen:expect([[
abcdef^m̆̉̐̐̇̈ å |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('<bs>')
screen:expect([[
abcde^ȧ̟̜̝̅̚m̆̉̐̐̇̈ å |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('<bs>')
screen:expect([[
abcd^ ȧ̟̜̝̅̚m̆̉̐̐̇̈ å |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('<esc>')
api.nvim_buf_set_lines(0, 0, -1, true, { 'wow 🧑🌾🏳x' })
feed('^Rabcd')
screen:expect([[
abcd^🧑🌾🏳x |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('e')
screen:expect([[
abcde^🏳x |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('f')
screen:expect([[
abcdef^x |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('<bs>')
screen:expect([[
abcde^🏳x |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('<bs>')
screen:expect([[
abcd^🧑🌾🏳x |
{1:~ }|
{5:-- REPLACE --} |
]])
feed('<bs>')
screen:expect([[
abc^ 🧑🌾🏳x |
{1:~ }|
{5:-- REPLACE --} |
]])
end)
end)