;;; cider-selector.el --- Buffer selection command inspired by SLIME's selector -*- lexical-binding: t -*- ;; Copyright © 2012-2013 Tim King, Phil Hagelberg, Bozhidar Batsov ;; Copyright © 2013-2019 Bozhidar Batsov, Artur Malabarba and CIDER contributors ;; ;; Author: Tim King ;; Phil Hagelberg ;; Bozhidar Batsov ;; Artur Malabarba ;; Hugo Duncan ;; Steve Purcell ;; This program is free software: you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; This program is distributed in the hope that it will be useful, ;; but WITHOUT ANY WARRANTY; without even the implied warranty of ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License ;; along with this program. If not, see . ;; This file is not part of GNU Emacs. ;;; Commentary: ;; Buffer selection command inspired by SLIME's selector. ;;; Code: (require 'cider-client) (require 'cider-eval) (require 'cider-scratch) (require 'cider-profile) (defconst cider-selector-help-buffer "*CIDER Selector Help*" "The name of the selector's help buffer.") (defvar cider-selector-methods nil "List of buffer-selection methods for the `cider-selector' command. Each element is a list (KEY DESCRIPTION FUNCTION). DESCRIPTION is a one-line description of what the key selects.") (defvar cider-selector-other-window nil "If non-nil use `switch-to-buffer-other-window'. Not meant to be set by users. It's used internally by `cider-selector'.") (defun cider-selector--recently-visited-buffer (mode) "Return the most recently visited buffer, deriving its `major-mode' from MODE. Only considers buffers that are not already visible." (cl-loop for buffer in (buffer-list) when (and (with-current-buffer buffer (derived-mode-p mode)) ;; names starting with space are considered hidden by Emacs (not (string-match-p "^ " (buffer-name buffer))) (null (get-buffer-window buffer 'visible))) return buffer finally (error "Can't find unshown buffer in %S" mode))) ;;;###autoload (defun cider-selector (&optional other-window) "Select a new buffer by type, indicated by a single character. The user is prompted for a single character indicating the method by which to choose a new buffer. The `?' character describes the available methods. OTHER-WINDOW provides an optional target. See `def-cider-selector-method' for defining new methods." (interactive) (message "Select [%s]: " (apply #'string (mapcar #'car cider-selector-methods))) (let* ((cider-selector-other-window other-window) (ch (save-window-excursion (select-window (minibuffer-window)) (read-char))) (method (cl-find ch cider-selector-methods :key #'car))) (cond (method (funcall (cl-caddr method))) (t (message "No method for character: ?\\%c" ch) (ding) (sleep-for 1) (discard-input) (cider-selector))))) (defmacro def-cider-selector-method (key description &rest body) "Define a new `cider-select' buffer selection method. KEY is the key the user will enter to choose this method. DESCRIPTION is a one-line sentence describing how the method selects a buffer. BODY is a series of forms which are evaluated when the selector is chosen. The returned buffer is selected with `switch-to-buffer'." (let ((method `(lambda () (let ((buffer (progn ,@body))) (cond ((not (get-buffer buffer)) (message "No such buffer: %S" buffer) (ding)) ((get-buffer-window buffer) (select-window (get-buffer-window buffer))) (cider-selector-other-window (switch-to-buffer-other-window buffer)) (t (switch-to-buffer buffer))))))) `(setq cider-selector-methods (cl-sort (cons (list ,key ,description ,method) (cl-remove ,key cider-selector-methods :key #'car)) #'< :key #'car)))) (def-cider-selector-method ?? "Selector help buffer." (ignore-errors (kill-buffer cider-selector-help-buffer)) (with-current-buffer (get-buffer-create cider-selector-help-buffer) (insert "CIDER Selector Methods:\n\n") (cl-loop for (key line nil) in cider-selector-methods do (insert (format "%c:\t%s\n" key line))) (goto-char (point-min)) (help-mode) (display-buffer (current-buffer) t)) (cider-selector) (current-buffer)) (cl-pushnew (list ?4 "Select in other window" (lambda () (cider-selector t))) cider-selector-methods :key #'car) (def-cider-selector-method ?c "Most recently visited clojure-mode buffer." (cider-selector--recently-visited-buffer 'clojure-mode)) (def-cider-selector-method ?e "Most recently visited emacs-lisp-mode buffer." (cider-selector--recently-visited-buffer 'emacs-lisp-mode)) (def-cider-selector-method ?q "Abort." (top-level)) (def-cider-selector-method ?r "Current REPL buffer." (cider-current-repl)) (def-cider-selector-method ?m "Current connection's *nrepl-messages* buffer." (nrepl-messages-buffer (cider-current-repl))) (def-cider-selector-method ?x "*cider-error* buffer." cider-error-buffer) (def-cider-selector-method ?p "CIDER profiler buffer." cider-profile-buffer) (def-cider-selector-method ?d "*cider-doc* buffer." cider-doc-buffer) (def-cider-selector-method ?s "*cider-scratch* buffer." (cider-scratch-find-or-create-buffer)) (provide 'cider-selector) ;;; cider-selector.el ends here