如何在Vim中查找和替换而不必键入原始单词?


53

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

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

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

有没有更快的方法?


1
.重复最后一个“命令块”(不确定确切的术语。例如:move + action + text。),例如:( cwtoto<Esc>从光标更改为带有“ toto”的单词的结尾), c/foo<enter>bar<Esc>(从光标更改为“ foo”,然后替换为“ bar”。然后可以移动(用光标,hjkl或数字+ hjkl(执行n次),G(转到文件末尾),/ something(位于“东西”)等),然后按一下.以重做相同的“命令块”
Olivier Dulac

我确实使用了很多!但是,如果要拉出一个单词然后在多个位置替换另一个单词,则此方法不起作用。那是我遇到问题的主要用例,类似n ve "0p。不幸的是不能只用复制.
乔尔

5
n ce ctrl-r 0 <esc> n.n.n.n.n.n.
Rich

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

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

Answers:


81

实际上,我的工作流程与您的工作流程非常相似(复制和粘贴相似的代码块,然后:s用于更改变量名称),尤其是当我编写很多行相似的行(除了它们使用哪个变量)时。但是我有几件事可能对您有用。

一般vim的东西

  1. 首先,这将帮助您意识到:s//foo/g它将填充您上次使用的任何搜索词并将其替换为foo。仅此一项就可以节省大量时间,但是将其与星号命令结合使用(在当前光标下搜索单词)可以节省大量时间。例如,将全部更改foospam,而不是

    :%s/foo/spam/g
    

    这要求您同时输入foo和spam(可能是长变量),您可以简单地导航到foo,然后执行以下操作:

    *:%s//spam/g
    

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

    (旁注:星号命令将添加单词边界,即光标所在单词的周围\<\>周围。如果您不希望这样做,请g*改用。)

  2. 如果我可以一次击键重复替换操作,那将是更可取的

    您可以@:用来重做最后一个替代命令。如果您想:s在很多行上执行命令,那很好,但是并不是所有的行都:%s行不通。另一个选项是确认标志,即:%s///gc。这将使您可以键入yn在每次出现时决定是否要替换它。

    正如Desty指出的那样,您还&可以重新运行最后一个替代命令,但是请注意,这不会保留您的标志(例如globalconfirm)。这是好是坏取决于您的个人意见。如果您想使用它但也保留其标志,则可以

    nnoremap & :&&<cr>
    
  3. 如果要更改块中的每个匹配项,但不更改文件中的每个匹配项,则可以始终使用该块的可视化选择,它将填充

    :'<,'>
    

    替代,将其限制为您选择的行。当与星号方法结合使用时,我发现这非常有用。此外,如果要在同一行代码块上执行多个替代命令,则可以使用gv重新选择相同的行来执行另一条命令。(无论:substitute是普通命令还是普通命令)

    如果执行其他替换命令,则无需重新选择相同的行,因为它们:'<,'> 仍将在相同的行上运行。但是,键入更方便

    gv:s
    

    而不是

    :'<,'>s
    

我喜欢的插件/映射

  1. 我下面的映射.vimrc扩展了星号方法,因此我只需要键入新的所需名称即可:

    "(R)eplace all
    nnoremap <leader>r yiw:%s/\<<C-r>"\>//g<left><left>
    

    要使用此功能,只需将光标放在要更改的单词上,然后输入即可<leader>rNewVarName<cr>。请注意,这将替换所有匹配项,而不会给您提供确认选项,因此您可能不太喜欢它。当然,您可以将其更改为

    nnoremap <leader>r yiw:%s/\<<C-r>"\>//gc<left><left><left>
    

    允许确认选项。

    编辑:跟随Mass的回答rcorre的评论,您也可以将其写为

    nnoremap <leader>r :%s/\<<C-r><C-w>\>//g<left><left>
    

    这样做的好处是可以使您的未命名寄存器保持不变。

  2. 例如,如果您要同时重命名一组相似的变量,请更改

    fooSuffix
    PrefixFoo
    foo1
    SHOUTINGFOO
    

    barSuffix
    PrefixBar
    bar1
    SHOUTINGBAR
    

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

    :%S/foo/bar/g
    

    它会为您处理大写。

这些应该对您有很大帮助,但请让我知道您对这些方法是否不满意或缺乏。我很乐意谈论更多方法。:)


推荐读物:


哦,我在这里学到了几件事。真好!您可以采用的另一种方法是<leader>rc(或cr/c在末尾添加。另外,我已经set gdefault设定好了,因为我几乎永远都不想替换一个结果,所以它就是 yiw:%s/\<<C-r>"\>//<left><left>
Wayne Werner

2
太棒了!
Scott Lundberg

2
如果您ctrl+r-ctrl+w在映射中使用(如@Mass所建议的那样),则它应该可以正常工作,但不会弄乱您的yank寄存器(可能不希望如此)
rcorre

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

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

20

c_CTRL-R地图的家人可以提高您的工作流程相当多:

  1. 使用时:s/,您无需键入变量名,只需CTRL-R CTRL-W将光标下的单词插入命令行即可(注意:这不会像特殊字符一样转义特殊字符*)。

  2. 使用进行少量更改后ce,您可以使用来搜索旧单词/ CTRL-R -。然后使用.重复更改。

  3. 如果您需要更改搜索模式,CTRL-R /则将现有搜索放入命令行。

  4. 最后,您可以使用 CTRL-R {register}


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

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

12

还有另外两种方法(令我惊讶!)尚未被提及。

使用gn命令

gn就像n命令一样工作,除了除了跳到比赛之外,它还进入视觉模式,并选择了整个比赛。

因此,要更改单词(或任何可以与正则表达式匹配的单词!),请先搜索1,然后按,然后依次按cgn要替换为匹配的文本并Esc返回到正常模式。

(您提到有时要使用的替换文本存储在"0yank寄存器中-请注意,您可以快速输入此文本,而无需通过Ctrl+R0在插入模式下按来重新键入它。)

然后,您可以跳至下一场比赛只需单击一次即可重复更改.

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

1:快速,使用此页面上的其他建议,如*Ctrl+RCtrl+W等。另见彼得Rincker的评论,了解有关如何开始填充您的更多建议。

使用选择模式

(我从romainl 在vim subreddit上帖子中学到了这一点。我不知道他是自己发明还是只是将其传递了。)

选择模式有点像视觉模式,除了如果您开始键入,则所选文本会立即替换为您键入的内容(例如大多数其他编辑器中选择的工作方式)。

因此,您可以使用此三重映射来进行快速查找/替换,如果需要,可以更改中间文件:

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

普通模式映射:进入选择模式时,单词离所选光标最近。

然后,您可以输入所选单词的替代词,并且(无需Esc)使用以下命令:

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

然后,您可以输入新的替换项,也可以按.使用:

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


昨天gn我在回答vi.stackexchange.com/a/13696/488时确实提到了该方法的价值!您的答案虽然格式好得多。
TankorSmash

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

我会建议有某种“视觉之星”插件与使用gn。为您提供更多搜索选项。穷人的视觉星图:xnoremap * :<c-u>let @/=@"<cr>gvy:let [@/,@"]=[@",@/]<cr>/\V<c-r>=substitute(escape(@/,'/\'),'\n','\\n','g')<cr><cr>。相关的Vimcast情节:使用gn进行搜索匹配
Peter Rincker

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

7

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

我的工作流程通常如下:

  1. 将光标移至要替换的单词。
  2. 按住ctrl-n直到选中所有内容。
  3. c并开始键入替换项。

这里方便的事情是,您还可以执行除搜索替换之外的更复杂的操作,尽管我发现如果走得太远,该插件会开始出现一些故障。


4

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

在命令行窗口中,您还可以使用普通模式命令来编辑命令行,例如,这意味着您可以在命令行历史记录中拉动并粘贴部分先前的命令。

请注意,使用光标时,光标将被移至命令行窗口,这意味着CTRL-R CTRL-W和在光标下插入文本的类似方法在此处不起作用,这与常规命令行不同。


1
当您已经输入:substitute命令的一半时,也可以打开命令行窗口,方法是按<ctrl-f>
Rich

2

对于我来说,解决方案介于其他答案之间:

假设您有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

我要做的是:

  1. 我要替换的文本开头是“苹果”
  2. 点击v进入视觉模式
  3. 移至我要替换的最后一个单词,在这种情况下,需要几行 2j
  4. :s/<C-R><C-W>/它使用视觉选择作为的范围:s,然后将单词插入光标下方
  5. 我按一下<C-F>,这使您进入一种可以像编辑缓冲区一样编辑命令的模式。因此,您可以使用任何要选择替换字的补全或插件来完成操作。

总而言之,流程虽然如此,/apples<CR>2nv/apples<CR>nn:s/<C-R><C-W/oranges<CR>但看起来比实际情况要糟。

在您的情况下,如果您的范围包含一些要跳过的内容,则可以将追加/c:s命令中以跳过不需要的内容:s/<C-R><C-W/orange/c

或者(也许更简单),你可以搜索你想要替换整个单词,c焊割,然后gn.重复搜索,并再次应用相同的变化。它看起来像/\<apple\><CR>viwcorange<ESC>gn.gn.gn..ngn.要重复三个,然后跳过一个实例,然后最后一次重复。


您最后描述的方法对我不起作用(在Vim 7.4中,用调用vim -Nu NONE)。当我按第一下时,它只是发出哔声.。知道为什么吗?在任何情况下,这将是简单的使用ciw,而不是viwn,而不是gn做出这样的改变。
Rich

2

这与问题不完全相关,但仍应有助于选择要替换的词。

增量搜索(set incsearch)使您可以键入需要查找的单词的前缀,并且随着您键入的越多,vim会自动跳到该前缀的第一个匹配项。如果您按Enter键,搜索将被确认,并在Esc上放弃(在后一种情况下,光标将跳回到搜索开始之前的位置)。

但是,增量搜索提供了另一项有用的功能。当您已经输入单词的前缀并且光标位于正确的位置时,按<C-L>可以将光标下的单词中的字符添加到搜索字符串中。这样,您几乎无需按键即可输入替换的字符串。

因此,如果我想替换someOddVariableNameevenOdderVariableName

  1. 键入/someOddV并得出发生someOddVariableName;
  2. 按住<C-L>直到someOddVariableName完全出现在搜索行中;
  3. 按Enter确认搜索;
  4. :%s//evenOdderVariableName/g或您需要的任何替换命令;您可能希望在此处遵循其他答案。

2

你可以在缓冲区把变量名第一(yw复制的字),然后复制粘贴它:%s击中<C-r>"


2

如果要替换新的(粘贴的)块中的变量,我有两个建议:

自动更换

  • 转到新块的第一行,点击ma。这是在标记位置并为其命名a

  • 转到新块的最后一行,点击mb。现在,您有两个命名的位置。

  • 现在在两行之间替换,如下所示: :'a,'bs/oldVarName/newVarName/g

  • 也就是说,替换的范围介于您的两个标记之间。

手动方式

  • 转到新块的第一行。

  • 使用/oldVarName找到的第一个实例oldVarName

  • 像这样更改它: cwnewVarName<Esc>,这将把单词更改为newVarName并命中转义符。

  • 使用n去的下一个实例oldVarName

  • 使用.重复上次的变化(即重申,cw)。


2

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

:S/child{,ren}/adult{,s}/g正确地转换childadultChildrenAdultsCHILDADULT

如果要处理驼峰词,则需要在原始词和替换词中包括大写字母。

%:S/wordWrap/textBreak/g正确处理wordWrapwordwrap


0

正如其他人已经指出的那样,如果将光标放在单词的顶部,则可以使用Ctrl + r系列命令,因为Ctrl + r Ctrl + w可能是最适合使用的命令。

但是,该方法仅适用于插入一个单词(光标下方的一个单词),但是如果缓冲区中要使用的文本有几个部分,该怎么办?由于您无法在命令行中移动光标。最好的方法是将需要的文本部分复制到不同的寄存器中,这可以通过在复制操作符前面加上“ {reg_letter}(引号和寄存器中的字母)来完成。通过这种方式,可以稍后使用Ctrl + r {reg_letter}在命令行中插入各种单词,这有点尴尬,但可以。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.