如何在Vim中高效地反复粘贴相同的文本到多行?

10

目标

复制一行并使用它来覆盖其后面的一些行。

假设

在这种情况下,最好手动选择要应用替换的行。换句话说,不希望使用自动查找和替换。

类比

将此过程视为创建一个文本“印章”,然后浏览列表 - 每个项目都是“印章”行后面的文本行 - 并决定是否应该使用“印章”的内容覆盖该行(在前一种情况下,当然要用“印章”替换该行)。

将光标下的行替换为印章的内容,应尽可能简单;最好只需按.(重复上次更改)或@@(执行宏寄存器@的内容)即可。

问题

当需要多次替换时,直接进行以下步骤:

  1. 将光标定位到要复制的行(使用移动命令)。
  2. 进入逐行可视模式(通过V命令)。
  3. 复制所选文本(使用y命令)。
  4. 手动将光标定位到要替换的行(使用移动命令)。
  5. 再次进入可视模式以选择要替换的文本(使用V命令)。
  6. 粘贴选定的文本(使用p命令)。

然而,当需要进行多次替换时,该方法不起作用。具体来说,在步骤6中替换文本会覆盖包含最初复制并打算用作“印章”的行的(未命名)寄存器。

我尝试过的方法

我尝试使用"_y来将文本复制或删除到_寄存器中,从而避免丢失印章的内容,但我正在寻找的是在手动浏览列表并在适当时应用替换时最快捷和舒适的输入方式。

我不想使用的方法

如果可以的话,我宁愿不使用宏或“重映射”。

演示样例文件

请参见下面的起始文件样例及所需的最终阶段,以获得更进一步的说明。

样例文件,起始状态

此时,我选择blueberry并将其作为我的“印章”。

blueberry

apple
banana
coconut
apple
banana
coconut
apple
banana
coconut

示例文件,期望的最终状态

在经过列表后,我应用了一些替换,对某些行进行了“盖章”,使它们与“印章”blueberry行相同。

blueberry

apple
banana
blueberry
apple
banana
coconut
apple
banana
blueberry
6个回答

2
为了使你的工作流程正常运行,你需要从上一次复制的寄存器"0中粘贴,而不是默认的寄存器。
因此,使用Vy(或相同的yy)来像以前一样复制第一行,然后将光标定位到要替换的行上,执行以下操作:
V"0p

这将当前行替换为之前复制的文本,但不会覆盖已复制的文本。我希望我理解您的意思了!

编辑1:使用宏重复操作

我惊讶地发现无法使用.重复此操作,但这可能是由于使用了可视模式。要使用宏重复此操作,请执行以下操作:

qqV"0pq

宏可以通过按下@q@@来重复执行。 编辑2:使用 . 重复 以下是尝试使用.而不是使用可视化模式使其可重复的方法。在复制“印章”行并移动光标后,执行以下操作:
"_S<c-r>0<delete>

该操作使用插入模式的命令,将寄存器<0>中的内容插入。请注意,是必需的,因为邮戳行包含回车符。如果没有(即使用而不是进行复制),则可以省略。


没错,你理解得很正确。那么,我应该如何方便地重复这个操作,而不用录制宏呢?(例如,qq...然后@q或者@@) - Robottinosino
尝试使用 . 让它工作是非常棒的,但实际上并不起作用(例如,我需要先将 0 移动到行首,此时我可能最好使用宏,并且它绝对不能用于带有缩进的“标记”行)。话虽如此,使用 . 的想法正是我希望达到的...(当然,我已经知道如何记录宏了,我甚至在我的问题中提到了这一点) - Robottinosino
假设您没有重新映射它,您可以使用 Y 而不是 yyVy 来复制初始行。(许多人将 Y 重新映射为从光标到行尾的复制,就像 DC。但是 Y 的默认操作是复制整行) - Trevor Powell

1
一个非常简单的解决方案:只需将此脚本放入您的.vimrc文件中,然后通过输入两个逗号(,,)切换“删除键”的“缓冲区覆盖”副作用行为以进入“无副作用”模式。
在这种模式下,您的工作流现在可以按照您所描述的方式正常工作:无论您喜欢什么,都可以复制、选择、粘贴和重复删除,而您的缓冲区始终保持不变。如果您希望恢复vim的正常缓冲区更改行为,请再次输入两个逗号(,,)。
该脚本是这里的被接受的答案: vim toggling buffer overwrite behavior when deleting

1

我认为如果不进行至少一点“重新映射”,你是无法达到目标的。

我已经使用这个工具很长时间了:

vnoremap <leader>p "_dP

pP仍然像往常一样工作,当我想要重复相同的粘贴时,我只需在视觉选择上按下,p。您还可以映射单个功能键以使整个过程更快。

此外,您是否了解替换中的c标志?

:%s/coconut/blueberry/c

每次匹配都会要求您确认。


替换操作中的 c 标志绝对棒极了。非常感谢您教给我!顺便说一句,考虑到您已经使用那个映射很"长"时间了,似乎不只是我一个人需要多次“盖章”。我会考虑您的建议... 但我总是更喜欢临时宏而不是永久重映射。 - Robottinosino
临时映射怎么样?:vnoremap <leader>p "_dP。我经常需要用新的URL替换长的URL:vi",p非常有用。 - romainl

1

可以使用 Ex 命令来实现所述的工作流程。

对于单个替换,复制“标记”行(使用 yyVy:y 或其他方式),然后反复使用 :put:delete 命令的组合:

:pu|-d_

像其他Ex命令一样,这个命令可以很容易地使用@:快捷键重复执行(参见:help @:)——除非在此期间发出了另一个Ex命令(在这种情况下,将重复执行该命令)。

当然,您也可以将上述Ex命令记录为宏,并以这种方式调用它。


1

这里有很多回答概述了一般的键或命令。我将它们转化为我的ReplaceWithRegister插件,该插件还处理了许多边角情况,并允许通过 . 命令快速重复。我经常使用您描述的创建印章并替换技术,并发现我的脚本是不可或缺的。如果您不喜欢它,插件页面还提供了其他插件的链接。


0

从要复制的行的开头开始:

  1. y$ 复制整行(不包括换行符)。
  2. jk 前进到下一行进行替换(如有需要,重复此步骤)。
  3. 使用您复制的文本替换该行
    • C<c-r>0<esc>0 (第一次)
    • . (随后)
  4. 如果有更多行要替换,请转到 2。

每一步之后,光标将保留在第0列。


你写道:“每一步之后,光标将保持在第零列。” - 你确定吗?在我的机器上它不起作用。话虽如此,我仍然感谢你的回答。 - Robottinosino
@Robottinosino 请问哪个步骤可以让光标水平移动? - Trevor Powell
我说错了:只是光标不会自动移动到第一列... - Robottinosino
没错。如果你喜欢的话,你可以使用 +- 代替 jk;它们分别将光标移动到下一行和上一行的第一列。这些键离主键盘区有点远,不过这只是我通常不太喜欢去的地方而已。 :) - Trevor Powell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接