Reputation: 1715
I am writing a small plugin for gvim that automatically increases or decreases the width of the gui according to the number of vertical splits. The plugin works something like this
if has("gui_running")
augroup resize
autocmd WinEnter * call <SID>ResizeSplits()
autocmd WinLeave * call <SID>ResizeSplits()
autocmd BufLeave * call <SID>ResizeSplits()
augroup END
endif
Here ResizeSplits()
is the function that resizes the gui window:
function! s:ResizeSplits()
let l:count = 0
windo if winwidth(winnr()) < &columns |
\ let l:count += 1 |
\ endif
if l:count > 0
let l:totwidth = l:count - 1 + l:count*80
else
let l:totwidth = 80
endif
if &columns != l:totwidth
execute 'set co=' . l:totwidth
endif
endfunction
The plugin almost works as I want it to, but not quite. It seems like the BufLeave
event (and similar ones) sometimes executes before windows are closed. This is a problem for instance when I do <c-w>o
or :only
. The problem is that the ResizeSplits
function does not work, since it still counts the old number of windows.
Is there another autocommand that can be used to detect when the number of windows has been changed, or a BufLeave
-like event that is guaranteed to be executed after windows are destroyed/removed?
It is trivial to get my plugin to work with mappings, but I am not able to get it to work reliably with ex commands like :only
and :close
.
Upvotes: 2
Views: 334
Reputation: 1715
I found a solution that seems to work pretty well. First, I rewrote the ResizeSplits
function:
function! s:ResizeSplits()
let l:curwin = winnr()
let l:colwidth = 80 + &foldcolumn
if &number
let l:colwidth += &numberwidth
endif
let l:count = 0
windo if winwidth(winnr()) < &columns |
\ let l:count += getwinvar(winnr(), 'count') |
\ endif
if l:count > 0
let l:totwidth = l:count - 1 + l:count*l:colwidth
else
let l:totwidth = l:colwidth
endif
if &columns != l:totwidth
silent! execute 'set co=' . l:totwidth
silent! execute 'wincmd ='
endif
silent! execute l:curwin . 'wincmd w'
endfunction
Where the important change is that I have defined a variable w:count
that is either 0 or 1. The function is used with the following autocommands:
if has("gui_running")
augroup vimrc_autocommands
autocmd WinEnter * let w:count = 1 | call <SID>ResizeSplits()
autocmd BufEnter * let w:count = 1 | call <SID>ResizeSplits()
autocmd WinLeave * call <SID>ResizeSplits()
autocmd BufHidden * let w:count = 0 | call <SID>ResizeSplits()
autocmd BufWinLeave * let w:count = 0 | call <SID>ResizeSplits()
augroup END
endif
This seems to work in almost every case I've tried. There is only one case where :only
and <c-w>o
still does not work: If the windows have the same buffer. A simple mapping remedies the <c-w>o
:
nnoremap <c-w>o <c-w>o:call <sid>ResizeSplits()<cr>
I will of course be happy if anyone finds a better solution.
Upvotes: 2
Reputation: 172580
There's no exact event for :close
/ :quit
; the closest is BufWinLeave
, but that doesn't fire when the buffer is still visible in another buffer. You could combine that with BufLeave
, but then have to check that the buffer is actually not visible any more.
To only handle unlisted buffers, you can add a condition checking 'buflisted'
in the executed autocmd.
I don't think there's a way to intercept the corner cases you've described. Especially :only
could be tricky. I can only propose to work around this with additional autocmds on CursorHold
and CursorMoved
. This way, the incorrect state will persist only for a short time.
Upvotes: 1