-- 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('ix') eq(' x', curbuf_contents()) end) it('CTRL-@', function() -- Inserts last-inserted text, leaves insert-mode. insert('hello') feed('ix') expect('hellhello') -- C-Space is the same as C-@. -- CTRL-SPC inserts last-inserted text, leaves insert-mode. feed('ix') expect('hellhellhello') -- CTRL-A inserts last inserted text feed('ix') expect('hellhellhellhelloxo') end) describe('Ctrl-R', function() it('works', function() command("let @@ = 'test'") feed('i"') expect('test') end) it('works with multi-byte text', function() command("let @@ = 'påskägg'") feed('i"') expect('påskägg') end) it('double quote is removed after hit-enter prompt #22609', function() local screen = Screen.new(50, 6) feed('i') screen:expect([[ {18:^"} | {1:~ }|*4 {5:-- INSERT --} | ]]) feed("=function('add')") screen:expect([[ {18:"} | {1:~ }|*4 ={25:function}{16:(}{26:'add'}{16:)}^ | ]]) feed('') 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('') screen:expect([[ ^ | {1:~ }|*4 {5:-- INSERT --} | ]]) end) end) describe('Ctrl-O', function() it('enters command mode for one command', function() feed('ihello world') feed(':let ctrlo = "test"') 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') feed(':startinsert') 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') feed(':startinsert') 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') feed(':startinsert') feed('ooo') expect('hello oooworld') end) end) describe('Ctrl-V', function() it('supports entering the decimal value of a character', function() feed('i076167') expect('L§') end) it('supports entering the octal value of a character with "o"', function() feed('io114o247') expect('L§') end) it('supports entering the octal value of a character with "O"', function() feed('iO114O247') expect('L§') end) it('supports entering the hexadecimal value of a character with "x"', function() feed('ix4cxA7') expect('L§') end) it('supports entering the hexadecimal value of a character with "X"', function() feed('iX4cXA7') expect('L§') end) it('supports entering the hexadecimal value of a character with "u"', function() feed('iu25bau25C7') expect('►◇') end) it('supports entering the hexadecimal value of a character with "U"', function() feed('iU0001f600U0001F601') expect('😀😁') end) it('entering character by value is interrupted by invalid character', function() feed('i76c76u3c0ju3c0U1f600jU1f600') expect('LcLπjπ😀j😀') end) it('shows o, O, u, U, x, X, and digits with modifiers', function() feed('i') expect('') feed('cc') expect('') feed('cc') expect('') feed('cc') expect('') end) end) it('Ctrl-Shift-V supports entering unsimplified key notations', function() feed('i') expect('') 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 ') feed('iβββ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('') 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('') screen:expect([[ abcdefgh^å | {1:~ }| {5:-- REPLACE --} | ]]) feed('') screen:expect([[ abcdefg^ å | {1:~ }| {5:-- REPLACE --} | ]]) feed('') screen:expect([[ abcdef^m̆̉̐̐̇̈ å | {1:~ }| {5:-- REPLACE --} | ]]) feed('') screen:expect([[ abcde^ȧ̟̜̝̅̚m̆̉̐̐̇̈ å | {1:~ }| {5:-- REPLACE --} | ]]) feed('') screen:expect([[ abcd^ ȧ̟̜̝̅̚m̆̉̐̐̇̈ å | {1:~ }| {5:-- REPLACE --} | ]]) feed('') 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('') screen:expect([[ abcde^🏳️‍⚧️x | {1:~ }| {5:-- REPLACE --} | ]]) feed('') screen:expect([[ abcd^🧑‍🌾🏳️‍⚧️x | {1:~ }| {5:-- REPLACE --} | ]]) feed('') screen:expect([[ abc^ 🧑‍🌾🏳️‍⚧️x | {1:~ }| {5:-- REPLACE --} | ]]) end) end)