Persist scratch buffer across sessions

- Adds doom/open-project-scratch-buffer (persistent project scratch
  buffers)
- Prefix arg = open scratch buffer in current window, for both
  doom/open-scratch-buffer and doom/open-project-scratch-buffer.
- Rename doom/delete-scratch-files ->
  doom/delete-persistent-scratch-file
- Remove doom-scratch-buffer-display-fn
- Rename doom-scratch-files-dir -> doom-scratch-dir
- Add SPC p s keybind to open project scratch buffer
This commit is contained in:
Henrik Lissner 2019-04-17 11:01:51 -04:00
parent 20a63a35ab
commit 6cd30c926b
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
3 changed files with 104 additions and 51 deletions

View file

@ -1,12 +1,12 @@
;;; core/autoload/scratch.el -*- lexical-binding: t; -*- ;;; core/autoload/scratch.el -*- lexical-binding: t; -*-
(defvar doom-scratch-files-dir (concat doom-etc-dir "scratch/") (defvar doom-scratch-default-file "__default"
"Where to store project scratch files, created by "The default file name for a project-less scratch buffer.
`doom/open-project-scratch-buffer'.")
(defvar doom-scratch-buffer-display-fn #'display-buffer Will be saved in `doom-scratch-dir'.")
"The function to use to display the scratch buffer. Must accept one argument:
the buffer to display.") (defvar doom-scratch-dir (concat doom-etc-dir "scratch")
"Where to save persistent scratch buffers.")
(defvar doom-scratch-buffer-major-mode nil (defvar doom-scratch-buffer-major-mode nil
"What major mode to use in scratch buffers. This can be one of the "What major mode to use in scratch buffers. This can be one of the
@ -16,77 +16,129 @@ following:
nil Uses `fundamental-mode' nil Uses `fundamental-mode'
MAJOR-MODE Any major mode symbol") MAJOR-MODE Any major mode symbol")
(defvar doom-scratch-buffers nil
"A list of active scratch buffers.")
(defvar-local doom-scratch-current-project nil
"The name of the project associated with the current scratch buffer.")
(defvar doom-scratch-buffer-hook () (defvar doom-scratch-buffer-hook ()
"The hooks to run after a scratch buffer is made.") "The hooks to run after a scratch buffer is created.")
(defun doom--load-persistent-scratch-buffer (name)
(let ((scratch-file (expand-file-name (or name doom-scratch-default-file)
doom-scratch-dir)))
(make-directory doom-scratch-dir t)
(if (not (file-readable-p scratch-file))
nil
(erase-buffer)
(insert-file-contents scratch-file)
(set-auto-mode)
t)))
;;;###autoload
(defun doom-scratch-buffer (&optional mode directory project-name)
"Return a scratchpad buffer in major MODE."
(let* ((buffer-name (if project-name
(format "*doom:scratch (%s)*" project-name)
"*doom:scratch*"))
(buffer (get-buffer buffer-name)))
(with-current-buffer (get-buffer-create buffer-name)
(unless buffer
(setq buffer (current-buffer)
default-directory directory
doom-scratch-current-project project-name)
(setq doom-scratch-buffers (cl-delete-if-not #'buffer-live-p doom-scratch-buffers))
(cl-pushnew buffer doom-scratch-buffers)
(doom--load-persistent-scratch-buffer project-name)
(when (and (eq major-mode 'fundamental-mode)
(functionp mode))
(funcall mode))
(add-hook 'kill-buffer-hook #'doom|persist-scratch-buffer nil 'local)
(run-hooks 'doom-scratch-buffer-created-hook))
buffer)))
;; ;;
;; Library ;;; Persistent scratch buffer
;;;###autoload ;;;###autoload
(defun doom-scratch-buffer (&optional file mode text) (defun doom|persist-scratch-buffer ()
"Return a scratchpad buffer in major MODE with TEXT in it. "Save the current buffer to `doom-scratch-dir'."
(write-region
(point-min) (point-max)
(expand-file-name (or doom-scratch-current-project doom-scratch-default-file)
doom-scratch-dir)))
If FILE is a valid path, open it as if it were a persistent scratchpad." ;;;###autoload
(if file (setq file (file-truename file))) (defun doom|persist-scratch-buffers ()
(let ((buffer "Save all scratch buffers to `doom-scratch-dir'."
(if file (dolist (buffer (cl-delete-if-not #'buffer-live-p doom-scratch-buffers))
(with-current-buffer (find-file-noselect file)
(rename-buffer (format "*doom:scratch (%s)*" (file-name-nondirectory file)))
(current-buffer))
(get-buffer-create "*doom:scratch*"))))
(with-current-buffer buffer (with-current-buffer buffer
(when (and (functionp mode) (doom|persist-scratch-buffer))))
(not (eq major-mode mode)))
(funcall mode))
(when text
(insert text))
(run-hooks 'doom-scratch-buffer-hook)
(current-buffer))))
;;;###autoload ;;;###autoload
(defun doom/open-scratch-buffer (&optional arg) (add-hook 'kill-emacs-hook #'doom|persist-scratch-buffers)
"Opens a scratch pad window in the same major-mode.
If ARG (universal argument), then open a persistent scratch pad buffer. You'll
be prompted for its name, or to open a previously created. These are stored in
`doom-scratch-files-dir'.
If a region is active, copy its contents to the scratch pad." ;;
;;; Commands
;;;###autoload
(defun doom/open-scratch-buffer (&optional arg project-p)
"Opens the (persistent) scratch buffer in a popup.
If ARG, switch to it in the current window."
(interactive "P") (interactive "P")
(let (projectile-enable-caching) (let (projectile-enable-caching)
(funcall (funcall
doom-scratch-buffer-display-fn (if arg
#'switch-to-buffer
#'pop-to-buffer)
(doom-scratch-buffer (doom-scratch-buffer
(when arg
(if-let* ((file (read-file-name "Open scratch file > " doom-scratch-files-dir "scratch")))
file
(user-error "Aborting")))
(cond ((eq doom-scratch-buffer-major-mode t) (cond ((eq doom-scratch-buffer-major-mode t)
(unless (or buffer-read-only (unless (or buffer-read-only
(derived-mode-p 'special-mode) (derived-mode-p 'special-mode)
(string-match-p "^ ?\\*" (buffer-name))) (string-match-p "^ ?\\*" (buffer-name)))
major-mode)) major-mode))
((null doom-scratch-buffer-major-mode) nil) ((null doom-scratch-buffer-major-mode)
nil)
((symbolp doom-scratch-buffer-major-mode) ((symbolp doom-scratch-buffer-major-mode)
doom-scratch-buffer-major-mode)) doom-scratch-buffer-major-mode))
(and (region-active-p) default-directory
(buffer-substring-no-properties (when project-p
(region-beginning) (region-end))))))) (doom-project-name))))))
;;;###autoload ;;;###autoload
(defun doom/switch-to-scratch-buffer (&optional arg) (defun doom/open-project-scratch-buffer (&optional arg)
"Switches to a scratch pad buffer in the current window. "Opens the (persistent) project scratch buffer in a popup.
Otherwise, does exactly what `doom/open-scratch-buffer' does." If ARG, switch to it in the current window."
(interactive "P") (interactive "P")
(let ((doom-scratch-buffer-display-fn #'switch-to-buffer)) (doom/open-scratch-buffer arg 'project))
(doom/open-scratch-buffer arg)))
;;;###autoload ;;;###autoload
(defun doom/delete-scratch-files () (defun doom/revert-scratch-buffer ()
"Deletes all scratch buffers in `doom-scratch-files-dir'." "Revert scratch buffer to last persistent state."
(interactive) (interactive)
(dolist (file (directory-files doom-scratch-files-dir t "^[^.]" t)) (unless (string-match-p "^\\*doom:scratch" (buffer-name))
(delete-file file) (user-error "Not in a scratch buffer"))
(message "Deleted '%s'" (file-name-nondirectory file)))) (when (doom--load-persistent-scratch-buffer doom-scratch-current-project)
(message "Reloaded scratch buffer")))
;;;###autoload
(defun doom/delete-persistent-scratch-file (&optional arg)
"Deletes a scratch buffer file in `doom-scratch-dir'.
If prefix ARG, delete all persistent scratches."
(interactive)
(if arg
(progn
(delete-directory doom-scratch-dir t)
(message "Cleared %S" (abbreviate-file-name doom-scratch-dir)))
(make-directory doom-scratch-dir t)
(let ((file (read-file-name "Delete scratch file > " doom-scratch-dir "scratch")))
(if (not (file-exists-p file))
(message "%S does not exist" (abbreviate-file-name file))
(delete-file file)
(message "Successfully deleted %S" (abbreviate-file-name file))))))

View file

@ -735,6 +735,7 @@
:desc "Find other file" "o" #'projectile-find-other-file :desc "Find other file" "o" #'projectile-find-other-file
:desc "Switch project" "p" #'projectile-switch-project :desc "Switch project" "p" #'projectile-switch-project
:desc "Find recent project files" "r" #'projectile-recentf :desc "Find recent project files" "r" #'projectile-recentf
:desc "Scratch buffer" "s" #'doom/open-project-scratch-buffer
:desc "List project tasks" "t" #'+default/project-tasks :desc "List project tasks" "t" #'+default/project-tasks
(:prefix ("x" . "terminal") (:prefix ("x" . "terminal")
:desc "Open eshell in project" "e" #'projectile-run-eshell :desc "Open eshell in project" "e" #'projectile-run-eshell

View file

@ -136,7 +136,7 @@ prevent the popup(s) from messing up the UI (or vice versa)."
:slot -1 :vslot -2 :ttl 0) :slot -1 :vslot -2 :ttl 0)
("^\\*Compil\\(?:ation\\|e-Log\\)" ("^\\*Compil\\(?:ation\\|e-Log\\)"
:vslot -2 :size 0.3 :ttl nil :quit t) :vslot -2 :size 0.3 :ttl nil :quit t)
("^\\*\\(?:\\(?:doom:\\)?scratch\\|Messages\\)" ("^\\*\\(?:scratch\\|Messages\\)"
:autosave t :ttl nil) :autosave t :ttl nil)
("^\\*Man " ("^\\*Man "
:size 0.45 :vslot -3 :ttl 0 :quit t :select t) :size 0.45 :vslot -3 :ttl 0 :quit t :select t)