VS Code 与 Vim

背景介绍

Vim 是从 vi 发展出来的一个文本编辑器。和 Emacs 并列成为类 Unix 系统用户最喜欢的编辑器。

Vim 提供了一系列很强大的编辑文本的操作方式,可以使用纯键盘完成编辑文本操作。再搭配许多插件可以配置成一个 IDE。但是 Vim 有着一个比较陡峭的学习曲线,并且不是开箱即用的。

如果没有特定的开发环境限制,可以使用

更漂亮易上手的编辑器 (Visual Studio Code) + 更流畅的编辑体验 (Vim)

来提高编码效率,各取所长。

如今市面上几乎所有的 IDE 和编辑器都提供有 Vim 的相关插件。例如我所体验过的

本文的所有操作都基于 Visual Studio Code + Vim 插件 演示。(虽然我更喜欢 Sublime Text…)


先来点甜头 例子

举例几个实际例子,可以想像之前是怎么操作的

  1. 字母顺序写反了 ab 写成了 ba
    running

  2. 删除 HTML 元素中的内容 / 删除双引号之间的内容
    running

安装

  1. 打开 VS Code 的扩展商店
  2. 搜索 Vim
  3. 点击安装

running

Vim 模式

模式意味着你的键盘会根据你所使用的模式获得不同的功能。也就是说,根据你所使用的模式,在键盘上键入一个按键会产生不同结果。

就像主键盘区上方的数字健可以直接按数字键 1 输入 1 也可以通过 shift + 1 输入 ,这样一个健位上就具有了不同的功能。

常用模式

  • normal 模式—— 默认模式,可以通过键盘移动光标、删除字符、复制、粘贴等。
  • insert 模式—— 正常的输入模式
  • visual 模式—— 选中文本,类似通过鼠标选择文本

Vim 插件安装完成后,默认是 normal 模式,normal 模式下不能输入文本。按 i 键切换到 insert 模式。

如何判断编辑器当前所处模式

  • 通过光标样式判断
  • VS Code 底部 status 栏会显示当前模式

running

安装 Vim 后的副作用

  • Ctrl + c 不再是复制,而是切换到 normal 模式
  • Ctrl + v 不再是粘贴,而是进入 visual 模式
  • Ctrl + f 不再是搜索,而是向前滚动一页

如果不希望 vim 改变这些健

1
2
3
4
5
6
7
8
9
10
11
// 禁用 vim 使用 Ctrl
"vim.useCtrlKeys": false,

// 或者更精细地控制 Vim 启用或禁用的映射,自行取舍。
"vim.handleKeys": {
"<C-w>": false, // 禁用 Vim 使用 Ctrl w
"<C-o>": false,
"<C-n>": false
}

Tips: 禁用 Ctrl 键后,下面的 Vim 中包含 Ctrl 的操作将失效。

模式切换

insert 切换 normal

  • Esc
  • Ctrl + c
  • Ctrl + [

normal 切换 insert

  • i(insert) 在当前字符前进入 insert 模式
  • a(append) 在当前字符后进入 insert 模式
  • o(open) 在当前行下开启新的一行进入 insert 模式
  • I 在行首进入 insert 模式
  • A 在行尾进入 insert 模式
  • O 在当前行上开启新的一行进入 insert 模式
  • gi 在最后一次 insert 的地方进入 insert 模式

normal 模式

normal 模式下 hjkl 可以像键盘上的方向键一样上下左右移动光标而不是输入。hjkl 适用于短距离移动。

1
2
3

← h j k l →

running

数字搭配命令使用,快乐加倍。
{count}{command}

快速移动

单词之间移动
Vim 中的一个单词是

  • 字母和数字组成 (w、e、b、ge)
  • 其他非空白字符序列 (W、E、B、GE)
1
2
hello vim world
HelloVimWorld(){}
  • w 移动至下个单词开头
  • e(end) 移动至下个单词结尾 end
  • b(back) 移动至上个单词开头 back
  • ge 移动至上个单词末尾
  • gE

以上命令还可大写,大小写的区别在于单词的范围不同。

  • 小写只由字母组成的单词
  • 大写是以非空白符分割的单词

{n}w/e/b/ge/gE 跳转第几个单词

running


行间搜索移动

  • f{character} 移至行中下一个出现的字符
  • F{character} 移至行中上一个出现的字符
  • t{character} 移至行中下一个出现的字符前
  • T{character} 移至行中上一个出现的字符后

重复查找上次的搜索命令,避免再次输入。

  • ; 下一个
  • , 上一个
  • {n}; 第 n 个

running


水平移动

  • 0 移至行首
  • ^ 移至行首的非空白字符
  • $ 移至行尾
  • g_ 移至行尾的非空白字符

running


垂直移动

  • ( 上个句子
  • ) 下个句子
  • { 上个段落
  • } 下个段落
  • % 移动光标到括号的另一半包括 () [] {}

段落和句子都是以空行来分割。

running


页面移动

  • gg 第一行
  • G 最后一行
  • {n}gg/{n}G 移动到第 n 行
  • H(head) 移动到屏幕顶部
  • M(middle) 移动到屏幕中间
  • L(lower) 移动到屏幕低部
  • zz 当前行放到屏幕中间

running


语义移动

  • gd 跳转到定义
  • gf 跳转到导入的源文件
  • gh 相当于将鼠标悬停在光标所在的位置。

running

文本对象

文本对象可以理解为范围,hjkl 移动是以一个字符为单位移动。
使用文本对象可以更大范围的移动操作

  • w 单词
  • s 句子
  • p 段落
  • ( 被 { 包围的块
  • { 被 ( 包围的块
  • ' 被 ‘ 包围的块
  • " 被 “ 包围的块
  • t 被 html 标签包围的块

快速删除

d (delete)

  • dd 删除当前行
  • {n}dd 删除 n 行
  • df{character} 从当前字符向后删除到目标字符
  • dF{character} 从当前字符向前删除到目标字符
  • dt{character} 从当前字符向后删除到目标字符前
  • dT{character} 从当前字符向前删除到目标字符后
  • x = dl 删除一个字符
  • X = dh 删除前一个字符
  • {n}x 删除 n 个字符

配合水平移动删除

  • d^ 从当前字符删除到行首
  • d0 从当前字符删除到行首
  • d$ = D 从当前字符删除到行尾
  • dg_ 从当前字符删除到行尾

配合文本对象删除

  • dw 从光标向后删除单词
  • d{n}w 从光标向后删除 n 个单词
  • d{n}aw 从当前单词向后删除 n 个单词
  • db 从光标向前删除单词
  • diw 删除单词
  • daw 删除单词 + 单词后的空格
  • dit 删除 html 标签包裹的内容
  • dat 删除 html 标签 + 包裹的内容
  • di( 删除 () 包裹的内容
  • da( 删除 () + 包裹的内容
  • di{ 删除 {} 包裹的内容
  • da{ 删除 {} + 包裹的内容
  • di" 删除 “” 包裹的内容
  • da" 删除 “” + 包裹的内容
  • di' 删除 ‘’ 包裹的内容
  • da' 删除 ‘’ + 包裹的内容

i - inner
a - around

快速修改

r (replace)

  • r 替一个字符

c (change)

  • c 的命令基本可以把上面快速删除中的 d 都替换成 c。唯一区别是 c 最后会进入到 insert 模式。

s

  • s = ch 删除光标下的字符并进入 insert 模式
  • {n}s 删除 n 个字符并进入 insert 模式
  • S 删除当前行并进入 insert 模式
  • ~ 切换单个字符大小写
  • g~ 当前行所有字符字符切换大小写

Command 模式

VS Code 只支持部分 Vim 的 command 命令。
运行命令以 : 开头

  • :edit {relative-path-to-file} = :e {path} 打开或创建文件 建议创建文件使用,打开文件使用 Ctrl + p
    相对路径是相对的当前打开文件。

保存和关闭文件

  • :write = :w 保存文件
  • :quit = :q 关闭文件
  • :wq 保存并关闭

如果文件未更改或为只读则将失败。同样,:quit将关闭文件,但如果文件未保存的更改将失败。
后面加 ! 强制执行

  • :wall = :wa 保存所有文件
  • :qall = :qa 关闭所有文件
  • :wqall = :wqa 保存和关闭所有文件
  • :qall! = :qa! 关闭所有文件不保存

删除/修改/复制 d c y
:[range]command[options]

  • :[range]delete [register] 删除多行到寄存器
  • ·@: 重复上一个ex命令
  • ·@@ 重复一次后,您可以继续重复此操作

command 模式范围
:{start},{end} 起始行到结束行 1,2d
:{start},{offset} 起始行和偏移范围 1,+2d

  • . 当前行 .,+2d
  • % 整个文件 %d
  • 0 文件开头 0,10d
  • $ 文件结尾 10,$d
  • :'<,'>

command 替换

:[range]s/{pattern}/{substitute}/{flags}

  • range 范围
  • s/ 当前行
  • %s 整个文件
  • pattern 搜索模式 支持正则
  • substitute 要替换的文本
  • flags 选项
    • -g 全局
    • -i 不区分大小写
    • -c 确认每一次替换
  • :s/led/gold 当前行第一个 led => gold
  • :s/led/gold/g 全局

command 模式搜索

搜索以 / 或 ? 开头

  • /{pattern} 向后搜索 pattern 是个正则表达式
  • ?{pattern} 向前搜索
  • / = n 下一个匹配项 重复搜索
  • ?= N 上一个匹配项
  • gn 选中下一个匹配项
  • gN 选中上一个匹配项
  • dgn 删除下一个匹配项
  • dgN 删除上一个匹配项

Visual 模式

Visual 模式相当于用鼠标选择文本,但是在 Vim 中你可以用键盘来完成。

  • v 逐字符选择文本
  • V 逐行选择文本
  • Ctrl + v 使用矩形块选择文本
  • o 切换选择方向
  • d 删除

insert 模式

  • ctrl h 删除上一个字符
  • ctrl w 删除上一个单词
  • ctrl u 删除当前行
  • ctrl a 移动到行首
  • ctrl e 移动到行尾
  • ctrl b 向前移动
  • ctrl f 向后移动

终端中也可以使用

复制粘贴

在 Vim 中

  • y 复制
  • Y 复制当前行
  • p 在当前字符后粘贴
  • p 在当前字符前粘贴
  • yyp 重复当前行
  • ddp 交换上下行
  • xp 交换字符
  • yl 复制当前字符

配合文本对象使用

  • yaw 复制一个单词
  • yas 复制一个单词
  • yi(
  • yip
  • yap

寄存器

寄存器是一个特殊的剪贴板,你可以选择在其中一次保存多个内容。就像有很多盒子,你可以自由的选择将复制、删除的内容放在哪个盒子中,需要的时候可以自由拿出来(粘贴)。Vim 的删除、复制与粘贴命令均需要使用 Vim 寄存器。

通过在命令前加 "寄存器名 前缀的方式可指定寄存器,否则 Vim 将缺省使用无名寄存器。

若想在 Vim 和操作系统外部程序间共享复制内容,则必须使用 Vim 系统剪贴板。

  • "" 无名寄存器 默认使用「无名寄存器」""p === p
  • "[a-z] 有名寄存器,以 26 个英文字母命名,使用小写字母引用有名寄存器会覆盖该寄存器的原有内容,而用大写字母引用则会将新内容 追加 到该寄存器的原有内容之后。

特殊寄存器

  • "0 复制寄存器
  • "+ 系统剪贴板
  • "% 当前文件名
  • ". 上次插入的文本
  • "[1-9] 存储最后 9 次删除或更改命令的内容
  • :reg {name} 查看寄存器内容

Vim 宏 (marcro)

可以看作是一些系列命令的集合,可以录制一系列操作然后用于「回放」

在 normal 模式下,按下 q{register} 后开始进入宏录制状态,VS Code 编辑器左下角会显示 Recording

  • q{register} 开始录制 再按 q 结束录制 录制保存在寄存器中
  • @a 回放保存在 a 寄存器中的操作
  • @@ 重复上次回放
  • {n}@{register}

Example

1
2
3
4
5
6
rusty sword,obsidian dagger,silver poniard,broadsword 转换成

<li>rusty sword</li>
<li>obsidian sword</li>
<li>silver poniard</li>
<li>broadsword poniard</li>

插件

VS Code 中内置了许多有用的 Vim 插件

vim-suround

默认启用

  • ds 删除
  • cs 更改
  • ys 添加

Example

  • ds' 删除周围的 '
  • ds(
  • ds[
  • ds{
  • cs"'" 改成 '

vim-sneak

默认禁用,setting.json 中 vim.sneak: true 开启。

  • s{char}{char} 类似 f 命令,不过现在 s 可以搜索两个字符更容易定位。
  • S{char}{char} 向上搜索

Input Method

在插入模式输入中文再进入到 normal 模式后,需要切换回英文输入法。Input Method 避免了这种问题。
使用方法参考 如何解决VSCode Vim中文输入法切换问题?

vim-easymotion

默认禁用,setting.json 中 vim.easymotion: true 开启。

easymotion 试图通过消除记数来简化 Vim 中运动的使用。当使用 easymotion 时,它会使用叠加层(在相关文字上方)标识。输入该标识跳转过去。easymotion 适用于四处移动。

“vim.leader”: “space” 可以自行映射 leader 键盘。

  • <leader><leader>w 标记后面所有单词的开头
  • <leader><leader>b
  • <leader><leader>bdw
  • <leader><leader>e
  • <leader><leader>ge
  • <leader><leader>j
  • <leader><leader>k
  • <leader><leader>f{char}
  • <leader><leader>F{char}
  • <leader><leader>t{char}
  • <leader><leader>T{char}
  • <leader><leader>s{char}

Setting

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// VS Code setting.json 中设置
"editor.lineNumbers": "relative", // 设置相对行号
"vim.cursorStylePerMode.normal": "underline", // 设置 normal 模式下光标为下划线
"vim.leader": "<Space>", // 自定义空格 leader 键 默认是 /


// 自定义映射 使其更适应你的工作方式

// 1. insert 模式下输入 jj 进入到 normal 模式 jj => ESC
"vim.insertModeKeyBindingsNonRecursive": [
{
"before": ["j", "j"],
"after": ["<ESC>"]
}
],

// 2. 更快速的移动 normal 模式下 J => 5j,K => 5k
"vim.normalModeKeyBindingsNonRecursive": [
{
"before": ["J"],
"after": ["5", "j"]
},
{
"before": ["K"],
"after": ["5", "k"]
},

// 映射到 VS Code 的命令 space + w => workbench.action.files.save
{
"before": ["leader", "w"],
"commands": [
"workbench.action.files.save",
]
}
]

VS Code Vim 有趣的映射

Tips

  • Ctrl + Shift + p 打开命令面板输入 Toggle Vim Mode 快速切换 Vim 开关
  • . 重复最后一次修改
  • >> 向右缩进
  • << 向左缩进
  • u 撤销
  • * 跳转到下一个相同单词
  • # 跳转到上一个相同单词

Chrome 插件 Vimium

Vimium 提供了在浏览器上使用 Vim 功能

  • j 向下移动网页
  • k 向上移动网页
  • gg 移动到网页顶部
  • G 移动到网页底部
  • J 上一个标签页
  • K 下一个标签页

让键盘适应工具

可以选择 ESC 和 Ctrl 比较容易触碰的键盘,或者把 CapsLock 映射成 Ctrl。

⌨️

  • HHKB
  • Poker2

我使用 Vim 几个阶段

从第一次接触到现在也有 3-4 年左右了吧。

  1. 最开始在学校的时候看网上的一个视频课程,里面用的 Sublime + Vim 插件。算是我的 Vim启蒙。导致我到现在一直对 Sublime Text 情有独钟。当时基本只会 hjkl dd gg p 一些简单的操作。
  2. 学了文本对象,f{char} t{char} d/c{文本对象} 等 稍微熟练了些。
  3. 开始了解 VS CodeVim 的搭配。不得不说 VS Cod 中的 VimSublime 中的 Vim功能强大了不少。

有时可能同时会有同事在我电脑上操作,会很不习惯。这种时候 toggle vim mode 就很好使了。

End

先挑选对自己有用的用到日常开发中,不要一口一个大胖子。
Vim 实在是太强大了,写这篇文章时,也有很多是现学(抄)的,熟能生巧。
VS CodeVim 的结合会有一些功能重合或冲突的地方根据自己习惯取舍,最适合自己的就是最好的。

参考链接 🔗

https://vimjc.com/
https://github.com/VSCodeVim
https://coding.imooc.com/learn/list/50.html
https://www.zhihu.com/question/303850876
https://www.barbarianmeetscoding.com/blog/2019/02/08/boost-your-coding-fu-with-vscode-and-vim