10

I leave Emacs running all day, and will often will do a lot of file system manipulation on files that are open in Emacs buffers. For example, I might switch to a different branch in Git that has a very different directory structure.

Unfortunately this leaves me with a bunch of open buffers that Emacs now registers as "modified" buffers referring to "new" files. Unless I'm careful to manually close them each time I do this, it's easy to reflexively save those buffers which then results in spurious, probably duplicate files in my project. I can't even close emacs without answering the "save this buffer" question for each one.

Is there a way to automatically close unmodified buffers when the underlying file disappears? Similar in spirit to a global-auto-revert-mode that also handles when a file is removed entirely?

1

1 Answer 1

6

I think something along these lines could work:

(defun buffer-backed-by-file-p (buffer)
  (let ((backing-file (buffer-file-name buffer)))
    (if (buffer-modified-p buffer)
        t
      (if backing-file
          (file-exists-p (buffer-file-name buffer))
        t))))

(mapc 'kill-buffer (-remove 'buffer-backed-by-file-p (buffer-list)))

The buffer-backed-by-file-p function checks if the file is backed by a file - if not, returns true, otherwise returns if the backing file exists. We then take the list of buffers, filtering out anything but those that should have a backing file, but the file doesn't exist, and then kill them.

Requires dash.el for -remove, because I was too lazy to find a built-in to achieve the same thing.

Hope this helps!

3
  • You still have to run that mapc command any time you want the buffers-not-backed-by-files to be deleted, though, don't you? So it's not fully automatic. For full automation you'd have to either make sure that mapc runs on repeatedly on its own or use something like inotify to run it for you when a file actually gets deleted.
    – izkon
    Oct 3, 2017 at 15:27
  • Yes, one would need to run this periodically, or in response to inotify events or similar. Myself, I'd just run it periodically, every minute or so. Not perfect, but good enough for most cases. (run-with-timer 60 60 (lambda () (mapc 'kill-buffer (-remove 'buffer-backed-by-file-p (buffer-list))))) is what I'd use as a first approximation.
    – algernon
    Oct 3, 2017 at 15:47
  • 2
    Implemented this, and it worked great. I used this little wrapper function to call it, in case anyone needs something similar: ` (defun kill-removed-buffers () (interactive) (let ((to-kill (-remove 'buffer-backed-by-file-p (buffer-list)))) (mapc 'kill-buffer to-kill) (message "Killed %s buffers" (length to-kill))))` Sorry I can't get it to format correctly in the comment box.
    – levand
    Oct 5, 2017 at 15:09

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.