evil-replace-with-register 的精简/改进版本

众所周知,evil 会自动把被删除的文本复制到 register 里,那么想要把替换一段文本又不想把被删除掉的文本写入 register 里(说人话就是只想删除文本不想剪切),这样就能够持续的把一段文本替换到多处。如果不想用任何插件,想通过 vim 命令来直接解决的办法就是先选中文本然后再输入命令"ay,也就是通过把这段文本复制到 a 寄存器里,然后对于想要替换的文本要先选中,然后再输入 "ap 也就是把寄存器 a 里的文本替换这段选中的文本。

以上的操作看着就知道有多麻烦,在之前我是使用 evil-replace-with-register 插件来解决这个问题,这个插件提供了一个命令绑定到 gr 从而可以实现类似其他常用 vim 命令的方便的操作方式,比如 yiw 复制了一个单词以后,就可以一直不停的 griw (当然也是 dot-repeatable 的)去替换你想要替换掉的单词,而不必担心被删除掉的文本被剪切了。

但是这个命令对于 visual block 的实现不太友好,简单来说他没办法做到以下的情形:

aa ee
bb gg
cc hh

没办法通过块选择以及块替换把以上的文本替换成

aa aa
bb bb
cc cc

原因在于 这个插件处理 block 情形时没有调用 evil 的原生api,选择自己逐行处理的,因此没办法处理这种替换文本和被替换的区域都是个整体的情形。

其实调用 evil 的原生 api 能非常简单的解决这个问题,我写了一个简单的代码就解决了 block 的场景,同时对于更加常用的 line-wise 模式和 char-wise 模式的支持也没有问题。

(evil-define-operator evil-replace-with-register (count beg end type register)
    "Replacing an existing text with the contents of a register"
    :move-point nil
    (interactive "<vc><R><x>")
    (setq count (or count 1))
        (if (eq type 'block)
                (evil-visual-paste count register)
            (delete-region beg end)
            (evil-paste-before count register)))

这个是不是用快捷键P就能解决?

不是。p/P只是光标位置之前粘贴和之后粘贴的区别。和是否会把被删除文本写到寄存器无关。

想多次粘贴的基本都是复制来的内容,这样只要一直按 "0p 就可以了,我把它绑到zp了,好按很多。

万一是剪切的东西想第二次粘贴,"2p 就行,第三次就 "3p ,等等。但是真的非常少见,给 "0p 绑个好用的键基本解决这里90%的问题。哦这个情况因为比上一段讲的少很多,多按几个键也没关系,这里也可以用补全框架的show kill ring功能手动去最近的kill ring里选。

"0p 必须先选中啊,不能在动作后面接 textobject / motion,哪怕给 "0p 绑到了别的按键上,也还是要先按 v 选中。

比如要替换接下来的两行内容,就是 vj"0pgrj 的区别, 把这个常用的动作写成一个 operator 还是方便很多的。