So, context: I'd like to use foldexpr to fold by indentation, but including the start and end lines (that are not indented). For example:

function TestFunction(args)

Would be entirely replaced by the fold text, which I have configured to place an ellipsis between the first and last lines, for example:

function TestFunction(args) ... end

I can achieve this using :lua vim.opt.foldexpr = "max([indent(v:lnum+1), indent(v:lnum), indent(v:lnum-1)])/&tabstop", but with this solution, adjacent functions are merged into one fold, for example (with fold values shown in the first column):

0 local variable = 7
1 function SayHello()
1     print("hello!")
1 end
1 function SayGoodbye()
1     print("goodbye!")
1 end

Gets folded to function SayHello() ... end, completely hiding SayGoodbye(). After reading the manual page for fold-expr, I found that returning >n and <n will start and end fold level n respectively, after which I tried using:

vim.opt.foldexpr = "indent(v:lnum+1) > indent(v:lnum) ? '>' . (indent(v:lnum+1)/&tabstop) : (indent(v:lnum-1) > indent(v:lnum) ? '<' . (indent(lnum-1)/&tabstop) : indent(v:lnum)/&tabstop)"

Sorry for the disgusting expression, it's essentially saying: "if the next line is more indented then start a fold, or if the previous line is more indented then end a fold, otherwise just use indent". However, this just excludes the last line from each fold, e.g.

function Test() ... last statement

I'm not sure if this is just how <n works, or if I've made a mistake. Is it even possible to differentiate between adjacent folds on the same level? If anyone has any advice I'd love to hear it! My current options are as follows:

vim.opt.foldexpr = "indent(v:lnum+1) > indent(v:lnum) ? '>' . (indent(v:lnum+1)/&tabstop) : (indent(v:lnum-1) > indent(v:lnum) ? '<' . (indent(lnum-1)/&tabstop) : indent(v:lnum)/&tabstop)"
vim.opt.foldtext = "repeat(' ', indent(v:foldstart)) . trim(getline(v:foldstart)) . ' ... ' . trim(getline(v:foldend))"

This is my first post on a forum like this so I apologize if it's terribly written/formatted and missing info. Please let me know where I've messed up if need be.

1 Answer 1


Solved, turns out I'm just stupid because <n and >n work fine. If anyone else has a similar issue, this code worked for me and can probably be adapted into a one-line expression pretty easily:

function! FX()
    if indent(v:lnum+1) > indent(v:lnum)
        return '>' . (indent(v:lnum+1)/&tabstop)
    elseif indent(v:lnum-1) > indent(v:lnum)
        return '<' .  (indent(v:lnum-1)/&tabstop)
        return (indent(v:lnum)/&tabstop)

set foldmethod=expr
set foldexpr=FX()

(The original poster is me before I made an account)

  • We might be able to link the accounts. Saving for follow-up. In the meantime, have you considered leaving a blank line between functions?
    – D. Ben Knoble
    Sep 10, 2023 at 22:51
  • I did think about it, but I mostly just wanted to see if it was possible. I did end up getting it very close to how I wanted, but sadly the method I was using it consistently update folds with foldexpr (zx on InsertLeave) would open all the folds whenever they updated, so I kinda gave up.
    – Sneez
    Sep 12, 2023 at 5:42
  • See vi.stackexchange.com/help/merging-accounts
    – D. Ben Knoble
    Sep 12, 2023 at 12:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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