您可以使用该maparg()
功能。
要测试用户是否<C-c>
在正常模式下映射了某些内容,您可以编写:
if !empty(maparg('<C-c>', 'n'))
如果用户映射了一些内容,以将{rhs}
变量存储在变量中,则应编写:
let rhs_save = maparg('<C-c>', 'n')
如果您想了解有关映射的更多信息,例如:
- 沉默(
<silent>
争论)吗?
- 它在当前缓冲区(
<buffer>
参数)本地吗?
- 是
{rhs}
一个表达式(评价<expr>
参数)?
- 它会重新映射
{rhs}
(nnoremap
vs nmap
)吗?
- 如果用户有另一个以开头的映射
<C-c>
,Vim是否等待输入更多字符(自<nowait>
变量)?
- ...
然后,您可以给出第三个和第四个参数:0
和1
。
0
因为您要查找的是映射而不是缩写,并且1
因为您想要的是具有最大信息量而不只是{rhs}
值的字典:
let map_save = maparg('<C-c>', 'n', 0, 1)
假设用户在映射中没有使用任何特殊参数,并且没有重新映射{rhs}
来还原它,则可以简单地编写:
let rhs_save = maparg('<C-c>', 'n')
" do some stuff which changes the mapping
exe 'nnoremap <C-c> ' . rhs_save
或者可以确定并恢复所有可能的参数:
let map_save = maparg('<C-c>', 'n', 0, 1)
" do some stuff which changes the mapping
exe (map_save.noremap ? 'nnoremap' : 'nmap') .
\ (map_save.buffer ? ' <buffer> ' : '') .
\ (map_save.expr ? ' <expr> ' : '') .
\ (map_save.nowait ? ' <nowait> ' : '') .
\ (map_save.silent ? ' <silent> ' : '') .
\ ' <C-c> ' .
\ map_save.rhs
编辑:对不起,我只是意识到,如果用户{rhs}
在映射的中调用脚本本地函数,它将无法按预期工作。
假设用户内部具有以下映射vimrc
:
nnoremap <C-c> :<C-U>call <SID>FuncA()<CR>
function! s:FuncA()
echo 'hello world!'
endfunction
当他点击时<C-c>
,它会显示消息hello world!
。
然后在您的插件中,保存包含所有信息的字典,然后像这样临时更改其映射:
let map_save = maparg('<C-c>', 'n', 0, 1)
nnoremap <C-c> :<C-U>call <SID>FuncB()<CR>
function! s:FuncB()
echo 'bye all!'
endfunction
现在,它将显示bye all!
。您的插件可以完成一些工作,结束后,它会尝试使用上一个命令恢复映射。
它可能会失败,并显示以下消息:
E117: Unknown function: <SNR>61_FuncA
61
只是将在其中执行映射命令的脚本的标识符。它可以是任何其他数字。如果您的插件是来自用户系统的第42个文件,则它将为42
。
在脚本内部,执行映射命令时,Vim会自动将表示法<SID>
转换为特殊的键代码<SNR>
,后跟脚本唯一的数字和下划线。它必须这样做,因为当用户点击时<C-c>
,映射将在脚本之外执行,因此它将不知道在哪个脚本FuncA()
中定义。
问题在于原始映射是从与您的插件不同的脚本中获取的,因此这里的自动翻译是错误的。它使用脚本的标识符,而应使用用户的标识符vimrc
。
但是您可以手动进行翻译。词典map_save
包含一个名为'sid'
的键,其值是正确的标识符。
因此,为使先前的恢复命令更健壮,可以替换map_save.rhs
为:
substitute(map_save.rhs, '<SID>', '<SNR>' . map_save.sid . '_', 'g')
如果{rhs}
包含原始映射的<SID>
,则应正确翻译。否则,应保持不变。
而且,如果您想稍微缩短代码,则可以将照顾特殊参数的4行替换为:
join(map(['buffer', 'expr', 'nowait', 'silent'], 'map_save[v:val] ? "<" . v:val . ">": ""'))
该map()
函数应将列表中的每个项目都转换['buffer', 'expr', 'nowait', 'silent']
为相应的映射参数,但前提是其内部的键map_save
非零。并且join()
应将所有项目连接成一个字符串。
因此,保存和还原映射的更可靠的方法可能是:
let map_save = maparg('<C-c>', 'n', 0, 1)
" do some stuff which changes the mapping
exe (map_save.noremap ? 'nnoremap' : 'nmap') .
\ join(map(['buffer', 'expr', 'nowait', 'silent'], 'map_save[v:val] ? "<" . v:val . ">": ""')) .
\ map_save.lhs . ' ' .
\ substitute(map_save.rhs, '<SID>', '<SNR>' . map_save.sid . '_', 'g')
编辑2:
我面临与您相同的问题,即如何在图形插件中保存和还原映射。我想我发现了2个问题,这些问题在我写这篇文章时都没有看到,对此感到抱歉。
第一个问题,假设用户<C-c>
在全局映射中使用,也在缓冲区局部映射中使用。例:
nnoremap <C-c> :echo 'global mapping'<CR>
nnoremap <buffer> <C-c> :echo 'local mapping'<CR>
在这种情况下,maparg()
将优先进行本地映射:
:echo maparg('<C-c>', 'n', 0, 1)
---> {'silent': 0, 'noremap': 1, 'lhs': '<C-C>', 'mode': 'n', 'nowait': 0, 'expr': 0, 'sid': 7, 'rhs': ':echo ''local mapping''<CR>', 'buffer': 1}
这也是证实:h maparg()
:
The mappings local to the current buffer are checked first,
then the global mappings.
但是,也许您对缓冲区局部映射不感兴趣,也许您想要全局映射。
我发现可靠地获取有关全局映射的信息的唯一方法是尝试使用相同的键临时取消映射潜在的,带有阴影的缓冲区局部映射。
可以通过4个步骤完成:
- 使用键保存(潜在的)本地缓冲区映射
<C-c>
- 执行
:silent! nunmap <buffer> <C-c>
以删除(潜在的)缓冲区本地映射
- 保存全局映射(
maparg('<C-c>', 'n', 0, 1)
)
- 恢复本地缓冲区映射
第二个问题如下。假设用户没有将任何内容映射到<C-c>
,则的输出maparg()
将是一个空字典。在这种情况下,还原过程不在于安装映射(:nnoremap
),而在于销毁映射(:nunmap
)。
要尝试解决这两个新问题,您可以尝试使用以下功能保存映射:
fu! Save_mappings(keys, mode, global) abort
let mappings = {}
if a:global
for l:key in a:keys
let buf_local_map = maparg(l:key, a:mode, 0, 1)
sil! exe a:mode.'unmap <buffer> '.l:key
let map_info = maparg(l:key, a:mode, 0, 1)
let mappings[l:key] = !empty(map_info)
\ ? map_info
\ : {
\ 'unmapped' : 1,
\ 'buffer' : 0,
\ 'lhs' : l:key,
\ 'mode' : a:mode,
\ }
call Restore_mappings({l:key : buf_local_map})
endfor
else
for l:key in a:keys
let map_info = maparg(l:key, a:mode, 0, 1)
let mappings[l:key] = !empty(map_info)
\ ? map_info
\ : {
\ 'unmapped' : 1,
\ 'buffer' : 1,
\ 'lhs' : l:key,
\ 'mode' : a:mode,
\ }
endfor
endif
return mappings
endfu
...和恢复它们的这个:
fu! Restore_mappings(mappings) abort
for mapping in values(a:mappings)
if !has_key(mapping, 'unmapped') && !empty(mapping)
exe mapping.mode
\ . (mapping.noremap ? 'noremap ' : 'map ')
\ . (mapping.buffer ? ' <buffer> ' : '')
\ . (mapping.expr ? ' <expr> ' : '')
\ . (mapping.nowait ? ' <nowait> ' : '')
\ . (mapping.silent ? ' <silent> ' : '')
\ . mapping.lhs
\ . ' '
\ . substitute(mapping.rhs, '<SID>', '<SNR>'.mapping.sid.'_', 'g')
elseif has_key(mapping, 'unmapped')
sil! exe mapping.mode.'unmap '
\ .(mapping.buffer ? ' <buffer> ' : '')
\ . mapping.lhs
endif
endfor
endfu
该Save_mappings()
功能可用于保存映射。
它需要3个参数:
- 键列表;例:
['<C-a>', '<C-b>', '<C-c>']
- 模式 示例:
n
用于普通模式或x
可视模式
- 布尔标志;如果是
1
,则表示您对全局映射感兴趣,如果是0
,则对本地映射感兴趣
有了它,您可以在正常模式下使用C-a
,C-b
和键C-c
在字典中保存全局映射:
let your_saved_mappings = Save_mappings(['<C-a>', '<C-b>', '<C-c>'], 'n', 1)
然后,稍后,当您要还原映射时,可以调用Restore_mappings()
,将包含所有信息的字典作为参数传递:
call Restore_mappings(your_saved_mappings)
保存/恢复本地缓冲区映射时,可能会出现第三个问题。因为在我们保存映射的那一刻到尝试还原它们的那一刻之间,当前缓冲区可能已更改。
在这种情况下,也许Save_mappings()
可以通过节省当前缓冲区(bufnr('%')
)的数量来改善功能。
然后,Restore_mappings()
将使用此信息在正确的缓冲区中还原缓冲区本地映射。我们可能可以使用该:bufdo
命令,为后者加上一个计数(与先前保存的缓冲区号匹配),并在其后加上mapping命令。
也许像这样:
:{original buffer number}bufdo {mapping command}
我们必须首先使用该bufexists()
函数检查缓冲区是否仍然存在,因为与此同时它可能已被删除。