阅读 962

一开始看不上亲密接触后又让你欲罢不能的 vim

每一个程序员都有一款心心念叨的代码编辑器。而在众多妖颜魅惑的编辑器偶像团体前,vim 就像个不加粉饰的农村姑娘,咋一看是那样朴实无华,难有倾心。但只要走近一点,来个亲密接触,又会被她的似水柔情所俘获。

这是青笔亲身经历。公司从腾讯空降技术总监,时不时会分享一些他的开发经验。其中说到编辑器,他的脸上总会泛起淡淡桃花,“我就喜欢 vim!” 。尽管我们曾一致向他引荐当下红极一时集美貌与才华于一身的 vscode 。他却依然心有所属,不为所动。

多少次,我百思不得其解,这个“相貌平平”的 vim 到底有何魔力,能牢牢扣住总监的心弦。在我真正走进她的内心世界,多次昼夜相伴之后。我最终也被她的清新脱俗所拜倒。

接下来就让我们一起来了解这名神秘的“女子”。

1. vim 历史

以下是网上找到一张图。如对 vim 发展历史感兴趣可以参考维基百科的词条 Vim (text editor)

en.wikipedia.org/wiki/Vim_(t…

早期的 UNIX 操作系统上标准的编辑器是 ed ,这是一个面向行的编辑器,只能显示当前编辑的行。1976年,Bill Joy 在 ed 上做了扩展,使之支持了全屏显示和编辑,命名为 ex 。ex 是 ed 的超集,通过在 ex 输入命令 vi 来启动扩展功能。后来这个命令由于经常使用而被独立出来,也就是现在 Linux (包括 macOS)系统预装的命令行编辑器 vi 。直到 1991 年,Bram Moolenaar 才开发了 vi 的改进版(improvement),命名为 VIM (寓意:Vi IMprovement),同年 11 月 4 日发布了 vim 的第一个版本。下图来自 vim.org 网站首页的一条新闻,也就是去年的11月4日迎来了 vim 的27岁生日。

2. 内置教程

vim 自带了一个面向初学者开发的教程。可通过独立的子程序 vimtutor 启动教程。

如果系统已经安装了 vim (macOS 预装了 vim,部分 linux 发行版可能需要手动安装),可以在直接在终端输入 vimtutor 命令打开教程。

vimtutor
复制代码

教程实际也是使用 vim 打开系统上的一个文本文件。文件通常在 /usr/share/vim/vim80/tutor/ 目录下。当然,这个目录下有很多个文件,分别是不同语言版本的教程。使用 vimtutor 的不同之处是能自动打开用户所在地的语言版本。

上面看到的简体中文版实际就是文件 /usr/share/vim/vim80/tutor/tutor.zh_cn.utf-8

因此,你可以将其复制一份放到当前目录下,再使用 vim 打开,用做练习,随意编辑文件内容。

cp /usr/share/vim/vim80/tutor/tutor.zh_cn.utf-8 ./
vim tutor.zh_cn.utf-8
复制代码

但如果按照教程来练习,个人用起来感觉最舒服的方式是:同时打开两个终端,一个使用 vimtutor 打开本地教程用于阅览,另一个将教程的英文版复制到当前目录,然后使用 vim 在复制的英文版上进行编辑练习(因为我们实际应用场景大多是英文环境,包括编辑代码和配置文件)。

复制英文版教程并进行编辑练习:

cp /usr/share/vim/vim80/tutor/tutor ./
vim tutor
复制代码

3. 工作模式

对于习惯了 GUI 编辑器的开发者,刚开始接触 vim ,很容易被 vim 的工作模式搞得一头雾水,而且很可能就此放弃使用 vim 。但如果熟悉了,就觉得一切也理所当然。按照维基百科的介绍,vim 一共有 12 种不同模式,但只有 6 种基本模式,剩下 6 种都是基本模式的变种。但实际我们最常接触的只有两类模式:命令模式和编辑模式。其中命令模式又细分为,Normal(默认)模式和 Cmdline (底部命令行) 模式。而 Normal 模式是启动 vim 进入的默认模式,并且在任何模式下,按 ESC (键盘左上角)键都将回到这个模式,作用类似于手机的 Home 键。因此,当你开始迷糊自己当前处在什么模式下,最好的办法就是按 ESC 回到 Normal 模式。我们使用 vim 的绝大多数命令都在 Normal 模式运行,而 Cmdline(命令行)模式通常只在退出和保存时使用,因此,如不特别说明,当我们在说命令模式时,就是指代 Normal 模式。此外,本文将 Cmdline 统称为命令行模式

下图是三种模式的切换方式:

如果要简单概括这三种模式的使用场景,那就是:在命令模式中执行光标导航,复制粘贴删除,撤销重做,以及查找替换等操作;在编辑模式下,编写代码或进行创作;在命令行模式模式下,输入 q 退出,输入w保存,输入wq保存并退出。当然这里还不是全部的操作,但是熟悉这些操作,基本就能将 vim 应用得得心应手了。

4. 命令模式

4.1 移动光标

要使用 vim 熟练地进行代码和文本的编辑,首先需要能够精确快速的将光标定位到要编辑的地方。最基本的就是上下左右移动光标了。这些操作当然可以使用键盘的四个方向键来完成。但是更常用的是使用字母输入区域连续的四个按键 h, j, k, l 。事实上,当你按照标准的手势接触键盘,你右手的食指触碰的就是 j 键(按键有个凸起)。因此,在你保持正常输入的手势下,顺其自然地敲动食指,就能将光标往下移动一行,而紧挨着的中指接触的是 k 键,进行的是和 j 相反的操作,即将光标向上移动一行,你会发现这样用起来相当顺手,这就理解为什么要使用字母按键来进行光标移动操作了。当然这是在命令模式下,按下字母键不会作为输入而改变编辑内容。

  • h: 向左移动一个字符
  • l: 向右移动一个字符
  • j: 向下移动一行
  • k: 向上移动一行

虽然可以使用 j, k 左右两边的 h 和 l 来实现向左和向右移动光标。这在一次只移动一个字符时,也还是很方便。但是如果向左或向右移动一个字(word,英文中空格和标点符号隔开的单词,标点符号也算一个字)时,还使用 h 和 l 就显得有些笨拙了。向左(向后)和向右(向前)移动一个字的方法如下:

  • w: 向右或向前移动一个字,光标定位在字的首字符
  • b: 向左或向后移动一个字,光标定位在字的首字符

命令模式按下字母w将光标向右移动一个字:

命令模式按下字母b将光标向左移动一个字:

如果想近一步扩大光标单次移动的范围,就要用到按句子和段落来前后移动来。两对圆括弧()分别将光标向后和向前移动一个句子,对应两个花括弧{}分别将光标向后和向前移动一个段落。

  • ( : 向后移动一个句子,光标定位在句子开始
  • ) : 向前移动一个句子,光标定位在句子开始
  • { : 向后移动一个段落,光标定位在段落开始
  • } : 向前移动一个段落,光标定位在段落开始

命令模式按下)(向前和向后移动一个句子:

命令模式按下}{向前和向后移动一个段落:

此外你还可以在屏幕所见范围内进行快速移动光标。分别使用大写的HML。但是这三种定位并不是很精确,通常用做快速大范围级定位,然后再使用前面的命令进行更加精确的定位。读者可以自行尝试。

  • H: 将光标定位到屏幕顶部一行的最左端
  • M: 将光标定位到屏幕中间一行
  • L: 将光标定位到屏幕的底部一行

还有一种我们非常熟悉的应用场景,就是在调试程序抛出异常时,通常会显示出异常产生的行号,这时就需要根据行号快速将光标定位到指定的位置。这个命令相比前面的命令要复杂一点,它需要先输入代表行号的一个或多个数字,然后按下大写字母G来完成。例如将光标定位到文件的第 80 行。需要先在命令模式输入80,然后快速按下大写字母G

4.2 删除/撤销

将删除和撤销两个操作放在一起讲,其中一个原因是考虑到可能因为还不知道如何撤销删除操作,而害怕尝试删除操作的心理(尽管我们已经事先做了备份,但这应该是一种普通的心理和人性害怕失去是同样的道理)。我们知道可以撤销,因此在删除这件事上就有了“安全感”。也许你不是这么认为,但是这样还是能帮助我们更好的练习和记忆(我们可以循环往复地练习删除撤销)。

和移动光标一样,删除也可以按不同粒度进行。如删除单个字符,字,行,句子,段落以及屏幕首尾。删除操作由用字母d加上表示删除范围的标识符构成。同时删除具体范围还受光标当前所在的具体位置决定。例如删除字使用字母组合dw,如果光标出现在单词hello的第二个字符e上,此时在命令模式连续按下dw,将删除从e开始之后的整个字,但是会e前面的h不会删除,删除后的结果就是还剩一个字母h。其他粒度的删除,也遵循相似的规律。

删除单个字符

删除单个字符有两者方法。两者等效,都是删除光标所在的字符,但是使用x更简单一些,因为只需要输入一个字母。

  • x
  • dl

删除字

  • dw: 从光标所在位置开始,删除到字的末尾(包含光标所在位置的字符)
  • db: 与dw相反方向删除,即删除光标所在位置前面的字符(不包含光标所在位置的字符)

删除行

  • dd: 删除光标所在的行
  • 3dd: 删除从光标开始的3行,当然这是一个例子,可以更改前面的数字删除任意数量的行

一次删除3行:

删除到行尾

如果你不想删除整行。而是从光标开始到行尾的字符,可以直接使用一个大写D来实现。

使用大写字母D删除从光标位置开始到行尾的字符:

从行首删除

与删除到行尾对应的是使用d0从行首删除:

d^与d$

如果对正则表达式熟悉,应该很容易猜到两者的含义。也是删除行首和行尾,但是与前面的 d0D 所不同的是不删除行首和行尾的空格。

  • d^: 删除光标前面知道行首的字符,包含行首的空格
  • d$: 删除从光标开始知道行尾的字符,包含行尾的空格

删除句子和段落

熟悉了前面的删除,以下删除句子和段落也可以以此类推。唯一区别仅仅在于删除粒度不同。

  • d(: 从句首删除
  • d): 删除到句尾
  • d{: 从段首删除
  • d}: 删除到段尾

替换:删除并输入

说到删除不得不再提到替换。因为两者就像兄弟一般,实际替换像删除的哥哥,只比弟弟删除多做了一步。就是在删除后紧接着进入编辑模式,在已删除的地方进行编辑。这两个组合动作不就是替换嘛。需要注意的还有,替换会改变当前工作模式,也就是进入下文会讲到的编辑模式,因此在完成输入后,需要按ESC重新回到命令模式。与删除d对应的替换命令是c。因此将删除里讲到的所有操作中的d换成c就是对应的替换操作,当然执行后的细节会有些差异。但是这种差异在你亲自试过后就很容易理解,因此不作过多论述。读者可以自行尝试。

撤销操作

撤销操作很简单,命令模式下,按下小写字母u(undo)即可。可以连续按多次,以快速撤销多个历史操作。

4.3 复制/粘贴

复制 y

复制使用字母y。它的用法和删除操作d很像,不同的是y是复制而不是删除。

下面以复制行为例。其他粒度的复制类比删除操作d即可。

dd删除当前行对应的复制当前行操作是yy,当然也可以复制多行,和删除多行格式相似。在yy前加上要复制的行数。例如3yy复制从光标所在行开始的三行。

  • yy: 复制光标所在的行
  • 3yy: 复制从光标开始的3行,这里只是例子,实际可以是任意行数

粘贴 p

粘贴命令就简单多了。它没有没有那么多的粒度区分,而只需要将已经复制的内容插入到当前光标之后或者之前的位置。分别使用小写的p和大写的P

  • p: 将复制的内容插入到光标之后
  • P: 将复制的内容插入到光标之前

4.4 查找字符串

在命令模式输入斜杠/(向下查找)或问号?(向上查找),光标定位到终端底部一行,输入要查找的字符串,再按回车(RETURN),首先定位到一个找到的字符串,接着按小写的n查找下一个,按大写的N查找前一个。

5. 进入编辑模式

上面介绍的所有命令都是在命令模式下进行的。如果要开始编写代码或其他文字编辑工作,就需要从命令行模式进入编辑模式,否则输入的字母(除能进入编辑模式的字母外)都将视作命令,而不会编辑到到文件中。

进入编辑模式有以下6种方式,区别仅在于进入编辑模式后,光标所在的位置的不同。

  • i: 在当前光标前插入
  • I: 在行首插入
  • a: 在当前光标后追加
  • A: 在行尾追加
  • o: 在当前光标所在行之添加新行
  • O: 在当前光标所在行之添加新行

6. 命令行模式

在命令模式下,按下冒号:,在终端底部出现输入行,表示当前是命令行模式。在命令行模式可以使用 vim 自身支持的很多命令。如前文已经谈到的最常用用到的就是退出和保存。

  • :q + RETURN: 退出编辑
  • :w + RETURN: 保存不退出
  • :wq + RETURN: 保存并退出

常用的还有对 vim 进行设置。以下命令都需要在输入完后按下回车键 RETURN 才能执行(和在终端执行命令方式差不多)。

  • :syntax on: 开启语法高亮
  • :set number: 显示行号
  • :set tabstop=4: 设置 Tab 键宽度
  • :set expandtab: 使用空格替代 Tab
  • :set softtabstop=4: 设置软件 Tab (自动Tab)宽度为 4
  • :set shiftwidth=4: 设置自动缩进宽度为 4
  • :set autoindent: 开启自动缩进,通常用于编写程序

当然以上对 vim 的设置仅仅影响当前打开的 vim 。要想每次打开都使用同样的设置,需要将设置命令统一保存到 vim 的配置文件 ~/.vimrc 中,方法下一节将讲到。

此外,可以使用help来获取帮助。例如输入:help user-manual + RETURN 查看用户手册。

如果说以上都是 vim 内部执行命令,那么在命令行模式下,实际也可以调用外部 shell 的命令。方法是以感叹号!开头,标识感叹号!之后的命令是一条外部 shell 的命令。如查看当前目录下的文件,:!ls -l。第一次按下 RETURN 会隐藏当前编辑区域,显示终端界面,第二次按下 RETURN 再次回到 vim 编辑界面。

7. 配置文件

前文已经讲到如果想要每次打开 vim 都使用同样和界面和设置,需要将设置命令统一保存在配置文件中。在 linux 系统(含macOS)推荐保存的路径是 ~/.vimrc。如果从来没有设置过,这个文件可能还不存在。这就需要我们先创建一个。为了不覆盖已有的,我们可以使用如下命令:

test -f ~/.vimrc || vim ~/.vimrc
复制代码

这条 shell 命令会先判断 ~/.vimrc 是否存在,不存在就新建一个空白的配置文件,并使用 vim 打开这个新建的配置文件。

可选的方式也可以复制一份系统的上的配置文件,然后在此基础上添加和覆盖默认配置。

test -f ~/.vimrc || cp /usr/share/vim/vimrc ~/.vimrc
复制代码

以下是青笔自己的配置文件参考:

colorscheme default     " 设置颜色主题
syntax on               " 语法高亮
filetype on             " 检测文件的类型
set number              " 显示行号

set ruler               " 在编辑过程中,在右下角显示光标位置的状态行
set laststatus=2        " 显示状态栏 (默认值为 1, 无法显示状态栏)
set statusline=\ %<%F[%1*%M%*%n%R%H]%=\ %y\ %0(%{&fileformat}\ %{&encoding}\ %c:%l/%L%)                        " 设置在状态行显示的信息

set tabstop=4           " Tab键的宽度
set expandtab           " 使用空格替换Tab
set softtabstop=4
set shiftwidth=4        " 统一缩进为4

set autoindent          " vim使用自动对齐,也就是把当前行的对齐格式应用到下一行(自动缩进)
set cindent             " (cindent是特别针对 C语言语法自动缩进)
set smartindent         " 依据上面的对齐格式,智能的选择对齐方式,对于类似C语言编写上有用

set scrolloff=3         " 光标移动到buffer的顶部和底部时保持3行距离

set incsearch           " 输入搜索内容时就显示搜索结果
set hlsearch            " 搜索时高亮显示被找到的文本

set foldmethod=indent   " 设置缩进折叠
set foldlevel=99        " 设置折叠层数
nnoremap <space> @=((foldclosed(line('.')) < 0) ? 'zc' : 'zo')<CR>
                        " 用空格键来开关折叠

" 自动跳转到上次退出的位置
if has("autocmd")
    au BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif
endif
复制代码

关于配置文件的更多信息,也可通过 vim 内置的帮助手册来查看。与配置相关的手册为 usr_05 。在命令行模式输入:help usr_05(冒号用于进入命令行模式,输入后回车)即可打开该手册。

总结

本文从 vim 的发展历史开始,以具备能够熟练驾驭 vim 编辑器来满足日常代码编写的基本操作为主线,讲解了在 vim 中进行模式切换,光标导航,删除,撤销,替换,复制,粘贴,插入文本,以及配置编辑器外观设置等必要技能。当然,vim 支持的操作和命令还有很多。本文只介绍到基础部分。但是却是我们进行代码编辑最常用到的功能。熟练掌握后,可以进一步查看手册学习更多的技巧。查看手册方式上文已经给出。即在命令行模式输入::help user-manual 再回车。

如果本文能让你重新认识了 vim 这位眉目清新又纯洁善良的“姑娘”,并且从此对她有了一份独有的爱意,算是本文创作的最大收获所在^^。


本文首发公号

关注下面的标签,发现更多相似文章
评论