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?

4
  • 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 8, 2014 at 0: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 8, 2014 at 0:44
  • And I agree that (and (buffer-file-name) t) looks strange.
    – mbork
    Oct 8, 2014 at 0: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 8, 2014 at 14:19

4 Answers 4

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.

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.

2
  • Thanks. Is there any significant difference to choose the function or the variable?
    – mbork
    Oct 8, 2014 at 0: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 8, 2014 at 0: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.

1
  • 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 8, 2014 at 0: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"))
1
  • 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 for a solution that I consider better.
    – rdrg109
    Dec 20, 2022 at 18:37

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.