LV02-vimscript-02-映射.md

本文主要是vimscript中映射的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
点击查看本文参考资料
点击查看相关文件下载
--- ---

一、什么是vim映射

什么是映射?说实话,我自己理解的应该就跟数学中的函数一样,给了一个x,通过某种映射,对应到一个y,在vim中大概也是这样,比如说我把行删除操作dd映射为-,那么当我在正常模式下,按下-的时候,vim会认为我按下了dd,这样,光标所在行就会被删掉了,大概就这个意思。

VIM: map http://vimcdoc.sourceforge.net/doc/map.html#map.txt

一些说明:

1
2
3
4
5
n前缀: 在普通模式下生效
v前缀: 在可视模式下生效
i前缀: 在插入模式下生效
c前缀: 在EX命令模式下生效
nore前缀: 非递归

二、基本语法

1. 特殊按键写法

点击查看 vim 中功能键写法
按键写法 说明
<k0> - <k9> 小键盘数字 0 - 9
<F1> - <F12> 功能键 F1 - F12
<S-a> Shift+a 组合键
<C-a> CTRL+a 组合键
<A-a> ALT+a 组合键
<Esc> Esc 键
<BS> Backspace 键
<CR> Enter 键
<Space> 空格键
<nop> 表示无操作

【注意】映射的时候,要确保映射过后没有冲突。

2. 基本映射

最开始应该先了解的一种映射就是map,映射,这种映射不限定在某种模式下才生效,也就是说该种方式映射的按键,在normal模式中起作用,同样也会在visual模式下起作用。

2.1 定义一个映射

1
:map {lhs} {rhs}   " 在映射命令作用的模式中把键序列 {lhs} 映射为 {rhs} 

例如,在命令模式中执行:

1
:map - dd          " 对 - 进行映射,映射的功能为 dd

将光标置于某行文本中,然后按下-按键,会发现,这行文本被删除了,这就是映射发挥了的作用。

2.2 列出所有映射

有的时候想要知道目前已经设置了哪些映射,以防止产生冲突,可以在命令模式下执行:

1
:map

这时在vim底部就会以这样的格式打印出所有的映射。

image-20220305114409184

【说明】使用:map!命令只列出插入和命令行模式的映射。imap:vmap:omap:nmap命令则只是列出相应模式下的映射。

点击查看前两栏字符说明
字 符模 式
<Space>普通、可视、选择和操作符等待
n普通
v可视和选择
s选择
x可视
o操作符等待
!插入和命令行
i插入
l插入、命令行和 Lang-Arg 模式的 ":lmap" 映射
c命令行
t终端作业

2.3 取消映射

有些映射不想要了,怎么取消呢?在命令模式下执行:

1
:unmap {lhs}  " lhs 表示要取消的映射,例如 :unmap - 即可取消 - 的映射

这样就可以取消刚才不想要的映射了。

2.4 关于注释

前边说过,有的时候脚本中的注释会失效,其中键盘映射就无法使用注释。在命令模式下执行:

1
:map <space> :set nu!<cr> " change row number

预想的结果是,按下空格键,切换当前行号的布尔选项,原来显示的,现在关闭,原来未显现的,现在打开。按下空格后,我发现,却是是开始了切换,但是,是一直在切换,行号在显示和关闭之间来回反复横跳。其实上边那句命令在vim中,vim会认为我们按下空格的时候是想执行:set nu!<cr> " change row number命令,这显然是错误的,也导致了无法预料的结果产生。

3. 模式映射

其实进行映射的命令远不止一个map,这个还有很多,通过:help map查阅官方文档会发现,还有以下命令可以实现映射,甚至可以在不同模式下进行只针对于该模式的映射。

点击查看详情
CommandMode
NormInsCmdVisSelOprTermLang
[nore]mapyes - - yesyesyes - -
n[nore]mapyes - - - - - - -
[nore]map! - yesyes - - - - -
i[nore]map - yes - - - - - -
c[nore]map - - yes - - - - -
v[nore]map - - - yesyes - - -
x[nore]map - - - yes - - - -
s[nore]map - - - - yes - - -
o[nore]map - - - - - yes - -
t[nore]map - - - - - - yes -
l[nore]map - yesyes - - - - yes
COMMANDSMODES ~
:map :noremap :unmapNormal, Visual, Select, Operator-pending
:nmap :nnoremap :nunmapNormal
:vmap :vnoremap :vunmapVisual and Select
:smap :snoremap :sunmapSelect
:xmap :xnoremap :xunmapVisual
:omap :onoremap :ounmapOperator-pending
:map! :noremap! :unmap!Insert and Command-line
:imap :inoremap :iunmapInsert
:lmap :lnoremap :lunmapInsert, Command-line, Lang-Arg
:cmap :cnoremap :cunmapCommand-line
:tmap :tnoremap :tunmapTerminal-Job

3.1 normal映射

3.1.1 定义一个映射

1
:nm[ap] {lhs} {rhs} " 在映射命令作用的模式中把键序列 {lhs} 映射为 {rhs} 

例如,在命令模式中执行:

1
:nmap \ dd

normal模式下,按下\vim会删除当前行。然后进入Visual模式,再次按下\,这个时候什么都不会发生,这是因为因为我们告诉了vim这个映射仅在normal模式下工作(同时\的默认行为是什么都不做)

3.1.2 取消一个映射

1
:nun[map] {lhs}

例如,在命令模式下执行:

1
:nummap \

normal模式下,按下\vim不再有任何操作。

3.2insert映射

3.2.1 定义一个映射

1
:im[ap] {lhs} {rhs} " 在映射命令作用的模式中把键序列 {lhs} 映射为 {rhs} 

例如,在命令模式中执行:

1
:imap <c-d> dd

按我们的理解就是,在insert模式下通过按键Ctrl+d删除整行。但是运行过后,仅仅是在文件中添加了两个d字符!这句话的意思实际是 当我按下<c-d>时,相当于我 按了两次d,在普通模式下,就会删除一行,但是这是在插入模式,那可不就是插入了两个d字符。所以可以修改为在命令模式中执行:

1
:imap <c-d> <esc>ddi  " <esc>表示回到normal模式,i表示进入插入模式

这次按下Ctrl+d后,在插入模式下删除了这一行,并且又回到插入模式,这个映射,在平时使用的时候无疑会带来巨大的方便。

3.2.2 取消一个映射

1
:iu[nmap] {lhs}

例如,在命令模式下执行:

1
:iunmap <c-d>

insert模式下,按下Ctrl + dvim不再有任何操作。

3.3 visual映射

3.3.1 定义一个映射

1
:vm[ap] {lhs} {rhs} " 在映射命令作用的模式中把键序列 {lhs} 映射为 {rhs} 

例如,在命令模式中执行:

1
:vmap \ U

进入visual模式并选中一些字符串,按下\vim中的小写字母转换为大写。若是之前的:nmap \ dd未禁用的话,回到normal模式,再次按下\,会发现这一行被删掉了,这样,同样的按键在不同的模式就有了不一样的功能。

3.3.2 取消一个映射

1
vu[nmap] {lhs}

例如,在命令模式下执行:

1
:vunmap \

visual模式下,选中字符串,按下\vim不再有任何操作。

4. 精确映射

当我们了解了映射之后,应该会定义很多便于自己操作的映射,是否会出现这样的情况呢?万一有呢,在命令模式下执行:

1
2
:nmap - dd
:nmap \ -

normal模式中,我们按下-,它会删除当前行,然后我们按下\的时候应该是会出现-的功能,但是在这个模式下,-似乎没什么特别的功能,应该是没有现象才对,但是,放我们按下的时候,会发现,光标所在行被删除了。原来vim\解释为-,但是-又被解释为dd,于是这一行就被删除了。这无疑有时候也会给我们带来困扰。在命令模式执行以下命令,删除上边的映射。

1
2
:nunmap -
:nunmap \

4.1 递归

接下来,在命令模式中执行:

1
:nmap dd O<esc>jddk
点击查看命令说明

上边命令理论上的含义如下:

命令含义
O按下 dd 后,在当前行的上一行添加新行,同时进入 insert 模式,此时光标位于新插入的行
<esc>然后退出insert`模式,光标依然位于新插入的行
j再向下移动一行,回到最开始的行
dd删除当前行,也就是最开始的行
k最后向上移动一行

这个映射,按照我们想的,理应由vim解释为:在normal模式下按下dd,清除当前行内容。

但是当我们在normal模式下真正按下dd的时候,vim不断向下滚屏,按下Ctrl+c才能停止,按gg回到首行,发现,当时所在行的后边有非常多的空行。这是为什么呢?上边的例子可以很好的帮助我们理解产生这种现象的原因。

命令含义
:nmap dddd存在映射
O按下 dd 后,在当前行的上一行添加新行,同时进入 insert 模式,此时光标位于新插入的行
<esc>然后退出insert`模式,光标依然位于新插入的行
j再向下移动一行,回到最开始的行
dd本应为删除当前行,也就是最开始的行,但是由于 dd 存在映射,所以这里对于vim来说,会再次被映射。
O按下 dd 后,在当前行的上一行添加新行,同时进入 insert 模式,此时光标位于新插入的行
<esc>然后退出insert模式,光标依然位于新插入的行
j再向下移动一行,回到最开始的行
dd......

这样下来,每次遇到dd,都被解释为在上一行新建一行,退出insert模式,再向下移动一行,回到最开始的行。这个映射会一直持续下去,此时就出现了上边不断滚屏,出现很多空行的现象。

*map系列命令的一个缺点就是存在递归的危险。如果我们安装一个插件,插件映射了同一个按键为不同的行为,两者冲突,此时就有一个映射无效了。

4.2 非递归映射

4.2.1 定义一个非递归映射

vim中,提供了这样一组映射命令:

1
2
3
4
:no[remap]  {lhs} {rhs}
:nn[oremap] {lhs} {rhs}
:ino[remap] {lhs} {rhs}
:vn[oremap] {lhs} {rhs}

这些命令创建的映射在运行时,可以避免映射的嵌套和递归。在命令模式下执行:

1
2
:nmap x dd
:nnoremap \ x

此时,当我们按下\时,vim将忽略x的映射,仅按照x的默认操作执行,即删除当前光标下的字符而不是删除整行。

这个时候,我们来修改上边的命令为:

1
:nnoremap dd O<esc>jddk

这个时候dd的效果就是清空当前行。

4.2.2 删除一个非递归映射

在相应的模式下使用相应的删除操作即可。

1
2
3
4
:unm[ap]  {lhs}
:nun[map] {lhs}
:iu[nmap] {lhs}
:vu[nmap] {lhs}

每一个*map系列的命令都有个对应的*noremap命令,包括:noremap/nnoremapvnoremapinoremap,这些命令将不递归解释映射的内容。我们进行映射的时候,毫无疑问,无论在任何时候都应该使用这样的映射,这样会减少自己带给自己的麻烦。

5. leader

每次我们像:nnoremap <space> dd这样映射一个按键都会覆盖掉<space>的原有功能。 如果哪天我们想用<space>了,那该怎么办呢?

  • 映射按键序列

vim可以映射多个按键。在命令模式下执行:

1
2
:nnoremap -d dd
:nnoremap -c ddO

norma模式下敲入 -d-c,会发现第一个映射是删除一行,第二个是删除一行并进入insert模式。这就意味着我们可以用一个不常用的按键(如-)作为前缀,后接其它字符作为一个整体 进行映射。

  • Leader

前边提到的前缀,在vim中,被称之leader键。设置leader按键的命令为:

1
:let mapleader = "-"

我们可以设置自己喜欢的按键为leader作为映射前缀,尽管这样会屏蔽这个按键原来的功能,所以可以找一些几乎永远不可能会用到的按键,例如,-等,就几乎用不到。创建新的映射时,可以使用<leader>表示我们设置的leader按键。如:

1
:nnoremap <leader>d dd

这个时候,我们按下leader按键(这里就是 -)和d,时vim会删除当前行。

  • Local Leader

vim还有一个leader,叫做local leader。这个leader用于那些只对某类文件 (如Python文件、HTML文件)而设置的映射。localleader设置方式为:

1
:let maplocalleader = ","

现在我们就可以在映射中使用<localleader>了,使用方法和<leader>一样,只要敲命令的时候使用localleader的前缀就好了。

6. 更多Mappings

上一节介绍leader的时候,键入多个按键的时候发现,我们必须快速键入,有所停顿的话,会有奇怪的事情发生。这是因为在vim中,多字符映射的时候是有连续性的,如果单个字符有单独的功能,而且停顿时间过长的话,vim并不会将其解释为映射后的功能。

举个例子,在命令模式中执行:

1
:nnoremap jk dd

当我们快速在normal模式下,键入jk两个按键时,这两次按键会被VIm认为是一起的,于是vim就会按照映射命令将其解释为dd即删除当前行,此时映射生效。

当我们在normal模式下,先按一下j,停顿一下,再按k,那么这个时候,vim就会认为这是两个命令,按下j,然后经过短暂停顿,光标会向下移动一行,再来按k键的时候,光标会再向上移动一行,此时的映射就并没有生效。

这个映射会给我们进行光标移动操作时带来很大的麻烦,可以先删除它,在命令模式中执行:

1
:nunmap jk

然后,在normal模式下快速输入jk会像往常一样下移一行然后又上移一行。

三、 Abbreviations

官方文档对这一部分解释为缩写,也属于map.txt中的一部分。它与映射有点类似,但是它多数是在插入,替换和命令行模式中使用。如果输入一个是缩写的单词,它会被替换成所表示的东西。这可以在经常输入的长单词时节省键击,并且能用它来自动更正经常犯的拼写错误。

1. 基本命令

这里记录一些常用的操作。

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
" 列出缩写
:ab[breviate] " 列出所有缩写
:ab[breviate] {lhs} " 列出以 {lhs} 开头的缩写.

" 增加缩写
:ab[breviate] [<expr>] [<buffer>] {lhs} {rhs} " 增加一个从 {lhs} 到 {rhs} 的缩写
:ia[bbrev] [<expr>] [<buffer>] [lhs] [rhs] " 仅在插入模式中使用。
:ca[bbrev] [<expr>] [<buffer>] [lhs] [rhs] " 仅在命令行模式中使用。
" 不进行重映射而增加缩写
:norea[bbrev] [<expr>] [<buffer>] [lhs] [rhs] " 与 :ab 一样
:inorea[bbrev] [<expr>] [<buffer>] [lhs] [rhs] " 仅在插入模式中使用。
:cnorea[bbrev] [<expr>] [<buffer>] [lhs] [rhs] " 仅在命令行模式中使用。

" 删除缩写
:una[bbreviate] [<buffer>] {lhs} " 从列表中删除 {lhs} 的缩写。
:iuna[bbrev] {lhs}
:cuna[bbrev] {lhs}

:abc[lear] [<buffer>] " 删除所有的缩写。
:iabc[lear] [<buffer>] " 为插入模式删除所有的缩写。
:cabc[lear] [<buffer>] " 为命令行模式删除所有的缩写。

" 使用缩写
lhs<space> " 插入或者命令模式都可以,后边会多一个空格
lhs<Tab> " 插入或者命令模式都可以,后边会多一个 Tab
lhs<Enter> " 插入模式可以,后边会多一个 Enter, 命令模式最好不要这样
  • 使用实例

如果我们想在文本插入时快速插入很多东西,比如自己的邮箱,那应该怎么做呢?在命令模式中执行:

1
:iab @@ 12345678900@qq.com

进入insert模式,键入@@符号后,按下<space>或者Tab或者<Enter>,会发现,@@被替换成了12345678900@qq.com

2. 与Mappins区别

其实,看起来缩写和映射还是很像的,特别是在插入模式下,接下来我们看看他们有什么区别。

  • 缩写

在命令模式中执行:

1
:iab ssig --<cr>fanhua<cr>--

进入插入模式,输入下边的话:

1
2
ssig
Larry Lessig wrote the book.

会发现,输入ssig然后换行的时候,ssig被替换成--<cr>fanhua<cr>--,输入下边一句话的时候,即便单词中含有ssig(Lessig),按下空格的时候也不会被替换。

  • Mappings

在命令模式执行:

1
2
:iuna ssig             " 删除缩写
:inoremap ssig --<cr>fanhua<cr>--

进入插入模式,输入下边的话:

1
2
ssig
Larry Lessig wrote the book.

会发现,快速输入ssig然后换行的时候,ssig被替换成--<cr>fanhua<cr>--,输入下边一句话的时候,即便单词中含有ssig(Lessig),只要快速键入,vim就会将ssig替换掉。这就说明,缩写会注意前后字母,完全匹配的时候才会进行替换,相对而言,可能比映射更好些。

【注意】输入ssig的时候要快速,因为是在插入模式,所以停顿时间太长的话,vim就直接认为是输入了单个字符,而不会将其解释为映射。

四、映射实践

上边那么多的内容,到此为止。现在我们应该可以随意来映射我们自己的按键,以便于让自己的工作更加快速顺利的进行。

1. 快速编辑vim配置文件

有时我们正在进行工作,突然发现加个映射会加速我们的进度。我们要立即将其加到~/.vimrc 文件中,但是又不想退出当前的文件而去修改~/.vimrc。那么此时我们应该怎样将这个映射添加进去呢?

1.1 编辑配置文件映射

我们可以在一个分屏中打开~/.vimrc文件以快速编辑添加映射,然后退出继续原来的工作。在命令模式下执行:

1
2
:let mapleader = ","          " 设置 leader 按键
:nnoremap <leader>ev :vsplit $MYVIMRC<cr>
点击查看命令说明
:nnoremap <leader>ev leader+ev 进行映射
:vsplit 打开一个新的纵向分屏,也可以用 :split 进行横向的分屏
$MYVIMRC 相当于环境变量,就是指定~/.vimrc文件的特殊vim变量
<cr> 回车(Enter)

此时在normal模式中快速(不然好像会进入可视模式)敲入,ev三个按键,就会在一个纵向分屏中打开~/.vimrc文件。

1.2 重读配置文件映射

当我们在~/.vimrc文件中添加一个映射并不是立即生效的。这是因为~/.vimrc文件只在启动vim的时候才会读取。所以是上边我们通过映射的命令修改的配置文件不会立即生效,需要运行以下命令才能生效,在命令模式执行:

1
:source $MYVIMRC<cr>

当然如果我们可以对这个命令进行映射,会方便许多:

1
2
:let mapleader = ","          " 设置 leader 按键
:nnoremap <leader>sv :source $MYVIMRC<cr>

当我们编辑过~/.vimrc文件后,按下:w<Enter>保存,然后快速按下,sv三个按键,就会刷新配置文件,直接生效(在当前窗口),其他窗口的话,需要切换后再进行刷新。

2. 单词加双引号

在命令模式中执行:

1
2
:let mapleader = ","    " 设置 leader 键
:nnoremap <leader>" viw<esc>a"<esc>hbi"<esc>lel
点击查看命令说明
viw高亮选中单词
<esc>退出visual模式,此时光标会在单词的最后一个字符上
a移动光标至当前位置之 后 并进入insert模式
"插入一个"
<esc>返回到normal模式
h左移一个字符
b移动光标至单词头部
i移动光标至当前位置之 前 并进入insert模式
"插入一个"
<esc>返回到normal模式
l右移一个字符,光标置于单词的头部
e移动光标至单词尾部
l右移一个字符,置光标位置在第一个添加的引号上

【说明】我们使用的是nnoremap而不是nmap,所以尽管映射了字符序列中的字符也不会有任何影响。

normal模式下,移动光标至一个单词上(开头,结尾,中间都可以), 输入<leader>"vim会将那个单词用双引号包围起来。

本文未完,还有后续。。。。。。