Vim 是一个功能强大的文本编辑器,适用于编程和文档编辑。Vimscript 是 Vim 的脚本语言,可以帮助用户实现更高效的工作流。本文将详细介绍 Vimscript 的基本用法,包括常用命令、数据类型、函数、按键映射等,帮助您更好地掌握 Vimscript。

一、基本命令

在 Vim 中输入 :help <CMD> 可以查看该命令详细的帮助信息,比如 :help echo:help python

打印信息

打印信息可以使用 :echo "hi, axiaoxin.",也可以使用 :echom "hi, Mr.axiaoxin",运行时都会输出后面的字符串。他们的区别在于 :echom 会在运行后将其打印的信息保存在 messages 的列表中,而 :echo 则不会。运行 :messages 可以查看 Vim 所有历史输出的信息,用 echom 可以更方便地进行调试信息的输出。

二、Vimscript 基本数据类型及强制类型转换

1. 数字

数字包括整型、浮点型,基本的数字运算和 Python 都差不多:

:echom 100
:echom 0xff "16进制
:echom 010  "8进制
:echom 019  "不合法8进制,自动被视为十进制

2. 字符串

字符串不能使用 + 连接,+ 只用于数字。当用 + 连接字符串时,会强制将字符串转为开头的数字再相加。正确的连接符号为 .

:echom "Hello, " + "world"  " return 0
:echom "3 mice" + "2 cats"  " return 5
:echom "3 mice" + "cats 2"  " return 3
:echom 10 + "10.1"          " return 10
:echom 10 + 10.1            " return 20.1

:echom "Hello, " . "world"  " return hello, world

当整数和字符串用 . 连接时,会将整数转换成字符串,但不能将浮点数和字符串连接,会报错。

当字符串用单引号包住时,里面的特殊字符只会显示为原来的样子:

:echo "foo\nbar"    "显示为两行
:echom "foo\nbar"   "显示为foo^@bar,^@表示换行字符
:echom '\n\\'       "显示为\n\\,失去本身的意义
:echom 'That''s enough.'    "显示为That's enough. 单引号需要用'来转义,用\是错误语法

字符串也可以像 Python 一样切片::echo "abcd"[0:2]

用下标取字符串中的字符不能使用负数下标,:echo "abcd"[-1] 返回为空,但可以使用负数进行切片::echo "abcd"[-2:]

3. 列表

列表下标从 0 开始,比如 :echo [0, [1, 2]][1] 输出 [1, 2]:echo [0, [1, 2]][-2] 输出倒数第二个元素 0,同样支持 Python 风格的切片操作。列表可以相加::echo ['a', 'b'] + ['c'] 返回 ['a', 'b', 'c']

列表函数

add(list, item)
len(list)
get(list, index, default)   " 获取给定index的item,index超过range返回default
index(list, item)            " 返回-1表示不存在该item
join(list[, joiner])
reverse(list)
sort(list)

function! Sorted(l)
    let new_list = deepcopy(a:l)
    call sort(new_list)
    return new_list
endfunction

4. 字典

取值:

:echo {'a': 1, 100: 'foo',}['a']
:echo {'a': 1, 100: 'foo',}[100]
:echo {'a': 1, 100: 'foo',}.a
:echo {'a': 1, 100: 'foo',}.100

删除有两种方式,一种是 :let a = remove(dict, key),另一种是 unlet dict.key,区别在于第一种方式有返回值(即被删除 key 的 value),第二种没有,如果删除一个不存在的 key 会报错:

:let foo = {'a': 1, 'b': 2}
:let test = remove(foo, 'a')
:unlet foo.b
:echo foo
:echo test

赋值:

:let foo.x = 11

字典方法:

get(dict, key, default)
has_key(dict, key)  " 返回0/1
items(dict)          " 返回list

:echo items({'a': 100, 'b': 200}) 返回 [['a', 100], ['b', 200]]

三、Vimscript 变量

变量赋值

:let foo = "bar"
:echo foo

使用 let 关键字,等号左右可以有空格也可以没有,直接使用变量名。

使用变量 scope 指定为当前 buff 的 local 变量:

:let b:foo = "bar"
:echo b:foo

g: 代表 global。

选项变量

:set textwidth=80
:echo &textwidth

使用 set 关键字,当变量为设置选项时,等号左右不能有空格,而且 echo 时必须加 &。也可以使用 let 赋值,但是前面要加 &:let &textwidth = 100

Local 选项变量

在同一窗口 split 多个窗口,在不同的窗口设置自己的 local 变量:

:let &l:number = 1

注册变量:

:let @h = 'hi axiaoxin'  " 现在按 "hp  h 中保存的值粘贴到当前位置

选中文字按 y 复制后,执行 :echo @" 便可以打印出按 y 复制的值。

:echo @/ 可以查看查找过的文字,如果没有查找过则返回 noh,noh 就是在你查找高亮后取消高亮的命令 :noh

四、Vimscript 控制结构

if 语句

1 代表 True,0 代表 False,非空字符串如果不是数字开头被强制转换为 0(False),数字字符串或数字开头的字符串为 True:

:if 1
:    echom "ONE"    "display
:endif

:if 10 > 1
:   echom "foo"     "display
:elseif 100 == 10
:   echom "bar"
:else
:   echom "ooxx"
:endif

:if "9024"
:    echom "WHAT?!" "display
:endif

:if "something"
:    echom "INDEED" "no display
:endif

当比较字符串的时候不要使用 == 来判断相等,因为有 :set ignorecase:set noignorecase 来修改是否区分大小写。如果 :if "foo" == "FOO" 前被添加了 :set ignorecase,该判断依旧为 True。

正确的方式应该总是使用 ==?==# 来判断,无论是否使用 set ignorecase or noignorecase,? 总是不区分大小写,而 # 总是区分大小写,< > 同样适用:

:if "foo" ==# "FOO"
:   echom "no, it couldn't be"
:elseif "foo" ==# "foo"
:   echom "this must be the one"
:endif

五、Vimscript 布尔设置开关

所有布尔设置都是通过 :set <name> 来开启,通过 :set no<name> 来关闭。

也可以用 toggle 来设置自动开或关,:set <name>!

检查某个布尔设置的开关情况可以使用 :set <name>?,比如查看是否显示行号用 :set number?,如果是显示行号则显示 number,不显示行号显示为 nonumber

特别笔记下 :set shiftround 命令,其作用是在你按 >< 或者是在插入模式下按 C-TC-D 时的缩进取整对齐。比如我设置了 :set shiftwidth=4,然后有如下缩进不规则的文本:

  asdasd
   asd

:set noshiftround,然后光标移动到行首,按 >> 将其缩进一次,输出为:

      asdasd
      asd

然后再按一次 >>,输出为:

        asdasd
        asd

最后再按一次 << 将其反向缩进,输出为:

      asdasd
      asd

可以看出,没有使用 shiftround,缩进时文本会不规则,而使用 shiftround 后的缩进更加整齐。

六、Vimscript 函数定义与调用

1. 定义函数

使用 function 定义函数,endfunction 结束函数,函数参数用 a: 作为前缀:

function! MyFunction(arg1, arg2)
    return a:arg1 + a:arg2
endfunction

调用函数时使用 call

:let result = call('MyFunction', [5, 10])
:echo result  " 15

2. 匿名函数

可以使用 function 直接定义匿名函数,但无法通过 call 直接调用,只能使用 :execute

:execute 'call function("add", [5, 10])'

3. lambda 表达式

Vim 8.0 之后支持 lambda 表达式,使用 {} 定义:

:let add = {a, b -> a + b}
:echo add(5, 10)  " 15

七、Vimscript 按键映射

在 Vim 中,可以使用 nnoremapinoremapvnoremap 等命令进行按键映射。

1. 普通模式映射

使用 nnoremap 进行普通模式的映射:

:nnoremap <F5> :echo "Hello, World!"<CR>

2. 插入模式映射

使用 inoremap 进行插入模式的映射:

:inoremap jk <Esc>

3. 可视模式映射

使用 vnoremap 进行可视模式的映射:

:vnoremap <C-c> "+y

八、总结与建议

Vimscript 是 Vim 的强大脚本语言,掌握其基本用法可以极大提升您的 Vim 使用效率。可以从常用的命令、数据类型、函数、按键映射等方面入手,逐步熟悉并灵活应用这些知识。

建议多实践,尝试编写一些简单的 Vim 插件或配置,逐渐深入了解 Vimscript 的魅力。同时,定期查阅 Vim 的帮助文档,学习更多的内置函数和命令,保持对 Vim 的探索与热爱。


也可以看看