想必用过Vim的人都知道,在Vim里面,以下命令可以替换当前文件的内容:

:[range]s/{要被替换的模式}/{替换的内容}/[flags]

其中range指定替换命令生效的范围。flags指定替换的一些选项,常用的有:

  • c 替换前进行确认

  • g 如果缺乏该选项,只会替换第一个。一般我们所说的替换是全部替换,即加了g选项的替换。

  • i 忽略大小写

  • e 忽略错误

具体细节请:vert help substitude查看。

本文将以此为起点,介绍一些替换小技巧。写这篇文章,主要是分享个人的一些脑洞心得,顺便向接触过Vim的人安利下Vim的一些哲学。

实话实说,使用命令来替换有违于(大多数)程序员的习惯。一般提起“替换”,第一感觉都是按下某个快捷键,然后在某个窗口中输入“查找内容”,再输入“替换内容”,按确定。靠敲命令来替换,总会让人想起sed这样的老东西,想起它那咒文一样的指令(如果还想得起来的话)。

还好,Vim的映射机制让想起这一切不再困难。你仅需映射一份模板:

" 把下面映射添加到vimrc中
" 设置替换命令的模板
nnoremap <leader>s :%s///gc<left><left><left><left>
" :vert help c_<C-R>
" Ctrl-r " 插入最近一次复制/删除的文本
nnoremap <leader>sl :%s/<C-R>"/<C-R>"/gc<left><left><left>
" Ctrl-r Ctrl-w 插入当前光标下的词。
nnoremap <leader>sc :%s/\<<C-R><C-W>\>/<C-R><C-W>/gc<left><left><left>

这样就不用记住完整的替换命令了,仅需填两个空。是不是又回到了熟悉的“查找内容”/“替换内容”模式呢?

Vim哲学第N条:用映射消除重复的劳动。

一般在文本编辑的过程中,常常会有重复某几个步骤的情况。一个合格的Vimer应该学会用映射或其他机制来减少无益的操作。所谓时间就是生命,珍爱生命,从灵活使用映射开始。

" :vert help map 查看如何在Vim中使用映射
" :vert help recording 查看如何在Vim中使用录制

看了刚才的内容,应该不会对Vim里面的替换操作感到陌生了。接下更进一步,教多几个小技巧。

回顾替换命令:

:[range]s/{要被替换的模式}/{替换的内容}/[flags]

最前面的range也是有些门道的。range是Vim中的一个概念,表示文本的某个范围。常用的range有两种:一种是m,n,表示从第m行到第n行,其中.表示当前行,而$表示最未行;另一种是%,表示整个文件,等价于1,$。更多的形式请查看:vert help range

凭借这一点,我们可以实现指定替换的范围,减轻确认时的工作量。下面介绍个例子:

vnoremap <leader>s :s///gc<left><left><left><left>

这个跟前面的normal模式下的设置模板的映射很像,不过有两点不同。一点是,这是作用在visual模式下的映射;另一点是,这个模板里没有指定范围。在visual模式下使用命令,默认范围是当前选中的范围。(参见:vert help v_:

于是乎,我们可以这样使用:

  1. vi{选中当前大括号(代码块)里面的内容。

  2. <leader>s发动映射。

这么一来,替换将仅在当前大括号内生效。在替换局部变量时,比起全局替换,这样的替换方式无疑会更高效。

什么?你说你用Python?嗯,你可以考虑下借助第三方插件来选中代码块:

Vim哲学第N+1条:用好组合技

很多情况下,替换操作涉及多个文件。由于缺乏项目管理的功能,编辑器在这方面自然比不上IDE。不过Vim还是支持对多个文件执行替换操作,虽说有点儿粗糙。Vim提供了名为argdo的机制,可以在多个文件上执行同样的命令。

" 在dataType.cpp和dataType.h中替换filename为fn
:args dataType.cpp dataType.h
" :vert help argdo
:argdo %s/filename/fn/gce | update

args命令接收文件列表,而argdo命令接收要执行命令,update则写入更新了的文件内容。(注意这里的|是用来连接%s/filename/fn/gcupdate成单一的参数,不是管道符)

关于args的更多内容,参见这篇文章:http://vimcasts.org/episodes/populating-the-arglist/

我们可以更进一步,实现全项目内的替换。假设你的项目用git作版本管理,那么通过git ls-files可以获取全部文件名。然后用grep -l pattern $(git ls-files)可以筛选出含有pattern的文件。接下来就是把这份文件列表传递给args:

" 查找整个项目中含有filename的文件,并作为参数传递给args
:args `grep -l filename $(git ls-files)`
:argdo %s/filename/fn/gce | update

就是这样。

Vim哲学第N+2条:善用外部命令来拓展Vim的能力


spacewander
5.6k 声望1.5k 粉丝

make building blocks that people can understand and use easily, and people will work together to solve the very largest problems.


引用和评论

0 条评论