我想优化Vim中的“查找和替换”工作流程。这是我经常做的事情,因为我敢肯定你们大多数人也会这样做。通常-复制块并在几个地方更改变量的名称。我知道,我知道,这可能会触发您“为什么要复制和粘贴代码”的反射,但是我们不要走那条路了……有很多有效的用例:)

我'我很清楚搜索和替换命令::s:%s,但我不喜欢它们。它迫使我同时输入要搜索的完整变量名和要更改的变量名。也许有更好的方法来解决用:%s进行打字的次数?我经常使用描述性长的变量名,因此对我来说确实是一个大问题。我也不喜欢从头开始输入变量名容易产生错字,并且会浪费时间和精力来寻找错字。我更喜欢输入一次,然后复制和粘贴以尽可能完全避免这种情况。

我当前的工作流程使用移动/拉动/选择/搜索/放置的某种组合来移动文件和一一替换。它虽然不是很好,但具有避免输入完整的变量名的好处。我可能只需要输入/的前几个字母或根据周围的情况使用另一个移动命令(例如fx),然后按ve选择整个单词。我也不介意必须为每个实例重复一次。如果没有确认每个更改,我永远不会做一个完整的替换。但是,如果我可以一次击键来重复替换操作(这种方法无法做到),那将是非常可取的。每个替换通常类似于n然后ve然后p(或更糟糕的是"0p

是否有更快的方法?

评论

。重复最后一个“命令块”(不确定确切的术语。例如:move + action + text。例如:cwtoto (从光标到单词的结尾,用“ toto”更改),c / foo <输入> bar (从光标更改为“ foo”之前,然后替换为“ bar”),然后可以移动(用光标,hjkl或数字+ hjkl(执行n次),G(转到至文件末尾),/ something(在“ something”之前)等),然后按。重做相同的“命令块”

我确实使用了很多!但是,如果要拉出一个单词然后在多个位置替换另一个单词,则此方法不起作用。那是我有问题的主要用例,类似n ve“ 0p。不幸的是,不能仅用。
n ce ctrl-r 0 n.n.n.n.n.n.

@Joel您还可以记录一个宏(先是qa,然后是您想要的所有内容,然后q是:创建宏a),然后使用:@a重播它(通常,使用134 @ a进行134次)。宏可以根据需要进行精心设计(例如:找到某物,移至正确的位置,然后将单词改成适当的位置,然后移至正确的位置,以便能够在更好的位置再次被调用)

我从这个问题中学到了比在网站上其他任何问题都有用的VIM技巧。谢谢!

#1 楼

实际上,我的工作流程与您的工作流程非常相似(复制和粘贴相似的代码块,然后使用:s更改变量名称),尤其是当我编写很多行相似的行(除了它们使用哪个变量)时。但是我做了几件事,您可能会觉得有用。您使用了last并将其替换为foo。仅此一项就可以节省大量时间,但是将其与星号命令结合使用(在当前光标下搜索单词)可以节省大量时间。例如,要将所有:s//foo/g更改为foo,而不是做
:%s/foo/spam/g



要求您同时输入foo和spam(可能是长变量),您可以只需导航至foo,然后执行以下操作即可:
    *:%s//spam/g

这样,您可以通过搜索快速获取要更改的变量的名称,而无需键入它。这也可以与您当前大量使用spam的当前工作流程很好地配合。启用n也是有帮助的,因为这样您就可以直观地看出要替换的变量在哪里。 。如果您不希望这样做,请改用hlsearch。)



如果我可以一次击键重复进行替换操作,那将是更可取的选择。



您可以使用\<重做最后一个替代命令。如果您想对很多行执行\>命令,但不是全部都执行,那很好,因此g*将不起作用。另一个选项是确认标志,即@:。这样,您可以在每次出现时键入:s:%s来决定是否要替换它。
正如Desty指出的那样,您也可以执行&以重新运行最后一个替代命令,但是请注意,这不会保留您的标志(例如:%s///gcy)。是好是坏取决于您的个人意见。如果您想使用它但也保留它的标志,则可以
    nnoremap & :&&<cr>



如果要更改块中的每个匹配项,但不能更改每个匹配项在文件中,您始终可以使用块的可视化选择,它将选择填充
:'<,'>



,以将其限制为所选行。当与星号方法结合使用时,我发现这非常有用。另外,如果要在同一行代码块上执行多个替换命令,则可以使用gv重新选择相同的行以执行另一条命令。 (无论是n还是常规命令)
如果执行其他替代命令,则无需重新选择相同的行,因为global仍将在同一行上运行。但是,键入
    gv:s


    :'<,'>s

我喜欢的插件/映射更方便


我有以下映射在我的扩展了星号方法的confirm中,这样我只需键入新的所需名称即可:
"(R)eplace all
nnoremap <leader>r yiw:%s/\<<C-r>"\>//g<left><left>



要使用此名称,只需将光标放在所需的单词上进行更改,然后键入:substitute,您就完成了。请注意,这将替换所有匹配项,而不会给您提供确认它们的选项,因此您可能不太喜欢它。当然,要允许使用Confirm选项,您可以将其更改为
    nnoremap <leader>r yiw:%s/\<<C-r>"\>//gc<left><left><left>

edit:遵循Mass的回答和rcorre的评论,您也可以将其写为
    nnoremap <leader>r :%s/\<<C-r><C-w>\>//g<left><left>

如果您要同时重命名一组相似变量,例如,更改
fooSuffix
PrefixFoo
foo1
SHOUTINGFOO
,则可以使未命名的(yank)寄存器保持不变。






    barSuffix
    PrefixBar
    bar1
    SHOUTINGBAR

单独替换所有这些可能会很痛苦。这是tpope / vim-abolish派上用场的地方。您可以在一个命令中将所有这些替换为:
    :%S/foo/bar/g

,它会为您解决大写字母的问题。
这些应该对您有很大帮助,但是请告诉我这些内容您不喜欢或感觉不到的方法。我很乐意谈论更多方法。 :)

推荐阅读:


:'<,'>


.vimrc


<leader>rNewVarName<cr>


:h s_c

:h *


:h g*


评论


哦,我在这里学到了几件事。真好!您可以采用的另一种方法是 rc(或cr),最后添加该/ c。另外,我已经设置了gdefault set,因为我几乎从未想过替换单个结果,所以它就是yiw:%s / \ << C-r>“ \> //

–Wayne Werner
17年9月26日在12:11

太棒了!

–斯科特·伦德伯格
17年9月26日在15:16

如果您在映射中使用ctrl + r-ctrl + w(如@Mass所建议),则它应能正常工作,但不会弄乱您的yank寄存器(这可能会或可能会不理想)

–rcorre
17年9月26日在17:23

使用突出显示的文本块(在剪切/拉紧等之前)时要记住的一件事是,您可以在正常模式下输入gv以重新突出显示最后突出显示的文本。

–user859
17-09-27在12:13



我已经编写了github.com/hauleth/sad.vim插件,该插件的工作方式类似于方法2。hovewer允许您使用。执行进一步的替换。

– Hauleth
17年9月29日在21:37

#2 楼

c_CTRL-R系列地图可以大大改善您的工作流程:


使用:s/时无需键入变量名,只需使用CTRL-R CTRL-W将光标下的单词插入命令行即可(注意:这不会像*一样转义特殊字符。)
使用ce进行少量更改后,您可以使用/ CTRL-R -搜索旧词。然后使用.重复更改。
如果需要更改搜索模式CTRL-R /会将现有的搜索放入命令行中。
最后,您可以使用CTRL-R放置任何寄存器的内容。 {register}


评论


好点子!我总是忘了CtrlR。我会看看我是否也可以使用它。

–乔尔
17-09-25在23:54

值得一提的是Ctrl-R /吗?如果要使用与上一个搜索词几乎但不完全相同的搜索词,我会在:s命令中经常使用它。

–丰富
17年9月27日在15:32

#3 楼

还有另外两种方法(令我惊讶的是)还没有被提及。除了跳到匹配项之外,它还会进入可视模式,并选中整个匹配项。

因此要更改单词(或任何可以与正则表达式匹配的单词!),首先要搜索它,然后按cgn,然后按一下要替换匹配项的文本,然后按Esc键返回正常模式。这样您就可以快速输入该内容,而不必在插入模式下按Ctrl + R0来重新输入。)

然后您可以跳至下一个匹配项,并使用一个。击键!

如果您不想在任何地方进行替换,只需按un键即可撤消更改并跳至下一场比赛。

1:通过使用此页面上的其他建议,例如*,Ctrl + RCtrl + W等,可以快速。有关如何最初填充搜索的更多建议,另请参阅Peter Rincker的注释。

使用选择模式

(我是从romainl在vim subreddit上的帖子中学到的。我不知道他是自己发明还是只是将其传递了。)类似于可视模式,除非您开始输入所选文本,否则立即将其替换为您键入的文本(例如大多数其他编辑器中选择的工作方式)。

因此,您可以使用此三重映射来完成快速查找/替换,您可以在必要时更改中间文件:

nnoremap § *``gn<C-g>
inoremap § <C-o>gn<C-g>
snoremap <expr> . @.


普通模式映射:使用最接近所选光标的单词进入选择模式。 >
然后您可以输入替换项对于选定的单词,并且(不按Esc键)使用:

插入模式映射:跳转到下一个匹配项并进入选择模式。

然后,您可以输入新的替换项或按。使用以下命令:

选择模式映射:重新输入最后插入的文本-基本上重复一次最后插入。

评论


昨天我在答案vi.stackexchange.com/a/13696/488的末尾提到了gn方法,因为它的价值是什么!您的答案虽然格式好得多。

–TankorSmash
17-9-27在16:56



@TankorSmash所以你做到了!请注意,我是多么想念的。请注意,尽管我实际使用的方法与您的不同(我只调用gn一次),并且它是特定的方法(而不仅仅是gn命令),但我还没有提到它。

–丰富
17-09-27在18:04



我建议您将某种“视觉之星”插件与gn一起使用。为您提供更多搜索选项。穷人的视觉星图:xnoremap *: let @ / = @“ gvy:let [@ /,@”] = [@“,@ /] / \ V =替代(转义(@ /,'/ \'),'\ n','\\ n','g') 。相关的Vimcasts情节:使用gn操作搜索匹配

– Peter Rincker
17-09-27在19:48



我创建了插件sad.vim来帮助实现这种流程。

– Hauleth
17年9月28日在9:24

#4 楼

插件https://github.com/terryma/vim-multiple-cursors在很大程度上解决了这个问题。

我的工作流程通常如下:


将光标移到要替换的单词上。
按住ctrl-n直到所有内容都被选中。
c并开始键入替换字符。您还可以执行除搜索替换之外的更复杂的操作,尽管我发现如果您走得太远,该插件会开始出现一些故障。

#5 楼

进行尚未提及的另一种处理方式:您可以使用命令行窗口(在正常模式下以q:打开-有关更多信息,请参见:help q:),您可以在其中完全访问Vim的自动完成功能(i_CTRL-N / i_CTRL-P

在命令行窗口中,您还可以使用普通模式命令来编辑命令行,例如,这意味着您可以拉动和粘贴零件命令行历史记录中的先前命令。不像在常规命令行中那样在那里工作。

评论


在输入:substitute命令的一半时,也可以通过按键来打开命令行窗口。

–丰富
17年9月27日在18:17

我最喜欢的是ctrl-f。这似乎是最自然的-像往常一样使用命令行,并在必要时跳转到功能更强大的窗口,无需更改工作流程。

–斯里·卡迪米蒂(Sri Kadimisetty)
20年1月9日在22:51

#6 楼

解决这个问题的方法介于我的其他答案之间:

假设您有6条相同的行,并且您想在中间的两行中替换“ apple”:

I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days
I have apples for days


我要做什么:


我要替换的文本开头是“苹果”。进入可视模式
转到我要替换的最后一个单词,在这种情况下,需要几行,因此, v,然后在光标下插入单词
我点击了2j,这使您进入一种可以编辑命令的模式,就好像它是一个缓冲区一样。因此,您可以使用任何要选择替换词的补全或插件来完成操作。

对于您的情况,可以将:s/<C-R><C-W>/附加到:s命令中,以跳过不需要的内容,如果您的范围包含要跳过的内容:<C-F>

或者(也许更简单),您可以搜索要替换的整个单词,将其挂住,然后重新搜索并再次应用相同的更改。看起来像/apples<CR>2nv/apples<CR>nn:s/<C-R><C-W/oranges<CR>重复三遍,然后跳过一个实例,然后再重复一次。

评论


最后描述的方法对我不起作用(在Vim 7.4中,用vim -Nu NONE调用)。当我按下第一个按钮时,它只是发出哔哔声。在任何情况下,使用ciw代替viw并使用n代替gn以这种方式进行更改会更简单。

–丰富
17-09-27在18:02



#7 楼

这并不完全与问题相关,但仍应有助于选择要替换的单词。当您键入越来越多时,它会自动跳到该前缀的第一个匹配项。如果您按Enter键,搜索将被确认,并在Esc上放弃(在后一种情况下,光标将跳回到搜索开始之前的位置)。 。当您已经输入了单词的前缀并且光标位于正确的位置时,请按set incsearch,将光标下的单词中的字符添加到搜索字符串中。这样,您几乎无需按键即可输入替换后的字符串。 <C-L>的出现;someOddVariableName直到evenOdderVariableName完全出现在搜索行中;
按Enter确认搜索;

/someOddV或您需要的替换命令;您可能希望在此处遵循其他答案。


#8 楼

您可以先将变量名放在缓冲区中(用yw复制一个单词),然后单击:%s将其复制粘贴到<C-r>"中。

#9 楼

如果要替换新的(粘贴的)块内的变量,我有两个建议:块,击中ma。这是标记位置并命名为a
转到新块的最后一行,单击mb。现在您有两个命名位置。
现在在两行之间进行替换,如下所示::'a,'bs/oldVarName/newVarName/g
也就是说,替换的范围在您的两个标记之间。手动方式


转到新块的第一行。
使用/oldVarName查找oldVarName的第一个实例。
将其更改为:cwnewVarName<Esc>,即会将单词更改为newVarName并进行转义。
使用n转到oldVarName的下一个实例。

#10 楼

这里的许多其他答案已经涵盖了改善查找和替换工作流程的内置方法,但我想提及Tim Pope的插件abolish.vim。它可以替代您想要覆盖的更大范围的案件,通常是由于大小写变化或因为您需要处理单词的单数和复数形式。

:S/child{,ren}/adult{,s}/gchildadultChildren。 /> Adults可以正确处理CHILDADULT

#11 楼

正如其他人已经指出的那样,如果将光标放在单词的顶部,则可以使用Ctrl + r系列命令,这可能是最适合使用的Ctrl + r Ctrl + w
但是,该方法仅适用于插入一个单词(光标下方的一个单词),但是如果缓冲区中有几个文本部分又会插入该单词怎么办?我们要用吗?在命令行中移动光标时,
最好的方法是
将需要的部分文本复制到不同的寄存器中,您可以
通过在复制操作符前面加上“ {reg_letter}(引号和寄存器中的字母)
来实现。这样以后就可以在命令行中使用Ctrl + r {reg_letter插入各种单词
。 }。这有点尴尬
但是可以用。

但是回到以前的想法,如果我们在编写时将光标位置移到缓冲区上方,那将是很好的这样,我们
就可以使用Ctrl + r Ctrl + w从不同位置“拾取”文本,所以我创建了一个
插件,该插件可以执行以下操作:EXtend.vim
还有很多其他功能可以进行替换操作。