如果它们不能有任何副作用,我该如何创建有用的折叠表达式?

时间:2013-11-19 04:21:38

标签: vim

我正在尝试为Ruby创建一个非常简单的fold表达式:

let s:fold_indent = []

function! RubyFoldLevel(lnum)
    let l:prevline = getline(a:lnum - 1)
    let l:curline = getline(a:lnum)
    let l:nextline = getline(a:lnum + 1)

    " if l:curline=~'^\s*\(module\)\|\(class\)\|\(def\)\s'
    if l:curline=~'^\s*def\s'
        add(s:fold_indent, indent(a:lnum))
        echo s:fold_indent
        return 'a1'
    elseif l:curline=~'^\s*end\s*$'
    \ && len(s:fold_indent) > 0
    \ && indent(a:lnum) == s:fold_indent[-1]
        unlet s:fold_indent[-1]
        echo s:fold_indent
        return 's1'
    end

    return '='
endfunction

我的计划是每当我偶然发现"a1"时添加到折叠级别(def),然后在找到{{1}时从折叠级别"s1"中减去} 处于相同的缩进级别。在这里,我试图通过在堆栈中保存缩进并仅在堆栈中最顶层的项目与当前行的缩进级别匹配时结束折叠。

这不起作用,因为折叠表达式实际上无法编辑end的内容(注意s:fold_indent。每次打印echo s:fold_indent。)我认为这是有意义的因为无论顺序如何能够在任何一行上使用表达式都是有用的,但是如果没有它,我就无法弄清楚如何编写一个有用的折叠表达式。

是否有可能在不在功能之外维护堆栈的情况下实现我的计划?

2 个答案:

答案 0 :(得分:1)

折叠表达式中的副作用仅指更改文本或切换窗口(在Vim中称为 textlock )。问题在于您错过了:call前面的add()

call add(s:fold_indent, indent(a:lnum))

(当您手动调用该函数时,这一点就变得很明显了。)


尽管如此,使用共享状态是有问题的,因为您无法控制Vim在哪里(以及以何种顺序)评估折叠表达式。对于Ruby折叠,我宁愿依赖语法高亮(已定义def ... end的区域)来提供折叠信息(以及Vim附带的syntax/ruby.vim已经做了。)

答案 1 :(得分:0)

好吧,我想我弄明白了。我的功能现在会在遇到moduleclassdef的任何时候缩进。遇到end时,函数会在之前的行上向后迭代,直到找到与module相同的缩进级别classdefend ,如果找到一个,它会减去折叠水平。

经过几分钟的测试和几行代码处理特殊情况(0或1行折叠)后,它似乎完美无缺:

function! RubyFoldLevel(lnum)
    let l:prevline = getline(a:lnum - 1)
    let l:curline = getline(a:lnum)
    let l:nextline = getline(a:lnum + 1)
    let l:fold_starter = '^\s*\(\(module\)\|\(class\)\|\(def\)\)\s'
    let l:fold_ender = '^\s*end\s*$'

    " Prevent 1-line folds.
    if indent(a:lnum - 1) == indent(a:lnum + 1)
    \ && l:prevline =~ l:fold_starter
    \ && l:nextline =~ l:fold_ender
        return "="
    end

    if l:prevline=~l:fold_starter
    \ && !(l:curline =~ l:fold_ender && indent(a:lnum - 1) == indent(a:lnum))
        return 'a1'
    end

    if l:nextline=~l:fold_ender
    \ && !(l:curline =~ l:fold_starter && indent(a:lnum) == indent(a:lnum + 1))
        let l:cursor = a:lnum + 1

        while 1
            let l:cursor = prevnonblank(l:cursor - 1)

            " Fold starter found at this indentation level.
            if indent(l:cursor) == indent(a:lnum + 1)
            \ && getline(l:cursor)=~l:fold_starter
                return 's1'
            end

            " No fold starter found at this indentation level.
            if indent(l:cursor) < indent(a:lnum + 1)
                break
            end
        endwhile
    end

    return '='
endfunction

setlocal foldmethod=expr
setlocal foldexpr=RubyFoldLevel(v:lnum)

毋庸置疑,此折叠表达式仅适用于具有“正确”缩进的源文件。