在 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_
。这将允许保持函数脚本本地化。但这很麻烦,这里不需要(因为自动加载是正确的解决方案),而且它还会向其他开发人员发出错误信号(折叠功能并不意味着稳定和可重用)。
我是编写 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_
。这将允许保持函数脚本本地化。但这很麻烦,这里不需要(因为自动加载是正确的解决方案),而且它还会向其他开发人员发出错误信号(折叠功能并不意味着稳定和可重用)。