3

I would like emacs to check for a ~/emacs.d/scratch.txt and use it's contents instead of the default message when emacs starts.

How can this be done?

3

initial-buffer-choice allows to specify a path to a file or directory:

(let ((filename "~/.emacs.d/startup.txt"))
  (when (file-exists-p filename)
    (setq initial-buffer-choice filename)))
| improve this answer | |
  • 1
    This has a fairly big disadvantage that the file gets loaded, so running emacs somefile.txt will not show the file given in the command line, but the startup file instead. It also doesn't use the scratch buffer name which is ignored for buffer switching in some cases. – ideasman42 Feb 10 '18 at 8:15
  • Well, you're changing what the initial buffer is, so it's hardly surprising it's not named *scratch* anymore. As for the other thing, if I run emacs somefile.txt, it opens that file in a split, additionally to the buffer I've specified. This is hardcoded in startup.el. – wasamasa Feb 10 '18 at 9:39
  • 1
    My aim was simply to have text from a file to replace the default text in *scratch*, not to open a file or split windows. Basically, if I open emacs with no files: show a buffer with useful content, otherwise behave as normal. – ideasman42 Feb 10 '18 at 9:59
3

This will do the job.

(let ((filename (concat user-emacs-directory "scratch.txt")))
  (when (file-exists-p filename)
    (let ((scratch-buf (get-buffer "*scratch*")))
      (when scratch-buf
        (with-current-buffer scratch-buf
          (erase-buffer)
          (insert-file-contents filename))))))
| improve this answer | |
  • Perfect! No initial-buffer-choice needed. – ideasman42 Feb 10 '18 at 12:19
  • Update, one down side is you can see the buffers changing at startup, updating own answer with different solution. – ideasman42 Feb 13 '18 at 2:29
  • I've edited own answer based on this one - which only runs when no other buffers are loaded (avoiding flickering at startup) – ideasman42 Feb 17 '18 at 21:51
  • Consider using expand-file-name instead of concat. (expand-file-name "scratch.txt" user-emacs-directory )) – RichieHH Feb 24 at 6:21
2

This can be done using initial-buffer-choice,

While initial-buffer-choice can be set to a filename, this will load the file as well as any files passed via the command line (splitting the window or not even showing the buffer depending on your setup).
So reading file data into *scratch* buffer has the advantage that exact behavior is preserved, just replacing the text.

This example:

  • Only runs when the user starts without loading a file.

  • Loads in a text file into the startup buffer.

  • Users a default startup.txt, optionally taking a user defined startup file.

  • Sets the mode based on the filename, so users can pass in startup.org for an org-mode buffer.

  • Adds a short commented line at the top of the file, eg:

    # Emacs: 28.0, time: 0.93, packages: 58

  • When no startup file is found, the name of the file to create is referenced.

;; Load startup text when available.
;;
;; Example usage:
;;
;;   (my-scratch-buffer-from-file)
;;
;; Or if you like to use an org-mode scratch buffer,
;; an option file path can be passed in.
;; The file extension is used to set the mode:
;;
;;   (my-scratch-buffer-from-file (concat user-emacs-directory "scratch.org"))
;;

(defvar my-scratch-buffer-from-file--value nil)
(defun my-scratch-buffer-from-file (&optional scratch-file)
  (setq inhibit-startup-screen t)
  (setq initial-scratch-message nil)
  (when scratch-file
    (setq my-scratch-buffer-from-file--value scratch-file))

  (setq
   initial-buffer-choice
   (lambda ()
     (if (buffer-file-name)
         (current-buffer) ;; leave as-is
       (let ((original-buffer (current-buffer))
             (filename
              (or my-scratch-buffer-from-file--value
                  (concat user-emacs-directory "scratch.txt")))
             ;; Not essential, just gives some handy startup info.
             (startup-info
              (format
               "Emacs: %d.%d, time: %.2f, packages: %d"
               emacs-major-version
               emacs-minor-version
               (float-time (time-subtract after-init-time before-init-time))
               (length package-activated-list))))
         (with-current-buffer (get-buffer-create "*scratch*")
           ;; Don't track undo.
           (buffer-disable-undo)

           ;; Set the mode based on the filename, users may use filenames that infer modes.
           (let ((buffer-file-name filename))
             (set-auto-mode t))

           ;; Use the comment character set by the mode where possible.
           (let ((comment-start-or-empty
                  (if comment-start
                      ;; Ensure one trailing space (some comments include space).
                      (concat
                       (replace-regexp-in-string "[[:space:]]*$" "" comment-start)
                       " ")
                    "")))
             (if (file-exists-p filename)
                 (insert-file-contents filename)
               (insert
                comment-start-or-empty
                (format "Scratch buffer, create '%s' to replace this text on startup."
                        filename))
               (goto-char (point-min)))
             ;; Add some startup info above the static text.
             (insert comment-start-or-empty startup-info "\n\n"))
           (buffer-enable-undo)
           (set-buffer original-buffer)))))))


Others might suggest how this can be done better, it seems to work well enough though.

| improve this answer | |
  • 1
    With proper indentation and the closing parentheses not spread apart. – wasamasa Feb 9 '18 at 9:01
  • @wasamasa done. – ideasman42 Feb 22 at 0:07
  • Your formatter does somewhat better, but it's still far from idiomatic. Even this thing does better than that: t3x.org/lisp64k/grinding.html – wasamasa Feb 22 at 11:44
  • It looks like this doesn't wrap long lines, so I don't think it's usable in it's current form. – ideasman42 Feb 22 at 23:42
  • Thats a lot of code to replace initial-scratch-message. Also see the package melpa.org/#/elisp-format . Not ideal but is any elisp formatter? Everyone seems to have their own way.... – RichieHH Feb 24 at 6:28
1

I would use initial-scratch-message.

(let ((file "~/.emacs.d/scratch.txt"))
  (when (file-exists-p file)
    (setq initial-scratch-message
      (with-temp-buffer
        (insert-file-contents file)
        (buffer-string)))))
| improve this answer | |

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.