Combine and Conquer in Vim
Combine and Conquer
Much of Vim’s power stems from the way that operators and motions can be combined. In this tip, we’ll look at how this works and consider the implications.
Operator + Motion = Action
The d{motion}
command can operate on a single character (dl
), a complete word (daw
), or an entire paragraph (dap
). Its reach is defined by the motion. The same goes for c{motion}
, y{motion}
, and a handful of others. Collectively, these commands are called operators. You can find the complete list by looking up :h operator , while Table 2, Vim's Operator Commands, on page 25, summarizes some of the more common ones.
Trigger | Effect |
---|---|
c |
Change |
d |
Delete |
y |
Yank into register |
g~ |
Swap case |
gu |
Make lowercase |
gU |
Make uppercase |
> |
Shift right |
< |
Shift left |
= |
Autoindent |
! |
Filter {motion} lines through an external program |
Table 2—Vim’s Operator Commands |
The g~
, gu
, and gU
commands are invoked by two keystrokes. In each case, we can consider the g
to be a prefix that modifies the behavior of the subsequent keystroke. See Meet Operator-Pending Mode, on page 27, for further discussion.
The combination of operators with motions forms a kind of grammar. The first rule is simple: an action is composed from an operator followed by a motion. Learning new motions and operators is like learning the vocabulary of Vim. If we follow the simple grammar rules, we can express more ideas as our vocabulary grows.
Suppose that we already know how to delete a word using daw
, and then we learn about the gU
command (see :h gU ). It’s an operator too, so we can invoke gUaw
to convert the current word to SHOUTY case. If we then expand our vocabulary to include the ap
motion, which acts upon a paragraph, then we find two new operations at our disposal: dap
to delete, or gUap
to make the whole paragraph shout.
Vim’s grammar has just one more rule: when an operator command is invoked in duplicate, it acts upon the current line. So dd
deletes the current line, while >>
indents it. The gU command is a special case. We can make it act upon the current line by running either gUgU
or the shorthand gUU
.
Extending Vim’s Combinatorial Powers
The number of actions that we can perform using Vim’s default set of operators and motions is vast. But we can expand these even further by rolling our own custom motions and operators. Let’s consider the implications.
Custom Operators Work with Existing Motions
The standard set of operators that ships with Vim is relatively small, but it is possible to define new ones. Tim Pope’s commentary.vim plugin provides a good example.2 This adds a command for commenting and uncommenting lines of code in all languages supported by Vim.
The commentary command is triggered by gc{motion}
, which toggles commenting for the specified lines. It’s an operator command, so we can combine it with all of the usual motions. gcap
will toggle commenting for the current paragraph. gcG
comments from the current line to the end of the file. gcc
comments the current line.
If you’re curious about how to create your own custom operators, start by reading :h :map-operator .
Custom Motions Work with Existing Operators
Vim’s standard set of motions is fairly comprehensive, but we can augment it further by defining new motions and text objects.
Kana Natsuno’s textobj-entire plugin is a good example.3 It adds two new text objects to Vim: ie
and ae
, which act upon the entire file.
If we wanted to autoindent the entire file using the =
command, we could run gg=G
(that is, gg
to jump to the top of the file and then =G
to autoindent everything from the cursor position to the end of the file). But if we had the textobj-entire plugin installed, we could simply run =ae
. It wouldn’t matter where our cursor was when we ran this command; it would always act upon the entire file.
Note that if we had both the commentary and textobj-entire plugins installed, we could use them together. Running gcae
would toggle commenting throughout the current file.
If you’re curious about how to create your own custom motions, start by reading :h omap-info .
结合与征服
Vim的强大功能在很大程度上源自于操作符和动作指令可以被组合使用的方式。在这个技巧中,我们将探讨它是如何工作的,并考虑其含义。
操作符 + 动作 = 行动
d{motion}
命令可以操作一个单独的字符(dl
)、一个完整的单词(daw
)或一个整个段落(dap
)。其作用范围由动作指令定义。c{motion}
、y{motion}
以及其他一些命令同样如此。这些命令统称为操作符。你可以通过查找:h operator
找到完整列表,而第25页的表2,Vim的操作命令,总结了一些更常见的操作符。
g~
、gu
和gU
命令通过两次按键调用。在每种情况下,我们可以认为g
是一个修改后续按键行为的前缀。更多讨论见第27页的遇见操作符-等待模式。
操作符与动作的组合形成了一种语法。第一条规则很简单:一个行动由一个操作符后跟一个动作组成。学习新的动作和操作符就像学习Vim的词汇一样。如果我们遵循简单的语法规则,随着词汇量的增长,我们可以表达更多的想法。
假设我们已经知道如何使用daw
删除一个单词,然后我们学习了gU
命令(见:h gU
)。它也是一个操作符,所以我们可以调用gUaw
将当前单词转换为大写。如果我们然后扩展我们的词汇表包括ap
动作,它作用于一个段落,那么我们发现有两个新的操作可用:dap
删除,或gUap
使整个段落大写。
Vim的语法只有另一条规则:当一个操作命令被重复调用时,它作用于当前行。所以dd
删除当前行,而>>
缩进它。gU
命令是一个特例。我们可以通过运行gUgU
或简写gUU
使其作用于当前行。
扩展Vim的组合能力
我们可以使用Vim默认的操作符和动作集合执行的动作数量是巨大的。但通过创建我们自己的自定义动作和操作符,我们可以进一步扩展这些功能。让我们考虑一下其含义。
自定义操作符与现有动作兼容
随Vim一起提供的标准操作符集相对较小,但定义新的操作符是可能的。Tim Pope的commentary.vim插件提供了一个很好的例子。这个插件为Vim支持的所有语言中的代码行添加了注释和取消注释的命令。
评论命令通过gc{motion}
触发,它为指定的行切换注释。这是一个操作命令,所以我们可以将它与所有常用的动作结合。gcap
将切换当前段落的注释。gcG
从当前行评论到文件的末尾。gcc
评论当前行。
如果你好奇如何创建你自己的自定义操作符,请开始阅读:h :map-operator
。
自定义动作与现有操作符兼容
Vim的标准动作集相当全面,但我们可以通过定义新的动作和文本对象进一步增强它。
Kana Natsuno的textobj-entire插件是一个很好的例子。它为Vim添加了两个新的文本对象:ie
和ae
,它们作用于整个文件。
如果我们想使用=
命令自动缩进整个文件,我们可以运行gg=G
(即,gg
跳到文件顶部然后=G
从光标位置到文件末尾自动缩进一切)。但如果我们安装了textobj-entire插件,我们可以简单地运行=ae
。当我们运行这个命令时,光标的位置无关紧要;它总是作用于整个文件。
注意,如果我们同时安装了commentary和textobj-entire插件,我们可以将它们一起使用。运行gcae
将切换整个当前文件的注释。
如果你好奇如何创建你自己的自定义动作,请开始阅读:h omap-info
。