Simon
Simon

Reputation: 77

Folding on Regex in Vim

I use Pandoc when writing, and paste in html code for more complex tables. This can make the file look untidy, and I would like to use Vim's foldexpr to fold these table elements using a regular expression. This regular expression:

<table\b[^>]*>(?:(?=([^<]+))\1|<(?!table\b[^>]*>))*?</table>

works to highlight the table code in Sublime Text and BBEdit, but I don't seem able to get it to work in Vim (very much my editor of choice).

Looking at "Vim Scripting the Hardway", I was hoping to write a simple function like this:

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

function! GetTableFold(lnum)
    if getline(a:lnum) =~? '\v<table\b[^>]*>(?:(?=([^<]+))\1|<(?!table\b[^>]*>))*?  </table>
    return '-1'
endif
return '0'
endfunction

in order to hide the table element in my file but it doesn't work. When I try to do a simple search for the regex in Vim I also get a "E64: ? follows nothing" error.

Any ideas would be very welcome!

Upvotes: 2

Views: 891

Answers (1)

Randy Morris
Randy Morris

Reputation: 40927

You've got a couple of issues with the way you're doing this. For one, you've got syntax issues with your regex (as Ingo pointed out). Also, I think you don't quite understand how vim calculates the fold levels. Vim calls your function "GetTableFold" once for every line in the file. This then returns a foldlevel for that specific line. Because you have no context other than the line number you can't just match the line to a regex that spans multiple lines. Instead, you have to calculate whether or not your line falls in this range manually.

I haven't written any vimscript in a while so this may be a bit buggy but it seems to work in the few simple cases I tried. At the very least it should give you a base of where to start.

function GetTableFold(lnum)
    let n = a:lnum
    while n > 0
        let currline = getline(n)
        let prevline = getline(n-1)
        if currline =~ '\v\<table\>|\<\/table\>'
            return '1'
        elseif currline =~ '\v^\s*$'
            " special case for blank lines
            return '-1'
        elseif prevline =~ '\v\<\/table\>'
            " special case for the line after </table>
            return '0'
        endif
        let n -= 1
    endwhile
    return '0'
endfunction

set foldmethod=expr
set foldexpr=GetTableFold(v:lnum)

This makes some nasty (reasonable?) assumptions such as you will never have <table> and </table> on the same line. Given your use case of only putting complex tables in your pandoc files I think this is a safe assumption. It does not handle tables within tables too well. If you need that you'll have to tweak this a bit.

Upvotes: 3

Related Questions