1

I'm writing a script that creates a window that I'd like to be completely transient, like a help window. Specifically, if the user closes every window but this one, I want Vim to quit. (In the same way that if you have a single window open with no unsaved files, then the string of commands: :help, Ctrl-W, :quit, will cause Vim to quit.)

I'd also like the buffer not to appear in the buffer list and be deleted when the window closes, but my current code (below) achieves this.

The window works exactly how I want if I buftype to be help or quickfix, but as the documentation states, "you are not supposed to set this manually"/"You are not supposed to change this!". Also, these buftypes will be reflected in some people's status lines, which is not ideal — the buffer is not actually either of these things.

My current settings for the buffer:

setlocal buftype=nofile
setlocal bufhidden=delete
setlocal nobuflisted
setlocal noswapfile
setlocal nomodifiable
setlocal readonly
setlocal nomodified

I had a look to see if I could hack it by setting up an autocmd to quit Vim if all the other windows are closed, but there doesn't appear to be a event for window closes.

  • How about the autocmd for WinEnter? When the last window that isn't yours is exited, you should get a winEnter on your window, and can check that yours is the only window and then quit. – John O'M. Mar 14 '15 at 12:56
3

You can use the autocommand for WinEnter to accomplish this.

First, mark your buffer with a token for your plugin. We'll use a buffer local variable of our choosing:

function MakeMyWindow()
    " Stuff like your setlocal buftype and such goes here
    let b:mypluginmarker = 1    " re-label this variable to be named after your plugin
endfunction

Then make a function that looks for the token if there is only one window, and quit if that is the case:

function TestMyWindowIsOnlyWindow()
    if winnr('$') == 1 && exists("b:mypluginmarker") && b:mypluginmarker == 1
        quit
    endif
endfunction

The function call winnr('$') returns the window number of the last window in the current tab. If the last window is 1, then there is only one window open. If that's the case, we check if the marker variable we created on our buffer exists in the current (only) window, and if it exists, we check that it is equal to 1 (the value we set before). If these conditions exist, we quit (on this window). I like the extra check for the value on top of the exists check to allow someone to prevent the closure by changing the variable's value without them having to know how to :unlet a variable.

Finally, Set an autocommand to call our test function on WinEnter.

au WinEnter * call TestMyWindowIsOnlyWindow()

The reason this works is that when someone closes (or quits) a window and there are other windows, the window where the cursor goes gets a WinEnter event.

NOTE We use a buffer local variable instead of a window local variable since it is the window holding the buffer which is "transient" (thanks @Rich for pointing this out). If we used a window local variable, and the buffer changed, vim would still exit if the window became the last, which isn't what a user would expect.

NOTE This method will not close the tab (or Vim) if the cursor was already in your window when the other window(s) are closed (for example, by executing Ctrl+W O). This behaviour is probably what people would expect anyway though, and does behave like the help window.

NOTE This method will close the tab if the other window was active and closed by a method other than :quit (for example, Ctrl+W C), which is unlike the behaviour of a help window. This may or may not be an issue for you.

  • This answer has the slight problem that if you do <kbd>Ctrl+o</kdb> in the "help" window and then attempt to switch back to the regular hidden buffer (with, e.g. :b1), Vim closes. Switching the variable to be buffer local fixes this problem, though. (Which makes sense. Although I stated that I want the window to be transient, it's really any window that currently contains the buffer that I want to behave differently.) – Rich Mar 16 '15 at 22:19
  • @Rich I can't reproduce what you describe. Could you provide more detail? I tried starting with a plain text file, called the function to create the transient window, did Ctrl+o, making the transient window the only window, then did :b1, and I was left with the text file again. – John O'M. Mar 19 '15 at 5:12
  • Hmm. Must be something to do with my other settings/plugins causing the problem. I'll try to narrow it down when I get a chance. Still, is there any reason why using a buffer-variable instead of a window-variable is a bad idea? – Rich Mar 19 '15 at 10:04
  • I found a reproduction: Start with plain text file, create transient window. Change buffer in that window (e.g. :b1), switch to other window, close that window. Vim exits. Using a buffer variable does make more sense here; I'm updating the answer to reflect this. – John O'M. Mar 25 '15 at 3:57

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.