LV02-vimscript-02-映射.md
本文主要是vimscript中映射的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。
点击查看使用工具及版本
Windows | windows11 |
Ubuntu | Ubuntu16.04的64位版本 |
VMware® Workstation 16 Pro | 16.2.3 build-19376536 |
点击查看本文参考资料
参考方向 | 参考原文 |
Learn vimscript the Hard Way | https://learnvimscriptthehardway.stevelosh.com/ |
vim在线文档 | http://vimcdoc.sourceforge.net/ |
VIM 中文帮助 | https://vimcdoc.sourceforge.net/doc/help.html |
vim教程 | https://www.w3cschool.cn/vim/ |
一、什么是vim
映射
什么是映射?说实话,我自己理解的应该就跟数学中的函数一样,给了一个x
,通过某种映射,对应到一个y
,在vim
中大概也是这样,比如说我把行删除操作dd
映射为-
,那么当我在正常模式下,按下-
的时候,vim
会认为我按下了dd
,这样,光标所在行就会被删掉了,大概就这个意思。
VIM: map | http://vimcdoc.sourceforge.net/doc/map.html#map.txt |
一些说明:
1 | n前缀: 在普通模式下生效 |
二、基本语法
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
底部就会以这样的格式打印出所有的映射。
【说明】使用: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
查阅官方文档会发现,还有以下命令可以实现映射,甚至可以在不同模式下进行只针对于该模式的映射。
点击查看详情
Command | Mode | |||||||
Norm | Ins | Cmd | Vis | Sel | Opr | Term | Lang | |
[nore]map | yes | - | - | yes | yes | yes | - | - |
n[nore]map | yes | - | - | - | - | - | - | - |
[nore]map! | - | yes | yes | - | - | - | - | - |
i[nore]map | - | yes | - | - | - | - | - | - |
c[nore]map | - | - | yes | - | - | - | - | - |
v[nore]map | - | - | - | yes | yes | - | - | - |
x[nore]map | - | - | - | yes | - | - | - | - |
s[nore]map | - | - | - | - | yes | - | - | - |
o[nore]map | - | - | - | - | - | yes | - | - |
t[nore]map | - | - | - | - | - | - | yes | - |
l[nore]map | - | yes | yes | - | - | - | - | yes |
COMMANDS | MODES ~ |
:map :noremap :unmap | Normal, Visual, Select, Operator-pending |
:nmap :nnoremap :nunmap | Normal |
:vmap :vnoremap :vunmap | Visual and Select |
:smap :snoremap :sunmap | Select |
:xmap :xnoremap :xunmap | Visual |
:omap :onoremap :ounmap | Operator-pending |
:map! :noremap! :unmap! | Insert and Command-line |
:imap :inoremap :iunmap | Insert |
:lmap :lnoremap :lunmap | Insert, Command-line, Lang-Arg |
:cmap :cnoremap :cunmap | Command-line |
:tmap :tnoremap :tunmap | Terminal-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 + d
,vim
不再有任何操作。
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 | :nmap - dd |
在normal
模式中,我们按下-
,它会删除当前行,然后我们按下\
的时候应该是会出现-
的功能,但是在这个模式下,-
似乎没什么特别的功能,应该是没有现象才对,但是,放我们按下的时候,会发现,光标所在行被删除了。原来vim
把\
解释为-
,但是-
又被解释为dd
,于是这一行就被删除了。这无疑有时候也会给我们带来困扰。在命令模式执行以下命令,删除上边的映射。
1 | :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 dd | dd存在映射 | ||
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 | :no[remap] {lhs} {rhs} |
这些命令创建的映射在运行时,可以避免映射的嵌套和递归。在命令模式下执行:
1 | :nmap x dd |
此时,当我们按下\
时,vim
将忽略x
的映射,仅按照x
的默认操作执行,即删除当前光标下的字符而不是删除整行。
这个时候,我们来修改上边的命令为:
1 | :nnoremap dd O<esc>jddk |
这个时候dd
的效果就是清空当前行。
4.2.2 删除一个非递归映射
在相应的模式下使用相应的删除操作即可。
1 | :unm[ap] {lhs} |
每一个*map
系列的命令都有个对应的*noremap
命令,包括:noremap
/nnoremap
、 vnoremap
和inoremap
,这些命令将不递归解释映射的内容。我们进行映射的时候,毫无疑问,无论在任何时候都应该使用这样的映射,这样会减少自己带给自己的麻烦。
5. leader
键
每次我们像:nnoremap <space> dd
这样映射一个按键都会覆盖掉<space>
的原有功能。 如果哪天我们想用<space>
了,那该怎么办呢?
- 映射按键序列
vim
可以映射多个按键。在命令模式下执行:
1 | :nnoremap -d dd |
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 | " 列出缩写 |
- 使用实例
如果我们想在文本插入时快速插入很多东西,比如自己的邮箱,那应该怎么做呢?在命令模式中执行:
1 | :iab @@ 12345678900@qq.com |
进入insert
模式,键入@@
符号后,按下<space>
或者Tab
或者<Enter>
,会发现,@@
被替换成了12345678900@qq.com
2. 与Mappins
区别
其实,看起来缩写和映射还是很像的,特别是在插入模式下,接下来我们看看他们有什么区别。
- 缩写
在命令模式中执行:
1 | :iab ssig --<cr>fanhua<cr>-- |
进入插入模式,输入下边的话:
1 | ssig |
会发现,输入ssig
然后换行的时候,ssig
被替换成--<cr>fanhua<cr>--
,输入下边一句话的时候,即便单词中含有ssig(Lessig)
,按下空格的时候也不会被替换。
Mappings
在命令模式执行:
1 | :iuna ssig " 删除缩写 |
进入插入模式,输入下边的话:
1 | ssig |
会发现,快速输入ssig
然后换行的时候,ssig
被替换成--<cr>fanhua<cr>--
,输入下边一句话的时候,即便单词中含有ssig(Lessig)
,只要快速键入,vim
就会将ssig
替换掉。这就说明,缩写会注意前后字母,完全匹配的时候才会进行替换,相对而言,可能比映射更好些。
【注意】输入ssig
的时候要快速,因为是在插入模式,所以停顿时间太长的话,vim
就直接认为是输入了单个字符,而不会将其解释为映射。
四、映射实践
上边那么多的内容,到此为止。现在我们应该可以随意来映射我们自己的按键,以便于让自己的工作更加快速顺利的进行。
1. 快速编辑vim
配置文件
有时我们正在进行工作,突然发现加个映射会加速我们的进度。我们要立即将其加到~/.vimrc
文件中,但是又不想退出当前的文件而去修改~/.vimrc
。那么此时我们应该怎样将这个映射添加进去呢?
1.1 编辑配置文件映射
我们可以在一个分屏中打开~/.vimrc
文件以快速编辑添加映射,然后退出继续原来的工作。在命令模式下执行:
1 | :let mapleader = "," " 设置 leader 按键 |
点击查看命令说明
: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 | :let mapleader = "," " 设置 leader 按键 |
当我们编辑过~/.vimrc
文件后,按下:w<Enter>
保存,然后快速按下,sv
三个按键,就会刷新配置文件,直接生效(在当前窗口),其他窗口的话,需要切换后再进行刷新。
2. 单词加双引号
在命令模式中执行:
1 | :let mapleader = "," " 设置 leader 键 |
点击查看命令说明
viw | 高亮选中单词 |
<esc> | 退出visual模式,此时光标会在单词的最后一个字符上 |
a | 移动光标至当前位置之 后 并进入insert模式 |
" | 插入一个" |
<esc> | 返回到normal模式 |
h | 左移一个字符 |
b | 移动光标至单词头部 |
i | 移动光标至当前位置之 前 并进入insert模式 |
" | 插入一个" |
<esc> | 返回到normal模式 |
l | 右移一个字符,光标置于单词的头部 |
e | 移动光标至单词尾部 |
l | 右移一个字符,置光标位置在第一个添加的引号上 |
【说明】我们使用的是nnoremap
而不是nmap
,所以尽管映射了字符序列中的字符也不会有任何影响。
在normal
模式下,移动光标至一个单词上(开头,结尾,中间都可以), 输入<leader>"
,vim
会将那个单词用双引号包围起来。
本文未完,还有后续。。。。。。