-- Insert-mode tests. 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 expect = helpers.expect local command = helpers.command local eq = helpers.eq local eval = helpers.eval local curbuf_contents = helpers.curbuf_contents local api = helpers.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(60, 6) screen:set_default_attr_ids({ [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText [1] = { foreground = Screen.colors.Blue }, -- SpecialKey [2] = { foreground = Screen.colors.SlateBlue }, [3] = { bold = true }, -- ModeMsg [4] = { reverse = true, bold = true }, -- MsgSeparator [5] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg [6] = { foreground = Screen.colors.SeaGreen, bold = true }, -- MoreMsg }) screen:attach() feed('i') screen:expect([[ {1:^"} | {0:~ }|*4 {3:-- INSERT --} | ]]) feed('={}') screen:expect([[ {1:"} | {0:~ }|*4 ={2:{}}^ | ]]) feed('') screen:expect([[ {1:"} | {0:~ }| {4: }| ={2:{}} | {5:E731: Using a Dictionary as a String} | {6:Press ENTER or type command to continue}^ | ]]) feed('') screen:expect([[ ^ | {0:~ }|*4 {3:-- 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) screen:set_default_attr_ids({ [0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText [1] = { bold = true, reverse = true }, -- StatusLine [2] = { reverse = true }, -- StatusLineNC [3] = { bold = true }, -- ModeMsg }) screen:attach() 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 │{0:~ }| {0:~ }│{0:~ }| {2:[No Name] [+] }{1:[No Name] [+] }| {3:-- INSERT --} | ]], } feed('k') screen:expect { grid = [[ foo │ | foo │^βββ | foo │{0:~ }| {0:~ }│{0:~ }| {2:[No Name] [+] }{1:[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 = { { helpers.fn.col('.'), helpers.fn.virtcol('.') } } for _ = 2, #expected_cols do feed('') table.insert(cols, { helpers.fn.col('.'), helpers.fn.virtcol('.') }) end eq(expected_cols, cols) end it('works with tabs and spaces', function() local screen = Screen.new(30, 2) screen:attach() 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 = Screen.new(30, 2) screen:attach() 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 = Screen.new(30, 2) screen:attach() 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 = Screen.new(50, 2) screen:attach() 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 = Screen.new(17, 4) screen:attach() 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 = Screen.new(50, 2) screen:attach() 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 = Screen.new(30, 3) screen:attach() 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) end)