开发一个 Vim Todo 列表插件

2019-01-15 ⏳1.3分钟(0.5千字) 🕸️

今天收到了 lemon0910 同学的评论,甚为喜悦。lemon0910 建议写几篇分析插件实现思路的文章,这跟我个人的想法不谋而合。正所谓授人以鱼不如授人以渔,何乐而不为呢?本篇就通过开发一个 todo 插件来分析任务类插件的实现思路。如果你对开发 vim 插件还不太了解,请先阅读这篇这篇

功能概览

任务类插件是我自己的提法,其基本特征是提取并展示任务列表,通过列表可以跳转到对应文件的对应位置。这里的任务一般代表某个文件的某行内容。典型的任务有:

一个任务一定包含文件路径和行号两个属性,有的任务也会包含列信息。

预备知识

vim 有个 QuickFix 特性,专门用来支持处理各种任务。我们可以通过执行:copen打开 QuickFix 窗口,这个窗口是全局惟一的。因为我们还没有写入任务,所以新打开的 QuickFix 窗口是空的。

那如何写入 QuickFix 任务呢?vim 为我们提供了setqflist()函数。这个setqflist()接受三个参数:

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 可以实现多数任务类插件。