codecamp

Vimscript Operator-Pending映射

这一章我们将来探索Vim映射系统中另外一个神奇的部分:“Operator-Pending映射”。开始之前,我们先解释下这里面的几个词含义。

一个Operator(操作)就是一个命令,你可以在这个命令的后面输入一个Movement(移动)命令,然后Vim开始对文本执行前面的操作命令,这个操作命令会从你当前所在的位置开始执行,一直到这个移动命令会把你带到的位置结束。

常用到的Operator有dyc。例如:

按键   操作       移动
----   --------   -------------
dw     删除       到下一个单词
ci(    修改       在括号内
yt,    复制       到逗号

Movement映射

Vim允许你创建任何新的movements,这些movements可以跟所有命令一起工作。执行下面的命令:

:onoremap p i(

在缓冲区中输入下面的文字:

return person.get_pets(type="cat", fluffy_only=True)

把光标放到单词“cat”上,然后敲击dp。结果会发生什么?Vim会删除括号内的所有文字。你可以把这个新建的movements当作“参数”。

onoremap命令会告诉Vim当它在等待一个要附加在operator后面的movement的时候,如果这个movement是p的话,它会把它当作i(。所以当我们在运行dp的时候,就象是在对Vim说“delete parameters”,而Vim会把它理解为“在括号内删除”。

我们现在可以立马对所有的operators使用这个新建的映射。再次在缓冲区中输入上面的文字(或者直接把之前修改恢复一下)。

return person.get_pets(type="cat", fluffy_only=True)

把光标放到单词“cat”上,然后敲击cp。这次又会发生什么?Vim会删除括号中的所有文字,不过这一次删除之后Vim会处于插入模式,这是因为你使用的是“change”,而不是“delete”。

再看一个示例。执行下面的命令:

:onoremap b /return<cr>

现在把下面的文字输入到缓冲区:

def count(i):
    i += 1
    print i

    return foo

把光标放到第二行的i上,然后按下db。会发生生么?Vim把整个函数体中直到return上面的内容都删除了,return就是上面的映射使用Vim的通用查找得到的结果。

当你想搞清楚怎么定义一个新的operator-pending movement的时候,你可以从下面几个步骤来思考:

  1. 在光标所在的位置开始。
  2. 进入可视模式(charwise)。
  3. ... 把映射的按键放到这里 ...
  4. 所有你想包含在movement中的文字都会被选中。

你所要做的工作就是在第三步中填上合适的按键。

改变开始位置

你可能已经从上面所学的东西中意识到一个了问题。如果我们定义的movements都是从光标所在的位置开始的话,那么这就会限制我们做一些我们想使用movement来做的事情。

但是Vim并不会限制你去做你想做的事情,所以对于这个问题肯定有解决办法。执行下面的命令:

:onoremap in( :<c-u>normal! f(vi(<cr>

这个命令看起来有些复杂,不过我们还是先试试它能干什么。将下面的文字输入缓冲区:

print foo(bar)

把光标放到单词print上面,然后敲击cin(。Vim会删除括号内的内容然后进入插入模式,并且光标会停留在括号的中间。

你可以将这个映射理解为“在下一个括号内(inside next parentheses)”。它会对当前行光标所在位置的下一个括号内的文本执行operator。

我们再创建一个“在上一个括号内(inside last parentheses)”的movement进行对照。(在这里使用“前一个(previous)“可能更准确,但这会覆盖“段落(paragraph)”movement)

:onoremap il( :<c-u>normal! F)vi(<cr>

先试试确保这个命令可以工作。

那么这些映射是怎么工作的呢?首先,<c-u>比较特殊,可以先不用管(你只需要相信我这个东西可以让这个映射在任何情况下都能正常工作)。如果我们删除它的话,这个映射会变成这个样子:

:normal! F)vi(<cr>

:normal!会在后面的章节谈到,现在你只需要知道它可以在常用模式下模拟按下按键。例如,运行:normal! dddd会删除两行,就像按下dddd。映射后面的<cr>是用来执行:normal!命令的。

那么现在我们可以认为这个映射的关键是运行下面这些按键组成的命令:

F)vi(

This is fairly simple: 这个命令很容易理解:

  • F): 向后移动到最近的)字符。
  • vi(: 进入可视模式选择括号内的所有内容。

这个movement结束在在可视模式下选择中我们想操作的文本,然后Vim会对选中的文本执行操作,就像通常情况一样。

一般规则

下面两条规则可以让你可以很直观的以多种方式创建operator-pending映射:

  • 如果你的operator-pending映射以在可视模式下选中文本结束,Vim会操作这些文本。
  • 否则,Vim会操作从光标的原始位置到一个新位置之间的文本。

练习

为"around next parentheses"和"around last parentheses"创建operator-pending映射

为打括号创建类似的in/around next/last的mappings。

阅读:help omap-info,看看你可不可以搞清楚<c-u>是干啥的。

Vimscript 自动命令组
Vimscript 更多Operator-Pending映射
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

vi命令手册

Vimscript编程参考

Vim 其他内容

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }