4

I had made some random notes in my scratch buffer.
By mistake, I pressed C-x C-c and killed the Emacs session.
All my content in the scratch buffer was gone.

Question:

When I try to close Emacs when the scratch buffer had been edited, I want Emacs to ask for a confirmation (offer to save or something) before it exits the current session. How can this be done? I'm not conversant with eLisp.

I searched the manual and there seems to be no way to change this behaviour.

Not duplicate:

My question is not duplicate to the question "never close scratch" in that, the other user is referring to killing the scratch buffer intentionally using kill-buffer C-x C-k. In my case, I unexpectedly exit out of Emacs either by closing the window or giving C-x C-c command.

Prasanna
  • 1,470
  • 16
  • 30
  • that's a nature of scratch buffer - it's not linked to any file. and there is no need to change it, just create normal buffer (like scratch.el or something) and when you try exit emacs, you will be asked as usual what to do with changes. – rsm Nov 02 '17 at 14:02
  • Possible duplicate of [never close scratch](https://emacs.stackexchange.com/questions/19254/never-close-scratch) – Drew Nov 02 '17 at 14:48
  • @Drew It may not be a duplicate in that, I would not be killing the scratch buffer individually, but quiting Emacs kills it (even if modified,without warnings). All I'm looking for is a way, in which, Emacs informs the user that scratch buffer is modified and request for save if needed – Prasanna Nov 02 '17 at 17:02
  • This is not an answer to your question, but it is generally a good idea to have a safeguard against inadvertently quitting emacs. The first sexp sets a short form of answering to all confirmation requests. Add these to your emacs config: (fset 'yes-or-no-p 'y-or-n-p) (setq confirm-kill-emacs 'y-or-n-p) – Heikki Nov 03 '17 at 07:12

4 Answers4

2

This is a commonly asked question. It might be a duplicate here (dunno). There are various tweaks or libraries that some users use to provide what you are asking - you will likely get some answers pointing to them.

But my suggestion is to not use *scratch* the way you are using it - maybe not use it at all. Instead, use C-x C-f foo.el or whatever, which tells Emacs that you might want to save whatever you're doing in that buffer. Emacs will also automatically save your work periodically after you do save it, if you do. And you need not save if you don't ever want to.

IOW, you get all of the liberty of buffer *scratch* (throw it away when done), but with the advantage that Emacs will do just what you requested: let you know before you exit that you have unsaved changes in that buffer. And you can put the buffer in any mode you like - it need not be emacs-lisp-mode. And emacs-lisp-mode gives you as much Lisp support as the default mode for *scratch* provides (in my opinion).

*scratch* really is for stuff that you intend to toss.

Prasanna
  • 1,470
  • 16
  • 30
Drew
  • 75,699
  • 9
  • 109
  • 225
  • 1
    `"*scratch* really is for stuff that you intend to toss."` Couldn't agree more. But I use it like scratch pad to keep a temporary copy of things, before they get better shape and go into some file. I don't want to lose it before my work is done - because of a mistake. I will have to resort to creating an extra file (`foo`), if I don't get a good solution – Prasanna Nov 02 '17 at 17:09
  • No, you **don't** have to "*resort to creating an extra file (foo)*. That's the point. Use `C-x C-f foo.el` and never bother to save your scratch work to disk. (You can even have Emacs create such a buffer for you by default.) *Because you visit it as a **file** buffer*, even though there is no file backing it up, Emacs will let you know that your work is not saved when you kill the buffer or you quit Emacs. This is the Emacs Way. Your habit of using `*scratch*` and expecting Emacs to warn you about not saving your work is a bad habit - nothing more. – Drew Nov 02 '17 at 17:31
  • 1
    Got it. Just now I tried it. Good idea and it would work for me. Edited you answer as there was a missing "C". To make up for the edit requirements - i had to change -`IMHO` to `in my opinion` – Prasanna Nov 02 '17 at 17:47
  • @ocodo: How is my comment, or my answer, *"out of order here"*? I mention an *alternative* to "persistent scratch", one that I and many others use, and have done so reliably for decades. It gives you all of the advantages I cited: appropriate mode, confirmation to save, etc. If you really want a `lisp-interaction-mode` buffer where you'll be prompted to save your work, then use `C-x f` with a file suffix that automatically gives you that mode. Simple, fail-safe. No one is arguing against "mistakes/outages happen and safety nets are good." Emacs has taken care of that concern since Day One. – Drew Sep 17 '22 at 16:59
  • @Drew maybe I misread what you were suggesting. I thought you meant to open a file, manually, every time you start Emacs. – ocodo Sep 18 '22 at 01:08
  • @ocodo: 1) I said nothing about anything like that. But if you're interested in having such a (file-visiting) buffer at startup then customize `initial-buffer-choice` to a file name. 2) I was talking about using *file-visiting* buffers - any number of them, anytime you like, on demand or by program, and just not saving them. They're "scratch" buffers in given modes, and Emacs prompts you to save them instead of just tossing them as it does with `*scratch*`. They're "persistent scratch buffers, if you like. In modes you choose. And you can toss them or save them - you're prompted automatically. – Drew Sep 18 '22 at 01:24
  • I meant this "Use C-x C-f foo.el and never bother to save your scratch work to disk" ... In your comment above FWIW. Anyway I apologize for using "out of order" it comes across as much harsher than intended. – ocodo Sep 18 '22 at 06:00
  • @ocodo: Wrt what you quote there: It was a reply to a comment that using `C-x C-f` "creates an extra file (foo)". It creates a file only if you confirm that you do, in fact, want to create a file (i.e., save the buffer). – Drew Sep 18 '22 at 16:23
  • @Drew - thanks for clarifying. Apologies! – ocodo Sep 19 '22 at 00:40
  • @Drew - The manual `C-x C-f` solution, that is troubling. The tendency for me (using Emacs since 1996, so maybe not as long as you have.) to work things out in `*scratch*` and lose things from time to time, because I'm not notified. By making another buffer available for the exact same purpose, I smell duplication, cognitive load and the need to build/reinforce a habit. Alternatively, I can make `*scratch*` pop up after restart with contents intact, with a one time yak shave, or better yet, installing a package. – ocodo Sep 19 '22 at 00:58
2

You could use the (bundled) savehist package to save and restore the scratch contents:

(use-package savehist
  :init
  (savehist-mode)
  :config
  (defun my-savehist-scratch ()
    (with-current-buffer (startup--get-buffer-create-scratch)
      (setq initial-scratch-message (buffer-string))))
  (add-hook 'savehist-save-hook #'my-savehist-scratch)
  (add-to-list 'savehist-additional-variables 'initial-scratch-message))

then you don't have to worry about it being killed, it'll be saved in ~/.emacs.d/history anyway.

EDIT: Thanks Y.E. for mentioning that this package will also give you persistent minibuffer histories (e.g. regex replaces, occur's and so on). I was already using savehist for that, but if you don't want to save minibuffer histories you may want to find a different solution.

unhammer
  • 1,127
  • 8
  • 22
  • 2
    Thank you for this idea, I really liked it and it works fine for the OP question case (preserve *\scratch\* contents on Emacs kill). Though there's one nuance that I cannot ignore: `(savehist-mode)` field of concern is saving `minibuffer` history, so it's like solving a task with a tool that's not purposed for the task (not restricted by itself if it works of course, but still **might** lead to some clashes/inconsistencies in future). I hope this doesn't sound too oppositional, my intention is to mention this nuance, that's it. – Y. E. Sep 07 '21 at 06:40
  • In emacs 29 it's probably easier to just `define-multisession-variable my-savehist-scratch` – unhammer Sep 19 '22 at 09:27
  • more context on `define-multisession-variable` (in Emacs 29)? – Y. E. Sep 19 '22 at 16:41
  • 1
    some intro at https://lars.ingebrigtsen.no/2022/01/20/13x10/ and source at https://git.savannah.gnu.org/cgit/emacs.git/tree/lisp/emacs-lisp/multisession.el – unhammer Sep 20 '22 at 09:23
2

persistent-scratch is another package which saves and restores the contents of the *scratch* buffer. The following is a possible setup.

(use-package persistent-scratch
  :ensure t
  :commands persistent-scratch-setup-default
  :hook (after-init . persistent-scratch-setup-default))
Swarnendu Biswas
  • 1,378
  • 1
  • 11
  • 24
0

After trying many different approaches I tend to agree that the accepted answer to the mentioned never close scratch is a way to go and may be adapted to the topic of this question too.

So, in my configuration I currently use this approach to ask for confirmation before killing Emacs or *scratch* buffer, if the latter is not empty:


;; Prompts on non-empty *scratch* buffer.
(defun yet-prompt-on-not-empty-scratch-buffer (question)
  "Prompt if *scratch* buffer is not empty.
Appends a confirmation QUESTION to the prompt message.
If *scratch* buffer is empty, returns true without prompting."
  (let ((scratch (get-buffer "*scratch*")))
    (if (and scratch (> (buffer-size scratch) 0))
        (y-or-n-p (format (concat
                           "Warning: *scratch* buffer is not empty. "
                           question)))
      t)))


;; Call prompting function on Emacs kill. 
(defun yet-verify-emtpy-scratch-on-emacs-kill ()
  "Require confirmation to kill Emacs if *scratch* buffer is not empty."
  (yet-prompt-on-not-empty-scratch-buffer "Really kill Emacs?"))

(add-to-list 'kill-emacs-query-functions
             #'yet-verify-emtpy-scratch-on-emacs-kill)


;; Call prompting function on a buffer kill.
(defun yet-verify-emtpy-scratch-on-buffer-kill ()
  "Require confirmation to kill *scratch* buffer if it is not empty."
  (if (equal "*scratch*" (buffer-name))
      (yet-prompt-on-not-empty-scratch-buffer "Really kill *scratch* buffer?")
    t))

(add-to-list 'kill-buffer-query-functions
             #'yet-verify-emtpy-scratch-on-buffer-kill)


Y. E.
  • 668
  • 4
  • 8