当删除时,Vim如何切换缓冲区覆盖行为

5
Vim非常好用,但是像许多人一样,当我想复制、删除和粘贴时,我的yank缓冲区会被删除操作覆盖掉,这真的很烦人。现在我知道有101种解决方法和映射,其中一些在像这个帖子中列举出来:Any way to delete in vim without overwriting your last yank? 但所有这些解决方案都有缺点,即使我是一个缓冲区大师(我不是),例如,过多的按键 - 而当我通常快速删除4个字符时,我通常会使用xxxx (只需一个按键,因为我按住它并等待自动重复),现在对我来说,切换到,x,x,x,x或其他映射以使用不同的缓冲区是不现实的。
真正理想的是一个模式切换,可以切换D、d、X和x键的副作用行为,以便它们交替地执行或不执行它们的文本写入缓冲区。这样我就可以简单地进入“无副作用”模式,随心所欲地删除,然后在准备好时粘贴。如有必要,重新启用副作用。有人知道如何做到这一点吗?
[更新:解决方案] 好的,我得到了:我编写了一个函数来切换“无副作用”模式......完美!请参见下面接受的正确答案。
[更新#2] 我的解决方案仍然非常好,当我需要大量删除和粘贴时,我经常使用它。但与此同时,我还发现了一种更轻便的方法,以满足复制、粘贴、删除的特定用例,其中要删除的文本是连续的。正常进行yank文本后,我使用v命令可视化突出显示要删除的文本,然后使用p命令直接将其粘贴覆盖。 这样就可以实现所需的效果,而不需要任何特殊映射。
这个工作流程唯一的问题是如果我想再次粘贴,原始的粘贴缓冲区会被粘贴突出显示的文本覆盖掉,但是通过以下在.vimrc中的映射很容易改变这种行为:
vnoremap p "_dp
vnoremap P "_dP

我认为你提供的那些解决方案是要放在 vimrc 文件中的。像 nnoremap dd "_dd 这样的代码是否在你的 vimrc 文件中可用? - none
不是很好,因为我已经使用dd删除整行了。当然,我的所有映射都在vimrc中,但这个解决方案仍然无法解决所有其他的d,比如dw、dl、2dw、d$...更不用说所有的x了。而且我必须在每次按键时输入特殊字符才能使用它。一个完整的模式会更好... - Magnus
我不明白全局切换开关如何比需要时将内容拖到命名缓冲区更简单。 - Prince Goulash
将剪贴内容复制到命名缓冲区是一个可以接受的备选方案,但它需要预见性 -- 我必须提前知道我想要重用刚刚剪贴或删除的文本。此外,如果有很多复制/粘贴操作,就会增加大量额外的按键操作。 - Magnus
你为什么要使用 xxxx 而不是 4x - Radon Rosborough
1
@RadonRosborough 因为那样我就得停下来数一下,而 xxxx 可以启发式地完成 - 只需不断击打 x 直到不需要的文本消失。 - Magnus
2个回答

10

好的,我明白了。这个在 .vimrc 文件中的脚本可以让我有效地切换“无缓冲区副作用”模式,当激活“无缓冲区副作用”模式时,d和x键不再覆盖缓冲区。

将以下内容添加到 .vimrc 中

function! ToggleSideEffects()
    if mapcheck("dd", "n") == ""
        noremap dd "_dd
        noremap D "_D
        noremap d "_d
        noremap X "_X
        noremap x "_x
        echo 'side effects off'
    else
        unmap dd
        unmap D
        unmap d
        unmap X
        unmap x
        echo 'side effects on'
    endif
endfunction
nnoremap ,, :call ToggleSideEffects()<CR>

然后,要切换进入和退出此模式,请使用逗号 (,) 键组合。


5

我认为尝试为每个删除/更改命令“关闭”副作用将会过于困难,如果不是不可能的话。处理这种情况的基本方法如下:

  • 使用黑洞("_)寄存器与您的删除或更改命令一起使用。例如:"_dd
  • 使用包含最近复制内容的"0寄存器进行粘贴。例如:"0p
  • 将文本复制到命名寄存器中。例如:"ayy,然后稍后执行"ap

我个人倾向于使用"0p方法,因为它符合我的思维方式。

现在,既然您没有要求这样做,我提供了一些函数,可以修改粘贴命令以在所谓的paste_copynopaste_copy模式之间切换。其中nopaste_copy是Vim的默认行为。将以下内容放入您的~/.vimrc中:

function! PasteCopy(cmd, mode)
  let reg = ""
  if exists('g:paste_copy') && g:paste_copy == 1 && v:register == '"'
    let reg = '"0'
  elseif v:register != '"'
    let reg = '"' . v:register
  endif
  let mode = ''
  if a:mode == 'v'
    let mode = 'gv'
  endif
  exe "norm! " . mode . reg . a:cmd
endfunction

command! -bar -nargs=0 TogglePasteCopy let g:paste_copy = exists('g:paste_copy') && g:paste_copy == 1 ? 0 : 1<bar>echo g:paste_copy ? '  paste_copy' : 'nopaste_copy'

nnoremap <silent> p :call PasteCopy('p', 'n')<cr>
nnoremap <silent> P :call PasteCopy('P', 'n')<cr>
nnoremap <silent> ]p :call PasteCopy(']p', 'n')<cr>
nnoremap <silent> [p :call PasteCopy('[p', 'n')<cr>
nnoremap <silent> ]P :call PasteCopy(']P', 'n')<cr>
nnoremap <silent> [P :call PasteCopy('[P', 'n')<cr>
vnoremap <silent> p :<c-u>call PasteCopy('p', 'v')<cr>
vnoremap <silent> P :<c-u>call PasteCopy('P', 'v')<cr>

您可以通过:TogglePasteCopy切换粘贴复制模式。您可能更喜欢像这样的映射。
nnoremap <leader>tp :TogglePasteCopy<cr>

作为结束语,我强烈建议使用"0p或使用命名寄存器,因为它们是vim原生的功能,而且不需要再担心一个额外的“模式”。


谢谢提供以上代码,我会测试一下!我发现“0p方法”不够充分,因为它只在我复制了文本时才有效,而当我删除文本时,这种方法通常无效。我讨厌为如此相似的操作使用两种不同的工作流程。我想一个命名缓冲区可能是vim能提供的最好的选择。也许可以有可能有状态地更改vim的默认缓冲区吗?这可能可以满足我正在寻求的某些模式化特征。 - Magnus
据我所知,除了'clipboard'之外,没有办法在vim中更改默认寄存器,而在这种情况下它也无用。我认为,对于您的工作流程,命名寄存器可能是最好的方法。我的建议是尝试使用它几周,然后考虑简化工作流程的方法。也许你只需要一个快速映射到黑洞寄存器或其他一些命名寄存器。 - Peter Rincker

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