我正在为Vim开发插件,我想定义一个仅在“执行插件”时可用的映射。

到目前为止,(简化的)插件工作流程如下:


用户调用插件命令

该命令调用预处理功能:

function! s:PreTreatmentFunction(function, ...)
    " Do some pretreatment stuff

    " Create a mapping to call the TearDown
    nnoremap <C-c> :call TeardDown()<CR>

    " Call a function depending on the parameter passed to this one
    if function == "foo"
        call Foo()
    else
        call Bar()
    endif
endfunction


调用另一个函数,该函数更改缓冲区的状态(上一个函数的最后一行中的Foo()Bar()
用户使用该映射调用拆解函数

拆解功能删除了创建的映射:

function! s:TearDown()
    " Do some tear down stuff

    " Remove the mapping
    unmap <C-c>
endfunction



我对处理映射的方式不满意:如果用户已经将其映射到

所以我的问题是:如何保存<C-c>映射到的内容(如果已映射),并在我的拆解函数中恢复它?
有内置功能吗?
我虽然了解q4312079请查看grep的结果,但感觉并不是真的“干净”。

几点注意事项:那,但是他们说使用ftplugin在这里是不可能的:该插件不依赖于文件类型
我可以创建一个变量来让用户选择要使用的键:这可能是我会做的但是我主要对如何执行保存和恢复感兴趣。
我可以聘用当地领导人,但我认为这有点过分,我仍然对保存和恢复事情很感兴趣。


#1 楼

您可以使用maparg()函数。

要测试用户是否在正常模式下将某些内容映射到<C-c>,请输入:
用户映射了一些内容,以便将{rhs}存储在变量中,您可以这样写:

if !empty(maparg('<C-c>', 'n'))


如果您想要有关映射的更多信息,例如:


它是沉默的(<silent>参数)吗?
它是否在当前缓冲区本地(<buffer>参数)?
{rhs}是表达式的求值(<expr>参数)吗?
是否重新映射{rhs}nnoremapnmap)?
如果用户有另一个以<C-c>开头的映射,Vim是否等待输入更多字符(<nowait>参数)? br />
然后,您可以给出第三个和第四个参数:010是因为您要查找映射而不是缩写,1是因为您想要具有最大信息量而不仅仅是{rhs}值的字典:

let rhs_save = maparg('<C-c>', 'n')



假设用户在映射中没有使用任何特殊参数,并且没有重新映射{rhs}来还原它,则只需编写以下内容:

let map_save = maparg('<C-c>', 'n', 0, 1)


或者可以确定并恢复所有可能的参数:

let rhs_save = maparg('<C-c>', 'n')

" do some stuff which changes the mapping

exe 'nnoremap <C-c> ' . rhs_save



编辑:对不起,我只是意识到这行不通如果用户在映射的{rhs}中调用脚本本地函数,则按预期进行。

假设用户在其vimrc内部具有以下映射:
>
当他按下<C-c>时,它会显示消息hello world!
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


现在,它将显示bye all!
您的插件完成了一些工作,当结束时,它会尝试使用上一个命令恢复映射。

它可能会失败,并显示以下消息:

nnoremap <C-c> :<C-U>call <SID>FuncA()<CR>

function! s:FuncA()
    echo 'hello world!'
endfunction


61只是将在其中执行映射命令的脚本的标识符。可以是任何其他数字。如果您的插件是源于用户系统的第42个文件,则它将为42

在脚本内,当执行映射命令时,Vim自动将符号<SID>转换为特殊键代码<SNR>,然后是脚本唯一的数字和下划线。它必须这样做,因为当用户点击<C-c>时,映射将在脚本之外执行,因此它将不知道在哪个脚本中定义了FuncA()

问题是原始映射的来源与您的插件使用的脚本不同,因此此处的自动翻译是错误的。它使用脚本的标识符,而应使用用户的vimrc的标识符。

但是您可以手动进行翻译。字典map_save包含一个名为'sid'的键,其值是正确的标识符。
因此,为使以前的还原命令更可靠,可以将map_save.rhs替换为: br />如果原始映射的{rhs}包含<SID>,则应正确翻译。否则,什么都不要更改。

如果您想稍微缩短代码,则可以将照顾特殊参数的4行替换为:

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


map()函数应将列表['buffer', 'expr', 'nowait', 'silent']中的每个项目转换为相应的映射参数,但前提是map_save中的键为非零。并且join()应该将所有项目都连接到一个字符串中。 >
Edit2:

我面临与您相同的问题,即如何在图形插件中保存和还原映射。而且我想我发现了两个问题,在我写这篇文章时,最初的答案都没有看到,对此感到抱歉。

第一个问题,假设用户在全局映射中使用<C-c>,但在本地缓冲区映射。示例:

E117: Unknown function: <SNR>61_FuncA


在这种情况下,maparg()将优先使用本地映射:

substitute(map_save.rhs, '<SID>', '<SNR>' . map_save.sid . '_', 'g')


已在:h maparg()中得到确认:

join(map(['buffer', 'expr', 'nowait', 'silent'], 'map_save[v:val] ? "<" . v:val . ">": ""'))


,但也许您对缓冲区局部映射不感兴趣,也许您想要全局映射。
唯一的方法我发现可靠地获得有关全局映射的信息,是尝试使用相同的键临时取消映射潜在的,阴影的,缓冲区局部的映射。

可以通过4个步骤完成:


使用键<C-c>保存(潜在的)缓冲区本地映射

执行:silent! nunmap <buffer> <C-c>删除(潜在的)缓冲区本地映射
保存全局映射(maparg('<C-c>', 'n', 0, 1)
还原本地缓冲区映射

第二个问题如下。假设用户没有将任何内容映射到<C-c>,那么maparg()的输出将是一个空字典。在这种情况下,恢复过程不在于安装映射(:nnoremap),而在于销毁映射(:nunmap)。

要解决这两个新问题,您可以尝试使用此功能来保存映射:

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')



Save_mappings()函数可用于保存映射。
它需要3个参数:


键列表;例如:['<C-a>', '<C-b>', '<C-c>']

一种模式;例如:n(用于普通模式)或x(用于可视模式)
布尔标志;如果是1,则表示您对全局映射感兴趣,如果是0,则表示对本地映射

有了它,您可以在正常模式下在字典中使用键C-aC-bC-c保存全局映射:如果要恢复映射,可以调用Restore_mappings(),将包含所有信息的字典作为参数传递: ,当保存/恢复本地缓冲区映射时。因为在保存映射的那一刻到尝试还原它们的那一刻之间,当前缓冲区可能已更改。

在这种情况下,也许可以通过保存当前缓冲区的编号(Save_mappings())。

,然后,bufnr('%')将使用此信息来还原右侧缓冲区中的本地缓冲区映射。我们可能可以使用Restore_mappings()命令,为后者加上一个计数(与先前保存的缓冲区号匹配),并在其后加上映射命令。 >
nnoremap          <C-c>    :echo 'global mapping'<CR>
nnoremap <buffer> <C-c>    :echo 'local  mapping'<CR>


我们必须首先使用:bufdo函数检查缓冲区是否仍然存在,因为与此同时它可能已被删除。

评论


太神奇了,这正是我所需要的。谢谢!

–statox♦
16年5月11日在11:51

#2 楼

在我的插件中,当我有临时映射时,它们总是在本地缓冲-我真的不在乎保存全局映射,也不在乎涉及它们的复杂事物。因此,我的lh#on#exit().restore_buffer_mapping()辅助函数-来自lh-vim-lib。

最后,将发生以下情况: