6

The chapter 28 of User Manual says the following about folding:

Folding is used to show a range of lines in the buffer as a single line on the screen.

And all the examples of folding that I've seen converts several lines to only one.


What I'm trying to do is to create a folding within one line. For example if I have the following line:

MyFunction (with, really, a, lot, of, parameters, which, make, my, line, too, long)

I'd like to be able to fold it this way:

MyFunction (+-- folded)


So far I have tried different things with no success:

  • I know that it's possible to use set foldmethod=marker to add marker in the text to fold lines to it doesn't seem to work within a line. For example the following doesn't work:

    MyFunction ( /*{{{*/ with, really, a, lot, of, parameters, which, make, my, line, too, long /*}}}*/)
    
  • I also know that it is possible to create folding using text objects: for example to fold a paragraph it is possible to use zfap where zf is the command to create a folding and ap the text object to fold. I've tried to extend that with zfi(, zfa(, zfib or zfab but that give the following:

    MyFunction (with, really, a, lot, of, parameters, which, make, my, line, too, long)/*{{{*//*}}}*/
    
  • Finally I tried it with visual selection: vib and zf but the result is the same as the previous method.


So my questions are:

  • Is it possible to use folding within a line? From what I have tried I think that folds might not be the good way to do it
  • If so, how can I do it?
  • If it is not possible with foldings, what should I try to use to get my result?

  • A bonus point would be to have a method which wouldn't need to add markers like /*{{{*/ and /*}}}*/ in my buffer but if it's the only way to do it I'm ok with that.

  • I'm afraid you'll need to define both your folding function (&foldexpr), and the &foldtext function as well. I've done similar things in VimFold4C, except I've never tried to fold only one line. I don't know if this is possible. But what about the conceal feature? – Luc Hermitte Mar 2 '16 at 9:28
  • @LucHermitte: :h 'foldexpr' says it is evaluated for each line to obtain its fold level so I guess that wouldn't work on a single line. I don't know very well the conceal feature but from what I've read it might be the good way to go indeed. Thanks for the heads up! – statox Mar 2 '16 at 9:36
  • @LucHermitte: I was also thinking about conceal, but it can only display one character in place of the concealed string. statox: does it suit you to fold with only one char ? If so I can try something with the conceal feature. – nobe4 Mar 2 '16 at 9:37
  • 1
    @Nobe4: Actually having only one character between the brackets to indicate a fold could be a good option. – statox Mar 2 '16 at 9:38
  • @statox, foldexpr is quite tricky. You can return the current foldlevel, the same as the previous one, or the previous one +1/-1 (which is extremely slow on big files), or start/stop a fold with a specific foldlevel. If you start another fold with the same level as the current level, you may be able to have several functions on single lines. But as I said, I've never tried this IIRC. – Luc Hermitte Mar 2 '16 at 9:57
8

You can hack around with some custom syntax and the use of the conceal:

syntax region FunctionArguments start=+(+hs=e+1 end=+)+he=e-1 conceal cchar=…
set concealleval=1

e.g.

function (a, b, c, d, e)
function (…)

Careful though, with this basic example, all () will be concealed, so a little more work must be done here, but it should be a good opportunity to learn.

Here, the content is hidden and replaced with the … char.

Unfortunately, you can't display more than one char with the conceal feature. The good news is you can apply new syntax to file without having to define a new syntax, so you can embed this in a function to activate-deactivate it.

  • 1
    You can keep the parenthesis by using start=+(+hs=e+1 end=+)+he=e-1. This will also avoid collapsing completely empty pairs of parenthesis (whitespace between will still be collapsed). If you don't mind unicode, is a one character ellipsis that works nicely for the cchar. – 8bittree Mar 2 '16 at 15:04
  • can you add a quick comment/reference on your hs=e+1 he=e-1 update ? – nobe4 Mar 2 '16 at 15:10
  • 1
    From :help :syn-pattern-offset: hs is the offset for where the highlighting starts, he is the offset for the end. e+1 and e-1 are the actual offsets, in this case one character right (+) or left (-) of the end of the matched pattern. – 8bittree Mar 2 '16 at 15:18
  • And it occurs to me that the end should probably actually be end=+)+he=s-1, or offset one character left of the start of the pattern. Offsetting from the end works in this case since the pattern is one character, but offsetting from the start of the end pattern works for multi-character patterns (such as /* ... */ comments). – 8bittree Mar 2 '16 at 15:23
4

EDIT Actually I should have digged the doc a little more: using the syntax match is much more appropriate than the syntax region for what I want to do:

The final solution that I will use it the following:

syntax match LargeParenthesis +(\zs.\{40,}\ze)+ conceal cchar=?

The syntax match allows to create a conceal on a single pattern instead of using start and end patterns as syntax region do.

The advantage of this solution is that using (\zs and \ze) I can match only the content of the brackets and not the brackets themselves. So a concealed function looks like MyFunction(?).

Also as I can use .\{40,} I can conceal only the brackets containing more than 40 characters which let me see the shorter function directly.

And finally syntax match works on single lines which is closer to what I wanted originally and don't create problems with nested or collapsing matches.

Many thanks to @Nobe4 who gave me useful gave me the hint of using syntax match!

For references :h syntax was pretty helpful.

I'll leave my original answer here because it is a different approach and I still think it can sometimes be useful to be able to manually select a portion of text to fold.


Original answer

Largely inspired by @Nobe4's solution I came with the following workaround:

The idea is to use the conceal feature of Vim and create syntax region on the fly. The workflow would be:

  • Visually selecting the text I want to conceal.
  • Hitting F1or whatever key is used on the mapping line.

To do so I used a function which get the visually selected text found here. Then I split the text in two parts to get a start and a end pattern to use in the syntax region. And finally I create the syntax region.

vmap <F1> <Esc>:call OneLineFolding()<CR>

function! OneLineFolding()
    " Get the text to fold
    let l:textToFold    = s:get_visual_selection()

    " Don't fold less than 3 characters
    if (strlen(textToFold) < 3)
        echo "The text to fold is too short"
        return 0
    endif

    " Get the beginning and the end of the text
    let l:startPattern  = strpart(l:textToFold, 0, strlen(textToFold)/2)
    let l:endPattern    = strpart(l:textToFold, strlen(textToFold)/2, strlen(textToFold))

    " Generate a (more or less) unique name for the syntax zone
    let l:regionName = "Region" . localtime()

    " Forge the command which will create the syntax region
    let l:command =  "syntax region " . l:regionName . " start=+" . l:startPattern . "+ end=+" . l:endPattern . "+ conceal cchar=-"

    " Execute the command
    execute l:command

endfunction

" Get visual selection (from https://stackoverflow.com/a/6271254/4194289)
function! s:get_visual_selection()
  let [lnum1, col1] = getpos("'<")[1:2]
  let [lnum2, col2] = getpos("'>")[1:2]
  let lines = getline(lnum1, lnum2)
  let lines[-1] = lines[-1][: col2 - (&selection == 'inclusive' ? 1 : 2)]
  let lines[0] = lines[0][col1 - 1:]
  return join(lines, "\n")
endfunction

That's not totally ideal but that allow to select some text with any motion and fold it.

I still can see some problems with this solution:

  • A recurrent problem when working with syntax region is collapsing and nested region: on this point my solution might not really robust.
  • I haven't tested it deeply but I'm afraid that if the string to fold contains special character it could mess with the command.
  • It is not intended to work with with text on several lines and it won't work properly with it.

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.