1

When Vim is invoked on multiple files from the command line, then if any of those files have not been visited with :next or via buffer switching, the :q command pointlessly warns about the situation with a message like "43 files left to edit" and refuses to quit, even though nothing has been modified. Is there a way to eliminate this behavior?

In the :help documentation, we have:

:q[uit]             Quit the current window.  Quit Vim if this is the last
                    window.  This fails when changes have been made and
                    Vim refuses to abandon the current buffer, and when
                    the last file in the argument list has not been
                    edited.
                    If there are other tab pages and quitting the last
                    window in the current tab page the current tab page is
                    closed tab-page.
                    Triggers the QuitPre autocommand event.
                    See CTRL-W_q for quitting another window.

which just confirms the behavior: "fails ... when the last file in the argument list has not been edited".

The forced execution :q! will work, but is not acceptable because it will discard unsaved changes.

The documentation does not seem to mention that if the command fails due to the last file not having been edited, simply repeating the command will then force it.

3
  • Perhaps set confirm? I think then you can choose to quit anyway
    – D. Ben Knoble
    Jun 2, 2021 at 23:33
  • @D.BenKnoble Experimenting with :set confirm, I can confirm that I like it better than :qa, because there is no new habit to learn. You just hit y in response and you're out: same character count as :qa. Still, I like the solution which just make :q work. Thanks though, I will turn on confirm because it provides a good UX for unsaved-changes situations.
    – Kaz
    Jun 3, 2021 at 2:53
  • Looking at the source, it seems there isn't an option for that right now; there is just a check for the confirm option and that's it. (check_more() in ex_docmd.c). It wouldn't be too hard to add an option for this (or expand confirm to accept more values) though, but that requires someone to write a patch, so if you're willing/able to spend some time on that you can fix it "properly". Other than that, there's just the workarounds you already posted. Jun 13, 2021 at 17:22

4 Answers 4

3

The :cabbrev-answer has downsides because the abbreviation is active everywhere in the : command line, even if restricted to that type of command using the <expr> mechanism.

Ideally, Vim would have a command execution hook: a piece of code that could be executed each time a command is run, or at least an interactive command. That hook could rewrite the command.

Turns out, we can use :autocmd as a close facsimile. Vim's :autocmd has useful hooks, one of which is QuitPre. And so, with this simple line in the .vimrc, all seems good:

:au QuitPre * qa

When the QuitPre event is triggered on any buffer, we issue the qa command. This effectively rewrites q to qa.

Various experiments are confirming to me that it has the right behavior, and fixes :x also. When multiple files are being edited, and there are no changes, both :x and :q quit, as desired.

When there are unsaved changes, :q still fails, and :x fails if other buffers are unsaved.

A curious behavior occurs with :q!, though all seems well nonetheless. When there are unsaved changes, it quits as expected. However, Vim's admonishments that there are unsaved changes briefly flash on the display for a fraction of a second.

4
  • This is definitely a better approach! Glad you found it. I still think the warning is useful and :qa is not too painful, but at least this doesn't have the shortcomings of cabbrev...
    – filbranden
    Jun 2, 2021 at 20:27
  • @filbranden Can you explain how the warning useful? If I were to complete the sentence "If it were not for that warning, then _____" I can hardly think of a "bad thing" to fill in the blank. I've never used any desktop application where you could have multiple document windows open that will refuse to quit if none of them have unsaved changes, based on some of the windows not having been raised to the foreground. All I can think of is that if you open multiple files with the intent to make certain changes, you might inadvertently quit without fulfilling the intent. Or something.
    – Kaz
    Jun 2, 2021 at 21:22
  • If you're editing a large number of files, it could be annoying to return to the correct one after inadvertently quitting. You have to :ls to list the buffers to locate that file, and :b<n> to jump to it by number. In other regards, Vim preserves the state, like the contents registers, and whatnot. You should be able to pick up the edit session. Still, someone who does that to themselves habitually will probably be spared some effort thanks to that warning. I think you lose your last command. If you're repeating something with . over the files, that goes away if you inadvertently quit.
    – Kaz
    Jun 2, 2021 at 21:30
  • 1
    If I open a list of files, it means I plan to look or modify them all (at least at first.) For example, vim src/*/*.[ch] and then add a copyright notice or license at the top of all files, or vim $(grep -r foo) to replace foo with bar or perhaps manually refactor all the code that's using foo everywhere. In those cases, the warning that I didn't go through the whole list is surely useful. Sure, I might want to bail out early (in which case :qa is adequate.)
    – filbranden
    Jun 2, 2021 at 22:09
1

Yet another alternative is to :set confirm. Then :q will prompt in this case, and you hit y (or a localized equivalent) to continue. No new habits.

1

I have found a better solution than :au QuitPre * qa.

Vim's complaint that unedited files remain is not literally based on that stated condition. The internal condition is actually "has the last file been visited"?

So that is to say, if we open a bunch of files:

vim *.c

and then do this:

:last
:q

it will quit. The warning:

E173: 57 more files to edit.

is not a lie per se, but it is misleading about the condition on which it is based. It should really say:

E173: There are 57 unvisited files and you've not visited the last one.

And so, the following solution presents itself:

:au QuitPre * last

That's it! Before quitting, run the :last command to visit the last file.

I have tested this and have not noticed any side effects. For a brief moment, you may catch glimpse of the editor's status line switching to showing the name of the last file before just prior to termination.

I discovered a funny thing while experimenting with this.

If you start Vim with no file argument at all (no file is being edited, just a buffer), the :last command produces this diagnostic:

E163: There is only one file to edit

E163 should say:

E163: No files are being edited, therefore there is no last file

No such error is not produced if you edit exactly one file and use :last, which makes sense: if you're editing one file, that's the last file.

In any case, even with the au QuitPre * last installed, I've not seen even the slightest glimpse of this E163 error message when quitting a no-file edit session.

Edit: An unwanted side effect of :au QuitPre last has been discovered, which is quite nasty.

I sometimes edit a fairly large file of more than 80,000 lines, for which the BufEnter action runs a custom Vim script for folding calculations. This takes a bit of time to run.

Now, when I save the file and exit with :x, there is a big delay.

If I take out the :au QuitPre last from my .vimrc, then :x is virtually instant.

It seems that Vim does not optimize :last when that is a no-op; it is behaving as if entering that buffer.

I can reproduce this issue by typing :last. Vim freezes for many seconds, even though nothing is being edited but that file.

An acceptable solution to this problem must avoid this side effect. Perhaps the command dispatched by QuitPre should be a conditional which executes last only if there are two or more buffers.

That solution is this:

:au QuitPre blast

Switching to the last buffer using :bl{ast} rather than :last does not exhibit the above problem. With bl, Vim does not re-execute the time-consuming folding-related scriptology if that buffer is already the one being edited. Yet this QuitPre hook still has the desired effect of suppressing the warning about unvisited files.

2
  • au QuitPre blast should be au QuitPre * blast. I'd edit, but SO is forcing me to change at least 6 characters and there is nothing else to improve :( Aug 23, 2021 at 1:30
  • @MartinvonWittich Looking into my .vimrc, I see that I have it as au QuitPre * blast. Not sure what happened here.
    – Kaz
    Aug 23, 2021 at 3:15
0

The :qa command does what I want.

By itself, that would not be an acceptable solution; however, it's possible to turn :q into :qa using

:cabbrev q qa

This accompanying definition also helps:

:cabbrev x xa

allowing :x to be used to save and quit a change even though there are unedited buffers.

Unfortunately, the abbreviatiosn are active throughout the : command line and other contexts. Based on user filbranden's excellent hint in the comments below, the following refinement ensures that the abbreviations are only expanded in a colon command:

:cabbrev <expr> q (getcmdtype()==":"?"qa":"q")
:cabbrev <expr> x (getcmdtype()==":"?"xa":"x")

This achieves the effect as if the abbreviation didn't exist outside of the : command line. Still, the mitigation is not perfect because q, if it appears as a delimited in the right ways, expands to qa almost anywhere in the command line. If we try to edit a file called q using:

:vi q

the abbreviation expands and qa is opened instead.

2
  • Folks, this was headed into discussion territory, so I moved it to chat. Let's take any further discussion that way, though it looked like things were dying out. Here's also a gentle reminder of the Be Nice policy. I don't see anything in here that's overtly rude, and I'm sure we can find ways to constructively express ourselves without words like "idiotic." We appreciate helpful contributions to the site.
    – D. Ben Knoble
    Jun 3, 2021 at 19:23
  • @D.BenKnoble In fact, "I made the point I wanted to make" signified the end of the discussion as far as I was concerned. The only reason I'm here right now was I noticed some missing comments in my history. It's not clear to me what parts of your comment are for me...I'm hoping it's "none" since I'm well beyond the age where I need "adult supervision". ;) Probably quite a few beyond when you no longer needed it.
    – B Layer
    Jun 6, 2021 at 7:05

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.