10

I'd like to check whether some (say, current) buffer is visiting a file or not. I could say:

(if (buffer-file-name) ...)

but it seems to be not very elegant - what I'm interested in is only the boolean value, not the actual name of the buffer in question. If the buffer-file-name function were written in Elisp, I could look into its source to find out what it uses - but it is written in C, and while I could install the Emacs sources, I'm afraid that I wouldn't find an elisp name for the function that checks what I'm after there anyway.

What I need it for is I want to create a directory based on the name of the current buffer's file, and currently I'm doing more or less this:

(make-directory (if (buffer-file-name) (file-name-base) "default-dir"))

So, what would be the Elisp-idiomatic way of doing this?

Drew
  • 75,699
  • 9
  • 109
  • 225
mbork
  • 1,647
  • 1
  • 13
  • 23
  • 2
    Not sure why you object to using `buffer-file-name` really, it's the right way to do it (if you really really want `t`, do ``(and (buffer-file-name) t)`` but that's uglier IMO). Its implementation is reading the `filename` field of the buffer C struct, which is anyway not accessible directly from Elisp. In the end, it's just a pointer that is either null or not. – Sigma Oct 08 '14 at 00:40
  • Well, if this is the right way, that's fine with me. As I said - I did not know the C implementation, and common sense says that asking for the filename when I only want to know whether there is any might be redundant. – mbork Oct 08 '14 at 00:44
  • And I agree that `(and (buffer-file-name) t)` looks strange. – mbork Oct 08 '14 at 00:45
  • If you don't think that `(if (buffer-file-name) ... )` is elegant, then you haven't been coding in elisp for very long. It only gets uglier from here. – nispio Oct 08 '14 at 14:19

4 Answers4

14

I'd argue that your usage is idiomatic elisp, since the buffer's name is a perfectly appropriate boolean value in its own right. Quoting from the manual:

There is an important aspect to the truth test in an if expression. So far, we have spoken of `true' and `false' as values of predicates as if they were new kinds of Emacs Lisp objects. In fact, `false' is just our old friend nil. Anything else—anything at all—is `true'.

To further the point, check out the code for clone-buffer. I expect you'll see the following:

(interactive
 (progn
   (if buffer-file-name
       (error "Cannot clone a file-visiting buffer"))
...

Note that this is testing the variable binding of buffer-file-name instead of calling the function without argument, (buffer-file-name), but the two should always behave the same.

purple_arrows
  • 2,373
  • 10
  • 19
9

You can use either (buffer-file-name) (with optional buffer argument) or the buffer-local buffer-file-name variable. Both evaluate to the same value for a given buffer.

That is the idiomatic way to do this in Elisp, though, so your code is fine. If you desperately wanted to you could always make a buffer-has-file-p wrapper function.

phils
  • 48,657
  • 3
  • 76
  • 115
  • Thanks. Is there any significant difference to choose the function or the variable? – mbork Oct 08 '14 at 00:46
  • 2
    I don't think so. If you need to indicate the buffer argument then `(buffer-file-name BUFFER)` is certainly nicer than `(with-current-buffer BUFFER buffer-file-name)`, but otherwise I don't think it matters which one you use (and as function is written in C, I doubt there's even much difference in performance). – phils Oct 08 '14 at 00:52
3

Just use buffer-file-name. In Lisp we often use a non-nil value to mean true.

The only times you might want to avoid this is if the function is costly or has unwanted side effects.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • I see. I know that anything non-`nil` is true, I just thought grabbing the name when I only want to know whether *any* name exists is "costly" - but seemingly it isn't. – mbork Oct 08 '14 at 00:47
1

From the "Buffer List" chapter of the documentation :

The list returned by buffer-list is constructed specifically; it is not an internal Emacs data structure, and modifying it has no effect on the order of buffers.

So you have to find a way to search in the list of live buffers. Here's one:

  (if (string-match-p (regexp-quote "My buffer name") (format "%s" (buffer-list)))
      (message "Open")
    (message "Not open"))
yPhil
  • 963
  • 5
  • 22
  • 1
    The sexp that is suggested in this answer (as of the time of this writing) might raise some false positives. It checks that `My buffer name` is mentioned in some buffer. A buffer could be named `main.txt - My buffer name` and this will yield true, even though the file with name `My buffer name` is not opened. See [this answer](https://emacs.stackexchange.com/a/29004) for a solution that I consider better. – rdrg109 Dec 20 '22 at 18:37