开发一个 Vim Todo 列表插件
涛叔今天收到了 lemon0910 同学的评论,甚为喜悦。lemon0910 建议写几篇分析插件实现思路的文章,这跟我个人的想法不谋而合。正所谓授人以鱼不如授人以渔,何乐而不为呢?本篇就通过开发一个 todo 插件来分析任务类插件的实现思路。如果你对开发 vim 插件还不太了解,请先阅读这篇和这篇。
功能概览
任务类插件是我自己的提法,其基本特征是提取并展示任务列表,通过列表可以跳转到对应文件的对应位置。这里的任务一般代表某个文件的某行内容。典型的任务有:
- 代码中的
TODO
,FIXME
等标记 - 代码中的编译错误或者 linter 信息
- 符合某正则表达式的所有文本内容
一个任务一定包含文件路径和行号两个属性,有的任务也会包含列信息。
预备知识
vim 有个 QuickFix 特性,专门用来支持处理各种任务。我们可以通过执行:copen
打开 QuickFix 窗口,这个窗口是全局惟一的。因为我们还没有写入任务,所以新打开的 QuickFix 窗口是空的。
那如何写入 QuickFix 任务呢?vim 为我们提供了setqflist()
函数。这个setqflist()
接受三个参数:
- list 理论上这是个任务列表,但我实验下来发现此参数没有任何作用
- action 表示操作类型,简单来说就是 a 表示追加,r 表示替换,f 表示清空
- what 这是主力参数,字典类型,我们只用到了以下字段:
- title QuickFix 窗口的标题
- items 已解析的任务列表,理论上应该使用第一个参数传递,现在只能使用此字段
- lines 未解析的任务列表,一般是编译器的错误信息,可以从中提取任务信息
- efm 配合 lines 使用,类似 c 语言 scanf 的格式定义,%f 匹配文件名,%l 匹配行号,%m匹配显示信息,其他的请参考
:h errorformat
QuickFix 的基础知识讲完了,完整信息请参考:h quickfix
。
Todo 插件
我们利用 QuickFix 特性开发一个简单的 Todo 插件,用来快速处理项目中的 TODO 标记。我日常使用的同类插件是 ack.vim,简单好用。
另外,我在代码里用到了 ag 。ag 全称 The Silver Searcher,比 grep 快很多,推荐大家使用。
下面奉上完整代码,保存到~/.vim/plugin/todo.vim
即可使用。代码很简单,大家看注释就会明白。
" 使用外部命令搜索整个项目的 TODO 标记
function s:FindTodo(args)
let cmd = 'ag TODO '.a:args " 注意参数引用方式
" systemlist() 执行系统命令并把 stdout 内容按行切割,返回一个 list
let lines = systemlist(cmd)
" 此处指定 efm 让 vim 解析搜索结果
call setqflist([], 'r', {'title':cmd, 'lines':lines, 'efm':'%f:%l:%m'})
" 打开 QuickFix 窗口
copen
endfunction
" 搜索当前文件内容,演示追加功能用法
function s:FindTodoBuf()
" 使用 f 清空已有列表
call setqflist([], 'f')
" 读取当前 buf 编号
let bufnr = bufnr('.')
let found = 0
let i = 1
" line('$') 表示获取最后一行的行号
" 此处遍历每一行内容
while i <= line('$')
" 读取指定行内容,行号从 1 开始
let line = getline(i)
" =~ 表示正则匹配,# 表示区分大小写
" 具体请参考 :h expr-=~#
if line =~# 'TODO'
let found = 1
let todo = {'bufnr':bufnr, 'text':line, 'lnum':i}
" 使用 a 逐个添加
" 此模式对异步 RPC 调用非常友好,所以提供本示例
call setqflist([], 'a', {'title':'TODO', 'items': [todo]})
endif
let i += 1
endwhile
if found
copen
endif
endfunction
" 简单的命令映射
command! TodoBuf call s:FindTodoBuf()
" 带参数的命令映射
" -nargs=* 表示零个或多个参数,详情参考 :h command-nargs
" -complete=file 表示开启按文件路径补全,详情参考 :h command-complete
" <q-args> 用来引用命令参数,详情参考 :h q-args
command! -nargs=* -complete=file Todo call s:FindTodo(<q-args>)
结语
本文介绍了 vim 的 QuickFix 特性并基于此实现了一个简单的 Todo 插件。通过 QuickFix 可以实现多数任务类插件。