Question

When setting folds manually, it would be handy if it were possible to set the commentstring in a context sensitive manner. Consider the case in which the language uses BCPL-style comment markers (i.e., comments begin with // and are terminated by a newline), the first line of the visual block contains a comment, and the last line does not. Currently, if commentstring is set to //%s, redundant // characters will be appended to the first line when zf is used to create a fold.

Is it possible to set commentstring so that the // characters are added only if they do not already appear on the line?

Était-ce utile?

La solution

According to :help fold-create-marker, automatic folding marker insertion does not work properly when:

  • The line already contains a marker with a level number. Vim then doesn't know what to do.
  • Folds nearby use a level number in their marker which gets in the way.
  • The line is inside a comment, commentstring isn't empty and nested comments don't work. For example with C: adding /* {{{ */ inside a comment will truncate the existing comment.

Thus, it is not possible to change the default zf behavior by modifying the commentstring setting.

However, it is possible to create a custom version of the zf command that would take into consideration that starting or ending (or both) lines of a fold could have comments. For example, consider the following mappings, one for marking a fold by visual selection, another for using with a motion command.

nnoremap <silent> <leader>zf :set opfunc=CreateMarkerFold<cr>g@
vnoremap <silent> <leader>zf :<c-u>call CreateMarkerFold(visualmode(), 1)<cr>
function! CreateMarkerFold(vt, ...)
    let range = map(['[<', ']>'], 'line("''".v:val[a:0])')
    let mark = split(&foldmarker, ',')
    let pat = escape(&commentstring, '\')
    let pat = '\V' . substitute(pat, '\\\@<!%s', '\\zs\\ze\\.\\{-}', '')
    for i in [1, 0]
        let line = getline(range[i])
        if line =~ pat
            let line = substitute(line, pat, escape(mark[i], '\'), '')
        else
            let line .= printf(&commentstring, mark[i])
        endif
        call setline(range[i], line)
    endfor
endfunction

Both of the mappings follow the same routine. Before adding starting and ending folding markers, it checks separately whether the first and the last lines of the block to fold match the commentstring pattern. For each of the two that do match, it inserts the corresponding marker inside the first found comment, at the very beginning of its text. Otherwise, the marker is decorated according to the commentstring template and added at the end of the line.

If in the latter case it is preferable to separate the marker on its own line, one can change the for loop as shown below.

    for i in [1, 0]
        let line = getline(range[i])
        if line =~ pat
            let line = substitute(line, pat, escape(mark[i], '\'), '')
            call setline(range[i], line)
        else
            call append(range[i] - !i, printf(&commentstring, mark[i]))
        endif
    endfor

Unlike the previous version of the loop, the order of processing the two lines is important: The ending marker line should be added first, if necessary, because inserting a line for the beginning marker would shift the following lines changing their numbers.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top