3

I am writing a filetype plugin that will (among other things) fold LaTeX documents by use of a custom foldlevel function. In order to make the folding efficient, I use foldmethod=manual during editing and I define a function to refresh the folds, something like:

function! RefreshFolds()
  setlocal foldmethod=expr
  normal! zx
  setlocal foldmethod=manual
endfunction

A small catch is that this requires me to refresh the folds manually after I open a buffer. In general, I want to refresh the folds manually, but I want them to be refreshed and defined when the buffer is opened. The first thought was that I could simply call Refreshfolds() during initialization, but for some reason this didn't work. Instead I have found that I can refresh the folds by utilizing a self destructing autocommand as follows:

augroup init_folds
  autocmd!
  autocmd CursorMoved <buffer> call InitFolds()
augroup END

function! InitFolds()
  call RefreshFolds()
  autocmd! init_folds
endfunction

This works as desired in most cases, but not if I open several buffers in separate tabs with vim -p *.tex. In this case, it seems the buffers are opened simultaneously, which means the autocommand is only run for one of the buffers before it self destructs.

What I want is to keep foldmethod=manual, but at the same time I want to ensure that folds are refreshed through RefreshFolds() after a buffer is opened. Does anyone know how to either fix the above approach so that it works when buffers are loaded simultaneously, or of a different approach that does not have the same problem?

Note: This problem comes from this Github issue.

  • What's wrong with BufReadPost? You could also use BufReadCmd. – Martin Tournoij Aug 12 '15 at 8:21
  • I also expected BufReadPost and BufReadCmd to work, but for some reason it doesn't work. However, I found that BufWinEnter does work. I didn't think of trying a different event, so thanks for suggesting! However, the main point of the question is not which autocommand event that is appropriate, but if there is a way to make this work even when I open several buffers simultaneously. Even with BufWinEnter I have the same problem as described. – Karl Yngve Lervåg Aug 12 '15 at 8:38
  • What vim version and patch level? I know I have run into bugs in 7.4 that have been fixed in the patches. – cxw Aug 12 '15 at 10:15
  • Your attempt doesn't make sense, autocmds are not applied in the order they are defined. Re: BufReadPost not working: perhaps you need a nested flag? Re: running a function only once per file: you can set a buffer-local variable to signal the function has run. – Sato Katsura Aug 12 '15 at 10:18
  • 1
    I don't understand the need for the augroups. A BufWinEnter autocmd like this is all you should need: au BufWinEnter * if &ft == "text" | call MyFunction() | endif. Get rid of the autocmd and augroup stuff in MyFunction() and leave just ... do stuff .... – garyjohn Aug 12 '15 at 15:05
2

The problem was that when the buffers are loaded simultaneously, then the autocommand group was deleted by one buffer before it was applied on the other buffers.

To solve the issue, I modified the code so that each buffer has a unique autocommand group name. I did this with a script local counter and by saving the name to a buffer local variable. I delete the variable when it is no longer needed.

For completeness, here is a working example:

let s:id = get(s:, 'id', 0) + 1
let b:my_augroup = 'init_folds_' . s:id
execute 'augroup' b:my_augroup
  autocmd!
  autocmd CursorMoved <buffer> call InitFolds()
augroup END

function! InitFolds()
  call RefreshFolds()
  execute 'autocmd!' b:my_augroup
  execute 'augroup!' b:my_augroup
  unlet b:my_augroup
endfunction
  • IMHO this is still crazy use of autocmds. But you don't seem to care to hear that anyway. – Sato Katsura Aug 12 '15 at 15:18
  • 1
    @SatoKatsura It is still the only solution to my problem that I know of so far. However, the problem itself might be badly described. Unfortunately, I do not really understand what about it that is difficult to understand. That is, your idea of setting a buffer local variable might also work. In any case, I see that I need to clarify the problem, so I will update the question. – Karl Yngve Lervåg Aug 12 '15 at 20:36
  • 1
    @SatoKatsura I do not understand why you say I "don't seem to care to hear that anyway"; I would be very happy to get alternative and perhaps better solutions. I have now updated the question in order to provide some more background. I think that should make it easier to see why my use of autocommands is not that crazy. And if they are, then I would be VERY happy to hear of a different way of doing this. – Karl Yngve Lervåg Aug 12 '15 at 21:06
  • No offense intended, but this is the only solution known to you because you stopped looking. How about calling windo normal zx to update the folding, and setting the defaults in a ftplugin for new files. autocmds are fragile commands with many known problems. They are not applied in a logical order, they may not refer to the file you think, they may not be applied at the right time, etc. You should try to avoid them when possible. Instead, you use CursorMoved which is one of the worst offenders, to do one-time operations. And the "self-destructing" thing might earn you a segfault. – Sato Katsura Aug 13 '15 at 2:17
  • Also, as far as I can tell the performance problem reported on GitHub has a very different root: foldexpr is called insanely often (and particularly so in insert mode) because of a long-standing bug in Vim, and you're making it worse with carelessly written regexps. Simply shuffling things around and getting rid of all those lookdbehinds can save you from going straight to the worst case scenario and speed things up considerably. But the better approach would be to simply disable foldexpr when in insert mode. There are other problems there, but you didn't ask about that. shrug – Sato Katsura Aug 13 '15 at 2:27

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.