Emacs - Can't get buffer-offer-save working
Asked Answered
S

3

9

I would like to have Emacs ask me whether I want to save a modified buffer, when that buffer is not associated with a file. To open a new buffer (not visiting a file) I have the following function in my .emacs file:

;; Creates a new empty buffer
(defun new-empty-buffer ()
  "Opens a new empty buffer."
  (interactive)
  (let ((buf (generate-new-buffer "untitled")))
    (switch-to-buffer buf)
    (funcall (and default-major-mode))
    (setq buffer-offer-save t)))

I thought setting "buffer-offer-save" to something not nil would made the trick. But whenever I kill the buffer with "kill-this-buffer", it gets instantly killed without asking anything.

This happens on GNU Emacs 23.1.1

Any ideas?

Thanks, W

Scintillation answered 1/3, 2010 at 17:56 Comment(1)
would associating the buffer to a file, in new-empty-buffer solve the problem?Weitzel
S
2

Edited to add use of buffers-offer-save. Note: the variable buffer-offer-save is only used upon exiting Emacs.

You can start with this code and customize it to what you want:

(add-to-list 'kill-buffer-query-functions 'ask-me-first)
(defun ask-me-first ()
  "prompt when killing a buffer"
  (if (or buffer-offer-save 
          (eq this-command 'kill-this-buffer)
          (and (buffer-modified-p) (not (buffer-file-name))))
      (y-or-n-p (format "Do you want to kill %s without saving? " (buffer-name)))
    t))

Upon further reflection, that is a bit heavy-handed because you get prompted for all buffers that get killed, and there are often lots of temporary buffers that Emacs uses. If you just want to be prompted when you try to interactively kill a buffer (that isn't associated with a file).

You can use this advice which only prompts you when you're interactively trying to kill a buffer:

(defadvice kill-buffer (around kill-buffer-ask-first activate)
  "if called interactively, prompt before killing"
  (if (and (or buffer-offer-save (interactive-p))
           (buffer-modified-p)
           (not (buffer-file-name)))
      (let ((answ (completing-read
                   (format "Buffer '%s' modified and not associated with a file, what do you want to do? (k)ill (s)ave (a)bort? " (buffer-name))
                   '("k" "s" "a")
                   nil
                   t)))
        (when (cond ((string-match answ "k")
                     ;; kill
                     t)
                    ((string-match answ "s")
                     ;; write then kill
                     (call-interactively 'write-file)
                     t)
                    (nil))
          ad-do-it)

        t)
    ;; not prompting, just do it
    ad-do-it))
Skyeskyhigh answered 1/3, 2010 at 18:32 Comment(7)
would associating the buffer to a file, in new-empty-buffer solve the problem?Weitzel
@Weitzel Well, doing that in new-empty-buffer would put the request at the wrong end, as in when he creates a new buffer. That said, he could modify new-empty-buffer to set a flag that the advice/function could key off. I guess I liked a solution w/out resorting to using new-empty-buffer.Skyeskyhigh
@Weitzel Note: my updated solution does a better job and pays attention to new-empty-buffer - I did a little reading on it (it was new to me, so much to learn).Skyeskyhigh
@Trey Your advice works great. I intuitively get what your code does, but I'll surely study it in greater detail. I'm taking my first steps with Emacs and I'm delighted. Many thanks.Scintillation
Just found a weird behavior (don't know if it's reproducible in other installations though). I open a new buffer with M-x new-empty-buffer, paste some text on it, then M-x text-mode, then kill-this-buffer. The buffer gets killed without any warnings.Scintillation
@Wintergalt That's because 'kill-buffer isn't called interactively ('kill-this-buffer is, which in turn calls 'kill-buffer). Plus when you switch major modes, it kills all local variables - thereby resetting buffer-offer-save to nil. See gnu.org/s/emacs/manual/html_node/elisp/… . I've updated the code above to check for 'kill-this-buffer.Skyeskyhigh
Found Trey's defadvice to work great when changing 'new-empty-buffer. See answer below with the edited 'new-empty-bufferScintillation
S
1

Modifying 'new-empty-buffer seems to make it work as I intended with Trey's defadvice.

;; Creates a new empty buffer
(defun new-empty-buffer ()
 "Opens a new empty buffer."
 (interactive)
 (let ((buf (generate-new-buffer "untitled")))
   (switch-to-buffer buf)
   (funcall (and default-major-mode))
   (put 'buffer-offer-save 'permanent-local t)
   (setq buffer-offer-save t)))

This makes buffer-offer-save permanent local in our new buffer, so it won't get killed with the rest of the local variables when switching major modes.

Scintillation answered 3/3, 2010 at 11:57 Comment(0)
H
1

buffer-offer-save asking on exiting Emacs but not on closing a buffer manually doesn't make sense, so why not “enlarge” its responsibilities?

(defadvice kill-buffer (around kill-buffer-ask activate)
  "If `buffer-offer-save' is non-nil and a buffer is modified,
prompt before closing."
  (if (and buffer-offer-save (buffer-modified-p))
      (when (yes-or-no-p "The document isn't saved. Quit? ")
        ad-do-it)
    ad-do-it))

It will not prompt if untitled buffer is newly created. It will not prompt if you use kill-buffer from Elisp. It will not prompt on Emacs system buffers like *Messages*. But it will prompt if you created an empty buffer and wrote something in it.

See also my answer on creating an empty buffer.

Heptagonal answered 12/12, 2014 at 15:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.