;;; scratch.el --- Mode-specific scratch buffers ;; Author: Ian Eure ;; Version: 1.4 ;; Package-Version: 20220319.1705 ;; Package-Commit: f000648c9663833a76a8de9b1e78c99a9d698e48 ;; Package-Requires: ((emacs "25.1")) ;; URL: https://github.com/ieure/scratch-el ;; Keywords: convenience, tools, files ;; Copyright (c) 1999-2017, 2019, 2022 Ian Eure ;; All rights reserved. ;; Redistribution and use in source and binary forms, with or without ;; modification, are permitted provided that the following conditions ;; are met: ;; 1. Redistributions of source code must retain the above copyright ;; notice, this list of conditions and the following disclaimer. ;; 2. Redistributions in binary form must reproduce the above ;; copyright notice, this list of conditions and the following ;; disclaimer in the documentation and/or other materials provided ;; with the distribution. ;; THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' ;; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ;; TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A ;; PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR ;; CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ;; SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ;; LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF ;; USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ;; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ;; OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT ;; OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ;; SUCH DAMAGE. ;;; Commentary: ;; ;; Scratch ;; ======= ;; ;; Scratch is an extension to Emacs that enables one to create scratch ;; buffers that are in the same mode as the current buffer. This is ;; notably useful when working on code in some language; you may grab ;; code into a scratch buffer, and, by virtue of this extension, do so ;; using the Emacs formatting rules for that language. ;; ;; Scratch is available from MELPA. ;; ;; ;; Usage ;; ===== ;; ;; - `M-x scratch' Immediately create a scratch buffer with the same ;; major mode as the current buffer’s. If the region is active, copy ;; it to the scratch buffer. If a scratch buffer already exists, pop ;; to it (and do nothing with the region). ;; ;; - `C-u M-x scratch' Prompts for a major mode to create a scratch ;; buffer with. ;; ;; ;; Binding ;; ======= ;; ;; `C-c s' is a good mnemonic binding for scratch-el: ;; ;; ;; (define-key (current-global-map) "\C-cs" #'scratch) ;; ;; ;; Customization ;; ============= ;; ;; If you want to customize the behavior of all scratch buffers, you ;; can place hooks in `scratch-create-buffer-hook'. ;; ;; For per-mode customizations, you can add a hook to the mode, and ;; check `scratch-buffer' inside it. For example, to set a default ;; title on all `org-mode' scratch buffers, you could do: ;; ;; ;; (add-hook 'org-mode-hook ;; (lambda () ;; (when scratch-buffer ;; (save-excursion ;; (goto-char (point-min)) ;; (insert "#+TITLE: Scratch\n\n"))))) ;; ;;; History: ;; 1999 Ian Eure ;; Initial version. ;; 2010-08-16 Ian Eure ;; Broke out into its own file. ;; 2012-08-30 Ian Eure ;; Dump current region into new scratch buffer ;; 2017-05-23 Ian Eure ;; Set up *sql* buffers so they know about the inferior process they ;; were created from. ;; Add mappings for additional inferior modes. ;; Fix checkdoc & package-lint issues. ;; ;; 2019-03-13 Ian Eure (v1.3) ;; Add `scratch-create-buffer-hook', which runs any time a new ;; scratch buffer is created. ;; Fix an issue where SQL scratch buffers weren't linked to the SQLi ;; buffers they were created from. ;; Fix byte-compilation warnings. ;; Substantially refactor & update code. ;; ;; 2022-03-19 Ian Eure (v1.4) ;; Depend on cl-lib instead of cl. ;; Address some minor linter complaints. ;;; Code: (require 'cl-lib) (require 'sql) (require 'subr-x) (defgroup scratch nil "Scratch buffers." :prefix "scratch-" :group 'editing) (defcustom scratch-mode-alist '((erc-mode . fundamental-mode) (sql-interactive-mode . sql-mode) (shell-mode . sh-mode) (inferior-python-mode . python-mode) (inferior-emacs-lisp-mode . emacs-lisp-mode) (cider-repl-mode . clojure-mode) (inferior-tcl-mode . tcl-mode) (inferior-octave-mode . octave-mode)) "Alist of mappings from major modes to major modes for `scratch'. Some interactive modes don't lend themselves well to scratch buffers; this alist is used to change the mode used by `scratch' for those buffers." :type '(alist :key-type symbol :value-type symbol)) (defcustom scratch-create-buffer-hook nil "Hooks to run when creating a scratch buffer." :type 'cons) (defvar scratch--history nil "History of scratch buffers.") (defvar scratch--major-mode-list nil "List of major modes `scratch' may use. See `scratch-list-modes'.") (defvar scratch-parent nil "The parent buffer of this scratch buffer.") (make-variable-buffer-local 'scratch-parent) ;;;###autoload (defvar scratch-buffer nil "Non-nil if the current buffer is a scratch buffer.") (make-variable-buffer-local 'scratch-buffer) (defun scratch--list-modes () "List known major modes." (cl-loop for sym the symbols of obarray for name = (symbol-name sym) when (and (functionp sym) (not (member sym minor-mode-list)) (string-match "-mode$" name) (not (string-match "--" name))) collect (substring name 0 -5))) (defun scratch--link-sql () "Link a scratch buffer to a SQLi buffer. This sets up the scratch buffer so `sql-send-buffer' etc work as expected." (let ((product (with-current-buffer scratch-parent sql-product))) (setq sql-product product sql-buffer scratch-parent))) (defun scratch--link-buffers () "Link a parent and child buffer. When a scratch buffer is created from a mode for an inferior process, and has features which rely on knowing the inferior process, link them." (cond ((eq (with-current-buffer scratch-parent major-mode) 'sql-interactive-mode) (scratch--link-sql)) (t nil))) (defun scratch--buffer-querymode () "Return the mode to use for a new scratch buffer. When called with a prefix argument, prompt the user." (cond ;; Prompt user for mode (current-prefix-arg (intern (concat (completing-read "Mode: " (scratch--list-modes) nil t nil scratch--history) "-mode"))) ;; Overriden mode for ex. sql->sqli, shell->sh ((assoc major-mode scratch-mode-alist) (cdr (assoc major-mode scratch-mode-alist))) ;; Default is the current mode (t major-mode))) (defun scratch--create (mode name) "Create scratch buffer for mode MODE, with base name NAME. Returns the new buffer." (let ((scratch-buffer t) (parent (current-buffer)) (contents (when (region-active-p) (buffer-substring-no-properties (region-beginning) (region-end))))) (with-current-buffer (get-buffer-create name) (funcall mode) (when contents (save-excursion (insert contents))) (setq-local scratch-buffer t) (unless current-prefix-arg (setq-local scratch-parent parent) (scratch--link-buffers)) (run-hooks 'scratch-create-buffer-hook) (current-buffer)))) ;;;###autoload (defun scratch (mode) "Get a scratch buffer for the MODE. When called interactively with a prefix arg, prompt for the mode." (interactive (list (scratch--buffer-querymode))) (let* ((name (thread-last (symbol-name mode) (replace-regexp-in-string "-mode$" "") (format "*%s*"))) (buf (get-buffer name))) (pop-to-buffer (if (bufferp buf) buf ; Existing scratch buffer ;; New scratch buffer (scratch--create mode name))))) (provide 'scratch) ;;; scratch.el ends here