vscode函数跳转插件_30 行代码实现 viml 函数跳转插件

vim 的插件大都使用 viml 开发。vim 插件一般会有一个 autoload 目录,公有的函数需要在这里定义。viml 强制要求函数名要跟所在源文件的路径对应起来。

例如,我们创建了一个 autoload/foo/bar.vim 的文件,那么 bar.vim 中定义的公有函数的名字必需以 foo#bar# 开头。反过来,如果我们想查看函数 foo#bar#baz() 的代码,我们就得找到某个 autoload/foo/bar.vim 文件。为什么这里要用某个这种描述呢?因为 vim 有个叫 runtimepath 的配置,里面存有一个逗号分割的文件夹列表。vim 在查找函数的时候会遍历 runtimepath 指定的文件夹,并搜索对应 autoload 文件夹内的 .vim 文件。假设把 runtimepath 设成 /a,/b,那么 vim 在查找 foo#bar#baz() 函数时会依次检查 /a/autoload/foo/bar.vim/b/autoload/foo/bar.vim,查到为止。

运行的时候 vim 会自动查找,可看我们要看代码话就只能自己手动查找了。这是一个相当无聊的过程。显然,我们需要一个插件。别怕,接下来我们就用 30 行代码实现一个 viml 跳转插件,从此告别无聊的手工查找操作。

ba9f16b4a681b142da893d5171dfa6a0.png
viml 跳转效果https://www.zhihu.com/video/1125856058945703936

在开始之前,我们先了解一些预备知识。

首先要解决的问题是获取光标所在位置的函数。

vim 为我们提供了 expand() 函数。如果你在 vim 执行 :echo expand('<cword>'),vim 就会输出光标所在的函数名。无论你把光标移动到 foo#bar#baz() 的哪个字母上,执行 :echo expand('<cword>') 都会输出 foo#bar#baz()

vim 还可以定义形如 s:foo() 这样的局部函数,这种函数只能在当前文件调用。如果你把光标移动到 s:foo()foo 上,执行 :echo expand('<cword>'),vim 只会输出 foo。这是为什么呢?简单 google 之后(关键词 vim expand cword)发现 vim 是根据 iskeyword 配置确定 <cword> 边界的。如果我们想输出 s:foo,则需要额外设置 set iskeyword+=:expand() 函数还有很多其他功能,大家可以使用 :h expand() 查阅。

再一个要解决的问题就是搜索了。我们知道,在 vim 中按 / 键开始搜索,输入内容后按回车,vim 回自动将光标移动到第一个匹配的位置。如果想在 viml 中执行搜索,则需要调用 search() 函数。第一个参数是搜索匹配条件,就是按 / 后输入的内容。第二个参数是控制位,可以控制搜索的行为,例如,w 表示从光标所在位置向下搜索,如果没有搜到,则回到文件开头继续搜索。其他信息请参考 :h search()

最后要解决 vim 如何打开目标文件的问题。我们要手动打开文件的话,可以执行 :e path/to/file。但要在 viml 执行同样的操作则需要写成 execute 'e path/to/file'

了解了基础知识,我们就能看懂这 30 行 viml 代码了。

" 在 ~/.vim/autoload/vim.vim 声名函数
function! vim#Jump()
    " 读取光标所在函数名并存储到变量 cword
    setlocal iskeyword+=:
    let cword = expand('<cword>')
    setlocal iskeyword-=:

    let flags = 'wes' " 设置默认搜索行为
    " 如果函数名包含 #
    if stridx(cword, '#') >= 0 && cword[len(cword)-1] != '#'
        " 以 # 分割函数名,得到一个数组
        let parts = split(cword, '#')

        " 最后一个元素是函数名,去掉
        call remove(parts, -1)
        " 拼接成源文件相对路径
        " foo#bar#baz() => foo/bar.vim
        let fpath = join(parts, '/').'.vim'

        " 读取全局配置
        let vim_jump_paths = get(g:, 'vim_jump_paths', '')
        " 拆分搜索路径
        " 如果是正在使用插件,已经包含在 &runtimepath 配置里了
        " 注意,使用 & 引用配置
        " 如果只是看别人的代码,则需要将插件根目录写入 g:vim_jump_paths 配置
        let paths = split(vim_jump_paths, ',') + split(&runtimepath, ',')

        " 遍历搜索路径
        for root in paths
            " 拼接源代码真实路径
            let p = root . '/autoload/' . fpath

            " 文件不存在则跳过
            if !filereadable(p)
                continue
            endif

            " 在当前窗口打开文件
            execute 'e ' . p
            let flags = 'we'
        endfor
    endif

    " 设置搜索模式
    " 以 v 开关表示使用标准正则匹配
    let pattern = "vfu(nction)?!? ".cword
    " 跳到对应的的函数定义
    call search(pattern, flags)
endfunction

最后,大家可以在自己的 vimrc 里面映射一个快捷键:

autocmd FileType vim nnoremap <c-]> :call vim#Jump()<cr>

这次给大家介绍了一个小插件从构思到开发的过程。30 行的插件肯定没法处理所有情形,但对于跳转到 viml 函数这个需求来说是足够了。写这篇文章不是为了推广这个 30 行的插件,而是为了推广插件的开发思路。希望能给大家带来些许启发。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值