Jade Dungeon

vimscript设置与按键

配置文件~/.vimrc

  • 打开配置文件::o $MYVIMRC
  • 生效修改::source $MYVIMRC

vimrc脚本

在「任意」系统中,在Vim中执行:echo $MYVIMRC命令显示配置文件的位置。

命令source让Vim读取指定的文件,并将其当做Vimscript执行。

  • <leader>ev打开配置文件。
  • 使用<leader>sv重读配置使修改生效。

注释

"开头注释一行。

打印信息

  • :messages命令:查看历史信息。
  • :echo命令:输出信息,任务结束后丢弃,:messages命令看不到。
  • :echom命令:输出信息,在:messages查看的历史信息中。
:echo "Hello, world!"

:echom "Hello again, world!"

:messages

Vim环境选项(Options)

查询选项设置

可以通过选项来控制vim的行为,通过选项的名称<name>加上后缀?构成<name>?的形式, 可以查询指定选项的值。 以是否显示行号的选项number为例:

:set number?     # 如果当前有显示行号,结果为:number
                 # 如果当前不显示行号,结果为:nonumber

布尔类型的选项

布尔类型选项只有「开启」与「关闭」两种状态。 通过在名称<name>加上前缀no组成no<name>的形式表示关闭选项。 以是否显示行号的选项number为例:

:set   number
:set nonumber

也可以加上后缀!组成<name>!的形式在开启与关闭两种状态之间切换。 还是以是否显示行号的选项number为例:

set number!

键值类型的选项

键值类型的选项不只有开与关两种状态,它需要一个值。 以行号宽度的numberwidth为例:

:set numberwidth=4

一次性设置多个值

:set number numberwidth=6

按键映射(Mapping)

格式:

(n|v|i)?(u)?map (key) (value)
  • (n|v|i):指定环境。
  • (u):删除映射。
  • key:按键。
  • value:映射的操作。

映射的场景:

  • map:在所有模式下映射
  • nmap:只在「普通模式」下映射
  • vmap:只在「Visual模式」下映射
  • imap:只在「插入模式」下映射

取消映射的场景:

  • unmap:在所有模式下删除映射
  • nunmap:只在「普通模式」下删除映射
  • vunmap:只在「Visual模式」下删除映射
  • iunmap:只在「插入模式」下删除映射

普通字符

在普通模式下使用map命令定义按键映射。

普通模式下映射-x

:map - x

普通模式下映射-dd

:map - dd

映射多个按键

Vim还可以映射多个按键:

:map -d dd

特殊字符

<keyname>的格式映射特殊字符

空格键选中光标所在的单词:

:map <space> viw

Ctrl + d删除当前行:

:map <c-d> dd

映射无法使用注释

映射命令无视注释,把整个内容作为映射的结果:

:map <space> viw " Select word

指定模式下映射

nmap指定normal模式下映射删除一行:

:nmap \ dd

vmap指定visual模式下映射,把选中的内容转为大写:

:vmap \ U

imap指定insert模式下映射。转为normal模式,删除一行,再回到insert模式:

:imap <c-d> <esc>ddi

删除映射

:nunmap -
:nunmap \

:vunmap -
:vunmap \

:iunmap -
:iunmap \

嵌套与递归

映射会嵌套,不小心还会造成递归,成为死循环:

:nmap dd O<esc>jddk

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

非递归映射

每一个*map系列的命令都有个对应的*noremap命令,包括: noremap/nnoremapvnoremapinoremap。这些命令将不递归解释映射的内容。

(n|v|i)?noremap (key) (value)
  • (n|v|i):指定环境。
  • key:按键。
  • value:映射的操作。

「任何时候」都应该使用非递归映射,当安装一个新的插件时, 可能你不会使用或记住每一个其创建的映射。即使你记住了, 你还得 回看下你的~/.vimrc文件以确保你自定义的映射与插件创建的没有冲突。

映射为空

把按键映射为<nop>无操作(no operation),即禁用该按键。 例如禁用Esc键:

:inoremap <esc> <nop>

前缀(Leaders)

通过映射按键有一个问题就是,用来映射的按键就用不到了,例如:

:nnoremap <space> dd

这样就不能再用空格了。

全局前缀

另一个定义快捷操作的方法是使用前缀(Leaders):

:let mapleader = "-"

这样就定义了-作为一个前缀,以后用这个前缀可以用来定义映射:

:nnoremap <leader>d dd

这样以后要用定义的前缀符号-加上d组成的-d就能触发dd

使用前缀的优点:

  • 首先,你某天可能会想要更换你的「leader」。在一个地方定义它使得更方便更换它。
  • 第二,其他人看你的~/.vimrc文件时,一旦看到<leader>就能够立即知道你的用意。 如果他们 喜欢你的~/.vimrc配置, 即使他们使用不同的leader也可以简单的复制你的映射配置。
  • 最后,许多Vim插件都会创建以<leader>开头的映射。如果你已经设置了leader, 你会更容易上手使用那些插件。

本地前缀

本地前缀(Local leader)于那些只对某类文件(如Python文件、HTML文件) 而设置的映射。

平时使用时反斜线用得少,所以一般用它来用为本地前缀:

:let maplocalleader = "\\"

因为\在Vimscript中是转义字符,所以要用\\

在映射中使用<localleader>代表本地映射的前缀。

缩写(Abbreviations)

自动纠错

定义在映射模式下的缩写替换:

:iabbrev adn and

以后在insert模式下输入:

One adn two.

会自动纠正为:

One and two.

缩写替换

还可以定义常用的短语,比如邮件地址与版权信息:

:iabbrev @@    steve@stevelosh.com
:iabbrev ccopy Copyright 2013 Steve Losh, all rights reserved.

关键字

在缩写以后输入一个「非关键字字符」(non-keyword character)后, 才会触发替换操作。

查看哪些关键字,要选中一个单词,然后用以下命令查看是不是关键字:

:set iskeyword?

输出的结果是关键字的合法格式,类似于:

iskeyword=@,48-57,_,128-167,224-235,-

这个格式很复杂,但本质上 "keyword characters"包含一下几种:

  • 下划线字符_
  • 所有字母字符,包括大小写。
  • ASCII值在48到57之间的字符(数字0-9)。
  • ASCII值在128到167之间的字符(一些特殊ASCII字符)。
  • ASCII值在224到235之间的字符(一些特殊ASCII字符)。

简单地说只要记住输入非字母、数字、下划线的字符就会引发abbreviations替换。 如果你想阅读这个选项格式的完整描述,你可以运行命令

:help isfname

缩写与映射的区别

abbreviations和mappings很像,但是他们的定位不同。例子:

运行命令:

:inoremap ssig -- <cr>Steve Losh<cr>steve@stevelosh.com

这个 mapping 用于快速插入你的签名。进入insert模式并输入ssig试试看。

看起来一切正常,但是还有个问题。进入insert模式并输入如下文字:

Larry Lessig wrote the book "Remix".

注意到Vim将Larry名字中的ssig也替换了!mappings不管被映射字符串的前后字符是什么-- 它只在文本中查找指定的字符串并替换他们。

运行下面的命令删除上面的mappings并用一个abbreviation替换它:

:iunmap ssig
:iabbrev ssig -- <cr>Steve Losh<cr>steve@stevelosh.com

再次试试这个abbreviation。

这次Vim会注意ssig的前后字符,只会在需要的时候替换它。

常用的例子

例子:修改vimrc的快捷键

绑定一个快速打开.vimrc的快捷键。垂直分屏窗口打开.vimrc

:nnoremap <leader>ev :vsplit $MYVIMRC<cr>

绑定一个重新加载.vimrc的快捷键:

:nnoremap <leader>sv :source $MYVIMRC<cr>

用符号包围单词

用双引号包围单词:

: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          <leader>d dd

只按键映射在当前缓冲区生效:

:nnoremap <buffer> <leader>d dd

Vim找不到一个跟它匹配的映射,它将会被解析了两个命令:

  • <leader>(这个什么都不会干)
  • x(通常会删除一个字符)。

localleader

如果我们需要设定一个只会用于特定缓冲区的映射,一般会使用<localleader>, 而不是<leader>

在编写一个会被其他人用到的插件的时候,这点显得尤其重要。 使用<localleader>来设置本地映射会防止你的插件覆盖别人用 <leader>设置的全局映射,因为他们可能已经对他们做设置的全局映射非常之习惯了。

setlocal的生效范围

set全局生效,setlocal对当前缓冲区有效:

:setlocal wrap

:setlocal nowrap

:setlocal number

:setlocal nonumber

不是所有的选项都可以使用setlocal进行设置。 如果你想知道某个特定的选项是否可以设置为本地选项, 执行:help查看它的帮助文档。

覆盖

缓冲区的映射有更高的优先级:

:nnoremap <buffer> Q x
:nnoremap          Q dd

生效的是第一行配置,优先级更高。

自动命令(autocmd)

例:Vim默认在第一次保存前不会在硬盘上创建文件。 现在设置自动在新建文件时自动保存到硬盘。

:autocmd BufNewFile * :write

自动命令的结构

让我们来深入分析下我们刚才创建的自动命令:

:autocmd BufNewFile * :write
         ^          ^ ^
         |          | |
         |          | 要执行的命令
         |          |
         |          用于事件过滤的「模式(pattern)」
         |
         要监听的「事件」

这个命令的第一部分是我们想监听的事件的类型。Vim提供了很多可以监听的事件。 这些事件包括:

  • 开始编辑一个当前并不存在的文件。
  • 读取一个文件,不管这个文件是否存在。
  • 改变一个缓冲区的filetype设置。
  • 在某段时间内不按下键盘上面的某个按键。
  • 进入插入模式。
  • 退出插入模式。
  • 等等…… 浏览:help autocmd-events查看自动命令可以绑定的所有事件。

这个自动命令的下一部分是一个「模式」, 这个模式可以进一步限定你要执行的命令的执行范围。

新开一个Vim实例,执行下面的命令:

:autocmd BufNewFile *.txt :write

这个跟之前的那个自动命令基本一样,不过这个自动命令只对后缀为.txt的文件有效, 也就是说当你新建的文件为txt文件的时候, Vim会在文件创建的时候自动执行write命令将文件保存到硬盘上。

例子:保存HTML文件前先格式化一下

:autocmd BufWritePre *.html :normal gg=G
  • BufWritePre这个事件会在保存任何字符到文件之前触发。
  • *.html模式只在扩展名为.html文件时触发。

多个事件触发

触发可以绑定多个事件,多个事件在分隔时用逗号分隔。

读与写HTML文件时都会触发:

:autocmd BufWritePre,BufRead *.html :normal gg=G

多个事件的配套

在Vim脚本编程中有一个不成文的规定,你应该同时使用BufReadBufNewFile (译注:这里不是BufWritePre)这两个事件来运行命令,这 样当你打开某个类型的文件,不论这个文件是否存在命令都会执行。执行下面的命令:

:autocmd BufNewFile,BufRead *.html setlocal nowrap

上面的命令会使得无论你在什么时候编辑HTML文件自动换行都会被关闭。

FileType事件

最有用的事件是FileType事件。这个事件会在Vim设置一个缓冲区的filetype的时候触发。

让我们针对不同文件类型设置一些有用的映射。

比如对于不同语言的源代码,定制不同的注释快捷键。运行命令:

:autocmd FileType javascript nnoremap <buffer> <localleader>c I//<esc>
:autocmd FileType python     nnoremap <buffer> <localleader>c I#<esc>
  • 后缀为.js文件,<localleader>c,光标所在的那一行会被注释掉。
  • 后缀为.py文件,<localleader>c,光标所在的那一行会被注释掉。