среда, 13 февраля 2019 г.

How to use Vim's folding: the Expr and Marker methods.

The idea:

Folding can save you some time when navigating through file(s), but in Vim not all languages-plugins have built-in folding methods. Not so long ago I created a simple folding for "AutoHotkey" language-plugin, and I wanted to share with everyone on how I did it. In Vim there are variable named foldmethod that determines the method of folding, possible values: manual, diff, marker, expr, indent and syntax. The most powerful out of all is expr, with it you can do pretty much any kind of folding, but for that you should know how the folding actually works, read the :h fold.

Lets back to my AutoHotkey language folding script. So for the start is - the location of the script, you can use the ~/.vim/ftplugin/{LANGUAGE}/{ANYNAME}.vim, so for the my AutoHotkey fold it will be: ~/.vim/ftplugin/autohotkey/folding.vim.

The "Marker" method:


The marker method is very simple: You just define the start point and the end point delimited by comma, for example:

setlocal foldmethod=marker
setlocal foldmarker=SECTION,ENDSECTION

(If you use have spaces you should escape them, e.g. my\ fold\ start for the "my fold start")

Quite simple. Just 2 lines. First line tells Vim to use marker fold method, second defines folding start as SECTION and end as ENDSECTION. So lets open the *.ahk file, and add the SECTION and ENDSECTION to it(in the comments of course :)), lets take for example that file:

; SECTION Variables
funname := "thefunction"
; ENDSECTION
;SECTION Functions
thefunction(x, y) {
    s := x+y
    msgbox, Sum: %s%
}
; ENDSECTION
; SECTION Main
%funname%(24,18)
; ENDSECTION

with the folding script, that I mentioned earlier, in Vim now it will look like:

+-- 3 lines: ; Variables
+-- 6 lines: ;Functions
+-- 3 lines: ; Main

And when you try to i(edit) it it will unfold and display the code that were folded. You can also fold back the section that you have under cursor with :foldclose or shorter: :foldc.

The "Expr" method:

Expr method could be one complex one line or an function, lets use a function:

setlocal foldmethod=expr
setlocal foldexpr=SECTFOLD(v:lnum)
let b:IN=1
let b:ST='^\s*.\?;\s*.\?SECTION'
let b:EN='^\s*.\?;\s*.\?ENDSECTION'
function! SECTFOLD(linenum)
    let lt = getline(a:linenum)
    if lt =~ b:ST
      let b:IN = b:IN+1
      return '>' . b:IN
    elseif lt =~ b:EN
      let b:IN = b:IN-1
      return '<' . b:IN
    endif
    return '='
endfunction

Looks quite complex isn't it? So, the logic is: set foldexpr to function, and pass the line number v:lnum, then process each line. The SECTFOLD function works like the marker fold method, but it has the regex support! and with it we can define that the fold won't actually fold the variable or text named SECTION, so we make it count only the lines starting with ;(comment) and n-spaces and a SECTION. The b:IN holds fold level, b:ST regex for fold start, b:EN fold end regex.

Q/A:


Q: Why using setlocal?
A: If you use set the folding will be gone global, and after closing the AutoHotkey file the folding method will work for all other files.

Q: Marker method inside comments?
A: The closest to regex is: ;\ SECTION,;\ ENDSECTION but you'll need to enter EXACTLY 1 space after ;, and it will work even in inline comments...

Q:Expr method checking for matching ENDSECTION, if no don't fold.
A:You can use the add subfunction inside if lt =~ b:ST, to check if there is ending match, use line('$') to find out the last line index, and line('.') for the current, then loop over that range and return true if there is ending, else do not start he fold in if lt =~ b:ST.

Комментариев нет:

Отправить комментарий