diff --git a/src/nvim/macros.h b/src/nvim/macros.h index a98c1e05a0..0406574990 100644 --- a/src/nvim/macros.h +++ b/src/nvim/macros.h @@ -75,7 +75,7 @@ do { \ if (*p_langmap \ && (condition) \ - && (p_lrm || KeyTyped) \ + && (p_lrm || (vgetc_busy ? typebuf_maplen() == 0 : KeyTyped)) \ && !KeyStuffed \ && (c) >= 0) \ { \ diff --git a/test/functional/normal/langmap_spec.lua b/test/functional/normal/langmap_spec.lua new file mode 100644 index 0000000000..e4349a22e7 --- /dev/null +++ b/test/functional/normal/langmap_spec.lua @@ -0,0 +1,280 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq, neq, call = helpers.eq, helpers.neq, helpers.call +local eval, feed, clear = helpers.eval, helpers.feed, helpers.clear +local command, insert, expect = helpers.command, helpers.insert, helpers.expect +local feed_command = helpers.feed_command +local curwin = helpers.curwin + +describe("'langmap'", function() + before_each(function() + clear() + insert('iii www') + command('set langmap=iw,wi') + feed('gg0') + end) + + it("converts keys in normal mode", function() + feed('ix') + expect('iii ww') + feed('whello') + expect('iii helloww') + end) + it("gives characters that are mapped by :nmap.", function() + command('map i 0x') + feed('w') + expect('ii www') + end) + describe("'langnoremap' option.", function() + before_each(function() + command('nmapclear') + end) + it("'langnoremap' is by default ON", function() + eq(eval('&langnoremap'), 1) + end) + it("Results of maps are not converted when 'langnoremap' ON.", + function() + command('nmap x i') + feed('xdl') + expect('dliii www') + end) + it("applies when deciding whether to map recursively", function() + command('nmap l i') + command('nmap w j') + feed('ll') + expect('liii www') + end) + it("does not stop applying 'langmap' on first character of a mapping", + function() + command('1t1') + command('1t1') + command('goto 1') + command('nmap w j') + feed('iiahello') + expect([[ + iii www + iii www + ihelloii www]]) + end) + it("Results of maps are converted when 'langnoremap' OFF.", + function() + command('set nolangnoremap') + command('nmap x i') + feed('xdl') + expect('iii ww') + end) + end) + -- e.g. CTRL-W_j , mj , 'j and "jp + it('conversions are applied to keys in middle of command', + function() + -- Works in middle of window command + feed('s') + local origwin = curwin() + feed('i') + neq(curwin(), origwin) + -- Works when setting a mark + feed('yy3p3gg0mwgg0mi') + eq(call('getpos', "'i"), {0, 3, 1, 0}) + eq(call('getpos', "'w"), {0, 1, 1, 0}) + feed('3dd') + -- Works when moving to a mark + feed("'i") + eq(call('getpos', '.'), {0, 1, 1, 0}) + -- Works when selecting a register + feed('qillqqwhhq') + eq(eval('@i'), 'hh') + eq(eval('@w'), 'll') + feed('ai') + expect('illii www') + feed('"ip') + expect('illllii www') + -- Works with i_CTRL-O + feed('0aihi') + expect('illllii hiwww') + end) + + describe('exceptions', function() + -- All "command characters" that 'langmap' does not apply to. + -- These tests consist of those places where some subset of ASCII + -- characters define certain commands, yet 'langmap' is not applied to + -- them. + -- n.b. I think these shouldn't be exceptions. + it(':s///c confirmation', function() + command('set langmap=yn,ny') + feed('qa') + feed_command('s/i/w/gc') + feed('yynq') + expect('wwi www') + feed('u@a') + expect('wwi www') + eq(eval('@a'), ':s/i/w/gc\ryyn') + end) + it('insert-mode CTRL-G', function() + command('set langmap=jk,kj') + command('d') + insert([[ + hello + hello + hello]]) + expect([[ + hello + hello + hello]]) + feed('qa') + feed('gg3|ahellojx') + feed('q') + expect([[ + helhellolo + helxlo + hello]]) + eq(eval('@a'), 'gg3|ahellojx') + end) + it('command-line CTRL-\\', function() + command('set langmap=en,ne') + feed(':e\'hello\'\rput =""') + expect([[ + iii www + hello]]) + end) + it('command-line CTRL-R', function() + helpers.source([[ + let i_value = 0 + let j_value = 0 + call setreg('i', 'i_value') + call setreg('j', 'j_value') + set langmap=ij,ji + ]]) + feed(':let i=1') + eq(eval('i_value'), 1) + eq(eval('j_value'), 0) + end) + -- it('-- More -- prompt', function() + -- -- The 'b' 'j' 'd' 'f' commands at the -- More -- prompt + -- end) + it('ask yes/no after backwards range', function() + command('set langmap=yn,ny') + feed('dd') + insert([[ + hello + there + these + are + some + lines + ]]) + feed_command('4,2d') + feed('n') + expect([[ + hello + there + these + are + some + lines + ]]) + end) + it('prompt for number', function() + command('set langmap=12,21') + helpers.source([[ + let gotten_one = 0 + function Map() + let answer = inputlist(['a', '1.', '2.', '3.']) + if answer == 1 + let g:gotten_one = 1 + endif + endfunction + nnoremap x :call Map() + ]]) + feed('x1') + eq(eval('gotten_one'), 1) + command('let g:gotten_one = 0') + feed_command('call Map()') + feed('1') + eq(eval('gotten_one'), 1) + end) + end) + it('conversions are not applied during setreg()', + function() + call('setreg', 'i', 'ww') + eq(eval('@i'), 'ww') + end) + it('conversions not applied in insert mode', function() + feed('aiiiwww') + expect('iiiiwwwii www') + end) + it('conversions not applied in search mode', function() + feed('/iiix') + expect('ii www') + end) + it('conversions not applied in cmdline mode', function() + feed(':call append(1, "iii")') + expect([[ + iii www + iii]]) + end) + + local function testrecording(command_string, expect_string, setup_function) + if setup_function then setup_function() end + feed('qa' .. command_string .. 'q') + expect(expect_string) + eq(helpers.funcs.nvim_replace_termcodes(command_string, true, true, true), + eval('@a')) + if setup_function then setup_function() end + -- n.b. may need nvim_replace_termcodes() here. + feed('@a') + expect(expect_string) + end + + local function local_setup() + -- Can't use `insert` as it uses `i` and we've swapped the meaning of that + -- with the `langmap` setting. + command('%d') + command("put ='hello'") + command('1d') + end + + it('does not affect recording special keys', function() + testrecording('A', 'hell', local_setup) + testrecording('>>', 'hello', local_setup) + command('nnoremap \\ x') + testrecording('\\', 'ello', local_setup) + testrecording('A', 'hello', local_setup) + end) + pending('Translates modified keys correctly', function() + command('nnoremap x') + command('nnoremap l') + testrecording('', 'ello', local_setup) + testrecording('x', 'hllo', local_setup) + end) + pending('handles multi-byte characters', function() + command('set langmap=ïx') + testrecording('ï', 'ello', local_setup) + -- The test below checks that what's recorded is correct. + -- It doesn't check the behaviour, as in order to cause some behaviour we + -- need to map the multi-byte character, and there is a known bug + -- preventing this from working (see the test below). + command('set langmap=xï') + testrecording('x', 'hello', local_setup) + end) + pending('handles multibyte mappings', function() + -- See this vim issue for the problem, may as well add a test. + -- https://github.com/vim/vim/issues/297 + command('set langmap=ïx') + command('nnoremap x diw') + testrecording('ï', '', local_setup) + command('set nolangnoremap') + command('set langmap=xï') + command('nnoremap ï ix') + testrecording('x', 'xhello', local_setup) + end) + -- This test is to ensure the behaviour doesn't change from what's already + -- around. I (hardenedapple) personally think this behaviour should be + -- changed. + it('treats control modified keys as characters', function() + command('nnoremap iw') + command('nnoremap ii') + testrecording('', 'whello', local_setup) + testrecording('', 'ihello', local_setup) + end) + +end)