Vim实用技巧-介绍以及各种模式

Vim实用技巧-介绍以及各种模式

[TOC]

Vim在学习的过程中的学习曲线一直是很难的,做很多操作有一种无从下手的感觉,也不知道从哪里去找,秉着要使用Vim作为开发的唯一IDE的这一想法,决定从头开始学习Vim。

本文技巧参考了《Practical Vim》一书中的内容,如果有条件,最好是能把这本书看了,很有用。我只是总结了他们只的精华.

学习前技巧

出厂设置

在以下命令的使用中如果出现了问题,可以使用Vim的出厂设置来配置

1
vim -u NONE -N

-u NONE是为了在使用vim的时候不加载vimrc,不使用配置vim就会进入兼容模式,很多有用的命令被禁用了,就需要使用-N来禁用兼容模式

也可以用一下命令来使用其他的vimrc,在后续的demo中需要用到.

1
vim -u NONE code/seesntial.vim

图形化vim

在mac上有macVim,win上是GVim,他们都提供了很多功能,如macVim可以Cmd+c,Cmd+v复制粘贴,看完了这篇文章,你会发现有更好的方法去执行它。

macVim下载地址

Vim解决问题的方式

其实就是避免重复我的工作,根据小小的命令能够简化我们的操作。

。命令

  • 通过.进复合操作

.命令会重复上次的修改-注意这里是说修改,那么什么是修改呢?

可以是整行,单个字符,整个文档。

Deme >命令是缩进命令,那么>G就是将之后所有的行都进行缩进,这里发现了一个额外命令,缩进N行用>NG可以实现。回到正题,我们需要做的是研究。命令,若果每一行都依次缩进呢?

image-20190615151421332

.命令可以很好的帮我们完成这一项工作。

>G,dd等都是在普通命令下的操作,进入插入模式下的所有操作,.都会帮我们记录下来,退出插入模式退出后,所有的操作都会执行一遍。

  • 通过.进行以退为进

假设我们要从以下代码的+进行插入空格的操作

1
var foo = "method("+argument1+","+argument2+")";

我们可以这么做:

image-20190615155402899

image-20190615155416592

来达到目的

f操作相当于/t 操作,查找下一个字符,然后找到+后删除+再插入空格+空格后退出编辑模式执行;执行查找下一个用+,最后一次;.来完成之后的操作.

.一样f也有复合操作,利用;可以朝赵上次的查找内容

我们改错了也可以进行回退,那么其他的复合操作如下:

image-20190615160046145

查找并替换

vim提供了一个命令,:substitute命令用于查找替换,.命令可以让我们从这些操作中解放出来.能够方便我们在这些操作之前跳转,这个命令之后进行介绍,那么首先要介绍的是*这个单词.

比如说有这么一段话:

the_vim_way/1_copy_content.txt

1
2
3
1 ...We're waiting for content before the site can go live...
2 ...If you are content with this, let's go ahead with it...
3 ...We'll launch as soon as we have the content...

需要替换其中的content,但是我们不想替换所有的content,怎么办?利用*可以实现这一目的:

image-20190615162054998

*可以查找下一项此单词的内容,n可以重复上次的工作,.可以重复上次的修改.是不是很爽?

tip: *查找的内容可以高亮,设置高亮的代码是

1
:set hls

.范式

之前的操作都是用一个键进行移动,然后后执行.这种类型的我们统称之为.范式

Vim的模式

普通模式,停顿时移开画笔

就是vim刚进入时候的模式,就像画家只花小部分的时间涂色一样,程序员只花一小部分时间写代码,大部分的时间是思考,阅读浏览.

vim可以用来控制撤销的粒度

u键可以触发撤销命令会撤销最新的修改,包括了普通模式,可视模式,命令模式的修改

  • 其他编辑器:随机撤销一个单词或者一个字符
  • vim编辑器:可以控制撤销的粒度,只要控制好esc就可以控制撤销单词.句子或者是段落

当光标到行尾时,可以按下<CR>,也可以按下ESC+o他们对于撤销u来说是不一样的,一般来说,如果你停顿了,那么就该退出插入模式了!

构造一些可重复的修改

Vim中哪种操作是最效率,可以称之为VimGolfhttp://vimgolf.com/,这是训练VimGolf的网址

在下面的例子中假如单词位于行尾,我们想反向删除单词

1
The end is nigh
  • 我们可以先用db删除一个单词,再用x删除多于的字符.那么这次的VimGolf为3分

  • 还可以用w先移动到单词的头部,然后用dw删除一个但系VimGolf为3分

  • 还可以用daw组合单词来删除这个单词aw意为a word.aw这个的用法后面介绍.

那么我们这时候用.的时候呢?会有什么问题?

  • 第一个相当于最后的删除操作x的重复
  • 第二个相当于最后的修改dw的删除工作
  • 第三个daw相当于真个操作

用count做简单的算术运算

很多普通模式下的命令可以前面加一个次数来执行指定的次数.

如果光标在数字上面<C-a><C-x>可以对数字进行+-操作,如果光标不在数字上面呢?他们会对下一个数字进行加减操作.这个数字必须在同一行内

例子:

1
.blog { background-position: 0px 0px }

我们要写一个new标签的,其他都一样把第一个像素改为-180

1
.news { background-position: -180px 0px }

执行顺序yyp先复制一行cW删除第一个单词,.new<ESC>插入new,f0找到第一个0,180<C-x>减去180,完成.

tip:对008操作+会怎么样呢?不会得到009,vim会对0开头的数字做为8进制运算.

操作符+命令符

d{motion},c{motion},y{motion}这些都是操作符,dl,daw,dap这些都是操作符+命令符

gu

介绍一个新的操作符gU这个的意思是将单词作用与大写,那么gu呢?就是讲单词作为小写.

因此gUaw将一个单词换成大写,gUap将一行换成大写.

  • vim有一个规则,就是一个操作符执行两次就说明对整行生效,gUgU也可以将一行改成大写dd删除整行>>缩进当前行.

image-20190616011454612

Vim可以自定义操作符和命令

  • 如果想自定义操作符可以执行:h map-operation来查看操作符.

  • 如果想自定义命令可以执行:h omap-info来查看命令.

如以下仓库https://github.com/tpope/vim-commentary

是添加注释的操作符\\等操作

Vim默认的命令已经非常多了

=

比如我们想自动缩进整个文件那么使用gg跳到文章的头,然后执行=G跳到文章尾,整个文件就自动缩进了

操作符等待模式

操作符待决模式(Operator-Pending mode)这个模式其实我们一直在执行,就是执行dw的时候,在dw之间会进入padding mode 这个模式下vim是等待下一个操作的.

插入模式

删除技巧

在插入模式下我们要删除一个单子,一段话,一个句子,是很费劲的,那么我们可以使用一些组合键来达到这一目的

  • 删除一个字符

  • 删除后一个单词

  • 删除前一行

这些命令在bash shell中也可以用

返回普通模式

我们使用是很不方便的,有没有办法能够在普通和插入模式中切换呢?有的

  • 切换到普通模式
  • <C-[> 切换到插入模式
  • 插入普通模式(这个需要多试试)

插入普通模式是什么东西?当我在写代码的时候突然需要执行普通模式的一条命令,那么就可以用插入普通模式,其会在执行一此命令之后,再自动进入插入模式.

当我们写代码写到顶部或者尾部的时候,我们需要重绘屏幕,我们需要执行zz命令,那么来回切换模式是不是很麻烦?就可以执行zz操作来搞定了.换到下一行也可以这样.

不离开插入模式复制粘贴

普通模式才会有复制粘贴,那么我们在插入模式下如何进行复制粘贴呢?

比如有下面一段话

1
2
Practical Vim, by Drew Neil
Read Drew Neil's

我们想把 Practical Vim放到最后一行中,并用.结束

可以现在普通模式下执行yt,来将,之前所有的文字复制下来,然后执行jA到最后一行,最后进入插入模式后执行<C-r>0来进行粘贴操作.

<C-r>{register}寄存器的使用

在插入模式中<C-r>{register}很方便的进行插入操作,可是寄存器中包含了大量的文本,你会发现vim有一些延迟,这是因为在寄存器中的粘贴时,相当于在键盘一个一个输入进来的

随时进行运算

表达式寄存器允许我们做一些运算.并且把结果插入到文档中.

我们可以在插入模式下使用<C-r>=来进入表达式寄存器.

比如有下面一段话:

1
6 chairs, each costing $35, totals $

我们要算其总的价格,那么可以在插入模式可以执行<C-r>=6*35即可在光标位置插入结果

插入Unicode编码

在插入模式下执行<C-v>{code}即可比如要插入大写字母A,Unicode 编码为065那么执行<C-v>056即可.想插入例如中文等其他字符可以执行<C-v>u1234即可,u代表16进制.

如果想知道一个东西的编码,可以将光标移动到上面执行ga即可

image-20190616023159520

插入合成字符

:digraphk 可以查看合成字符表,但是不好读,建议使用:digraph-table查看.

用替换模式替换文本

假设有这么一段话

1
2
Typing in Insert mode extends the line. But in Replace mode
the line length doesn't change.

我们想把两个句子和成一个句子,需要将.改为,并且把Bug的B改成小写的b

我们可以先执行f.来找到.然后执行R, b<ESC>来执行接下来的操作.

虚拟替换模式

可以在Vim中使用gR来使用,使用是以宽度来替换的

并且vim提供了单词替换的模式,r{char},gr{char}使用后可以替换覆盖一个字符后,马上回来原来的样子

可视模式

Vim的可视模式允许我们选中一块文本区域来操操作,表面看这很容易理解,但是Vim的可视模式和其他软件的做法是不同的.

Vim的三种可视模式:

  • 操作字符文本:按v
  • 行文本:按V是面向行的可视模式,或者<S-v>
  • 块文本:按下<C-v>是面向列块的可视模式
  • gv重新选择上次的高亮区域

三种不同的可视模式对于移动后产生的效果是不一样的可以试试进入不同模式下执行h,j,k,l有什么不同的效果

gv可以重新选择上次进入可视模式下的高亮区域.

可视模式下的切换

v后进入字符可视模式,再按下v回到普通模式,而V<S-v可以达到同样的效果

image-20190616202332868

高亮区域由两个端点.其中一个固定,另一个随光标移动,我们可以用o来调整当前活动其是的端点.还是结束的端点.

重复执行面向行的可视命令

.在使用对高亮区域修改时候,此修改会重复作用域相同范围的文本.我们对一个面向行的高亮选择区域做修改,然后使用.命令重复此修改.

比如有如下python代码

1
2
3
4
5
6
def fib(n):
a, b = 0, 1
while a < n:
print a,
a, b = b, a+b
fib(42)

while 语句下的两行代码需要缩进,我们可以进入到行选择模式V然后执行>再重复.即可

最好用操作符命令,而不是可视命令

主要是在可视命令下.命令有一些异常的表现

假设将链接里面的文字改成大写:

1
2
3
<a href="#">one</a>
<a href="#">two</a>
<a href="#">three</a>

我们可以用vit选择标签里面的内容,vit可以解毒为高亮选中标签内部的内容,it被称之为一种文本对象的特殊命令,之后介绍,然后用U将one转成大写.

再重复上面的工作j.目前为止一切正常

1
2
3
<a href="#">ONE</a>
<a href="#">TWO</a>
<a href="#">three</a>

再执行j.呢?会出现

1
2
3
<a href="#">ONE</a>
<a href="#">TWO</a>
<a href="#">THRee</a>

他会影响相同数量的文本,三个字符被替换,另一个没被替换.

这时候就用到了gU{motion}命令,三个字符全部都会替换成大写

所以我们如果想用.来做操作,最好不用选择模式.

块的可视模式编辑数据

看例子:

image-20190616204410753

我们先用<C-v>进入块的可视模式,然后向下移动光标,选中文本,按下x来删除列,并用.命令重复删除相同范围的文本,这时候用gv重新选择上次的高亮区域,然后输入r|接下来复制一行文本,再用Vr-替换本行所有的文本.

修改列文本

修改多行

1
2
3
li.one   a{ background-image: url('/images/sprite.png'); }
li.two a{ background-image: url('/images/sprite.png'); }
li.three a{ background-image: url('/images/sprite.png'); }

假设我们要将image换成components怎么做?

首先定位到image执行<C-v>再执行jje选择所有的image

我们首先执行c修改所有的image,再输入components,后执行<ESC>,<CR>

那么所有的image就替换好了

在长短不一样的地方添加文本

例如代码:

1
2
3
var foo = 1
var bar = 'a'
var foobar = foo + bar

我们需要再所有的末尾加入;,之前我们已经实现过了,如果在可视模式下应该怎么做呢?

先执行<C-v>进入可视模式,然后执行jj$选择所有行,再执行A;<ESC>退出即可.

命令行模式

历史~

Vim的祖先是vi,vi的祖先是ex,ed有是ex的祖先,所以vim编辑器有Ex命令.早期的Unix文本编辑器依旧流淌在Vim中,对于某些编辑任务来说,Ex任然是最佳的工具.

当按下:的时候,会切换到命令行模式.和shell有些蕾西,输入一条命名按下<CR>执行即可,在任何时候,我们都可以用<ESC>来回到普通模式

  • 我们在用/<C-r>=访问表达式寄存器的时候,命令行模式也可以被激活

image-20190616223615028

在命令行模式中,大部分按键只是简单的输入一个字符

Ex命令强大的地方是操作范围更大,并且能够在一次执行中修改多行.

如:

1
2
3
4
<html>
<head><title>Practical Vim</title></head>
<body><h1>Practical Vim</h1></body>
</html>

我们要跳转到对应的行的时候,可以执行:x打印本行为:p打印第三行为:3p答应1到4行为:1,4p

  • 符号.代表当前行,$代表尾行,我们要查看所有的行的话可以执行:.,$p
  • %代表所有的行

这种格式在使用:substitute命令的时候非常常用

%s/Practical/Progmatic/意思是替换全文本中的Practical为Programtic

在高亮区域中选中范围

如果我们首先在可视模式下选中了一段话,在执行:的话会预先填充一个范围如:'<,'>这时候我们在它之后加上命令的话就会执行对应的Ex命令如p

:'<,'>p会打印出所有的内容

如果想对部分的内容执行:substitute命令,这种范围会非常的方便,符号'<代表首行的标记'>代表尾行的标记.

用模式指定范围

vim如我们执行:/<html>/,/<\/html>/p这种就类似于选择的一般模式如:{start},{end}

用偏移修改范围

如果我们想在<html></html>之间执行一行Ex命令,但是不想包含可以这样做

:/<html>/+1,/<\/html>/-1p

比如我们想选择1-4行的代码也可以这样做:.,.+3p

image-20190616230353534

第0行是不存在的,但是作为特定场景下还是很用用的,比如把指定的范围内复制到文件开头时,可以用:copy{address}

在指定范围上执行普通命令

比如说我们想在一些列行上加一个分号,用.范式可以很快的完成工作,如果想做50次同样的修改怎么办呢?

1
2
3
4
5
var foo = 1
var bar = 'a'
var baz = 'z'
var foobar = foo + bar
var foobarbaz = foo + bar + baz
  • 比如说这个例子,我们可以先执行A;插入一行然后执行:hVG选择余下的剩余行,再执行:这时候Ex命令会出现:'<,'>,我们只需要再后面加上normal .即可对所有行进行操作.

  • 我们也可以用%normal A;来执行操作达到同样的效果

将整个文件注释掉:%normal i//

在执行命令前vim会把光标移动到起始的位置,所以不用担心位置.

重复上次的Ex命令

要使用上次的命令的话我们要使用@:

:寄存器总是保存着最后执行的命令在运行过一次@:后之后可以用@@来重复它

补全Ex命令

可以用tab默认补全,也可以写到一半用<C-d>来进行补全

把单词插入到命令行

为了节省时间,问你可以把活动窗口中的当前单词(或者是字符串)插入到命令行中.

在Vim命令行下,我们可以用<C-r>,<C-w>来把当前光标下的单词插入到命令行中.

比如我们要替换

1
2
3
4
var tally;
for (tally=1; tally <= 10; tally++) {
// do something with tally
};

里面的tall,可以先替换一处,替换好后光标停留在那里,我们可以执行,:%s//<C-r><C-w>/来替换所有的tally

同样的替换数字呢?<C-r>``就可以替换数字了.

命令行窗口

加入我们输入了两条命令:write,:!ruby %我们意识到可以简化命令用:write|!ruby %我们不必从头开始输入整条命令,但是怎么合并 两条命令呢?

执行q:进入命令行窗口

执行q/进入查找命令的历史窗口

如果在命令行模式:,可以执行<C-f>进入到命令行窗口,同样的查找模式也可以进入

image-20190617014418390

运行shell命令

:中加入感叹号!就可以运行外部程序,如果想查看当前的路径或者其他,可以执行以下命令

  • :!ls查找当前的文件有哪些

在vim的Ex命令中%代表的是当前的文件名,比如我们想执行编译一个Ruby文件当前文件,可以执行:!ruby %

如果我们想执行多条shell命令呢?可以进入shell模式下

shell模式

:shell退出的话是exit

shell挂起

shell提供了作业控制,比Vim提供的:shell要方便一些

  • shell提供了挂起的功能,可以用Ctrl-z挂起Vim所属于的进程
  • 在shell中使用jobs可以查看当前有哪些挂起的任务
  • 执行fg可以恢复当前挂起的任务

把缓冲区的内容作为标准输入输出

我们执行:!{cmd}的时候如果命令很少的话,没有问题,如果命令比较多的话没有记录,我们可以放到缓冲区中

:read !{cmd}

1
2
3
:write !sh
:write ! sh
:write! sh

使用这类命令要格外的小心!前两个命令都会把缓冲区的内容传递给外部的sh做为输入,而第三个命令是将缓冲区的内容写入一个名为sh的文件,

1
:h rename-files 查看一个很好的示例

使用外部命令过滤缓冲区的内容

当指定一个范围的时候:!{cmd}就会变的不同.[range]指定的行会给{cmd}作为输入,然后用{cmd}的输入覆盖[range]的内容.

比如:一个csv文件

1
2
3
4
first name,last name,email
john,smith,john@example.com
drew,neil,drew@vimcasts.org
jane,doe,jane@example.com

我们想用姓重新排列,我们可以用-t’,’告诉sort命令这些以,分割,用-k2告诉指定按照第二个参数排序

因为第一行是标题所以不想排序,那么我们从第二行开始.

:2,$!sort -t',' -k2

:[range]!{filter}

vim提供了非常方便指定范围的方法,我们想修改第二行到最后一行的代码只需要在第二行的时候执行!G即可既进入到全选的状态.

详细请看h !

image-20190617021934121

谢谢您的鼓励~