doomemacs/core/autoload/scratch.el
Henrik Lissner 8f9056906d
Rethink scratch buffer keybinds & commands
For non-evil users:

<leader> x    doom/open-scratch-buffer
<leader> X    doom/switch-to-scratch-buffer
<leader> p s  doom/open-project-scratch-buffer
<leader> p S  doom/switch-to-project-scratch-buffer

For evil users:

<leader> x    doom/open-scratch-buffer
<leader> b s  doom/open-scratch-buffer
<leader> b S  doom/switch-to-scratch-buffer
<leader> p s  doom/open-project-scratch-buffer
<leader> p S  doom/switch-to-project-scratch-buffer
2019-05-20 21:09:12 -04:00

162 lines
5.5 KiB
EmacsLisp

;;; core/autoload/scratch.el -*- lexical-binding: t; -*-
(defvar doom-scratch-default-file "__default"
"The default file name for a project-less scratch buffer.
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
following:
t Inherits the major mode of the last buffer you had selected.
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 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)))
;;
;;; Persistent scratch buffer
;;;###autoload
(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)))
;;;###autoload
(defun doom|persist-scratch-buffers ()
"Save all scratch buffers to `doom-scratch-dir'."
(setq doom-scratch-buffers (cl-delete-if-not #'buffer-live-p doom-scratch-buffers))
(dolist (buffer doom-scratch-buffers)
(with-current-buffer buffer
(doom|persist-scratch-buffer))))
;;;###autoload
(unless noninteractive
(add-hook 'kill-emacs-hook #'doom|persist-scratch-buffers))
;;
;;; Commands
;;;###autoload
(defun doom/open-scratch-buffer (&optional arg project-p)
"Opens the (persistent) scratch buffer in a popup.
If passed the prefix ARG, switch to it in the current window.
If PROJECT-P is non-nil, open a persistent scratch buffer associated with the
current project."
(interactive "P")
(let (projectile-enable-caching)
(funcall
(if arg
#'switch-to-buffer
#'pop-to-buffer)
(doom-scratch-buffer
(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)
((symbolp doom-scratch-buffer-major-mode)
doom-scratch-buffer-major-mode))
default-directory
(when project-p
(doom-project-name))))))
;;;###autoload
(defun doom/switch-to-scratch-buffer (&optional project-p)
"Like `doom/open-scratch-buffer', but switches to it in the current window."
(interactive)
(doom/open-scratch-buffer t))
;;;###autoload
(defun doom/open-project-scratch-buffer (&optional arg)
"Opens the (persistent) project scratch buffer in a popup.
If passed the prefix ARG, switch to it in the current window."
(interactive "P")
(doom/open-scratch-buffer arg 'project))
;;;###autoload
(defun doom/switch-to-project-scratch-buffer ()
"Like `doom/open-project-scratch-buffer', but switches to it in the current
window."
(interactive)
(doom/open-project-scratch-buffer t))
;;;###autoload
(defun doom/revert-scratch-buffer ()
"Revert scratch buffer to last persistent state."
(interactive)
(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))))))