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; -*-
(defvar doom-scratch-files-dir (concat doom-etc-dir "scratch/")
"Where to store project scratch files, created by
`doom/open-project-scratch-buffer'.")
(defvar doom-scratch-default-file "__default"
"The default file name for a project-less scratch buffer.
(defvar doom-scratch-buffer-display-fn #'display-buffer
"The function to use to display the scratch buffer. Must accept one argument:
the buffer to display.")
Will be saved in `doom-scratch-dir'.")
(defvar doom-scratch-dir (concat doom-etc-dir "scratch")
"Where to save persistent scratch buffers.")
(defvar doom-scratch-buffer-major-mode nil
"What major mode to use in scratch buffers. This can be one of the
@ -16,77 +16,129 @@ following:
nil Uses `fundamental-mode'
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 ()
"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
(defun doom-scratch-buffer (&optional file mode text)
"Return a scratchpad buffer in major MODE with TEXT in it.
(defun doom|persist-scratch-buffer ()
"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."
(if file (setq file (file-truename file)))
(let ((buffer
(if file
(with-current-buffer (find-file-noselect file)
(rename-buffer (format "*doom:scratch (%s)*" (file-name-nondirectory file)))
(current-buffer))
(get-buffer-create "*doom:scratch*"))))
;;;###autoload
(defun doom|persist-scratch-buffers ()
"Save all scratch buffers to `doom-scratch-dir'."
(dolist (buffer (cl-delete-if-not #'buffer-live-p doom-scratch-buffers))
(with-current-buffer buffer
(when (and (functionp mode)
(not (eq major-mode mode)))
(funcall mode))
(when text
(insert text))
(run-hooks 'doom-scratch-buffer-hook)
(current-buffer))))
(doom|persist-scratch-buffer))))
;;;###autoload
(defun doom/open-scratch-buffer (&optional arg)
"Opens a scratch pad window in the same major-mode.
(add-hook 'kill-emacs-hook #'doom|persist-scratch-buffers)
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")
(let (projectile-enable-caching)
(funcall
doom-scratch-buffer-display-fn
(if arg
#'switch-to-buffer
#'pop-to-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)
(unless (or buffer-read-only
(derived-mode-p 'special-mode)
(string-match-p "^ ?\\*" (buffer-name)))
major-mode))
((null doom-scratch-buffer-major-mode) nil)
((null doom-scratch-buffer-major-mode)
nil)
((symbolp doom-scratch-buffer-major-mode)
doom-scratch-buffer-major-mode))
(and (region-active-p)
(buffer-substring-no-properties
(region-beginning) (region-end)))))))
default-directory
(when project-p
(doom-project-name))))))
;;;###autoload
(defun doom/switch-to-scratch-buffer (&optional arg)
"Switches to a scratch pad buffer in the current window.
(defun doom/open-project-scratch-buffer (&optional arg)
"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")
(let ((doom-scratch-buffer-display-fn #'switch-to-buffer))
(doom/open-scratch-buffer arg)))
(doom/open-scratch-buffer arg 'project))
;;;###autoload
(defun doom/delete-scratch-files ()
"Deletes all scratch buffers in `doom-scratch-files-dir'."
(defun doom/revert-scratch-buffer ()
"Revert scratch buffer to last persistent state."
(interactive)
(dolist (file (directory-files doom-scratch-files-dir t "^[^.]" t))
(delete-file file)
(message "Deleted '%s'" (file-name-nondirectory file))))
(unless (string-match-p "^\\*doom:scratch" (buffer-name))
(user-error "Not in a scratch buffer"))
(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 "Switch project" "p" #'projectile-switch-project
: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
(:prefix ("x" . "terminal")
: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)
("^\\*Compil\\(?:ation\\|e-Log\\)"
:vslot -2 :size 0.3 :ttl nil :quit t)
("^\\*\\(?:\\(?:doom:\\)?scratch\\|Messages\\)"
("^\\*\\(?:scratch\\|Messages\\)"
:autosave t :ttl nil)
("^\\*Man "
:size 0.45 :vslot -3 :ttl 0 :quit t :select t)