Vim

Vim 基础系统教程

Posted by KalosAner on December 15, 2024

一、初识

Vim 主要有 4 种模式:

Normal 模式:默认进入的模式,也是最常用的模式。

Insert 模式:插入模式,像正常的文本编辑器一样输入。

Command 模式:命令模式,在底部输入命令。

Visual 模式:可视模式,对文本进行选择。

Normal 模式:基本移动

kjhl:分别对应上下左右

gg:跳到第一行

{n}gg:跳到第 n

G:跳到最后一行

<Ctrl-u>/<Ctrl-b>:向上翻半页/一页(当前屏幕显示的行数就是一页)

<Ctrl-d>/<Ctrl-f>:向下翻半页/一页(当前屏幕显示的行数就是一页)

zz/zt/zb:光标行设置为 屏幕居中/屏幕第一行/屏幕最后一行

进入 Insert 模式

Normal 模式下通过特定命令进入 Insert 模式:

  • i:代表 “insert”,当前光标之前开始输入
  • a:代表 “append”,当前光标之后开始输入
  • o:下方插入新的一行,然后开始输入
  • s:删除当前光标的字符,然后开始输入
  • I:在本行的开头开始输入
  • A:在本行的末尾开始输入
  • O:上方插入新的一行,然后开始输入
  • S:删除当前行,然后开始输入

Tips:大写字母和小写字母的操作存在关联,可以一起记忆。

进入 Command 模式

Normal 模式下输入: 进入 Command 模式

  • :w:保存当前文件
  • :q:退出
  • :q!:放弃当前更改,强制退出
  • :wq:保存当前更改,然后退出
  • :h {command}:显示关于命令的帮助

<Esc>:回到 Normal 模式

Tips:使用 :h {command} 进入到帮助文档之后,可以直接输入 :q 退出帮助文档

进入 Visual 模式

Normal 模式下按 v 进入可视模式

进入可视模式后可以用 Normal 模式下的移动命令选择文本

可视模式下 x/y:剪切/复制;回到 Normal 模式下 p:粘贴

Normal 模式下按 v 进入可视模式,一次选中一整行,在需要选中多行时很方便

<Esc> 回到 Normal 模式

二、移动与编辑

基于单词的移动

除了简单的 kjhl 移动之外,vim 还支持更高级的移动

w:代表 “word”,跳转到下一处单词的开头

b:代表 “back”,跳转到上一处单词的开头

e:代表 “end”,跳转到下一处单词的结尾

gee 的反向版本,跳转到上一处单词的结尾

wbe 版本以空格和各种符号为分隔符,大写版本 WBE 对应单词时连续的非空字符也就是只以空格为分隔符

Tips:这里的下/上一处单词的并不是下/上一个单词,而是下一处位置

Snipaste_2024-12-29_21-48-18

基于搜索的移动

行内搜索:

  • f{char}/t{char}:跳转到本行下一个 char 字符出现处/出现前

  • ;/,:快速向前/向后重复上一次 f/t 查找的 char;如果使用 ;/, 之前没有进行过 f/t 则默认查找当前光标指向的字符

  • F{char}/T{char}:往前搜索而非往后

文件中搜索:

  • /{pattern}:跳转到本文件中下一个 pattern 出现的地方
  • ?{pattern}:跳转到本文件中上一个 pattern 出现的地方
  • pattern:可以是正则表达式
  • *:等价于 /{pattern},其中 pattern 是当前光标下的单词

  • n/N:快速重复对 //? 中的字符或单词进行查找,n 是正向,N 是反向

基于标记的移动

  • m{makr}:把当前位置标记为 mark
  • ``{mark}:跳转到名为 mark` 的标记位置

marka-z 的字符

常用场景:当需要临时离开当前光标处,做一些事情后再快速地回来

内置标记:

  • ` `` `:上次跳转前的位置
  • ``.`:上次修改的位置
  • ``^`:上次插入的位置

其他实用的跳转

  • ^/$:跳转到本行的开始/结尾
  • %:跳转到匹配的配对符(括号等)处

Operator+Motion=Action

{operator}{motion}:一次编辑动作

常见操作符:

  • c:代表 “change”,修改,删除内容并进入插入模式
  • d:代表 “delete”,删除
  • y:代表 “yank”,复制
  • v:代表 “visual”,选中文本,进入可视模式

大部分操作符连续按两次 (cc/dd/yy):将其作用在光标所在的行上

“操作符+移动” 是非常重要的操作罗技,它允许你组合出各种动作

重要操作:. 命令

  • .:重复上一次修改
  • u:撤销上一次修改
  • <Ctrl-r>:重做上一次撤销

. 命令非常适合用于需要多次重复某一个修改动作的场景,省去了重复输入命令,提高效率

批量操作:数字+动作

{count}{action}:重复 countaction 动作 动作可以是移动动作或是编辑动作

  • 4j:向下移动 4 行
  • 3dw:删除 3 个单词
  • 2yy:复制 2 行
  • 4p:粘贴 4 次

数字+动作,是一种重要的批量操作方式,命令直观,语义明确

  • . 命令可以直观地看到每一次的变化,在合适的时候停止
  • 数字+动作则需要预先知道动作的次数

技巧:使用相对行号确定移动范围

当涉及行操作时,使用相对行号能够更直观地确定范围

:set relativenumber:开启相对行号

:set norelativenumber:关闭相对行号

小结

  • 掌握进阶的移动与编辑命令
  • (重要)掌握 {operator}{motion} 的操作哲学
  • 掌握用 . 命令重复上一次修改
  • (重要)掌握使用 {count}{action} 进行批量操作

三、文本对象操作

文本对象操作

textobjects:语义化的文本片段

格式:i/a + 对象

常见的对象:

  • w/Wsp:单词、句子、段落
  • (/)[/]{/}</``>/”`:配对符定义的对象

i 代表 “inner”,对象内部;a 代表 “a”,表示一个对象额外包括周围的空格或配对符

Snipaste_2024-12-30_11-37-46

举例:

文本对象提供了为文本赋予结构化的含义,允许我们以一个语义对象作为操作单元

[count]{operator}{textobjects}

  • diw:删除一个单词
  • ci(:修改小括号内部
  • yi{:复制大括号内部

通过组合 operatortextobjects,可以对不同的语义对象实施不同的操作,不仅十分灵活,而且语义明确,容易记忆

配合 . 命令或者 [count] 可以简单地完成多次对特定语义对象的操作

textobjects VS motion

{operator}{motion}{operator}{textobjects}

解耦了操作与操作的对象,大大提高了操作效率

  • motion 是能够移动光标的命令,可以独立使用(如 wbe
  • 文本对象只能跟在 operator 后面,不能独立使用(如 iw
  • motion 通过光标的移动确定 operator 的作用范围,范围更加灵活但不够明确
  • textobjects 则是显式地指定操作的对象,范围明确

扩展

{operator}{motion}{operator}{textobjects}

有些插件可以通过自定义 operatormotiontextobjects 进行扩展,实现更强大的操作

  • vim-easymotion:扩展 motion,更强大的跳转功能
  • vim-surround:定义了添加配对符(括号、引号等)的 operator
  • vim-commentary:定义了添加注释的 operator
  • targets.vim:扩展 textobjects,定义了新的文本对象,如函数参数等

操作符与命令补充

  • gu{textobjects}/gU{textobjects}/g~{textobjects}:操作符,转小写/转大写/翻转大小写
  • J:join,连接两行
  • <Ctrl-a>/<Ctrl-x>:增加数字/减少数字
  • g<Ctrl-A>:创建递增序列
  • </>:连按两下进行左/右缩进

建议:让你的命令更模块化

尽量使你的命令更模块化,具有清晰的含义与作用范围,以便于与 . 等命令协同

例如:dawdw 具有更清晰的语义,也更模块化

练习技巧:用 v 先可视化修改的范围

一开始不熟练时,可能不确定 motiontextobjects 具体覆盖了哪些部分文本,可以先用 v{motion}/v{textobjects} 将范围选中,再执行 c/d/y 等操作

但是,当熟练掌握后,尽可能直接使用完整的命令进行操作,因为相比于 viwddiw 更加模块化,也更容易进行重复操作

小结

了解常用的文本对象

(重要)掌握 {operator}{textobjects} 的操作哲学

尽量使你的命令模块化,语义清晰,易于重复

四、寄存器与宏

寄存器

Vim 提供了许多寄存器用于存放内容,可以理解为剪切板

一个字符对应一个寄存器(如 a-z0-9

特别的寄存器:

  • ":双引号,默认寄存器,平时复制、删除的内容都放在里面
  • %:当前文件名
  • .:上一次插入的内容
  • ::上一次执行的内容

通过 :reg {register} 查看对应寄存器中的内容

通过 :h registers 查看寄存器的帮助文档

指定寄存器

在复制/删除/粘贴等操作前加上 "{register} 就可以指定本次操作所用的寄存器

只要设计寄存器操作的都可以这样指定

  • "ayy:将这一行复制到 a 寄存器中
  • "bdiw:将单词删除,保存到 b 寄存器中
  • "cp:将 c 寄存器中的内容粘贴出来

常见用途:将想要持久保存的文本放到特定寄存器中,随时进行粘贴,避免被覆盖

寄存器字符大写:代表添加(append)而非覆盖。

宏(Macro):录制一系列键盘操作,并允许我们重放这些操作

操作序列存储在指定的寄存器中

  • q{register}:开始录制宏,存放在寄存器 register
  • 录制过程中按 q 退出录制
  • @{register}:重放寄存器 register 中的操作
  • @@:重放上一次宏操作

常见用法:q{register} 录制一段操作,@{register} 重放,然后一直 @@ 重放这段操作

注意:. 命令对宏不生效,. 命令只记录上一次修改,而宏可能包含多次修改

建议:让你的宏对连续重放友好

  1. 让你的光标移动更加 general
  2. 假设你要在多个特定的位置进行特定的操作,一个好的习惯是在宏录制的最后,跳转到下一个需要编辑的位置 即,宏包括【编辑动作】+【跳转到下一个需要编辑的动作】 这样一来,连续重放就可以实现对所有需要编辑的位置进行编辑操作

通过大写的寄存器,在宏的后面添加命令

如果宏是重放友好的,允许我们使用 [count]@{register} 直接重放 count

小结

  • 了解寄存器的概念
  • 了解常用的内置寄存器
  • (重要)掌握使用宏进行批量操作

五、命令模式

前面的教程大多集中在 Normal 模式

命令模式提供了除了 Normal 模式的编辑方式

命令模式的操作对象以”行“为基本单位

normal 和 global 命令提供强大的批量行操作

Ex 命令格式

:[range] {excommand} [args]

  • range:作用的范围,缺省默认本行
  • excommand:特殊的命令,适用于 Command 模式
  • args:后续的参数,依命令而定

一些 Ex Command ([x] 为寄存器,是可选项):

  • :[range] delete [x]:删除 range 中的行(到寄存器 x),delete 可简写为 d
  • :[range] yank [x]:复制range 中的行(到寄存器 x),yank 可简写为 y
  • :[range] print:将 range 中的行打印出来,print 可简写为 p

rangeaddress:指定范围

range 由一个或两个 address 构成,即 {address},{address}

address 可以是:

  • {lineno}:行号,如 3 代表第三行(O 代表第一行上面的虚拟行)
  • $:最后一行
  • .:光标所在行
  • /{pattern}/:下一个 pattern 所在的行

address 可以做加减法,.+3 代表光标往下第三行,$-3 代表倒数第 4 行

aaddress 组合成 range

address 组合出 range(可以混用):

  • 1, 3:文件的 1-3 行
  • ., .+4:当前-当前往下 4 行(共 5 行)
  • $-3, $:文件的最后 4 行

%:特殊的 range,代表当前文件的所有行

'< /'>:可视模式中选中范围的开头和结尾(可视模式下直接按 : 可以直接设置)

例子:

  • 1, 3 delete:删除 1~3 行
  • ., .+4 yank:复制当前~当前往下 4 行
  • $-3, $ print:打印文件的最后 4 行

行的复制、移动、粘贴

  • :[range] copy {address}:把 range 中的行复制到 address 后面
  • :[range] move {address}:把 range 中的行移动到 address 后面
  • :[address] put [x]:把寄存器 x 的内容粘贴到 address 后面

0 作为虚拟行的 address,可以用来将内容插入第一行

Ex 命令

删除、拷贝、打印:

  • :[range] delete [x]
  • :[range] yank [x]
  • :[range] print

复制、移动、粘贴:

  • :[range] copy {address}
  • :[range] move {address}
  • :[address] put [x]

Ex 命令和 Normal 模式下的编辑操作有区别?

  • 命令模式下的编辑操作以”行“为单位
  • 命令模式下的编辑操作无需移动光标

批量操作:normal 命令

格式::[range] normal {commands}

含义:range 中的所有行执行 Normal 模式下的命令 commands

  • range 设置为 %,可以对整个文件的所有行执行
  • :[range] normal .:配合 . 命令,效果拔群 常用做法:先做一次修改操作,再用 normal 命令在指定的行上完成操作

  • . 命令只能记录一次修改,用宏可以实现记录多个操作 :[range] normal @{register} 常用做法:先把想要的操作录制成宏,再用 normal 命令在指定的行上重放 宏

举例:1, 4 normal I#

批量操作:global 命令

格式::[range] global/{pattern}/[cmd]

含义:range 中包含 pattern 的所有行执行 Command 模式下的 Ex 命令

[cmd]:Ex 命令,缺省默认是打印(print

注意:normal 命令也是 Ex 命令

:[range] global/{pattern}/normal {commands}:对 range 中所有带 pattern 的行,执行 Normal 模式下的命令 commands

举例::% global /TODO/delete:删除所有带 TODO 的行

替换命令

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

pattern 替换为 string,默认只替换第一个匹配

flags

  • g:替换每一行的所有匹配
  • i:忽视大小写
  • c:替换前进行确认
  • n:计数而不是替换

:%s/Vim//gn:统计文件中所有 Vim 出现的次数(此时替换为什么无所谓,加了 n 就不会执行替换操作)

小结

  • 了解常见的 Ex 命令
  • 了解 addressrange 的概念
  • (重要)掌握如何使用 normal 命令批量运行 Normal 模式的命令
  • (重要)掌握如何使用 global 命令批量运行 Command 模式的 Ex 命令

Snipaste_2024-12-30_19-27-59

推荐资料:官方文档:vimtutor,《practical vim》

参考:https://www.bilibili.com/video/BV1s4421A7he/?spm_id_from=333.337.search-card.all.click&vd_source=e168f5e534dbfee9b7d558d35e4ee09e