Vim中有没有不覆盖上一次复制内容的情况下删除文本的方法?

332

我喜欢使用vim,但经常会遇到以下问题:

  • 复制一行
  • 移动到希望粘贴的位置
  • 删除该位置原有的内容
  • 粘贴你之前复制的内容,却发现它粘贴了你之前删除的内容

显然,正确的操作是先删除再复制。但如果我不想这样做,有什么技巧可以解决吗?Vim是否有一个可靠的粘贴缓冲区,或者是否有我可以更改的.vimrc设置?


3
Vi纯粹主义者会对我这样做感到不满,但你也可以使用“mswin”行为。它允许你使用Ctrl-C和Ctrl-V进行复制和粘贴。它使用自己的寄存器,因此你可以使用“yy”和“dd”任意次数。要使用此行为,请将以下行添加到您的*.vimrc*文件中:source $VIMRUNTIME/mswin.vimbehave mswin - Cristian Ciupitu
7
@Cristian - 它使用 * 寄存器。你可以使用vim命令将任意的移动内容复制到系统剪贴板,例如 "*y/foo<enter> 将把光标到下一个 "foo" 的文本复制到Windows剪贴板中。(如果使用 mswin/Ctrl-C,你需要先进入可视化模式。) - dash-tom-bang
@dash-tom-bang:是的,我不记得它是哪个寄存器了。 - Cristian Ciupitu
1
我发现http://vim.wikia.com/wiki/Replace_a_word_with_yanked_text非常有用和深入。 - Micha Wiedenmann
vim-easyclip(https://github.com/svermeulen/vim-easyclip)的设计旨在解决这个确切的问题。 - Steve Vermeulen
@CristianCiupitu 不错的想法,但是 msvin 不起作用了(再也不行了?)。ctrl+v 总是空的。 - phil294
13个回答

371

将内容传递到黑洞寄存器_

删除行而不将其粘贴到寄存器中:

"_dd

请参考 :help registers

如果你想要反复粘贴某个内容,最安全的做法可能是将其复制到一个“命名”寄存器中。

"aY
将一行文本复制到寄存器 a 中。使用 "ap 命令粘贴。

10
哈哈,我之前不知道有黑洞寄存器。谢谢你的启示。 - DarkDust
15
黑洞寄存器是这里的最佳选择,但我有时会觉得“Vp”更好,因为它更短。请注意,未命名的寄存器将填充先前选定的文本。 - Peter Rincker
3
谢谢提及黑洞寄存器,给你一个赞。 - Jeffrey Jose
3
粘贴插入模式:按下 <ctrl>-ra,将寄存器 'a' 中的内容粘贴到当前位置。 - SergioAraujo
26
我觉得将内容复制到“p”寄存器很有用。这样,粘贴就是“pp”。 - critium
显示剩余5条评论

211

你复制的那一行应该还在寄存器0中。所以执行:

"0p

粘贴该行代码(并随时删除)


11
:help "0:数字寄存器0包含最近一次yank命令复制的文本,除非该命令使用了"x寄存器。 - Rory O'Kane
3
这可能是最简单但有效的解决方案!我发现在99%的情况下,我只需要最近复制的内容。这个技巧刚好满足我的需要! - John Chain
啊,太好了!谢谢你。 - user2609980
1
如果您不想一直输入“0p”,您可以在我对类似问题的回答中找到一个简单的方法,将0设置为p的默认寄存器。链接在这里:https://dev59.com/QXVD5IYBdhLWcg3wNIvc#32488853 - Torben
这非常有用!我在我的~/.vimrc文件中添加了nnoremap <C-p> :let @*=@0<CR>p,这使我可以使用Ctrl-p将默认寄存器覆盖为寄存器0,然后粘贴已复制的文本。 - schpet

136

所有剪切和删除操作默认会写入未命名寄存器。但是,最近的剪切和最近的删除总是(分别)存储在编号寄存器中。寄存器0保存了最近的剪切内容。寄存器1-9保存最近的9个删除(其中1是最近的)。

换句话说,删除操作会覆盖未命名寄存器中最近的剪切内容,但它仍然存在于0寄存器中。在其他答案中提到的黑洞寄存器技巧("_dd)之所以有效是因为它防止覆盖未命名寄存器,但这并非必须。

您可以使用双引号引用寄存器,因此可以像这样粘贴最近剪切的文本:

"0p

这是一个很好的参考:


13
这是目前为止这里最好的回答。谢谢。 - Stickley
我一直想知道为什么复制或删除的文本会以如此不可预测的方式出现在哪个寄存器中,有时会覆盖,有时则不会。谢谢提供这个信息! - frankster
7
请注意,只有当复制或删除的内容至少为一行时,才会使用1-9号寄存器。参见:http://vimdoc.sourceforge.net/htmldoc/change.html#quote_number - dash-tom-bang
是的,那是一个很好的观点。 - Wayne
哇,我花了两年时间才学会这个,太棒了! - DZet
2
请注意,在插入模式下,您还可以使用 Ctrl-R 0 来粘贴上一次复制的内容。 - dimus

50

还有一种可能性:

  1. 像平常一样删除你的行
  2. 到你想要粘贴它们的地方,进入可视行模式(V
  3. 选择你想要替换的行
  4. 按下 p 来粘贴你的行

这种方法也有一个额外的好处,即缓冲区与替换的内容“交换”,我发现非常方便。


很好的提示 - 这绝对是我有时需要的东西。不过,我发现使用 XP(按住 Shift 键)在换行方面做得更好。 - Todd Owen
8
你的“额外好处”实际上是个阻碍,因为它防止你再次执行黏贴操作。正如这个例子中所提到的**here。或者,这个问题**建议重新映射键位,这可能是最优美的解决方案:vnoremap p "_dpvnoremap P "_dP。我认为这是结合你的v命令的最佳解决方案。 - mikew
2
你提出的好处正是原帖作者不想要的,也正是他为什么首先提出这个问题的原因。 - fiatjaf
1
@fiatjaf: 并非完全如此。 OP希望将复制的行粘贴到现有行上。首先删除现有行会覆盖当前的yank缓冲区。选择现有行,然后再进行粘贴会交换缓冲区。这解决了OP的问题(尽管如果您想多次粘贴相同的行,则无法重复该过程)。 - knittl
这太棒了! - Moshe
显示剩余2条评论

27

我使用以下映射,使得删除到黑洞寄存器更加容易:

nnoremap R "_d

这样,dd 变成了 Rd,而 d$ 变成了 R$。请注意,R 通常绑定到进入替换模式,但我发现我从未使用过它,因此它是“真正删除”功能的最容易记忆的键。


4
وˆ‘و›´ه€¾هگ‘ن؛ژن½؟用nnoremap d "_d,è؟™و ·ddن»چ然وک¯dd,d$ç­‰ن¹ںهگŒçگ†م€‚ - abhishek77in
2
如果你这样做,dd不会变成“_d_d”(这不是你想要的),对吗? - vitiral

9
" map paste, yank and delete to named register so the content
" will not be overwritten
nnoremap d "xd
vnoremap d "xd
nnoremap y "xy
vnoremap y "xy
nnoremap p "xp
vnoremap p "xp

等等,这有什么帮助呢?dd 仍然会用相同的寄存器替换已经复制的内容 yy - Eric Duminil
@EricDuminil 我想他的意思是“为每个操作替换x为不同的寄存器号码”,否则就像你所说的,没有任何意义。 - Santi
1
@Santi 可能需要明确地写出来。当 x 是一个有效的字母时,它不是最好的占位符。 - Eric Duminil

7
我写了yankstack.vim插件来解决这个问题。它为vim提供了类似于Emacs的kill ring的功能。你可以复制或删除多个内容,粘贴,然后在你复制/删除文本的历史记录中来回切换。我发现这比记住我将某些内容复制到哪个寄存器中更容易。
在我的.vimrc文件中,我有以下映射:
nmap <M-p> <Plug>yankstack_substitute_older_paste
nmap <M-P> <Plug>yankstack_substitute_newer_paste

这使我可以使用 ALT-p 或 ALT-SHIFT-p 在我的粘贴历史记录中来回切换。


4
针对你的具体问题,按照提问方式,你是否可以交换最后两个步骤的顺序呢?
  • 复制行 (不变)
  • 移动到新位置 (不变)
  • 粘贴已复制的行 (原来是第四步)
  • 删除不想要的行 (原来是第三步)

当然了,我通常会使用一个命名寄存器来完成这种操作,但有时候解决方法比想象中简单。


1
沿着同样的思路,您可以选择用“使用V选择要删除的行”替换最后两步,然后用p覆盖它们。 - Rory O'Kane
1
正如其他类似答案的评论所述,插入模式下使用“^”命令会改变yank寄存器的内容,其中包含您刚刚替换的行。这并非对每种情况都是致命的,但值得注意。 - underscore_d

2
你可以使用寄存器:"<register><command>

例如:
这个命令将一行复制到寄存器a,删除另一行,然后粘贴寄存器a
"ayy` (move) `dd"aP

8
作为vim的“寄存器”而闻名。“缓冲区”指的是其他东西。 - lessthanideal

1

如果你是一个恶意用户,你可能会考虑重新映射X来执行"_d"的等效操作。然而,对于我来说,完善这个实现有点棘手。尽管如此,我发现

(define-key evil-normal-state-map "X" 'evil-destroy)
(define-key evil-visual-state-map "X" 'evil-destroy)

(evil-define-operator evil-destroy (beg end type register yank-handler)
  "delete without yanking text"
  (evil-delete beg end type 95 yank-handler)
)

非常好地集成。例如,键入XX将类似于dd功能,X $也将类似于d $,X0将类似于d0等...

如果您想知道它是如何工作的,“95”代表“_ register”,因此它只是重新路由您的调用以删除,就像“_”是按下的寄存器一样。


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