Merge #5658 'Apply :lmap in macros'

This commit is contained in:
Justin M. Keyes 2018-05-17 02:13:31 +02:00 committed by GitHub
commit 2aa308c685
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 292 additions and 11 deletions

View File

@ -411,7 +411,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*

View File

@ -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 "<buffer>" 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:

View File

@ -312,6 +312,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*

View File

@ -1833,12 +1833,12 @@ void ex_loadkeymap(exarg_T *eap)
xfree(line);
}
// setup ":lnoremap" to map the keys
for (int i = 0; i < curbuf->b_kmap_ga.ga_len; ++i) {
// setup ":lmap" to map the keys
for (int i = 0; i < curbuf->b_kmap_ga.ga_len; i++) {
vim_snprintf((char *)buf, sizeof(buf), "<buffer> %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;

View File

@ -1852,8 +1852,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;
}
@ -1947,8 +1950,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));
}
@ -2023,6 +2027,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 (

View File

@ -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('qiahello<esc>q')
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('qilxxx<esc>q')
expect('lll')
eq(eval('@i'), 'lxxx')
feed('@i')
expect('llllll')
eq(eval('@i'), 'lxxx')
end)
end)

View File

@ -0,0 +1,233 @@
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 :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 <buffer>')
command('set keymap=dvorak')
command('set nomore')
local bindings = funcs.nvim_command_output('lmap')
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 } @+]]), bindings)
end)
end)
describe("'iminsert' option", function()
it("Uses :lmap in insert mode when ON", function()
feed('il<esc>')
expect('alllaaa')
end)
it("Ignores :lmap in insert mode when OFF", function()
command('set iminsert=0')
feed('il<esc>')
expect('llllaaa')
end)
it("Can be toggled with <C-^> in insert mode", function()
feed('i<C-^>l<C-^>l<esc>')
expect('lalllaaa')
eq(eval('&iminsert'), 1)
feed('i<C-^><esc>')
eq(eval('&iminsert'), 0)
end)
end)
describe("'imsearch' option", function()
it("Uses :lmap at search prompt when ON", function()
feed('/lll<cr>3x')
expect('lll')
end)
it("Ignores :lmap at search prompt when OFF", function()
command('set imsearch=0')
feed('gg/lll<cr>3x')
expect('aaa')
end)
it("Can be toggled with C-^", function()
eq(eval('&imsearch'), 1)
feed('/<C-^>lll<cr>3x')
expect('aaa')
eq(eval('&imsearch'), 0)
feed('u0/<C-^>lll<cr>3x')
expect('lll')
eq(eval('&imsearch'), 1)
end)
it("can follow 'iminsert'", function()
command('set imsearch=-1')
feed('/lll<cr>3x')
expect('lll')
eq(eval('&imsearch'), -1)
eq(eval('&iminsert'), 1)
feed('u/<C-^>lll<cr>3x')
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('qail<esc>q@a')
expect('aalllaaa')
eq(call('getreg', 'a'), 'ia')
end)
it(":lmap not applied to mappings", function()
command('imap t l')
feed('it<esc>')
expect('llllaaa')
end)
it("mappings applied to keys created with :lmap", function()
command('imap a x')
feed('il<esc>')
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<esc>')
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')
feed('qail<esc>q')
expect('alllaaa')
feed('@a')
expect('aalllaaa')
end)
it("is applied when using f/F t/T", function()
feed('flx')
expect('lllaa')
feed('0ia<esc>4lFlx')
expect('lllaa')
feed('tllx')
expect('llla')
feed('0ia<esc>4lTlhx')
expect('llla')
end)
it('takes priority over :imap mappings', function()
command('imap l x')
feed('il<esc>')
expect('alllaaa')
command('lmapclear')
command('lmap l a')
feed('il')
expect('aalllaaa')
end)
it('does not cause recursive mappings', function()
command('lmap a l')
feed('qaila<esc>q')
expect('allllaaa')
feed('u@a')
expect('allllaaa')
end)
it('can handle multicharacter mappings', function()
command("lmap 'a x")
command("lmap '' '")
feed("qai'a''a<esc>q")
expect("x'alllaaa")
feed('u@a')
expect("x'alllaaa")
end)
end)