一、按键映射
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 中的名称如下:
名称 | 对应按键 |
---|---|
退格键 | |
制表键 | |
回车 | |
Escape | |
空格键 | |
上方向键 | |
下方向键 | |
左方向键 | |
右方向键 | |
功能键 F1 到 F12 | |
#1, #2..#9,#0 | F1 到 F10 |
Insert | |
Delete | |
Home | |
End | |
上翻页 | |
下翻页 |
示例
" save file (ctrl-s)
:map :w
" copy selected text (ctrl-c)
:vmap y
" Paste clipboard contents (ctrl-v)
:imap P
" cut selected text (ctrl-x)
:vmap x
:map
表示将 Ctrl+S
组合键映射为 :w
(保存修改,写入文件)
其中
在 Vim 中即表示 Ctrl+S
,类似的用法还有
(Alt+S
)、
(Meta+S
)、
(Ctrl+Shift+S
)等。
:w
后面的
表示按下回车键。如命令最后没有加上
,则按下组合键
后只会将对应的 :w
输入到命令栏而不执行。
上面映射的保存文件功能还可以更加细化一些::imap
即该组合键适用于插入模式,按下 Ctrl+S
意味着会依照如下顺序执行命令:
-
:退出插入模式 -
:w
:将之前的修改写入文件 -
a
:回到插入模式继续编辑文件
对于 Gvim 下的按键映射,还可以调出对话框完成 Open
和 save-as
功能:
"Open new file dialog (ctrl-n)
:map :browse confirm e
"Open save-as dialog (ctrl-shift-s)
:map :browse confirm saveas
对于依次按下多个按键的组合键,如::map $1 :MyFunction1()
当按下 $
键后,Vim 会等待一秒钟,若一秒钟之内又按下了 1
键,则执行映射的 MyFunction1()
函数;若一秒钟之内没有任何按键按下,则执行 $
键原本的功能(移动光标到行尾)。
二、Vim 脚本
Vim 脚本是指用 VimScript
编写的为 Vim 添加自定义功能的单独的文件。如下面的 hello.vim
脚本:
" hello.vim
function! SayHello()
echo 'Hello, World!'
endfunction
command! Hello call SayHello()
nnoremap Q :Hello
其中 function!
用于定义 SayHello()
函数,command!
用于将调用该函数的行为绑定给 Hello
命令,nnoremap Q
则用于将 :Hello
命令的执行绑定给键盘上的 Q
按键。
在 Vim 中使用 :source
命令导入刚刚创建的脚本::source hello.vim
之后执行 :Hello
命令或者按下 Q
按键即可在命令栏输出 Hello, World!
字符串。
变量
变量的赋值使用 :let
命令:
: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,用来修饰函数的参数
当变量没有任何作用域前缀修饰时,默认为全局变量(除非该变量在函数内部定义)。
变量作用域前缀的使用参考如下脚本:
let 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 语句语法格式如下:
if condition1
code-to-execute-if-condition1-is-true
elseif condition2
code-to-execute-if-condition2-is-true
endif
其中判断条件 condition
可以有以下几种形式:
val1 == val2
val1 != val2
val1 > val1
val1 < val2
val1 <= val2
val1 >= val2
str1 =~ str2
str1 !~ str2
字符串比较中的 str2
可以是某个模式,支持正则表达式。
if-else 语句的使用可以参考如下脚本,根据当前时间切换不同的配色:
" 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 循环的语法格式如下:
for var in range
do-something
endfor
或
for var in list
do-somthing
endfor
代码示例
" 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 循环的语法格式:
while condition
execute-this-code
endwhile
代码示例:
let x=0
while x <= 5
echo "x is now " x
let x+=1
endwhile
列表与字典
关于 Vim 中列表与字典的操作,可以参考如下代码:
let 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
等)以完成特定的需求,常见示例如下:
let a = split("one two")
echo a
" => one
let mylist = ["one", "two", "three"]
call map(mylist, '"<" . v:val . ">"')
echo mylist
" => ['', '', '']
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
更复杂的示例(好神奇,没看懂。。。):
let 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 中定义函数的语法如下:
function Name(arg1, arg2,...argN) keyword
code-to-execute-when-function-is-called
endfunction
所有在函数体中定义的变量只在该函数内部可见,即作用域为 local。如果需要使用函数外部的变量,可以将其作为参数传递给函数,或者直接调用(需要在该变量名前加上全局作用域前缀 g:
)。
函数定义代码中使用由参数传递的变量时,需要加上 a:
作用域前缀。
参数列表
参考如下代码:
function 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
以列表的方式获取所有额外的参数:
function 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 配色的脚本:
let 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 代码示例
augroup demo
autocmd!
autocmd BufReadPost * echo 'Reading: ' . expand('')
autocmd BufWritePost * echo 'Writing: ' . expand('')
augroup END
上述脚本会添加如下功能:
打开文件时 Vim 命令栏输出 "Reading: :w
等命令保存文件时,命令栏输出 "Writing:
又如根据源文件类型设置不同的缩进风格:
filetype on
autocmd FileType ruby setlocal tabstop=2 softtabstop=2 shiftwidth=2 expandtab
autocmd FileType javascript setlocal ts=4 sts=4 sw=4 noet
参考资料
Hacking Vim 7.2
Modern Vim