在Vim中,有没有一种不将文本放入寄存器就可以删除的方法?

542

我经常使用Vim,想用刚刚复制的文本替换掉一段代码块。

但是当我删除要被替换的代码块时,该代码块自身会进入寄存器,从而清除我刚刚拷贝的文本。所以我已经养成了先复制、粘贴、再删除不想要的文本的习惯,但对于大段的代码块来说,这种方法变得很麻烦,因为需要将插入的文本和要删除的文本分开处理。

那么,在Vim中最简单快捷的替换文本的方法是什么呢?

  • 是否有一种方式可以在不将文本放入寄存器的情况下删除它?
  • 是否有一种方式可以指定“替换下一个单词”或“替换到下一个段落”之类的操作?
  • 还是最好的方法是利用多寄存器功能?

2
vim-easyclip(http://www.github.com/svermeulen/vim-easyclip)是专门为解决这个问题而设计的。 - Steve Vermeulen
1
我曾经问过一个问题,当时并不知道这个问题与你实际遇到的问题重复了(链接为https://dev59.com/tYLba4cB1Zd3GeqPYwMb),但是我的问题与标题问题不太一样。简而言之,使用`viwp`(代表单词)将所取内容替换即可。 - OJFord
30个回答

484

要删除一些东西而不保存到寄存器中,您可以使用“黑洞寄存器”:

"_d

当然,你也可以使用其他不存储你感兴趣内容的寄存器。


7
起初我认为这个命令没有正常工作。后来我意识到我使用的是美式键盘的“死键”版本。在这种键盘布局下,我需要输入"<space>_d"才能打出双引号。空格实际上是必需的。 - Sebastián Grignoli
8
要删除一个字符,请尝试:"_x - kenorb
75
这是一个标准文本编辑器胜出的领域。你只需按下删除键即可。 - Razor
37
你可以用 yy3yy(或其他)来复制某个东西,这样它就会被放置在无名寄存器和寄存器 0 中。然后你可以用 "0p 继续粘贴它,即使你之前已经使用了 d 命令。 - Halfgaar
5
@VincePanuccio 不需要。你可以在vim中像标准文本编辑器一样按x键删除一个键,但问题是如何删除该键而不将已删除字符移动到缓冲区,这样您仍然可以粘贴在删除前在缓冲区中的内容。 在记事本中试试看吧。 - user146043
显示剩余9条评论

109

是的。这比先删除“旧”文本稍微复杂一些,但是:

我首先开始...

line1
line2
line3
line4

old1
old2
old3
old4

我使用 Shift+V 键选中了第一行、第二行、第三行和第四行,并使用 D 命令将它们删除。

接着,我以同样的方式删除了旧的1-4行。

最后,执行以下操作:

"2p

这将粘贴倒数第二个复制的行(第1-4行)。"3p 将会粘贴倒数第三个,以此类推。

所以最终我会得到:

line1
line2
line3
line4

参考资料: Vim文档关于编号寄存器的说明


如果您使用<Shift-v>选择行,则只需使用d而不是dd即可删除它们。 - Heikki Naski
34
1到9号键用于删除。要粘贴最近使用'y'命令复制的内容,您需要使用“0p”。这在以下情况下非常有用:当您首先复制某些内容以替换其他内容,但其他内容覆盖默认的粘贴寄存器时,可以使用此功能。 - Rafael de F. Ferreira
1
要了解更多撤销操作的高级功能,请查看Gundo(图形化撤销)脚本 - Lumi
@RafaeldeF.Ferreira:你的评论并不是这个问题的答案,但它实际上解决了可能导致OP首先提出问题的实际问题。对我来说,那才是真正的问题。 - 0xc0de

86

VIM文档:编号为0的寄存器包含最近一次复制命令的文本,除非该命令使用["x]指定了另一个寄存器。

例如,我们复制了“foo”并删除了“bar” - 寄存器0仍然包含“foo”!因此,“foo”可以使用"0p粘贴

因此,从寄存器0粘贴可能是回答“如何以某种方式使用多寄存器功能”的最佳方法之一。

例如,将<Leader>0映射为从寄存器0中粘贴的快捷方式。

nnoremap <Leader>0 "+p

这样,普通的pP可以用于从未命名寄存器(默认行为)粘贴,当您特别想要从最后一次复制中粘贴时,请使用<Leader>0


57

拥有一个简单的映射,可以方便地将当前选择替换为缓冲区是很方便的。

例如,当您将此代码放入您的 .vimrc 文件中时:

" it's a capital 'p' at the end
vmap r "_dP

然后,在将某些内容复制到寄存器中(例如使用“y”),您只需选择要替换的文本,然后在键盘上简单地按下“r”。所选内容将被当前寄存器替换。
vmap - mapping for visual mode
"_d - delete current selection into "black hole register"
P - paste

1
更高级的版本请查看:https://dev59.com/5nVC5IYBdhLWcg3wcwsm#290723 - Luc Hermitte
我想使用 vmap <C-P> "_dP - Harry Lee
9
您可以直接将文本放置在不删除当前选定内容的情况下。例如,Vp会立即用复制的文本替换您当前的行。 - Jencel
1
这个解决方案无法解决经常用于删除的 d 问题。 - Victor Yarema

43

我把以下内容放在了我的vimrc文件中:

noremap  y "*y
noremap  Y "*Y
noremap  p "*p
noremap  P "*P
vnoremap y "*y
vnoremap Y "*Y
vnoremap p "*p
vnoremap P "*P

现在我使用剪贴板寄存器进行复制和粘贴操作,无需关心默认寄存器的状态。另外一个好处是能够方便地从其他应用程序粘贴内容。我知道我失去了一些功能(我不再能够管理剪贴板寄存器和默认寄存器中不同的剪贴信息),但是我实在无法跟踪多个寄存器/剪贴板。


8
除了所有的remap之外,你还可以使用set clipboard=unnamed或者set clipboard=unnamedplus,这将分别将默认剪贴板更改为*+ - Steve
8
如果你这样做,d 命令会覆盖之前复制到剪贴板寄存器的内容(yank),但这正是问题所想要避免的。 - 79E09796
1
@79E09796,我的意思并不是它会发生;我只是在评论这个答案而不是原来的问题。 - Steve
2
@tba 将 noremap 替换为 nnoremap(两个n),它应该可以正常工作。 - meain
1
@francojposa 我更新了答案以澄清,我只是指的是我不再能够管理剪贴板寄存器和默认寄存器的不同复制信息,这没关系,因为我无论如何都无法跟踪多个寄存器/剪贴板。 - Magnus
显示剩余3条评论

31

默认情况下,所有的yank和delete操作都会写入未命名寄存器。然而,最近的yank和delete总是分别存储在编号寄存器中。寄存器0保存最近的yank。寄存器1-9保存最近的9个delete(其中1是最近的)。

换句话说,delete操作将覆盖未命名寄存器中最近的yank,但在0寄存器中仍然存在。其他答案中提到的黑洞寄存器技巧("_dd)之所以可行是因为它可以防止覆盖未命名寄存器,但这并非必需。

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

"0p

这是一个很好的参考资料:


24

对于您提供的具体示例,如果我理解了问题,那么这可能有效:

*Highlight what you want to put somewhere else
*delete (d)
*Highlight the code that you want it to replace
*paste (p)

9
除了需要覆盖多个选择时,这个方法都很有效。 - user69173

16

为了强调EBGreen所说的:

如果你在选择文本时粘贴,选定的文本将被替换为粘贴的文本。

如果您想复制一些文本,然后在多个位置粘贴它,请使用"0p进行粘贴。编号为0的寄存器包含最近一次拷贝命令中的文本。


另外,您可以列出所有寄存器的内容:

:registers

那个命令使得像dbr的回答中所做的删除操作更加方便。你还会看到 /,%,# 寄存器。(同时参见:help registers)


最后,尝试使用 cwcW 命令来改变一个单词(包括或不包括一个尾随空格)。(使用大写字母W会包括标点符号。)


13

一种极简侵入性的解决方案:

寄存器 0 始终包含最后一次 yank 的内容(正如 Rafaelalex2k8idbrii 已经提到的那样)。不幸的是,总是选中寄存器 0 可能会很烦人,因此如果 p 默认使用 "0 ,那就太好了。这可以通过将以下行放入您的 .vimrc 文件中来实现:

noremap p "0p
noremap P "0P
for s:i in ['"','*','+','-','.',':','%','/','=','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
    execute 'noremap "'.s:i.'p "'.s:i.'p'
    execute 'noremap "'.s:i.'P "'.s:i.'P'
endfor

第一行将每个 p 笔画映射为 "0p。然而,这会阻止 p 访问任何其他寄存器。因此,所有具有显式选定寄存器的 p 笔画都被映射到循环内相应的命令行表达式。对于 P 也是如此。
这种方式保留了标准行为,除了隐式的 pP 笔画现在默认使用寄存器 0
提示 1:现在剪切命令是 "0d 而不只是 d。但我懒得打这么长 ;) 因此,我使用以下映射:
noremap <LEADER>d "0d
noremap <LEADER>D "0D

默认情况下,leader键是\,因此您可以轻松通过键入\d\D来剪切文本。
提示2:多键映射的默认超时时间相当短。您可能需要增加它以在选择寄存器时获得更多时间。有关详细信息,请参见:help timeoutlen,我正在使用:
set timeout timeoutlen=3000 ttimeoutlen=100

10
如果您正在使用Vim,则可以使用可视模式,这就像选择一样,但是具有分离模式的功能,这是vi/vim的基础。
您想要做的是使用可视模式选择源,然后复制,然后再次使用可视模式选择目标范围,然后从默认缓冲区粘贴文本。
例如:
在以下文本文件中:
1| qwer 2| asdf 3| zxcv 4| poiu
对于以下序列:ggVjyGVkp,您将得到:
1| qwer 2| asdf 3| qewr 4| asdf
解释如下:
- gg:跳到第一行。 - V:整行开始可视模式。 - j:向下移动一行(启动选择之前,它会使选择向下增长一行)。 - y:复制到默认缓冲区(两行已选择,它会自动退出可视模式)。 - G:跳到最后一行。 - V:开始可视模式(与之前相同)。 - k:上移一行(与以前相同,在启用可视模式的情况下,此操作会使选择向上增长一行)。 - p:粘贴(选择了最后两行,它将使用缓冲区中的内容替换这些行 - 在此示例中为前两行)。
这样做的一个小不便之处是,它把最后一个块放在了缓冲区中,因此不希望重复粘贴相同的内容,所以您会想要将源保存到一个命名缓冲区中,例如 "ay" (到称为 "a" 的缓冲区),并使用类似 "ap" 的东西进行粘贴(但是如果您正在编程,您可能不希望多次粘贴而是创建一个函数并调用它,对吧?对,没错!)。

如果你只使用vi编辑器,那么你将不得不使用隐式标记而不是可视化模式,:he mark查看更多信息。很抱歉,我并不擅长使用隐式标记,我更习惯于使用可视化模式。


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