一、按键映射
Vim 中的快捷键绑定可以通过以下命令配置:
:imap:只在 Insert 模式下生效的快捷键:cmap:只在 Command-line 模式下生效:nmap:只在 Normal 模式下生效:vmap:只在 Visual 模式下生效:map:在以上所有模式下生效:noremap:包含:inoremap、:nnoremap等,非递归映射
PS:关于递归映射,如 :map a b,:map b c。根据按键映射之间的传递,则有 a -> c 的关系。而 nore 则用于禁止这种递归行为。
做快捷键映射时,各功能按键在 Vim 中的名称如下:
| 名称 | 对应按键 |
|---|---|
<BS> |
退格键 |
<Tab> |
制表键 |
<CR>或<Enter>或<Return> |
回车 |
<Esc> |
Escape |
<Space> |
空格键 |
<Up> |
上方向键 |
<Down> |
下方向键 |
<Left> |
左方向键 |
<Right> |
右方向键 |
<F1> - <F12> |
功能键 F1 到 F12 |
#1, #2..#9,#0 |
F1 到 F10 |
<Insert> |
Insert |
<Del> |
Delete |
<Home> |
Home |
<End> |
End |
<PageUp> |
上翻页 |
<PageDown> |
下翻页 |
示例
1 | " save file (ctrl-s) |
:map <C-s> :w<cr> 表示将 Ctrl+S 组合键映射为 :w<cr>(保存修改,写入文件)
其中 <C-s> 在 Vim 中即表示 Ctrl+S,类似的用法还有 <A-s> (Alt+S)、<M-s> (Meta+S)、<C-S-s>(Ctrl+Shift+S)等。:w 后面的 <cr> 表示按下回车键。如命令最后没有加上 <cr>,则按下组合键 <C-s> 后只会将对应的 :w 输入到命令栏而不执行。
上面映射的保存文件功能还可以更加细化一些::imap <C-s> <esc>:w<cr>a
即该组合键适用于插入模式,按下 Ctrl+S 意味着会依照如下顺序执行命令:
<esc>:退出插入模式:w<cr>:将之前的修改写入文件a:回到插入模式继续编辑文件
对于 Gvim 下的按键映射,还可以调出对话框完成 Open 和 save-as 功能:1
2
3
4"Open new file dialog (ctrl-n)
:map <C-n> :browse confirm e<cr>
"Open save-as dialog (ctrl-shift-s)
:map <C-S-s> :browse confirm saveas<cr>
对于依次按下多个按键的组合键,如::map $1 :MyFunction1()<cr>
当按下 $ 键后,Vim 会等待一秒钟,若一秒钟之内又按下了 1 键,则执行映射的 MyFunction1() 函数;若一秒钟之内没有任何按键按下,则执行 $ 键原本的功能(移动光标到行尾)。
二、Vim 脚本
Vim 脚本是指用 VimScript 编写的为 Vim 添加自定义功能的单独的文件。如下面的 hello.vim 脚本:1
2
3
4
5
6
7
8" hello.vim
function! SayHello()
echo 'Hello, World!'
endfunction
command! Hello call SayHello()
nnoremap Q :Hello<CR>
其中 function! 用于定义 SayHello() 函数,command! 用于将调用该函数的行为绑定给 Hello 命令,nnoremap Q 则用于将 :Hello 命令的执行绑定给键盘上的 Q 按键。
在 Vim 中使用 :source 命令导入刚刚创建的脚本::source hello.vim
之后执行 :Hello 命令或者按下 Q 按键即可在命令栏输出 Hello, World! 字符串。
变量
变量的赋值使用 :let 命令:1
2
3
4:let mystring = "a string"
:let mynumber = 123
:let mylist = [1, 2, "three", 0x04 ,["five", "six"]]
:let mydict = {1: "one", 2: "two", "others": {3: "three", 4: "four"}}
Vim 脚本中支持以下类型的变量:
String:字符串,如"this is a string"Number:数字,包含十进制(123)、十六进制(0x8A)和八进制(012)List:有序列表,列表项支持多种数据类型混合Dictionary:字典,无序键值对Funcref:函数引用
PS:不同进制的数字之间可以直接进行算术运算
由单引号(')包裹的字符串不会转义 \n 等转义字符
关于字符串和数字之间的自动类型转换,参考以下规则:
| Input (type) | Result (type) |
|---|---|
| “hello” . “world” | “hello world” (string) |
| “number” . 123 | “number 123” (string) |
| “123” + 10 | 133 (number) |
| “123” - 10 . “hits” | “113 hits” (string) |
| “123” - 10 + “hits” | 113 (number) |
变量作用域
Vim 中变量的作用域通过不同的前缀来指定:
v:Vim 预定义的全局作用域g:全局变量前缀b:Buffer 范围内生效的变量t:Tab 范围内生效的变量w:Window 范围内生效的变量l:即 local,Function 范围内生效s:即 source,通过:source载入的当前脚本内生效a:即 argument,用来修饰函数的参数
当变量没有任何作用域前缀修饰时,默认为全局变量(除非该变量在函数内部定义)。
变量作用域前缀的使用参考如下脚本:1
2
3
4
5
6
7
8
9
10
11let g:sum = 10
function SumNumbers(num1, num2)
let l:sum = a:num1 + a:num2
if g:sum < l:sum
let g:sum = l:sum
endif
return l:sum
endfunction
echo SumNumbers(3, 4)
echo g:sum
条件语句
Vim 脚本中的 if-else 语句语法格式如下:1
2
3
4
5if condition1
code-to-execute-if-condition1-is-true
elseif condition2
code-to-execute-if-condition2-is-true
endif
其中判断条件 condition 可以有以下几种形式:
val1 == val2val1 != val2val1 > val1val1 < val2val1 <= val2val1 >= val2str1 =~ str2str1 !~ str2
字符串比较中的 str2 可以是某个模式,支持正则表达式。
if-else 语句的使用可以参考如下脚本,根据当前时间切换不同的配色:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17" note addition of zero
" this guarantees return from function is numeric
let currentHour = strftime ("%H")
echo "currentHour is " currentHour
if currentHour < 6 + 0
colorscheme darkblue
echo "setting colorscheme to darkblue"
elseif currentHour < 12 + 0
colorscheme morning
echo "setting colorscheme to morning"
elseif currentHour < 18 + 0
colorscheme shine
echo "setting colorscheme to shine"
else
colorscheme evening
echo "setting colorscheme to evening"
endif
循环
For 循环的语法格式如下:1
2
3for var in range
do-something
endfor
或1
2
3for var in list
do-somthing
endfor
代码示例1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16" range
for myvar in range(1,10)
echo myvar
endfor
" list
let mylist = ['a','b','c','d','e','f','g','h','i','j','k']
for itemvar in mylist
echo itemvar
endfor
" dictionary
let mydict = {"a": "apple", "b":"banana", "c": "citrus" }
for keyvar in keys(mydict)
echo mydict[keyvar]
endfor
While 循环的语法格式:1
2
3while condition
execute-this-code
endwhile
代码示例:1
2
3
4
5let x=0
while x <= 5
echo "x is now " x
let x+=1
endwhile
列表与字典
关于 Vim 中列表与字典的操作,可以参考如下代码: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
30let mylist = [1, 2, "three"]
echo mylist[2]
" => three
let mylist2 = [[1, 2, 3], ["four", "five", "six"]]
echo mylist2[1][0]
" => four
echo mylist2[0][-1]
" => 3
let mylist3 = [1, 2, 3, 4]
call add(mylist3, [5, 6])
echo mylist3
" => [1, 2, 3, 4, [5, 6]]
let mylist4 = [1, 2, 3, 4]
let mylist4 = mylist4 + [5, 6]
echo mylist4
" => [1, 2, 3, 4, 5, 6]
let mylist5 = [1, 2, 3, 4]
call remove(mylist5, 3)
echo mylist5
" => [1, 2, 3]
let mydict = {'banana': 'yellow', 'apple': 'green'}
echo mydict['banana']
" => yellow
echo mydict.apple
" => green
get map split join
可以对列表或字典中的值应用某个函数(如 join 和 map 等)以完成特定的需求,常见示例如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19let a = split("one two")
echo a
" => one
let mylist = ["one", "two", "three"]
call map(mylist, '"<" . v:val . ">"')
echo mylist
" => ['<one>', '<two>', '<three>']
let mylist2 = ["one", "two", "three"]
echo get(mylist2, 2, "none")
" => three
echo get(mylist2, 3, "none")
" => none
let mylist3 = ["one", "two", "three"]
let mystring = join(mylist3, "+")
echo mystring
" => one+two+three
更复杂的示例(好神奇,没看懂。。。):1
2
3
4
5
6
7
8let mynumbers = {0:'zero', 1:'one', 2:'two', 3:'three', 4:'four', 5:'five', 6:'six', 7:'seven', 8:'eight', 9:'nine'}
function mynumbers.convert(numb) dict
return join(map(split(a:numb, '\zs'), 'get(self, v:val, "unknown")'))
endfunction
echo mynumbers.convert(12345)
" => one two three four five
函数
Vim 中定义函数的语法如下:1
2
3function Name(arg1, arg2,...argN) keyword
code-to-execute-when-function-is-called
endfunction
所有在函数体中定义的变量只在该函数内部可见,即作用域为 local。如果需要使用函数外部的变量,可以将其作为参数传递给函数,或者直接调用(需要在该变量名前加上全局作用域前缀 g:)。
函数定义代码中使用由参数传递的变量时,需要加上 a: 作用域前缀。
参数列表
参考如下代码:1
2
3
4
5
6
7
8
9
10
11
12
13function PrintSum(num1, num2, ...)
let sum = a:num1 + a:num2
let argnum = 1
while argnum <= a:0
let sum += a:{argnum}
let argnum += 1
endwhile
echo "The sum is" sum
return sum
endfunction
let sum = PrintSum(1, 2, 3, 4)
" => The sum is 10
注意代码中 a:0(不定参数的长度)和 a:{argnum}(第 argnum 个额外参数)的用法。
此外还可以通过 a:000 以列表的方式获取所有额外的参数:1
2
3
4
5
6
7
8
9
10
11function PrintSum(num1, num2, ...)
let sum = a:num1 + a:num2
for arg in a:000
let sum += arg
endfor
echo "The sum is" sum
return sum
endfunction
let sum = PrintSum(1, 2, 3, 4)
" => The sum is 10
综合示例
根据当前时间自动切换 Vim 配色的脚本:1
2
3
4
5
6
7
8
9
10let g:Favcolorschemes = ["darkblue", "morning", "shine", "evening"]
function SetTimeOfDayColors()
" currentHour will be 0, 1, 2 or 3
let CurrentHour = (strftime("%H") + 0) / 6
execute "colorscheme " . g:Favcolorschemes[CurrentHour]
echo "set color scheme to " . g:Favcolorschemes[CurrentHour]
endfunction
call SetTimeOfDayColors()
三、Autocommands
Autocommands 即在特定条件下自动执行的命令,这些命令包括所有合法的 Vim 命令。
Vim 定义了一些事件(event)作为触发命令自动执行的开关,常见的 event 如下:
BufNewFile:在开始编辑一个新的文件时触发BufReadPre:在 Vim 移动到一个新的 buffer 前触发BufRead,BufReadPost:开始编辑新的 buffer 时,读取文件之后触发BufWrite,BufWritePre:在将 buffer 内容写入文件之前触发FileType:在确定了文件类型(filetype)之后触发VimResized:更改 Vim 的窗口大小后触发WinEnter,WinLeave:进入或离开某个 Vim 窗口时触发CursorMoved,CursorMovedI:Normal 或 Insert 模式下,光标移动后触发
Autocommands 代码示例
1 | augroup demo |
上述脚本会添加如下功能:
打开文件时 Vim 命令栏输出 “Reading: :w 等命令保存文件时,命令栏输出 “Writing:
又如根据源文件类型设置不同的缩进风格:1
2
3filetype on
autocmd FileType ruby setlocal tabstop=2 softtabstop=2 shiftwidth=2 expandtab
autocmd FileType javascript setlocal ts=4 sts=4 sw=4 noet