tags:

views:

1300

answers:

3

When trying to kill a buffer that contains changes in Emacs, the message: " Buffer [buffer] modified; kill anyway? (yes or no)" is displayed.

Instead of this I'd like to have Emacs ask me if I want to: 1. View a diff of what changed, 2. Save the buffer, 3. Kill the buffer.

How?

+4  A: 

You'll want to write some code to put in the kill-buffer-hooks and write-file-functions lists. Conceptually, what you want to do is

  1. test if the buffer has been modified
  2. display your message and get a response, and do what's requested
  3. then clear the modified flag so the normal kill-buffer doesn't come back and ask again.
Charlie Martin
The kill-buffer-hooks is run after the prompt the user is trying to change/avoid.
Trey Jackson
A: 

Option 1 is to learn elisp and hack the emacs source. You're talking about one of the oldest and most basic interactions in emacs, though, so my intuition tells me it won't be a quick and simple change. But I could be wrong.

If you're looking for a quicker solution, I have two:

  1. When you edit foofile in emacs, then exit without saving the buffer, emacs saves a copy of the buffer in #foofile#. You can diff this with the original.

  2. When you edit foofile in emacs then save the buffer, emacs saves a copy of the original file in foofile~. You can diff this saved original with the updated copy.

bradheintz
My intuition says that's bad intuition :-)
ShreevatsaR
Fair enough - I haven't hacked on the emacs core source, just some packages on the periphery, and that was years ago.
bradheintz
+15  A: 

The answer lies in using advice, because the hooks normally run when killing buffers run after the "buffer modified" prompt you want to change.

The following advice does what you want (I think). A couple of notes:

  1. When running the diff, the original buffer is marked as not modified - but you'll really need to save it.
  2. The other buffer in the diff doesn't get deleted

(defadvice kill-buffer (around my-kill-buffer-check activate)
  "Prompt when a buffer is about to be killed."
  (let* ((buffer-file-name (buffer-file-name))
         backup-file)
    ;; see 'backup-buffer
    (if (and (buffer-modified-p)
             buffer-file-name
             (file-exists-p buffer-file-name)
             (setq backup-file (car (find-backup-file-name buffer-file-name))))
        (let ((answer (completing-read (format "Buffer modified %s, (d)iff, (s)ave, (k)ill? " (buffer-name))
                                       '("d" "s" "k") nil t)))
          (cond ((equal answer "d")
                 (set-buffer-modified-p nil)
                 (let ((orig-buffer (current-buffer))
                       (file-to-diff (if (file-newer-than-file-p buffer-file-name backup-file)
                                         buffer-file-name
                                       backup-file)))
                   (set-buffer (get-buffer-create (format "%s last-revision" (file-name-nondirectory file-to-diff))))
                   (buffer-disable-undo)
                   (insert-file-contents file-to-diff nil nil nil t)
                   (set-buffer-modified-p nil)
                   (setq buffer-read-only t)
                   (ediff-buffers (current-buffer) orig-buffer)))
                ((equal answer "k")
                 (set-buffer-modified-p nil)
                 ad-do-it)
                (t
                 (save-buffer)
                 ad-do-it)))
      ad-do-it)))

Trey Jackson