2007年12月27日 星期四

Vim - Perl Fold

vim 內建之 foldmethod 有 syntax , indent , expr 以及 marker 與 manual ,但 syntax 常會 fold 太多,不然就是想 fold 的卻沒 fold 到。 如果用 indent fold 的位置又不大正確。而 marker 卻會寫入 #{{{ , 以及 #}}} 在程式碼裡面,對不使用 vim 的人來說有可能會很礙眼。 另外使用 manual 又太過繁瑣,一旦把 ~/.vim/view 清除掉,所有的 manual fold 都會不見,而且檔案格式、位置變動,fold 也會不正確。

因此前一陣子便因此針對自己的需求用 expr 來寫了一個 fold 給 perl 用,只 fold 我要用的 sub 以及 template 和 pod 。
Code 如下:
function! GetPerlFold()
if getline(v:lnum) =~ '^\v(sub|template|\=\w+)\s\S+'
return '>1'
elseif getline(v:lnum) =~ '^\v(\};?|\=cut)\s*$'
return '<1'
elseif getline(v:lnum) =~ '^\s*#.*{{{'
return 'a1'
elseif getline(v:lnum) =~ '^\s*#.*}}}'
return 's1'
else
return "="
endif
endfunction
然後只需要在 .vimrc 內針對 perl 檔呼叫這段設定即可:
autocmd  Filetype perl :call PERL_RC()

function! PERL_RC
setlocal foldmethod=expr
setlocal foldexpr=GetPerlFold()
endfunction
上述的作法唯一的缺點是,如果 } 號沒有正確的 indent ,sub 或 template 內的 } 號被放在行頭的話,fold 就會錯誤,上述情況比較容易出現在 HereDocument 的格式內。

另外,如果覺得 Folded Text 不好看,也可以加上我這段處理 Fold Text 的 Function:
function! FoldText()
let line = getline(v:foldstart)
let cms = &commentstring

let cms = substitute( cms , '%s' , '|' , 'g' )
let cms = substitute( cms , '[*<!>]' , '\\\0' , 'g' )
let cms = '\v(' . cms
if cms =~ '|$'
else
let cms .= '|'
endif
let cms .= '\{\{\{\d=)'

" strip commentstring
if line =~ cms
let sub = substitute( line, cms , '', 'g')
" strip prefix
elseif line =~ '^\v(sub|template)\s'
let sub = substitute( line, '^\v(sub|template)\s''?(\w+)''?.*','\1: \2','')
elseif line =~ '^\v\=(\w+)\s'
let sub = substitute( line, '^\v\=(\w+)\s(\w+)','pod: (\1) \2','')
else
let sub = line
endif

" strip space
let sub = substitute( sub, '^\s*' , '', 'g')
let sub = substitute( sub, '\s*$' , '', 'g')

" count line
let num = v:foldend - v:foldstart

" make format line
let fline = printf( "|%3d|- %s " , v:foldend - v:foldstart ,sub )

"return fold
return v:folddashes . v:folddashes . v:folddashes . fline
endfunction
然後在 PERL_RC 內加上以下設定即可:
setlocal foldtext=FoldText()
如果只是想使用 vim 內建的 perl fold 可以加上
let    perl_fold=1
let perl_fold_blocks=1

並且將 fdm (foldmethod) 設為 syntax。

最後還有一個
set foldcolumn=2
這樣會在左邊顯示兩欄 fold,如果螢幕夠大,使用 10 也無妨。

而其他想特別用 marker,或 syntax 的 file,可以使用 vim modeline 的技巧。譬如 .vimrc 就非常適合使用 marker,因為 .vimrc 是個人使用,非共用檔案,此外在 .vimrc 使用 marker fold 隨身攜帶也非常方便。

只要在檔頭加上:
" vim:fdm=marker:
即可。

# 如果 setlocal 無法運作,可將 setlocal 改為 set