在 vim 中调用脚本本地函数会出现错误?

calling script-local function in vim issues error?

我是编写 vim 插件和 vim 脚本的新手,并尝试自定义我的插件以使用以下自定义函数折叠我的代码文件。

setlocal foldmethod=indent
setlocal foldmethod=expr
setlocal foldexpr=<SID>FoldFunction(v:lnum)

function! s:GetNextNonBlankLine(lnum)
    let numlines = line('$')
    let current = a:lnum + 1

    while current <= numlines
        if getline(current) =~? '\v\S'
            return current
        endif

        let current += 1
    endwhile

    return -2
endfunction

function! s:FindIndentLevel(lnum)
    return indent(a:lnum) / &shiftwidth
endfunction

function! s:FoldFunction(lnum)
    if getline(a:lnum) =~? '\v^\s*$'
        return '-1'
    endif
    let this_indent = <SID>FindIndentLevel(a:lnum)
    let next_indent = <SID>FindIndentLevel(<SID>GetNextNonBlankLine(a:lnum))

    if next_indent == this_indent
        return this_indent
    elseif next_indent < this_indent
        return this_indent
    elseif next_indent > this_indent
        return '>' . next_indent
    endif
endfunction

如果我删除局部脚本前缀 (s:),那么它工作正常,但许多人建议将其转换为局部脚本函数,因为全局函数作用域不受欢迎。

所以我重写了它,但它给出了以下错误:

E81: Using <SID> not in a script context

出了什么问题?

是的,最好避免使用全局函数,尤其是当您将插件分发给其他人时。在您的本地 Vim 设置中,可能可以避免名称冲突,但您永远不会知道其他人。

不幸的是,Vim 仅自动处理映射和命令中的脚本局部函数(即 <SID> 前缀),而不是分配给选项(如此处的 'foldexpr')。这是Vim.

的缺点

一个简单的解决方法是使用自动加载脚本。即,将 :function 定义移动到(例如)~/.vim/autoload/FoldUtil.vim,并重命名函数:

function! FoldUtil#FoldFunction(...)

然后,在您的脚本中,您只需要:

setlocal foldmethod=indent
setlocal foldmethod=expr
setlocal foldexpr=FoldUtil#FoldFunction(v:lnum)

有了这个,您还可以获得自动加载的所有其他好处:大型脚本仅获取一次;您只需执行以上3条命令即可启用新的折叠方式。


请注意,您可以导出脚本的 ID;然后,您可以手动将 id 翻译成 <SID> 代表的 <SNR>NNN_。这将允许保持函数脚本本地化。但这很麻烦,这里不需要(因为自动加载是正确的解决方案),而且它还会向其他开发人员发出错误信号(折叠功能并不意味着稳定和可重用)。