3

I have found myself so extraordinarily frustrated with Vim's foldmethod=syntax behavior that I've contemplated dropping the editor entirely, despite being mostly happy with it for a few years now.

What I want is for the editor to not completely throw away the fold-state of individual lines, despite fold-levels changing.

There is a related question here: https://stackoverflow.com/questions/21280457/stop-vim-from-dynamically-updating-folds. The difference between this question and that one is that here I want to handle the following case:

{
    stuff
}

-> fold in the braces ->

+- { .. }

-> enter insert mode and type { other stuff at the top ->

{ other stuff
+- { .. }

-> leave insert mode ->

what happens

{ other stuff
{
    stuff
}

what I want

{ other stuff
+- { .. }

Now, ideally this would just be a simple configuration switch. I haven't found such a switch in the docs, but would love for someone to point at something simple.

my (very incomplete) attempt

I otherwise think that I could achieve what I want if I could keep my foldmethod=manual, but do the following:

  • on insert enter
    • for all lines, save whether or not the line is folded away (ignore their actual foldlevels)
    • save a copy of the buffer's contents
  • on insert leave
    • set the foldlevels for all lines to whatever they should be for foldmethod=syntax
    • diff the current buffer with the pre-insert buffer and use it to generate a partial map from line numbers in the post-insert buffer to the pre-insert buffer
    • set whether or not a fold is folded based on whether or not any of the post-insert line numbers in that fold, when mapped to their pre-insert line numbers, were previously folded

Depending on how the diff map is calculated this could inadvertently fold the current line, but I think I'd take that trade if I could keep the folds later in the file from flying open. If it ever became an annoyance I'm pretty sure some additional machinery could be added to force the highest foldlevels occupied by unmapped post-insert lines to stay open.

Having said all that...

I don't know vimscript, and my experience with ad hoc languages leaves me dreading the possibility. I think I'd be more willing to try it if quick individual ingredients were guaranteed to exist. My problems with this attempt are that:

  • I don't know if there's a simple way to get the pre-insert/post-insert diff (or to just have vim provide me with a line-number map pre-insert vs. post-insert)
    • I don't know if there's a way to backup whole buffers
    • I don't know how to get a programmatic representation of the diff
  • I don't know how to retrieve whether or not lines are folded for each line
  • I don't know how to set the foldlevels of all lines to what they would be for foldmethod=syntax
    • I don't know how to retrieve those foldlevels without necessarily setting foldmethod=syntax
  • I don't know how to, leaving foldlevel immutable, set the fold state of particular lines
  • I don't know if vim treats folds implicitly (as in, the abstraction used in foldmethod=expr is its internal representation) or explicitly (as in there are explicit objects that represent folds that can be manipulated)
    • If explicit I obviously don't know what those objects are

And my biggest, biggest concern:

  • I can't figure out how the flip to RTFM for this; where are the vim functions for folding? All I can find are keyboard commands.
  • 1
    Somethings are in foldexpr docs. I cant recall a function to get the fold level of a line, but you could :helpgrep fold. This is (one of) the reasons i dont mess with folds anymore. – D. Ben Knoble Aug 29 at 4:52
0

What you might have not found that you're missing are the three functions:

  • foldclosed(): If the line is in a closed fold, the result is the number of the first line in that fold. If the line is not in a closed fold, -1 is returned.
  • foldclosedend(): Similar, but return the last line of a closed fold.
  • foldlevel(): The fold level of a line.

Using foldclosed(), you can tell whether a line is in a closed fold or not. Using foldclosedend() you can jump to the next line after this fold.

If you keep 'foldmethod' set to manual, you might get away with only having to check the status of folds after you leave insert mode. Then apply setlocal foldmethod=syntax, back to manual and compare to what you had before. (If you don't need to map it before entering insert mode, you won't have to adjust line offsets to compensate for the insertion.)

At this step, you can also check foldlevel() before and after switching to syntax and back, so you'll see which blocks were now shifted due to possibly unbalanced braces.

You might want to consider other fold methods, such as a 'foldexpr' mainly taking indentation into consideration, but with additional considerations for the closing } and the starting function prototype. A method based in 'foldexpr' and using primarily indentation should be much more stable to insertions and edits than the syntax fold method, which might save you some grief.

See also:

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.