8

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?

5

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!

  • 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 '17 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 '17 at 15:47
  • 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 '17 at 15:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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