From 1aefbff641ec6da77aa1954cbc6d1f10e0f69346 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Wed, 19 Apr 2017 12:01:41 +0100 Subject: [PATCH 1/9] Add some basic tests for macros --- test/functional/normal/macro_spec.lua | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test/functional/normal/macro_spec.lua diff --git a/test/functional/normal/macro_spec.lua b/test/functional/normal/macro_spec.lua new file mode 100644 index 0000000000..102d8fc723 --- /dev/null +++ b/test/functional/normal/macro_spec.lua @@ -0,0 +1,30 @@ +local helpers = require('test.functional.helpers')(after_each) + +local eq = helpers.eq +local eval = helpers.eval +local feed = helpers.feed +local clear = helpers.clear +local expect = helpers.expect +local command = helpers.command + +describe('macros', function() + before_each(clear) + it('can be recorded and replayed', function() + feed('qiahelloq') + expect('hello') + eq(eval('@i'), 'ahello') + feed('@i') + expect('hellohello') + eq(eval('@i'), 'ahello') + end) + it('applies maps', function() + command('imap x l') + command('nmap l a') + feed('qilxxxq') + expect('lll') + eq(eval('@i'), 'lxxx') + feed('@i') + expect('llllll') + eq(eval('@i'), 'lxxx') + end) +end) From 20bfe0f2a34a69f85af262fe350761f85776ad46 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Tue, 4 Apr 2017 14:07:08 +0100 Subject: [PATCH 2/9] Account for :lmap in macros close #5652 Start by adding some tests --- test/functional/options/keymap_spec.lua | 206 ++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 test/functional/options/keymap_spec.lua diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua new file mode 100644 index 0000000000..ffb76a42bd --- /dev/null +++ b/test/functional/options/keymap_spec.lua @@ -0,0 +1,206 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear, feed, eq = helpers.clear, helpers.feed, helpers.eq +local expect, command, eval = helpers.expect, helpers.command, helpers.eval +local insert, call = helpers.insert, helpers.call +local funcs, dedent = helpers.funcs, helpers.dedent + +-- First test it's implemented using the :lmap and :lnoremap commands, then +-- check those mappings behave as expected. +describe("'keymap' / :lmap", function() + clear() + before_each(function() + clear() + insert("lllaaa") + command('set iminsert=1') + command('set imsearch=1') + command('lmap l a') + feed('gg0') + end) + + describe("'keymap' as :lnoremap", function() + -- Shows that 'keymap' sets language mappings without allowing + -- remapping of those mappings. + -- This equivalence allows us to only test :lmap commands and assert + -- they behave the same as 'keymap' settings. + -- It does rely on the absence of special code that implements 'keymap' + -- and :lmap differently but shows mappings from the 'keymap' after + -- typing :lmap. + it("'keymap' mappings are shown with :lmap", function() + command('lmapclear') + command('lmapclear ') + command('set keymap=dvorak') + command('set nomore') + local bindings = funcs.nvim_command_output('lmap') + eq(bindings, dedent([[ + + l " *@_ + l ' *@- + l + *@} + l , *@w + l - *@[ + l . *@v + l / *@z + l : *@S + l ; *@s + l < *@W + l = *@] + l > *@V + l ? *@Z + l A *@A + l B *@X + l C *@J + l D *@E + l E *@> + l F *@U + l G *@I + l H *@D + l I *@C + l J *@H + l K *@T + l L *@N + l M *@M + l N *@B + l O *@R + l P *@L + l Q *@" + l R *@P + l S *@O + l T *@Y + l U *@G + l V *@K + l W *@< + l X *@Q + l Y *@F + l Z *@: + l [ *@/ + l \ *@\ + l ] *@= + l _ *@{ + l a *@a + l b *@x + l c *@j + l d *@e + l e *@. + l f *@u + l g *@i + l h *@d + l i *@c + l j *@h + l k *@t + l l *@n + l m *@m + l n *@b + l o *@r + l p *@l + l q *@' + l r *@p + l s *@o + l t *@y + l u *@g + l v *@k + l w *@, + l x *@q + l y *@f + l z *@; + l { *@? + l | *@| + l } *@+]])) + end) + end) + describe("'iminsert' option", function() + it("Uses :lmap in insert mode when ON", function() + feed('il') + expect('alllaaa') + end) + it("Ignores :lmap in insert mode when OFF", function() + command('set iminsert=0') + feed('il') + expect('llllaaa') + end) + it("Can be toggled with in insert mode", function() + feed('ill') + expect('lalllaaa') + eq(eval('&iminsert'), 1) + feed('i') + eq(eval('&iminsert'), 0) + end) + end) + describe("'imsearch' option", function() + it("Uses :lmap at search prompt when ON", function() + feed('/lll3x') + expect('lll') + end) + it("Ignores :lmap at search prompt when OFF", function() + command('set imsearch=0') + feed('gg/lll3x') + expect('aaa') + end) + it("Can be toggled with C-^", function() + eq(eval('&imsearch'), 1) + feed('/lll3x') + expect('aaa') + eq(eval('&imsearch'), 0) + feed('u0/lll3x') + expect('lll') + eq(eval('&imsearch'), 1) + end) + it("can follow 'iminsert'", function() + command('set imsearch=-1') + feed('/lll3x') + expect('lll') + eq(eval('&imsearch'), -1) + eq(eval('&iminsert'), 1) + feed('u/lll3x') + expect('aaa') + eq(eval('&imsearch'), -1) + eq(eval('&iminsert'), 0) + end) + end) + it(":lmap not applied to macros", function() + command("call setreg('a', 'il')") + feed('@a') + expect('llllaaa') + eq(call('getreg', 'a'), 'il') + end) + it(":lmap applied to macro recording", function() + feed('qailq@a') + expect('aalllaaa') + eq(call('getreg', 'a'), 'ia') + end) + it(":lmap not applied to mappings", function() + command('imap t l') + feed('it') + expect('llllaaa') + end) + it("mappings applied to keys created with :lmap", function() + command('imap a x') + feed('il') + expect('xlllaaa') + end) + it("mappings not applied to keys gotten with :lnoremap", function() + command('lmapclear') + command('lnoremap l a') + command('imap a x') + feed('il') + expect('alllaaa') + end) + it("is applied when using f/F t/T", function() + feed('flx') + expect('lllaa') + feed('0ia4lFlx') + expect('lllaa') + feed('tllx') + expect('llla') + feed('0ia4lTlhx') + expect('llla') + end) + it('takes priority over :imap mappings', function() + command('imap l x') + feed('il') + expect('alllaaa') + command('lmapclear') + command('lmap l a') + feed('il') + expect('aalllaaa') + end) +end) From 8e71a26e1931e8e87e6db6e13b77dadd488790de Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Mon, 3 Apr 2017 15:59:41 +0100 Subject: [PATCH 3/9] Record :lmap transformed keys in gotchars() The mental model of :lmap and 'keymap' is of a transformation done before anything else. Hence when recording a macro, or writing to a scriptfile, the transformed keys should be recorded instead of the keys before the transformation. --- src/nvim/getchar.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 7df1bf8429..458c79ad4e 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1939,8 +1939,9 @@ static int vgetorpeek(int advance) char_u *save_m_keys; char_u *save_m_str; - // write chars to script file(s) - if (keylen > typebuf.tb_maplen) { + // Write chars to script file(s) + // Note: :lmap mappings are written *after* being applied. #5658 + if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) == 0) { gotchars(typebuf.tb_buf + typebuf.tb_off + typebuf.tb_maplen, (size_t)(keylen - typebuf.tb_maplen)); } @@ -2015,6 +2016,12 @@ static int vgetorpeek(int advance) else { int noremap; + // If this is a LANGMAP mapping, then we didn't record the keys + // at the start of the function and have to record them now. + if (keylen > typebuf.tb_maplen && (mp->m_mode & LANGMAP) != 0) { + gotchars(s, STRLEN(s)); + } + if (save_m_noremap != REMAP_YES) noremap = save_m_noremap; else if ( From 9beaf84d2f2bdef8e0400db78c364806008226f2 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Tue, 4 Apr 2017 14:28:20 +0100 Subject: [PATCH 4/9] Ensure :lmap mappings take preference If the mental model of :lmap mappings is a translation between your keyboard and vim proper, then they should take preference over :imap (and other) mappings. This patch makes that happen. --- src/nvim/getchar.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 458c79ad4e..d1a28ae3b0 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1844,8 +1844,11 @@ static int vgetorpeek(int advance) keylen = KEYLEN_PART_MAP; break; } - } else if (keylen > mp_match_len) { - /* found a longer match */ + } else if (keylen > mp_match_len + || (keylen == mp_match_len + && (mp_match->m_mode & LANGMAP) == 0 + && (mp->m_mode & LANGMAP) != 0)) { + // found a longer match mp_match = mp; mp_match_len = keylen; } From e01f35c4bb95ce81eebdba8c8b53d73964508e73 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Fri, 2 Jun 2017 15:23:18 +0100 Subject: [PATCH 5/9] :lnoremap mappings should not be remapped when replaying a recording --- test/functional/options/keymap_spec.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua index ffb76a42bd..ff78348517 100644 --- a/test/functional/options/keymap_spec.lua +++ b/test/functional/options/keymap_spec.lua @@ -181,8 +181,10 @@ describe("'keymap' / :lmap", function() command('lmapclear') command('lnoremap l a') command('imap a x') - feed('il') + feed('qailq') expect('alllaaa') + feed('@a') + expect('aalllaaa') end) it("is applied when using f/F t/T", function() feed('flx') From d989051220a5137e7490ca5020225da109e0af0b Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Wed, 24 Jan 2018 13:09:22 +0000 Subject: [PATCH 6/9] Split :lnoremap test into done and pending There is some behaviour that we keep with the recent changes, and some behaviour that we change. Instetad of having one failing test covering all behaviour, we split the test into two. --- test/functional/options/keymap_spec.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua index ff78348517..e1424cb8fe 100644 --- a/test/functional/options/keymap_spec.lua +++ b/test/functional/options/keymap_spec.lua @@ -178,6 +178,16 @@ describe("'keymap' / :lmap", function() expect('xlllaaa') end) it("mappings not applied to keys gotten with :lnoremap", function() + command('lmapclear') + command('lnoremap l a') + command('imap a x') + feed('il') + expect('alllaaa') + end) + -- This is a problem introduced when introducting :lmap and macro + -- compatibility. There are no plans to fix this as the complexity involved + -- seems too great. + pending('mappings not applied to macro replay of :lnoremap', function() command('lmapclear') command('lnoremap l a') command('imap a x') From 3b304fc04ac0ac7ffe24ba4b83fc0d0ba4b80cfd Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Wed, 24 Jan 2018 13:16:27 +0000 Subject: [PATCH 7/9] 'keymap' now uses :lmap instead of :lnoremap This means that the major way that :lmap mappings are applied works as one would expect with macros. This also means that having a translation with 'keymap' does not preclude using mappings in insert mode with :imap. --- src/nvim/digraph.c | 4 +- test/functional/options/keymap_spec.lua | 156 ++++++++++++------------ 2 files changed, 80 insertions(+), 80 deletions(-) diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index bc4c12e0b7..ffd13da48b 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1827,12 +1827,12 @@ void ex_loadkeymap(exarg_T *eap) xfree(line); } - // setup ":lnoremap" to map the keys + // setup ":lmap" to map the keys for (int i = 0; i < curbuf->b_kmap_ga.ga_len; ++i) { vim_snprintf((char *)buf, sizeof(buf), " %s %s", ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].from, ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].to); - (void)do_map(2, buf, LANGMAP, FALSE); + (void)do_map(0, buf, LANGMAP, FALSE); } p_cpo = save_cpo; diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua index e1424cb8fe..43fd1eb990 100644 --- a/test/functional/options/keymap_spec.lua +++ b/test/functional/options/keymap_spec.lua @@ -17,94 +17,94 @@ describe("'keymap' / :lmap", function() feed('gg0') end) - describe("'keymap' as :lnoremap", function() - -- Shows that 'keymap' sets language mappings without allowing - -- remapping of those mappings. - -- This equivalence allows us to only test :lmap commands and assert - -- they behave the same as 'keymap' settings. + describe("'keymap' as :lmap", function() + -- Shows that 'keymap' sets language mappings that allows remapping. + -- This equivalence allows us to only test :lmap commands and assert they + -- behave the same as 'keymap' settings. -- It does rely on the absence of special code that implements 'keymap' -- and :lmap differently but shows mappings from the 'keymap' after -- typing :lmap. + -- At the moment this is the case. it("'keymap' mappings are shown with :lmap", function() command('lmapclear') command('lmapclear ') command('set keymap=dvorak') command('set nomore') local bindings = funcs.nvim_command_output('lmap') - eq(bindings, dedent([[ + eq(dedent([[ - l " *@_ - l ' *@- - l + *@} - l , *@w - l - *@[ - l . *@v - l / *@z - l : *@S - l ; *@s - l < *@W - l = *@] - l > *@V - l ? *@Z - l A *@A - l B *@X - l C *@J - l D *@E - l E *@> - l F *@U - l G *@I - l H *@D - l I *@C - l J *@H - l K *@T - l L *@N - l M *@M - l N *@B - l O *@R - l P *@L - l Q *@" - l R *@P - l S *@O - l T *@Y - l U *@G - l V *@K - l W *@< - l X *@Q - l Y *@F - l Z *@: - l [ *@/ - l \ *@\ - l ] *@= - l _ *@{ - l a *@a - l b *@x - l c *@j - l d *@e - l e *@. - l f *@u - l g *@i - l h *@d - l i *@c - l j *@h - l k *@t - l l *@n - l m *@m - l n *@b - l o *@r - l p *@l - l q *@' - l r *@p - l s *@o - l t *@y - l u *@g - l v *@k - l w *@, - l x *@q - l y *@f - l z *@; - l { *@? - l | *@| - l } *@+]])) + l " @_ + l ' @- + l + @} + l , @w + l - @[ + l . @v + l / @z + l : @S + l ; @s + l < @W + l = @] + l > @V + l ? @Z + l A @A + l B @X + l C @J + l D @E + l E @> + l F @U + l G @I + l H @D + l I @C + l J @H + l K @T + l L @N + l M @M + l N @B + l O @R + l P @L + l Q @" + l R @P + l S @O + l T @Y + l U @G + l V @K + l W @< + l X @Q + l Y @F + l Z @: + l [ @/ + l \ @\ + l ] @= + l _ @{ + l a @a + l b @x + l c @j + l d @e + l e @. + l f @u + l g @i + l h @d + l i @c + l j @h + l k @t + l l @n + l m @m + l n @b + l o @r + l p @l + l q @' + l r @p + l s @o + l t @y + l u @g + l v @k + l w @, + l x @q + l y @f + l z @; + l { @? + l | @| + l } @+]]), bindings) end) end) describe("'iminsert' option", function() From cc58ec9a801510fe77edb34e02b0635c4f24f924 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Thu, 1 Jun 2017 09:20:56 +0100 Subject: [PATCH 8/9] Update documentation Update vim_diff.txt with :lmap differences, update documentation on 'keymap', and add tests. The tests added are to demonstrate the behaviour specified in the documentation of :loadkeymap. --- runtime/doc/map.txt | 3 ++- runtime/doc/mbyte.txt | 7 ++++--- runtime/doc/vim_diff.txt | 6 ++++++ test/functional/options/keymap_spec.lua | 15 +++++++++++++++ 4 files changed, 27 insertions(+), 4 deletions(-) diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 9b61fa6527..c64f8a1e0a 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -386,7 +386,8 @@ state for Insert mode is also used when typing a character as an argument to command like "f" or "t". Language mappings will never be applied to already mapped characters. They are only used for typed characters. This assumes that the language mapping -was already done when typing the mapping. +was already done when typing the mapping. Correspondingly, language mappings +are applied when recording macros, rather than when applying them. 1.4 LISTING MAPPINGS *map-listing* diff --git a/runtime/doc/mbyte.txt b/runtime/doc/mbyte.txt index 531629fddc..2d4a20ed72 100644 --- a/runtime/doc/mbyte.txt +++ b/runtime/doc/mbyte.txt @@ -834,7 +834,7 @@ keyboards and encodings. The actual mappings are in the lines below "loadkeymap". In the example "a" is mapped to "A" and "b" to "B". Thus the first item is mapped to the second item. This is done for each line, until the end of the file. -These items are exactly the same as what can be used in a |:lnoremap| command, +These items are exactly the same as what can be used in a |:lmap| command, using "" to make the mappings local to the buffer. You can check the result with this command: > :lmap @@ -849,8 +849,9 @@ Since Vim doesn't know if the next character after a quote is really an "a", it will wait for the next character. To be able to insert a single quote, also add this line: > '' ' -Since the mapping is defined with |:lnoremap| the resulting quote will not be -used for the start of another character. +Since the mapping is defined with |:lmap| the resulting quote will not be +used for the start of another character defined in the 'keymap'. +It can be used in a standard |:imap| mapping. The "accents" keymap uses this. *keymap-accents* The first column can also be in |<>| form: diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 3924dd4ebe..2d53f26028 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -309,6 +309,12 @@ Highlight groups: VimL (Vim script) compatibility: `count` does not alias to |v:count| +|:lmap|s are applied to macro recordings, in Vim if a macro is recorded while +using |:lmap|ped keys then the behaviour during record and replay differs. +'keymap' is implemented via |:lmap| instead of |:lnoremap| in order to allow +using macros and 'keymap' at the same time. +This means that you can use |:imap| on the results of keys from 'keymap'. + ============================================================================== 5. Missing legacy features *nvim-features-missing* diff --git a/test/functional/options/keymap_spec.lua b/test/functional/options/keymap_spec.lua index 43fd1eb990..7f6d623dc7 100644 --- a/test/functional/options/keymap_spec.lua +++ b/test/functional/options/keymap_spec.lua @@ -215,4 +215,19 @@ describe("'keymap' / :lmap", function() feed('il') expect('aalllaaa') end) + it('does not cause recursive mappings', function() + command('lmap a l') + feed('qailaq') + expect('allllaaa') + feed('u@a') + expect('allllaaa') + end) + it('can handle multicharacter mappings', function() + command("lmap 'a x") + command("lmap '' '") + feed("qai'a''aq") + expect("x'alllaaa") + feed('u@a') + expect("x'alllaaa") + end) end) From 9058a5a19a3f62fb156203e0226eaaabb8b8da56 Mon Sep 17 00:00:00 2001 From: Matthew Malcomson Date: Wed, 24 Jan 2018 20:30:47 +0000 Subject: [PATCH 9/9] clint --- src/nvim/digraph.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/nvim/digraph.c b/src/nvim/digraph.c index ffd13da48b..e3d5ca07d1 100644 --- a/src/nvim/digraph.c +++ b/src/nvim/digraph.c @@ -1828,11 +1828,11 @@ void ex_loadkeymap(exarg_T *eap) } // setup ":lmap" to map the keys - for (int i = 0; i < curbuf->b_kmap_ga.ga_len; ++i) { + for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) { vim_snprintf((char *)buf, sizeof(buf), " %s %s", ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].from, ((kmap_T *)curbuf->b_kmap_ga.ga_data)[i].to); - (void)do_map(0, buf, LANGMAP, FALSE); + (void)do_map(0, buf, LANGMAP, false); } p_cpo = save_cpo;