I am trying to implement a custom folding function in a way that precomputes all the folds in a single pass and then reports the required foldlevels as needed. To do so, I use a single dictionary that keeps the state and provides functions to work with its internals (inspired by this, this, etc.)

However, unless I am constantly updating the foldexpr as the file changes, I don't get any folding done (foldexpr keeps the older version of the passed dictionary / function?..) (see ex. below)

I wonder if there is a good way to implement this.

function! foldclass#new()
  let obj = {}
  let obj.foldlist = []

  function! obj.updatefolds()
    let self.foldlist = []
    let curline = 1
    let totlines = line('$')
    let curdepth = 0
    while curline <= totlines
      let theline = getline(curline)
      if theline =~? '\v.*function.*\(.*\)'
        let curdepth = curdepth + 1
      let self.foldlist = self.foldlist + [curdepth]
      if theline =~? '\v.*endfunction.*'
        let curdepth = curdepth - 1
      let curline = curline + 1

  function! obj.folder(lnum)
    if len(self.foldlist) == 0
      echom 'Still empty...'
      return 0
    return self.foldlist[a:lnum - 1]

  return obj

let b:bufobj=foldclass#new()
setlocal foldmethod=expr
setlocal foldexpr=b:bufobj.folder(v:lnum)

augroup autocmds
  autocmd BufWritePost *.vim call b:bufobj.updatefolds()
  "autocmd BufWritePost *.vim setlocal foldexpr=b:bufobj.folder(v:lnum)
augroup end
  • If I source the file as is, I get "Still empty..." in messages, but echo b:bufobj shows that the foldlist is indeed updated based on the file changes.
  • If I uncomment the second to last line, it works as intended.
  • Relying on correct reference counting is essentially asking for segfaults. Making obj a global variable would make a world of difference in terms of safety, but it will kill your warm, fuzzy feelings about writing OOP scripts in Vim. :) Commented Sep 7, 2016 at 18:43
  • @SatoKatsura, how so? Isn't it simply that as long as you have a buffer opened, you will have an associated variable? But the thought of just having one global dictionary repopulated on each WinEnter or some other event doesn't horrify me:)
    – maksay
    Commented Sep 8, 2016 at 10:20
  • Variables local to functions are deleted when they're no longer referenced. The bookkeeping involved is complicated, and every time there's a change that involves reference counting there are 3-4 subsequent patches that fix related segfaults. Most recently this happened when "true" lambdas were added. With a global variable you have less chances of the corresponding counter going to 0 too early. :) Commented Sep 8, 2016 at 12:11

1 Answer 1


In my opinion, you don't need object-oriented features for this simple use case, and you're shooting yourself in the foot with its complexities.

Edit: My first hunch was wrong, the dict attribute is not necessary here.

Try developing a procedural version first, and get that to work. Only then you may consider converting this to an OO version.

Your code works for me, though. You could try setting a breakpoint in your functions (:breakadd), or :set verbose=20 | normal! zx and see what gets executed.

  • It doesn't fix that for me - still the same problem. Regarding the help, thanks for the information. The next paragraph there also mentions: <blink> To avoid the extra name for the function it can be defined and directly assigned to a Dictionary in this way: > :let mydict = {'data': [0, 1, 2, 3]} :function mydict.len() : return len(self.data) :endfunction :echo mydict.len() The function will then get a number and the value of dict.len is a |Funcref| that references this function. ... </blink>
    – maksay
    Commented Sep 7, 2016 at 15:23
  • Hm, I :edit foldclass.vim, then :so %, then :w once. Then the b:bufobj is populated. zx forces a foldupdate, and then I see the functions folding. Commented Sep 7, 2016 at 15:32
  • This is weird.. same sequence of commands, no effect. b:bufobj was always populated for me, without dict as well, but the output of :echo foldlevel(3) is 0, while echo b:bufobj.folder(3) is 1
    – maksay
    Commented Sep 7, 2016 at 15:36
  • Which Vim version do you use? I have tried both Vim 7.4.046 and the latest patchlevel. Commented Sep 7, 2016 at 15:45
  • I tried with both 7.4.2296 (which I compiled with a different python, so I also checked) 7.4.1724 installed some time ago via homebrew.
    – maksay
    Commented Sep 7, 2016 at 15:54

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.