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

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 复制文本后
VimEnter Vim 启动后
VimLeavePre Vim 退出前
WinEnter 进入窗口
WinLeave 离开窗口
ColorScheme 切换颜色主题后
LspAttach LSP 连接到缓冲区
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 / nnoremap Normal
imap / inoremap Insert
vmap / vnoremap Visual
xmap / xnoremap Visual(不含 Select)
smap / snoremap Select
omap / onoremap Operator-pending
cmap / cnoremap Command-line
map / noremap Normal + 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