10

Here is how I reproduce the behavior I am observing.

First, I enter this command:

echo aaaaa > a
vim a

In Vim, I enter these commands:

:ls
:e #
:echo bufname('#')

Here is the output of the above three commands:

:ls
  1 %a   "a"                            line 1

:e #
E194: No alternate file name to substitute for '#'

:echo bufname('#')

The bufname('#') command produces no output.

Now I enter this command:

:bd #

The current buffer is deleted and it is replaced with a "[No Name]" buffer:

:ls
  2 %a   "[No Name]"                    line 1

I was expecting to get the E194 error on executing :bd #. Why did it delete the current buffer instead?

I am using VIM - Vi IMproved 8.0.

5
  • 1
    That is an interesting point. You can mention in your question that this is also the case in NVIM v0.3.0-dev, I checked.
    – 3N4N
    Jun 3, 2018 at 7:12
  • @LoneLearner I didn't really answer this because of the bounty but if you're going to offer one it would be nice if you'd award it to a deserving answer...alas, you haven't logged in in almost a week and the bounty period is over...
    – B Layer
    Jun 12, 2018 at 14:08
  • 1
    @BLayer Sorry, I forgot to award the bounty. You have written a fantastic answer. Once I have enough points in this Stack Exchange site, I will start another bounty for this question and award the bounty to you. I hope that would fix my mistake. Thank you for the great answer you have written. Nov 6, 2019 at 13:51
  • 1
    @LoneLearner Hey, you're welcome and no worries. I appreciate your comment. Don't worry about the bounty. Like I said it wasn't about the points. I just wanted to give you a heads up for the next time you put up a bounty. Put the points from here towards that. Cheers!
    – B Layer
    Nov 6, 2019 at 16:33
  • 1
    @LoneLearner Echoing what I said previously, my intention was not to pressure you into redoing the bounty, especially since it's such a large percentage of your current rep. It's clear, though, that it's something you wanted to do so I'll just say thank you and that I appreciate the integrity you've demonstrated since my first message. Cheers
    – B Layer
    Aug 20, 2020 at 1:53

1 Answer 1

8
+100

Evidence

Because there is no alternate file you're actually just running plain ol' :bd, deleting the current buffer...try it without # and you'll see the result is the same. A similar thing happens with :buffer, :sbuffer and at least a couple other commands that accept # as an argument: they silently behave as if no arguments were passed.

Along the same lines, if you try :bunload # you get this error: E90: Cannot unload last buffer. Run :bunload with no arguments and, once again, you'll get the same result.

The Docs

So we have evidence that # is being replaced by "nothing" (probably an empty string). Where do we go from here? I poked around the help files for a while trying to find mention of this behavior. There was nothing explicit but :h cmdline-lines says (scroll down a page or two) ...

When the character '%' or '#' is used where a file name is expected, they are expanded to the current and alternate file name.

I read that as Vim putting # through the expand() function (i.e. expand('#')) or at least the same underlying code used there.

:h expand() says:

Expand .. special keywords. .. When using '%' or '#', and the current or alternate file name is not defined, an empty string is used.

Sounds familiar.

The Code

Now none of the above is definitive or gives a clue as to Why? so I spent some more time digging...this time in the code. My C is very rusty and I don't have any good tools installed but I managed to find a function that does some setup for :bdelete called do_bufdel(). This sends command line arguments through buflist_findpat() which, if # is encountered, returns value curwin->w_alt_fnum. That's the "buffer number" of the alternate buffer...which can't be a positive value in our scenario. (There's no check for whether the alt file is valid/exists before that return value is selected.)

Back out in do_bufdel() a check is made against that return value for a buffer number less than 0 in which case the parameter processing loop is broken out of. That would result in no parameters being presented to the core :bdelete code...which is right in line with my earlier intuitions.

What's Next?

It appears to be working as designed in that I didn't see anything that looked like a clear bug. Possibly sin of omission, though...a corner case that has been overlooked and thus has no graceful handling. But only the developers that wrote this know for sure. So the final step would be to try to get their input. As Christian B. said asking on the vim-dev list is the way to go.

(Note that buflist_findpat() is a utility function so it wouldn't require a stretch of the imagination to assume that :bunload, :buffer, etc. are using it, too...that would explain their common behavior with respect to #.)

4
  • I'd think one more function for just checking if expanded buffer exists or not would do the job. Are you sure this was supposed to be by design? I think this should be listed as a bug.
    – 3N4N
    Jun 9, 2018 at 6:55
  • I think your research is correct. BTW: I don't think this is actually a bug. Jun 9, 2018 at 8:23
  • I just reworded my conclusion a bit....it doesn't look like a hard bug. OTOH, if one thinks about it one could conclude that it needs better handling. Probably only the developer knows for sure.
    – B Layer
    Jun 9, 2018 at 8:30
  • Yeah, one could ask on the vim-dev list to make sure. Jun 9, 2018 at 9:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.