使用多个光标不是Vimmer的事情
正如我在使用多光标(甚至带有插件)的注释中所说的,并不是真正的“遵循Vim方式”,我完全理解它对Sublime-Text的用户很有吸引力,但是您经常可以找到至少是借助Vim内置功能提高了效率。
当然,找到这些替代解决方案并不总是那么容易,有时会花费一些时间,但是随着您在Vim上的经验,它将变得更加容易,并且您会发现,随着时间的流逝,多个游标对您似乎完全没有用。
太酷了,但是我如何找到替代方法呢?
没有通用的答案,因为它在很大程度上取决于您要尝试做的事情,我将尝试就尝试的第一件事给出一些提示:
点命令 .
点命令可能是Vim中功能最强大的工具之一,它只允许我们重复上一次更改。我无法比德鲁•尼尔(Drew Neil)在他的“ 实用Vim”中更好地解释它。我认为每个Vimmer应该考虑阅读这本书。
该命令的优势在于,最后的更改可以是对字符,行或整个文件进行的操作。例如,更改可以由您进入插入模式的那一刻和您回到普通模式的那一刻来界定。
考虑到这一点,使用多光标可以轻松完成您想做的事情:
首先让我们建立环境:让我们按照您的建议编写
\section[]{}
然后进行可重复的更改
。光标现在位于上}
,单击F[
可回到[
字符上。然后使用进入插入模式i
并使用键入My first section in this book
并返回到普通模式ESC
:
\section[My first section in this book]{}
接下来是神奇的部分:让我们键入f{
将光标放在{
角色上,然后按一下.
以重复上一次更改:
\section[My first section in this book]{My first section in this book}
dot命令的所有挑战是学习如何进行可重复的更改:它与grokking Vim一起提供,但基本的目的是了解如何以可重复的方式进行更改。
例如,要在行尾插入半冒号,您更喜欢使用
A;
而不是$a;
。为什么?
因为A;
创建了原子动作,所以当您.
在另一行上使用时,无论您在行中的什么位置,都将在末尾插入分号。而使用时$a;
,将更改分为两部分,$a
并插入,;
所以如果使用.
它将在光标的当前位置插入分号。
注意 Vim中的魔术公式为n.
。一个非常酷的工作流程是:
- 搜索您要进行编辑的地方
/pattern
- 进行可重复的编辑
- 用于
n
转到下一个地方进行编辑
- 用于
.
重复编辑
- 重复最后两个步骤:您是世界之王(或至少是编辑之王)
巨集
宏是Vim中另一个非常重要的工具,因为它可以让您记录一系列击键并重复进行,就像您再次键入一样。
我以您的第二个用例为例:
variable1 = 2
my_variable2 = 12
var3 = 14
再次重要的是学习如何提高宏的效率(我将在后面给出一个反例):
variable1.someStuff = 2
my_variable2 = 12
var3 = 14
- 现在,您可以使用宏重复进行编辑。当您在右边进行编辑时,只需使用即可执行宏
@q
。由于我们要执行两次,因此您可以使用它,2@q
并且您将获得以下结果:
variable1.someStuff = 2
my_variable2.someStuff = 12
var3.someStuff = 14
注1:您可能已经注意到,0ea
在宏的开头使用确实非常重要。确实,如果在记录宏并再次执行它之前将光标放在第一个单词的末尾,则结果将是:
variable1.someStuff = 2
my_variable2 = 12.someStuff
var3 = 14.someStuff
作为您的光标,文本将在更改行后插入到光标的位置(在这种情况下,即行的结尾)
注意2:宏非常强大,您甚至可以在不熟悉时创建递归宏。这里的宏可能是:
`0ea.someStuff<Esc>j@q`
最终版本@q
会自己调用宏而不是使用2@q
;您将使用@q
完,所有工作都将完成。
视觉障碍
这是另一个技巧,它并不直接适用于您的用例,但是对于同时编辑大量行非常有用。让我们提取一下CSS代码:
li.one a{ background-image: url('/images/sprite.png'); }
li.two a{ background-image: url('/images/sprite.png'); }
li.three a{ background-image: url('/images/sprite.png'); }
如果将精灵从images
移到components
怎么办?
好了,您可以将光标放在上i
,images
然后按<C-v>
。这将启动可视块模式,该模式允许选择块。现在,您可以键入t/
以选择要更改的单词,并2j
选择所有出现的单词。
之后,您只需键入c
以更改单词,然后输入components
。当您退出插入模式时,您会看到:
li.one a{ background-image: url('/components/sprite.png'); }
li.two a{ background-image: url('/components/sprite.png'); }
li.three a{ background-image: url('/components/sprite.png'); }
全局命令
全局命令是一种工具,它允许在与模式匹配的行上应用ex模式命令,这再次是将相同的更改应用于不同位置而无需多个光标的好方法。
语法如下:
:[range] g / pattern / command
有关[range]
参数的更多详细信息,请参见:h :range
。我在这里不做详细说明,我只想提醒一下,它%
代表整个文件,'<,'>
代表最后选择,并1,5
代表文件的第1至5行。
此参数定义将由全局命令处理的行。如果未指定范围,则%
默认情况下将使用全局命令。
[pattern]参数是用于搜索引擎的搜索模式。由于它集成了搜索历史记录,因此您可以将该字段保留为空白,然后全局命令将使用搜索历史记录中的最后一个搜索模式。
最后,[command]参数是您可能习惯使用的ex命令。
现在,全局命令的行为非常简单:
- 遍历[range]参数中定义的所有行
- 如果当前行与定义的模式匹配,则应用命令
由于[command]参数是ex命令,因此您可以做很多事情。让我们采用以下不太有趣的伪代码,其中包含许多调试消息:
var myList = null
var i = 0
myList = new List()
echo "List instantiated"
for (i=0; i<10; i++)
myList.Add(i)
echo i . " added to the list"
echo "end of for loop"
现在,我们确定您确定此代码有效,并且想要删除这些无用的echo
语句:
因此,您只需使用以下功能:
:%global/echo/delete
也可以缩写为
:g/echo/d
需要注意的是%
消失了,global
简称g
和delete
作为d
。您可能会想到结果是:
var myList = null
var i = 0
myList = new List()
for (i=0; i<10; i++)
myList.Add(i)
注意1:我花了一些时间认识到的重要一点是,该
normal
命令是ex命令,这意味着您可以将其与global命令一起使用。这可能非常强大:假设我想复制所有包含echo的行,不需要宏,甚至不需要魔术公式n.
。我可以简单地使用
:g/echo/normal YP
和瞧:
var myList = null
var i = 0
myList = new List()
echo "List instantiated"
echo "List instantiated"
for (i=0; i<10; i++)
myList.Add(i)
echo i . " added to the list"
echo i . " added to the list"
echo "end of for loop"
echo "end of for loop"
注2: “如果我想在与特定模式不匹配的行上使用命令怎么办?”
global
有一个与之相反的vglobal
缩写命令v
,global
除了命令将应用于与[pattern]参数不匹配的行外,该命令的用法与之完全相同。如果我们申请的话
:v/echo/d
在前面的示例中,我们得到:
echo "List instantiated"
echo i . " added to the list"
echo "end of for loop"
该delete
命令已应用于未包含的行echo
。
在这里,我希望这些提示能给您一些想法,以帮助您摆脱多光标插件并以Vim的方式使用Vim ;-)
如您所见,这些示例非常简单,并且仅用来说明按照Vim的方法,您实际上很少需要多个游标。我的建议是,当您遇到认为有用的情况时,写下来,然后花一些时间来找到更好的解决方案。您最终有99%的时间会找到一种更快/更有效的方法。
另外,我会再重复一遍,但我真的鼓励您阅读
Drew Neil的《实用Vim》,因为这本书不是关于“如何在Vim中做到这一点”,而是关于“如何学习以Vim的方式思考”。这将使您以良好的方式为将来的问题构建自己的解决方案。
PS特别感谢@Alex Stragies的编辑工作以及他对这一长篇文章所做的更正。