强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Vim / Neovim 完全指南 / 09 - VimScript 编程

“VimScript is quirky, but it’s the glue that holds Vim’s ecosystem together.”

9.1 VimScript 基础

9.1.1 变量

" 变量作用域前缀
let g:var = "global"       " 全局变量
let b:var = "buffer"       " 缓冲区局部变量
let w:var = "window"       " 窗口局部变量
let t:var = "tab"          " 标签页局部变量
let s:var = "script"       " 脚本局部变量
let l:var = "local"        " 函数局部变量
let a:arg = "argument"     " 函数参数
let v:var = "vim"          " Vim 内置变量

" 在函数内 let 默认是函数局部
let myvar = "hello"

" 常量
const MY_CONST = 42

9.1.2 数据类型

" 数字
let num = 42
let hex = 0xff
let oct = 0777

" 浮点数
let float = 3.14
let sci = 1.5e10

" 字符串
let str1 = "hello"
let str2 = 'world'       " 单引号不解释转义

" 列表(数组)
let list = [1, 2, 3, "four"]

" 字典(哈希表)
let dict = {"name": "vim", "version": 9}

" 布尔
let flag = v:true
let flag2 = v:false
let nothing = v:null

9.1.3 运算符

" 算术
let sum = 1 + 2
let div = 10 / 3      " 整数除法
let mod = 10 % 3      " 取模

" 字符串连接
let greeting = "Hello, " . name . "!"

" 列表操作
let list = [1, 2] + [3, 4]    " [1, 2, 3, 4]
let sub = list[1:2]           " [2, 3]

" 比较
if a == b
if a != b
if a > b
if a =~ pattern       " 正则匹配
if a !~ pattern       " 正则不匹配
if a is b             " 引用相等

9.1.4 控制结构

" 条件
if condition
    " ...
elseif other_condition
    " ...
else
    " ...
endif

" 循环
for i in range(10)
    echo i
endfor

for item in mylist
    echo item
endfor

for [key, value] in items(mydict)
    echo key . ": " . value
endfor

let i = 0
while i < 10
    let i += 1
endwhile

" 异常处理
try
    " ...
catch /pattern/
    " ...
finally
    " ...
endtry

9.2 函数

9.2.1 函数定义

" 基本函数
function! Greet(name)
    echo "Hello, " . a:name . "!"
endfunction

" 有返回值
function! Add(a, b)
    return a:a + a:b
endfunction

" 可变参数
function! Sum(...)
    let total = 0
    for i in a:000
        let total += i
    endfor
    return total
endfunction

" 调用
call Greet("Vim")
let result = Add(1, 2)
echo Sum(1, 2, 3, 4, 5)

9.2.2 函数属性

" autoload 延迟加载
function! mylib#format(text)
    return "[" . a:text . "]"
endfunction
" 文件路径:autoload/mylib.vim

" dict 函数
function! MyObj.method() dict
    return self.name
endfunction

" 闭包
function! Counter()
    let count = 0
    return {-> let count += 1}
endfunction

9.2.3 内置函数

" 字符串
strlen("hello")           " 5
substitute("aabb", "a", "x", "g")  " xxbb
matchstr("hello", "llo")  " llo
toupper("hello")          " HELLO
split("a,b,c", ",")       " ['a', 'b', 'c']
join(['a', 'b'], '-')     " a-b

" 列表
len([1, 2, 3])            " 3
add([1, 2], 3)            " [1, 2, 3]
sort([3, 1, 2])           " [1, 2, 3]
map([1, 2, 3], {i, v -> v * 2})  " [2, 4, 6]
filter([1, 2, 3], {i, v -> v > 1})  " [2, 3]

" 字典
keys({"a": 1, "b": 2})   " ['a', 'b']
values({"a": 1, "b": 2}) " [1, 2]
has_key(dict, "a")        " 1 (true)

" 系统
system("ls")              " 执行外部命令
executable("python3")     " 是否存在

9.3 自动命令(Autocmd)

9.3.1 基本语法

" 创建自动命令
autocmd Event pattern command

" 使用 augroup 分组(避免重复)
augroup MyGroup
    autocmd!    " 清除组内已有命令
    autocmd BufWritePre *.py %s/\s\+$//e
    autocmd FileType python setlocal tabstop=4
augroup END

9.3.2 常用事件

事件触发时机
BufNewFile创建新文件
BufReadPre读取文件前
BufRead读取文件后
BufWritePre保存文件前
BufWritePost保存文件后
BufEnter进入缓冲区
BufLeave离开缓冲区
BufDelete删除缓冲区
FileType设置文件类型时
InsertEnter进入插入模式
InsertLeave离开插入模式
TextYankPost复制文本后
VimEnterVim 启动后
VimLeavePreVim 退出前
WinEnter进入窗口
WinLeave离开窗口
ColorScheme切换颜色主题后
LspAttachLSP 连接到缓冲区
CursorHold光标静止时
CursorMoved光标移动后

9.3.3 实用自动命令

" 高亮复制的文本(0.5 秒)
autocmd TextYankPost * silent! lua vim.highlight.on_yank()

" 保存时自动去除尾部空格
autocmd BufWritePre * :%s/\s\+$//e

" 打开文件时恢复光标位置
autocmd BufReadPost * if line("'\"") > 1 && line("'\"") <= line("$") | exe "normal! g'\"" | endif

" 特定文件类型设置
autocmd FileType python setlocal tabstop=4 shiftwidth=4
autocmd FileType javascript setlocal tabstop=2 shiftwidth=2
autocmd FileType go setlocal tabstop=4 shiftwidth=4 noexpandtab

" 自动创建目录
autocmd BufWritePre * if !isdirectory(expand("<afile>:p:h")) | call mkdir(expand("<afile>:p:h"), "p") | endif

9.4 映射(Mapping)

9.4.1 映射类型

命令模式
nmap / nnoremapNormal
imap / inoremapInsert
vmap / vnoremapVisual
xmap / xnoremapVisual(不含 Select)
smap / snoremapSelect
omap / onoremapOperator-pending
cmap / cnoremapCommand-line
map / noremapNormal + Visual + Operator-pending

注意:始终使用 noremap(非递归映射)除非你需要递归。

9.4.2 映射示例

" 基本映射
nnoremap <leader>w :w<CR>
nnoremap <leader>q :q<CR>

" 带参数的映射
nnoremap <leader>e :e <C-r>=expand("%:p:h")<CR>/

" 表达式映射
nnoremap <expr> j v:count ? 'j' : 'gj'
nnoremap <expr> k v:count ? 'k' : 'gk'

" 映射到 Lua 函数(Neovim)
nnoremap <leader>f <cmd>lua vim.lsp.buf.format()<CR>

" 清除映射
unmap <leader>f
mapclear

9.5 插件开发基础

9.5.1 插件目录结构

my-plugin/
├── plugin/
│   └── my-plugin.vim      " 自动加载
├── autoload/
│   └── my_plugin.vim      " 延迟加载
├── ftplugin/
│   └── python.vim         " 文件类型插件
├── doc/
│   └── my-plugin.txt      " 帮助文档
└── README.md

9.5.2 基本插件模板

" plugin/my-plugin.vim
if exists('g:loaded_my_plugin')
    finish
endif
let g:loaded_my_plugin = 1

" 默认配置
let g:my_plugin_enabled = get(g:, 'my_plugin_enabled', 1)

" 命令
command! MyPluginEnable  call my_plugin#enable()
command! MyPluginDisable call my_plugin#disable()

" autoload/my_plugin.vim
function! my_plugin#enable()
    let g:my_plugin_enabled = 1
    echo "My Plugin enabled"
endfunction

function! my_plugin#disable()
    let g:my_plugin_enabled = 0
    echo "My Plugin disabled"
endfunction

9.6 业务场景

场景技术
自动格式化autocmd BufWritePre
文件类型配置autocmd FileType
自定义命令command!
快捷键nnoremap / vnoremap
函数封装function! … endfunction
小型工具脚本plugin/ 目录

9.7 总结

概念核心语法
变量let g:var = value
函数function! Name(args) ... endfunction
自动命令autocmd Event pattern cmd
映射nnoremap lhs rhs
命令command! Name cmd

下一步第 10 章 - Lua 配置与 API → 学习 Neovim 的现代配置语言 Lua。


扩展阅读

  • :h eval — VimScript 表达式
  • :h function — 函数定义
  • :h autocmd-events — 事件列表
  • :h map-modes — 映射模式
  • Learn Vimscript the Hard Way