summaryrefslogblamecommitdiffstats
path: root/vim/plugin/foldtext.vim
blob: f6fad9a9390e609236831a8b22af1dfd5bf9c022 (plain) (tree)



















































































































































































































































































































                                                                                
" Base {{{
function Foldtext_base(...)
    " use the argument for display if possible, otherwise the current line {{{
    if a:0 > 0
        let line = a:1
    else
        let line = getline(v:foldstart)
    endif
    " }}}
    " remove the marker that caused this fold from the display {{{
    let foldmarkers = split(&foldmarker, ',')
    let line = substitute(line, '\V' . foldmarkers[0], ' ', '')
    " }}}
    " remove comments that vim knows about {{{
    let comment = split(&commentstring, '%s')
    if comment[0] != ''
        let comment_begin = comment[0]
        let comment_end = ''
        if len(comment) > 1
            let comment_end = comment[1]
        end
        let pattern = '\V' . comment_begin . '\s\*' . comment_end . '\s\*\$'
        if line =~ pattern
            let line = substitute(line, pattern, ' ', '')
        else
            let line = substitute(line, '.*\V' . comment_begin, ' ', '')
            if comment_end != ''
                let line = substitute(line, '\V' . comment_end, ' ', '')
            endif
        endif
    endif
    " }}}
    " remove any remaining leading or trailing whitespace {{{
    let line = substitute(line, '^\s*\(.\{-}\)\s*$', '\1', '')
    " }}}
    " align everything, and pad the end of the display with - {{{
    let line = printf('%-' . (62 - v:foldlevel) . 's', line)
    let line = strpart(line, 0, 62 - v:foldlevel)
    let line = substitute(line, '\%( \)\@<= \%( *$\)\@=', '-', 'g')
    " }}}
    " format the line count {{{
    let cnt = printf('%13s', '(' . (v:foldend - v:foldstart + 1) . ' lines) ')
    " }}}
    return '+-' . v:folddashes . ' ' . line . cnt
endfunction
" }}}
" Latex {{{
let s:latex_types = {'thm': 'Theorem', 'cor':  'Corollary',
                   \ 'lem': 'Lemma',   'defn': 'Definition'}
function s:lower_letter(i) " {{{
    return tolower(s:upper_letter(a:i))
endfunction " }}}
function s:roman_numeral(i) " {{{
    let numeral = ''
    let chars = 'ivxlcdm'
    let i = a:i
    for base in [0, 2, 4]
        let c1 = strpart(chars, base, 1)
        let c5 = strpart(chars, base + 1, 1)
        let c10 = strpart(chars, base + 2, 1)
        let digit = i % 10
        if digit == 1
            let numeral = c1 . numeral
        elseif digit == 2
            let numeral = c1 . c1 . numeral
        elseif digit == 3
            let numeral = c1 . c1 . c1 . numeral
        elseif digit == 4
            let numeral = c1 . c5 . numeral
        elseif digit == 5
            let numeral = c5 . numeral
        elseif digit == 6
            let numeral = c5 . c1 . numeral
        elseif digit == 7
            let numeral = c5 . c1 . c1 . numeral
        elseif digit == 8
            let numeral = c5 . c1 . c1 . c1 . numeral
        elseif digit == 9
            let numeral = c1 . c10 . numeral
        endif
        let i = i / 10
        if i == 0
            break
        end
    endfor

    return repeat('m', i) . numeral
endfunction " }}}
function s:upper_letter(i) " {{{
    if a:i <= 26
        return nr2char(char2nr('A') + a:i - 1)
    else
        return 'ERROR'
    endif
endfunction " }}}
function s:enumeration(depth, index) " {{{
    if a:depth == 0
        return a:index + 1
    elseif a:depth == 1
        return '(' . s:lower_letter(a:index + 1) . ')'
    elseif a:depth == 2
        return s:roman_numeral(a:index + 1)
    elseif a:depth == 3
        return s:upper_letter(a:index + 1)
    else
        return 'Error: invalid depth'
    endif
endfunction " }}}
function Foldtext_latex() " {{{
    let line = getline(v:foldstart)
    " format theorems/etc nicely {{{
    let matches = matchlist(line, '\\begin{\([^}]*\)}')
    if !empty(matches) && has_key(s:latex_types, matches[1])
        let type = s:latex_types[matches[1]]
        let label = ''
        let linenum = v:foldstart - 1
        while linenum <= v:foldend
            let linenum += 1
            let line = getline(linenum)
            let matches = matchlist(line, '\\label{\([^}]*\)}')
            if !empty(matches)
                let label = matches[1]
                break
            endif
        endwhile
        if label != ''
            let label = ": " . label
        endif
        return Foldtext_base(type . label)
    endif
    " }}}
    " format list items nicely {{{
    " XXX: nesting different types of lists doesn't give quite the correct
    " result - an enumerate inside an itemize inside an enumerate should use
    " (a), but here it will go back to using 1.
    if line =~ '\\item'
        let item_name = []
        let item_depth = 0
        let nesting = 0
        let type = ''
        for linenum in range(v:foldstart, 0, -1)
            let line = getline(linenum)
            if line =~ '\\item'
                if nesting == 0
                    let label = matchstr(line, '\\item\[\zs[^]]*\ze\]')
                    if len(item_name) == item_depth
                        if label != ''
                            let item_name += [label]
                        else
                            let item_name += [0]
                        endif
                    else
                        if type(item_name[item_depth]) == type(0) && label == ''
                            let item_name[item_depth] += 1
                        endif
                    endif
                endif
            elseif line =~ '\\begin{document}'
                break
            elseif line =~ '\\begin'
                if nesting > 0
                    let nesting -= 1
                else
                    let new_type = matchstr(line, '\\begin{\zs[^}]*\ze}')
                    if type == ''
                        let type = new_type
                    elseif type != new_type
                        let item_name = item_name[0:-2]
                        break
                    endif
                    let item_depth += 1
                endif
            elseif line =~ '\\end'
                let nesting += 1
            endif
        endfor
        " XXX: vim crashes if i just reverse the actual list
        " should be fixed in patch 7.1.287
        "let item_name = reverse(item_name)
        let item_name = reverse(deepcopy(item_name))
        for i in range(len(item_name))
            if type(item_name[i]) != type('')
                let item_name[i] = s:enumeration(i, item_name[i])
            endif
        endfor
        let type = toupper(strpart(type, 0, 1)) . strpart(type, 1)
        let line = type . ': ' . join(item_name, '.')
        return Foldtext_base(line)
    endif
    " }}}
    return Foldtext_base(line)
endfunction " }}}
" }}}
" C++ {{{
function Foldtext_cpp()
    let line = getline(v:foldstart)
    " strip out // comments {{{
    let block_open = stridx(line, '/*')
    let line_open = stridx(line, '//')
    if block_open == -1 || line_open < block_open
        return Foldtext_base(substitute(line, '//', ' ', ''))
    endif
    " }}}
    return Foldtext_base(line)
endfunction
" }}}
" Perl {{{
function Foldtext_perl()
    let line = getline(v:foldstart)
    " format sub names with their arguments {{{
    let matches = matchlist(line,
    \   '^\s*\(sub\|around\|before\|after\|guard\)\s*\(\w\+\)')
    if !empty(matches)
        let linenum = v:foldstart - 1
        let sub_type = matches[1]
        let params = []
        while linenum <= v:foldend
            let linenum += 1
            let next_line = getline(linenum)
            " skip the opening brace and comment lines and blank lines
            if next_line =~ '\s*{\s*' || next_line =~ '^\s*#' || next_line == ''
                continue
            endif

            " handle 'my $var = shift;' type lines
            let var = '\%(\$\|@\|%\|\*\)\w\+'
            let shift_line = matchlist(next_line,
            \   'my\s*\(' . var . '\)\s*=\s*shift\%(\s*||\s*\(.\{-}\)\)\?;')
            if !empty(shift_line)
                if shift_line[1] == '$self' && empty(params)
                    if sub_type == 'sub'
                        let sub_type = ''
                    endif
                    let sub_type .= ' method'
                elseif shift_line[1] == '$class' && empty(params)
                    if sub_type == 'sub'
                        let sub_type = ''
                    endif
                    let sub_type .= ' static method'
                elseif shift_line[1] != '$orig'
                    let arg = shift_line[1]
                    " also catch default arguments
                    if shift_line[2] != ''
                        let arg .= ' = ' . shift_line[2]
                    endif
                    let params += [l:arg]
                endif
                continue
            endif

            " handle 'my ($a, $b) = @_;' type lines
            let rest_line = matchlist(next_line, 'my\s*(\(.*\))\s*=\s*@_;')
            if !empty(rest_line)
                let rest_params = split(rest_line[1], ',\s*')
                let params += rest_params
                continue
            endif

            " handle 'my @args = @_;' type lines
            let array_line = matchlist(next_line, 'my\s*\(@\w\+\)\s*=\s*@_;')
            if !empty(array_line)
                let params += [array_line[1]]
                continue
            endif

            " handle 'my %args = @_;' type lines
            let hash_line = matchlist(next_line, 'my\s*%\w\+\s*=\s*@_;')
            if !empty(hash_line)
                let params += ['paramhash']
                continue
            endif

            " handle unknown uses of shift
            if next_line =~ '\%(\<shift\>\%(\s*@\)\@!\)'
                let params += ['$unknown']
                continue
            endif

            " handle unknown uses of @_
            if next_line =~ '@_\>'
                let params += ['@unknown']
                continue
            endif
        endwhile

        let params = filter(params[0:-2], 'strpart(v:val, 0, 1) != "@"') +
        \            [params[-1]]

        return Foldtext_base(sub_type . ' ' . matches[2] .
        \                    '(' . join(params, ', ') . ')')
    endif
    " }}}
    return Foldtext_base(line)
endfunction
" }}}

if exists("g:Foldtext_enable") && g:Foldtext_enable
    set foldtext=Foldtext_base()
endif
if exists("g:Foldtext_tex_enable") && g:Foldtext_tex_enable
    autocmd FileType tex  setlocal foldtext=Foldtext_latex()
endif
if exists("g:Foldtext_cpp_enable") && g:Foldtext_cpp_enable
    autocmd FileType cpp  setlocal foldtext=Foldtext_cpp()
endif
if exists("g:Foldtext_perl_enable") && g:Foldtext_perl_enable
    autocmd FileType perl setlocal foldtext=Foldtext_perl()
endif