12

If I ever close the *scratch* buffer it is always an accident.

I have persistent-scratch so it's as easy as a persistent-scratch-reload but it'd be nice if the scratch couldn't be killed. How can I do that?

fommil
  • 1,750
  • 11
  • 24
  • 1
    Do you actually make use of the features provided by the mode of `*scratch*`? If not - if you use it essentially for Emacs-Lisp code and you could just as easily use Emacs-Lisp mode, then consider not using `*scratch*` for your Lisp fiddling. Just use `C-x f` with a file buffer that you can save or toss at will. – Drew Jan 05 '16 at 00:06
  • In a similar way you can always restore scratch by just switching to `*scratch*`—Emacs will create it if it doesn't exist—and entering Lisp Interaction Mode. –  Jan 05 '16 at 05:37
  • @Drew yeah, I keep coming back to this idea over the years, e.g. a `~/.emacs.d/scratch.el`. But It just doesn't feel right, I don't know why. – fommil Jan 12 '16 at 23:04
  • Or just `tossa.el`, `tossb`,..., anywhere. It's trivial to do `%m ^toss` to mark all such files in a Dired buffer, then `D` to delete them all. – Drew Jan 13 '16 at 01:18
  • I'm giving the `scratch.el` approach another try. Maybe if I can clean up flycheck it'll be nice http://emacs.stackexchange.com/questions/19506 – fommil Jan 13 '16 at 12:07

3 Answers3

17

You can (ab-)use kill-buffer-query-functions for this purpose:

(add-hook 'kill-buffer-query-functions #'my/dont-kill-scratch)
(defun my/dont-kill-scratch ()
  (if (not (equal (buffer-name) "*scratch*"))
      t
    (message "Not allowed to kill %s, burying instead" (buffer-name))
    (bury-buffer)
    nil))

In my old Emacs configuration I used this to protect a bunch of important buffer like *Messages*.

Note that my function uses bury-buffer to achieve the effect of killing a buffer—doing the buffer away—without actually killing the buffer. Emacs will switch to a different buffer just as if you had killed scratch, but keep scratch alive and just put it at the end of the buffer list.

Or, simply

(add-hook 'kill-buffer-query-functions
          (lambda() (not (equal (buffer-name) "*scratch*"))))
fommil
  • 1,750
  • 11
  • 24
  • Please forgive me any mistakes in the code—I copy-pasted this answer together on mobile. –  Jan 04 '16 at 17:50
  • cool! BTW, why do you use the `#`? I don't think that's needed anymore – fommil Jan 04 '16 at 17:52
  • 3
    @fommil It makes the byte compiler warn if the symbol is not defined as a function. It does not make any difference semantically here (but can when using lambdas) but you should use it nonetheless to support the byte compiler. –  Jan 04 '16 at 17:56
  • 2
    @fommil Also, I think it's good style to use the function quote when referring to functions, if only to support readers of your code. Here it's clear that we refer to a function, but in other places it may not. In these situations the function quote can provide an important clue. –  Jan 04 '16 at 17:58
  • Your answer is useful, but I think you are missing the valuable use-case for *not* using the `#`, viz. if you use it together with `funcall` or `apply` (about 99% of the time): if you "omit" it, the `symbol-function` will be looked up every time `funcall` is called. Thus, if you, for example, add a hook `foo`, then you modify the function `foo`, you don't need to re-evaluate the code that adds the hook. – wvxvw Jan 04 '16 at 20:23
  • @wvxvw I'm not sure whether that's actually true. Do you have a reference for this? –  Jan 04 '16 at 20:28
  • @wvxvw IOW I don't think that the function quote expands to the `symbol-function`. Do you have a reference for this behaviour? –  Jan 04 '16 at 20:30
  • Just try `(defun foo () (message "foo 1"))`, then `(defun test-foo () (funcall 'foo) (funcall #'foo))`, then `(defun foo () (message "foo 2"))` then, when you call `(test-foo)` you should see "foo 1" "foo 2". – wvxvw Jan 04 '16 at 20:53
  • @wvxvw That doesn't seem to be a reasonable example to me. It's obviously just literal call; the byte compiler can just optimise funcall away in both cases. When would you ever write a funcall on a literal symbol?! –  Jan 04 '16 at 21:01
  • @wvxvw IOW it's funcall getting special treatment probably, not a function quote expanding to the function definition here, in my opinion. A counter example would be (progn (defvar bar nil) (set 'bar #'foo)). The value of the variable is a plain symbol afterwards, not a function definition, I think. Or look at the value of hook variables. –  Jan 04 '16 at 21:06
  • No, bytecompiler is not allowed to optimize such calls. They do different things. Think about `(funcall (intern (read-string "Guess function name: ")))` - there is no way to optimize this. – wvxvw Jan 04 '16 at 21:09
  • @wvxvw I'm not sure whether I can follow you. There's obviously a difference between a literal symbol and an expression. Why shouldn't the byte compiler be allowed to optimise the former?! –  Jan 04 '16 at 21:11
  • 1
    And besides: (eq 'foo #'foo) holds. Both forms literally evaluate to the same object, namely the symbol foo. –  Jan 04 '16 at 21:12
  • OK, I just checked it now. This must have been the case once, and this is how it works in Common Lisp, but it is no longer the case in Emacs Lisp. So, basically, there is no point in writing `#`, bytecompiled code is identical in both cases. In Common Lisp `#` macro will actually evaluate to a function, not a symbol, this is why the difference. – wvxvw Jan 04 '16 at 21:18
  • @wvxvw Ah, ok. There were probably changes to # in 24.1 when lexical scoping was added. I disagree about # though: It's still valuable for the byte compiler warnings (and to maintain lexical scoping in lambdas, though you shouldn't quote these at all). –  Jan 04 '16 at 21:22
  • I don't think the warning is warranted. Unlike in Common Lisp, Emacs Lisp resolves the function name dynamically *always*. This is just a wrong thing to do. Suppose this: `(defun foo ()) (defun test-foo () (funcall #'foo)) (fset 'foo 'hello-optimizers!)` – wvxvw Jan 04 '16 at 21:25
  • @wvxvw Well, it's a warning, not an error. And your example specifically doesn't stand against it: foo is a defined function in either case, regardless of its actual definition, and that's all the byte compiler cares about. "A wrong thing to do" is too bold a claim in my opinion—I see no use in continuing this discussion at this point. I'll only say that the warning has helped me catch typos and missing requires when developing large Emacs Lisp code. YMMV. Have a good night. –  Jan 04 '16 at 21:38
5

A new feature has been introduced for persistent scratch called "remember"

From https://www.masteringemacs.org/article/whats-new-in-emacs-24-4

The new command ``remember-notes`` creates a buffer which is saved

on ``kill-emacs``.

You may think of it as a \*scratch\* buffer whose content is preserved.

In fact, it was designed as a replacement for \*scratch\* buffer and can

be used that way by setting ``initial-buffer-choice`` to

``remember-notes`` and ``remember-notes-buffer-name`` to “\*scratch\*”.

Without the second change, \*scratch\* buffer will still be there for

notes that do not need to be preserved.
Talespin_Kit
  • 435
  • 2
  • 11
  • Neat! Also note that it has an associated variable `remember-notes-bury-on-kill` (which is `t` by default). It does what the name suggests – which seems quite relevant for the original question. – Harald Hanche-Olsen Jan 28 '16 at 08:59
  • Wow, thanks, that's a nice feature, for sure! –  Jan 28 '16 at 09:25
  • 1
    this doesn't work for me, the buffer is always called `notes` (which opens on startup) and it doesn't hijack `*scratch*` (I tried with and without escapes to the asterix) – fommil Jan 28 '16 at 10:02
  • @fommil Just curious. Why do you want the *scratch* to be persistent. Why not use *notes* for persistent and *scratch* for non persistent usage? – Talespin_Kit Jan 29 '16 at 15:50
2

ok, this whole discussion has prompted me to return to an approach I've tried to setup but @Drew has rekindled an interest in.

Create a file like this in ~/.emacs.d/scratch.el

;;; scratch.el --- Emacs Lisp Scratch -*- lexical-binding: t -*-


;; Local Variables:
;; flycheck-disabled-checkers: (emacs-lisp-checkdoc)
;; byte-compile-warnings: (not free-vars unresolved)
;; End:
;;; scratch.el ends here

thanks to https://emacs.stackexchange.com/a/19507/5142 for the Local Variables.

And then add the following to ~/.emacs.d/init.el as per @lunaryorn's answer:

;; *scratch* is immortal
(add-hook 'kill-buffer-query-functions
          (lambda () (not (member (buffer-name) '("*scratch*" "scratch.el")))))

(find-file (expand-file-name "scratch.el" user-emacs-directory))
fommil
  • 1,750
  • 11
  • 24