|
|
- ;;; slime-repl.el ---
- ;;
- ;; Original Author: Helmut Eller
- ;; Contributors: too many to mention
- ;; License: GNU GPL (same license as Emacs)
- ;;
- ;;; Description:
- ;;
-
- ;;
- ;;; Installation:
- ;;
- ;; Call slime-setup and include 'slime-repl as argument:
- ;;
- ;; (slime-setup '(slime-repl [others conribs ...]))
- ;;
- (require 'slime)
- (require 'slime-parse)
- (require 'cl-lib)
- (eval-when-compile (require 'cl)) ; slime-def-connection-var, which
- ; expands to defsetf not in cl-lib
-
- (define-slime-contrib slime-repl
- "Read-Eval-Print Loop written in Emacs Lisp.
-
- This contrib implements a Lisp Listener along with some niceties like
- a persistent history and various \"shortcut\" commands. Nothing here
- depends on comint.el; I/O is multiplexed over SLIME's socket.
-
- This used to be the default REPL for SLIME, but it was hard to
- maintain."
- (:authors "too many to mention")
- (:license "GPL")
- (:on-load
- (slime-repl-add-hooks)
- (setq slime-find-buffer-package-function 'slime-repl-find-buffer-package))
- (:on-unload (slime-repl-remove-hooks))
- (:swank-dependencies swank-repl))
-
- ;;;;; slime-repl
-
- (defgroup slime-repl nil
- "The Read-Eval-Print Loop (*slime-repl* buffer)."
- :prefix "slime-repl-"
- :group 'slime)
-
- (defcustom slime-repl-shortcut-dispatch-char ?\,
- "Character used to distinguish repl commands from lisp forms."
- :type '(character)
- :group 'slime-repl)
-
- (defcustom slime-repl-only-save-lisp-buffers t
- "When T we only attempt to save lisp-mode file buffers. When
- NIL slime will attempt to save all buffers (as per
- save-some-buffers). This applies to all ASDF related repl
- shortcuts."
- :type '(boolean)
- :group 'slime-repl)
-
- (defcustom slime-repl-auto-right-margin nil
- "When T we bind CL:*PRINT-RIGHT-MARGIN* to the width of the
- current repl's (as per slime-output-buffer) window."
- :type '(boolean)
- :group 'slime-repl)
-
- (defface slime-repl-prompt-face
- '((t (:inherit font-lock-keyword-face)))
- "Face for the prompt in the SLIME REPL."
- :group 'slime-repl)
-
- (defface slime-repl-output-face
- '((t (:inherit font-lock-string-face)))
- "Face for Lisp output in the SLIME REPL."
- :group 'slime-repl)
-
- (defface slime-repl-input-face
- '((t (:bold t)))
- "Face for previous input in the SLIME REPL."
- :group 'slime-repl)
-
- (defface slime-repl-result-face
- '((t ()))
- "Face for the result of an evaluation in the SLIME REPL."
- :group 'slime-repl)
-
- (defcustom slime-repl-history-file "~/.slime-history.eld"
- "File to save the persistent REPL history to."
- :type 'string
- :group 'slime-repl)
-
- (defcustom slime-repl-history-size 200
- "*Maximum number of lines for persistent REPL history."
- :type 'integer
- :group 'slime-repl)
-
- (defcustom slime-repl-history-file-coding-system
- (cond ((slime-find-coding-system 'utf-8-unix) 'utf-8-unix)
- (t slime-net-coding-system))
- "*The coding system for the history file."
- :type 'symbol
- :group 'slime-repl)
-
-
- ;; dummy defvar for compiler
- (defvar slime-repl-read-mode)
-
- (defun slime-reading-p ()
- "True if Lisp is currently reading input from the REPL."
- (with-current-buffer (slime-output-buffer)
- slime-repl-read-mode))
-
- ;;;; Stream output
-
- (slime-def-connection-var slime-connection-output-buffer nil
- "The buffer for the REPL. May be nil or a dead buffer.")
-
- (make-variable-buffer-local
- (defvar slime-output-start nil
- "Marker for the start of the output for the evaluation."))
-
- (make-variable-buffer-local
- (defvar slime-output-end nil
- "Marker for end of output. New output is inserted at this mark."))
-
- ;; dummy definitions for the compiler
- (defvar slime-repl-package-stack)
- (defvar slime-repl-directory-stack)
- (defvar slime-repl-input-start-mark)
- (defvar slime-repl-prompt-start-mark)
-
- (defvar slime-repl-history-use-mark nil
- "A non-nil value means that history will be replaced from the mark.
-
- Instead of replacing form input-start, look up history and replace input
- from the mark. Calling 'slime-repl-previous-input',
- 'slime-repl-previous-matching-input' or their -next counterparts with a prefix
- argument sets this variable for the duration of one history lookup.")
-
- (defun slime-repl-history-yank-start ()
- "The position which 'slime-repl-previous-input' will replace from.
-
- When 'slime-repl-history-use-mark' is non-nil, and (mark) is after the current
- input start, return it. Otherwise, return 'slime-repl-input-start-mark'."
- (if (and slime-repl-history-use-mark (mark))
- (max (mark) slime-repl-input-start-mark)
- slime-repl-input-start-mark))
-
- (defun slime-output-buffer (&optional noprompt)
- "Return the output buffer, create it if necessary."
- (let ((buffer (slime-connection-output-buffer)))
- (or (if (buffer-live-p buffer) buffer)
- (setf (slime-connection-output-buffer)
- (let ((connection (slime-connection)))
- (with-current-buffer (slime-repl-buffer t connection)
- (unless (eq major-mode 'slime-repl-mode)
- (slime-repl-mode))
- (setq slime-buffer-connection connection)
- (setq slime-buffer-package (slime-lisp-package connection))
- (slime-reset-repl-markers)
- (unless noprompt
- (slime-repl-insert-prompt))
- (current-buffer)))))))
-
- (defvar slime-repl-banner-function 'slime-repl-insert-banner)
-
- (defun slime-repl-update-banner ()
- (funcall slime-repl-banner-function)
- (slime-move-point (point-max))
- (slime-mark-output-start)
- (slime-mark-input-start)
- (slime-repl-insert-prompt))
-
- (defun slime-repl-insert-banner ()
- (when (zerop (buffer-size))
- (let ((welcome (concat "; SLIME " slime-version)))
- (insert welcome))))
-
- (defun slime-init-output-buffer (connection)
- (with-current-buffer (slime-output-buffer t)
- (setq slime-buffer-connection connection
- slime-repl-directory-stack '()
- slime-repl-package-stack '())
- (slime-repl-update-banner)))
-
- (defun slime-display-output-buffer ()
- "Display the output buffer and scroll to bottom."
- (with-current-buffer (slime-output-buffer)
- (goto-char (point-max))
- (unless (get-buffer-window (current-buffer) t)
- (display-buffer (current-buffer) t))
- (slime-repl-show-maximum-output)))
-
- (defun slime-output-filter (process string)
- (with-current-buffer (process-buffer process)
- (when (and (plusp (length string))
- (eq (process-status slime-buffer-connection) 'open))
- (slime-write-string string))))
-
- (defvar slime-open-stream-hooks)
-
- (defun slime-open-stream-to-lisp (port coding-system)
- (let ((stream (open-network-stream "*lisp-output-stream*"
- (slime-with-connection-buffer ()
- (current-buffer))
- (car (process-contact (slime-connection)))
- port))
- (emacs-coding-system (car (cl-find coding-system
- slime-net-valid-coding-systems
- :key #'cl-third))))
- (slime-set-query-on-exit-flag stream)
- (set-process-filter stream 'slime-output-filter)
- (set-process-coding-system stream emacs-coding-system emacs-coding-system)
- (let ((secret (slime-secret)))
- (when secret
- (slime-net-send secret stream)))
- (run-hook-with-args 'slime-open-stream-hooks stream)
- stream))
-
- (defun slime-io-speed-test (&optional profile)
- "A simple minded benchmark for stream performance.
- If a prefix argument is given, instrument the slime package for
- profiling before running the benchmark."
- (interactive "P")
- (eval-and-compile
- (require 'elp))
- (elp-reset-all)
- (elp-restore-all)
- (load "slime.el")
- ;;(byte-compile-file "slime-net.el" t)
- ;;(setq slime-log-events nil)
- (setq slime-enable-evaluate-in-emacs t)
- ;;(setq slime-repl-enable-presentations nil)
- (when profile
- (elp-instrument-package "slime-"))
- (kill-buffer (slime-output-buffer))
- (switch-to-buffer (slime-output-buffer))
- (delete-other-windows)
- (sit-for 0)
- (slime-repl-send-string "(swank:io-speed-test 4000 1)")
- (let ((proc (slime-inferior-process)))
- (when proc
- (display-buffer (process-buffer proc) t)
- (goto-char (point-max)))))
-
- (defvar slime-write-string-function 'slime-repl-write-string)
-
- (defun slime-write-string (string &optional target)
- "Insert STRING in the REPL buffer or some other TARGET.
- If TARGET is nil, insert STRING as regular process
- output. If TARGET is :repl-result, insert STRING as the result of the
- evaluation. Other values of TARGET map to an Emacs marker via the
- hashtable `slime-output-target-to-marker'; output is inserted at this marker."
- (funcall slime-write-string-function string target))
-
- (defun slime-repl-write-string (string &optional target)
- (case target
- ((nil) (slime-repl-emit string))
- (:repl-result (slime-repl-emit-result string t))
- (t (slime-repl-emit-to-target string target))))
-
- (defvar slime-repl-popup-on-output nil
- "Display the output buffer when some output is written.
- This is set to nil after displaying the buffer.")
-
- (defmacro slime-save-marker (marker &rest body)
- (declare (debug (sexp &rest form)))
- (let ((pos (cl-gensym "pos")))
- `(let ((,pos (marker-position ,marker)))
- (prog1 (progn . ,body)
- (set-marker ,marker ,pos)))))
-
- (put 'slime-save-marker 'lisp-indent-function 1)
-
- (defun slime-repl-emit (string)
- ;; insert the string STRING in the output buffer
- (with-current-buffer (slime-output-buffer)
- (save-excursion
- (goto-char slime-output-end)
- (slime-save-marker slime-output-start
- (slime-propertize-region '(face slime-repl-output-face
- slime-repl-output t
- rear-nonsticky (face))
- (let ((inhibit-read-only t))
- (insert-before-markers string)
- (when (and (= (point) slime-repl-prompt-start-mark)
- (not (bolp)))
- (insert-before-markers "\n")
- (set-marker slime-output-end (1- (point))))))))
- (when slime-repl-popup-on-output
- (setq slime-repl-popup-on-output nil)
- (display-buffer (current-buffer)))
- (slime-repl-show-maximum-output)))
-
- (defun slime-repl-emit-result (string &optional bol)
- ;; insert STRING and mark it as evaluation result
- (with-current-buffer (slime-output-buffer)
- (save-excursion
- (goto-char slime-repl-input-start-mark)
- (slime-save-marker slime-output-start
- (goto-char slime-repl-input-start-mark)
- (when (and bol (not (bolp))) (insert-before-markers-and-inherit "\n"))
- (slime-save-marker slime-output-end
- (slime-propertize-region `(face slime-repl-result-face
- rear-nonsticky (face))
- (insert-before-markers string)))
- (set-marker slime-output-end (point))))
- (slime-repl-show-maximum-output)))
-
- (defvar slime-last-output-target-id 0
- "The last integer we used as a TARGET id.")
-
- (defun slime-repl-emit-to-target (string target)
- "Insert STRING at target TARGET.
- See `slime-output-target-to-marker'."
- (let* ((marker (slime-repl-output-target-marker target))
- (buffer (and marker (marker-buffer marker))))
- (when buffer
- (with-current-buffer buffer
- (save-excursion
- ;; Insert STRING at MARKER, then move MARKER behind
- ;; the insertion.
- (goto-char marker)
- (insert-before-markers string)
- (set-marker marker (point)))))))
-
- (defun slime-repl-output-target-marker (target)
- (case target
- ((nil)
- (with-current-buffer (slime-output-buffer)
- slime-output-end))
- (:repl-result
- (with-current-buffer (slime-output-buffer)
- slime-repl-input-start-mark))
- (t
- (slime-output-target-marker target))))
-
-
- (defun slime-switch-to-output-buffer ()
- "Select the output buffer, when possible in an existing window.
-
- Hint: You can use `display-buffer-reuse-frames' and
- `special-display-buffer-names' to customize the frame in which
- the buffer should appear."
- (interactive)
- (pop-to-buffer (slime-output-buffer))
- (goto-char (point-max)))
-
- ;;;; REPL
- ;;
- ;; The REPL uses some markers to separate input from output. The
- ;; usual configuration is as follows:
- ;;
- ;; ... output ... ... result ... prompt> ... input ...
- ;; ^ ^ ^ ^ ^
- ;; output-start output-end prompt-start input-start point-max
- ;;
- ;; input-start is a right inserting marker, because
- ;; we want it to stay behind when the user inserts text.
- ;;
- ;; We maintain the following invariant:
- ;;
- ;; output-start <= output-end <= input-start.
- ;;
- ;; This invariant is important, because we must be prepared for
- ;; asynchronous output and asynchronous reads. ("Asynchronous" means,
- ;; triggered by Lisp and not by Emacs.)
- ;;
- ;; All output is inserted at the output-end marker. Some care must be
- ;; taken when output-end and input-start are at the same position: if
- ;; we insert at that point, we must move the right markers. We should
- ;; also not leave (window-)point in the middle of the new output. The
- ;; idiom we use is a combination to slime-save-marker,
- ;; insert-before-markers, and manually updating window-point
- ;; afterwards.
- ;;
- ;; A "synchronous" evaluation request proceeds as follows: the user
- ;; inserts some text between input-start and point-max and then hits
- ;; return. We send that region to Lisp, move the output and input
- ;; makers to the line after the input and wait. When we receive the
- ;; result, we insert it together with a prompt between the output-end
- ;; and input-start mark. See `slime-repl-insert-prompt'.
- ;;
- ;; It is possible that some output for such an evaluation request
- ;; arrives after the result. This output is inserted before the
- ;; result (and before the prompt).
- ;;
- ;; If we are in "reading" state, e.g., during a call to Y-OR-N-P,
- ;; there is no prompt between output-end and input-start.
- ;;
-
- ;; FIXME: slime-lisp-package should be local in a REPL buffer
- (slime-def-connection-var slime-lisp-package
- "COMMON-LISP-USER"
- "The current package name of the Superior lisp.
- This is automatically synchronized from Lisp.")
-
- (slime-def-connection-var slime-lisp-package-prompt-string
- "CL-USER"
- "The current package name of the Superior lisp.
- This is automatically synchronized from Lisp.")
-
- (slime-make-variables-buffer-local
- (defvar slime-repl-package-stack nil
- "The stack of packages visited in this repl.")
-
- (defvar slime-repl-directory-stack nil
- "The stack of default directories associated with this repl.")
-
- (defvar slime-repl-prompt-start-mark)
- (defvar slime-repl-input-start-mark)
- (defvar slime-repl-old-input-counter 0
- "Counter used to generate unique `slime-repl-old-input' properties.
- This property value must be unique to avoid having adjacent inputs be
- joined together."))
-
- (defun slime-reset-repl-markers ()
- (dolist (markname '(slime-output-start
- slime-output-end
- slime-repl-prompt-start-mark
- slime-repl-input-start-mark))
- (set markname (make-marker))
- (set-marker (symbol-value markname) (point))))
-
- ;;;;; REPL mode setup
-
- (defvar slime-repl-mode-map
- (let ((map (make-sparse-keymap)))
- (set-keymap-parent map lisp-mode-map)
- map))
-
- (slime-define-keys slime-prefix-map
- ("\C-z" 'slime-switch-to-output-buffer)
- ("\M-p" 'slime-repl-set-package))
-
- (slime-define-keys slime-mode-map
- ("\C-c~" 'slime-sync-package-and-default-directory)
- ("\C-c\C-y" 'slime-call-defun)
- ("\C-c\C-j" 'slime-eval-last-expression-in-repl))
-
- (slime-define-keys slime-connection-list-mode-map
- ((kbd "RET") 'slime-goto-connection)
- ([return] 'slime-goto-connection))
-
- (slime-define-keys slime-repl-mode-map
- ("\C-m" 'slime-repl-return)
- ([return] 'slime-repl-return)
- ("\C-j" 'slime-repl-newline-and-indent)
- ("\C-\M-m" 'slime-repl-closing-return)
- ([(control return)] 'slime-repl-closing-return)
- ("\M-p" 'slime-repl-previous-input)
- ((kbd "C-<up>") 'slime-repl-backward-input)
- ("\M-n" 'slime-repl-next-input)
- ((kbd "C-<down>") 'slime-repl-forward-input)
- ("\M-r" 'slime-repl-previous-matching-input)
- ("\M-s" 'slime-repl-next-matching-input)
- ("\C-c\C-c" 'slime-interrupt)
- (" " 'slime-space)
- ((string slime-repl-shortcut-dispatch-char) 'slime-handle-repl-shortcut)
- ("\C-c\C-o" 'slime-repl-clear-output)
- ("\C-c\M-o" 'slime-repl-clear-buffer)
- ("\C-c\C-u" 'slime-repl-kill-input)
- ("\C-c\C-n" 'slime-repl-next-prompt)
- ("\C-c\C-p" 'slime-repl-previous-prompt)
- ("\C-c\C-z" 'slime-nop)
- ("\C-cI" 'slime-repl-inspect)
- ("\C-x\C-e" 'slime-eval-last-expression))
-
- (slime-define-keys slime-inspector-mode-map
- ((kbd "M-RET") 'slime-inspector-copy-down-to-repl))
-
- (slime-define-keys sldb-mode-map
- ("\C-y" 'sldb-insert-frame-call-to-repl)
- ((kbd "M-RET") 'sldb-copy-down-to-repl))
-
- (def-slime-selector-method ?r
- "SLIME Read-Eval-Print-Loop."
- (slime-output-buffer))
-
- (define-minor-mode slime-repl-map-mode
- "Minor mode which makes slime-repl-mode-map available.
- \\{slime-repl-mode-map}"
- nil
- nil
- slime-repl-mode-map)
-
- (defun slime-repl-mode ()
- "Major mode for interacting with a superior Lisp.
- \\{slime-repl-mode-map}"
- (interactive)
- (kill-all-local-variables)
- (setq major-mode 'slime-repl-mode)
- (slime-editing-mode 1)
- (slime-repl-map-mode 1)
- (lisp-mode-variables t)
- (set (make-local-variable 'lisp-indent-function)
- 'common-lisp-indent-function)
- (slime-setup-completion)
- (set (make-local-variable 'tab-always-indent) 'complete)
- (setq font-lock-defaults nil)
- (setq mode-name "REPL")
- (setq slime-current-thread :repl-thread)
- (set (make-local-variable 'scroll-conservatively) 20)
- (set (make-local-variable 'scroll-margin) 0)
- (when slime-repl-history-file
- (slime-repl-safe-load-history)
- (add-hook 'kill-buffer-hook
- 'slime-repl-safe-save-merged-history
- 'append t))
- (add-hook 'kill-emacs-hook 'slime-repl-save-all-histories)
- ;; At the REPL, we define beginning-of-defun and end-of-defun to be
- ;; the start of the previous prompt or next prompt respectively.
- ;; Notice the interplay with SLIME-REPL-BEGINNING-OF-DEFUN.
- (set (make-local-variable 'beginning-of-defun-function)
- 'slime-repl-mode-beginning-of-defun)
- (set (make-local-variable 'end-of-defun-function)
- 'slime-repl-mode-end-of-defun)
- (run-mode-hooks 'slime-repl-mode-hook))
-
- (defun slime-repl-buffer (&optional create connection)
- "Get the REPL buffer for the current connection; optionally create."
- (funcall (if create #'get-buffer-create #'get-buffer)
- (format "*slime-repl %s*" (slime-connection-name connection))))
-
- (defun slime-repl ()
- (interactive)
- (slime-switch-to-output-buffer)
- (current-buffer))
-
- (defun slime-repl-mode-beginning-of-defun (&optional arg)
- (if (and arg (< arg 0))
- (slime-repl-mode-end-of-defun (- arg))
- (dotimes (i (or arg 1))
- (slime-repl-previous-prompt))))
-
- (defun slime-repl-mode-end-of-defun (&optional arg)
- (if (and arg (< arg 0))
- (slime-repl-mode-beginning-of-defun (- arg))
- (dotimes (i (or arg 1))
- (slime-repl-next-prompt))))
-
- (defun slime-repl-send-string (string &optional command-string)
- (cond (slime-repl-read-mode
- (slime-repl-return-string string))
- (t (slime-repl-eval-string string))))
-
- (defun slime-repl-eval-string (string)
- (slime-rex ()
- ((if slime-repl-auto-right-margin
- `(swank-repl:listener-eval
- ,string
- :window-width
- ,(with-current-buffer (slime-output-buffer)
- (window-width)))
- `(swank-repl:listener-eval ,string))
- (slime-lisp-package))
- ((:ok result)
- (slime-repl-insert-result result))
- ((:abort condition)
- (slime-repl-show-abort condition))))
-
- (defun slime-repl-insert-result (result)
- (with-current-buffer (slime-output-buffer)
- (save-excursion
- (when result
- (slime-dcase result
- ((:values &rest strings)
- (cond ((null strings)
- (slime-repl-emit-result "; No value\n" t))
- (t
- (dolist (s strings)
- (slime-repl-emit-result s t)))))))
- (slime-repl-insert-prompt))
- (slime-repl-show-maximum-output)))
-
- (defun slime-repl-show-abort (condition)
- (with-current-buffer (slime-output-buffer)
- (save-excursion
- (slime-save-marker slime-output-start
- (slime-save-marker slime-output-end
- (goto-char slime-output-end)
- (insert-before-markers (format "; Evaluation aborted on %s.\n"
- condition))
- (slime-repl-insert-prompt))))
- (slime-repl-show-maximum-output)))
-
- (defvar slime-repl-suppress-prompt nil
- "Supresses Slime REPL prompt when bound to T.")
-
- (defun slime-repl-insert-prompt ()
- "Insert the prompt (before markers!).
- Set point after the prompt.
- Return the position of the prompt beginning.
-
- If `slime-repl-suppress-prompt' is true, does nothing and returns nil."
- (goto-char slime-repl-input-start-mark)
- (unless slime-repl-suppress-prompt
- (slime-save-marker slime-output-start
- (slime-save-marker slime-output-end
- (unless (bolp) (insert-before-markers "\n"))
- (let ((prompt-start (point))
- (prompt (format "%s> " (slime-lisp-package-prompt-string))))
- (slime-propertize-region
- '(face slime-repl-prompt-face
- read-only t slime-repl-prompt t
- rear-nonsticky t front-sticky (read-only)
- inhibit-line-move-field-capture t
- field output)
- (insert-before-markers prompt))
- (set-marker slime-repl-prompt-start-mark prompt-start)
- (setq buffer-undo-list nil)
- prompt-start)))))
-
- (defun slime-repl-show-maximum-output ()
- "Put the end of the buffer at the bottom of the window."
- (when (eobp)
- (let ((win (if (eq (window-buffer) (current-buffer))
- (selected-window)
- (get-buffer-window (current-buffer) t))))
- (when win
- (with-selected-window win
- (set-window-point win (point-max))
- (recenter -1))))))
-
- (defvar slime-repl-current-input-hooks)
-
- (defun slime-repl-current-input (&optional until-point-p)
- "Return the current input as string.
- The input is the region from after the last prompt to the end of
- buffer."
- (or (run-hook-with-args-until-success 'slime-repl-current-input-hooks
- until-point-p)
- (buffer-substring-no-properties (slime-repl-history-yank-start)
- (if until-point-p
- (point)
- (point-max)))))
-
- (defun slime-property-position (text-property &optional object)
- "Return the first position of TEXT-PROPERTY, or nil."
- (if (get-text-property 0 text-property object)
- 0
- (next-single-property-change 0 text-property object)))
-
- (defun slime-mark-input-start ()
- (set-marker slime-repl-input-start-mark (point) (current-buffer)))
-
- (defun slime-mark-output-start ()
- (set-marker slime-output-start (point))
- (set-marker slime-output-end (point)))
-
- (defun slime-mark-output-end ()
- ;; Don't put slime-repl-output-face again; it would remove the
- ;; special presentation face, for instance in the SBCL inspector.
- (add-text-properties slime-output-start slime-output-end
- '(;;face slime-repl-output-face
- rear-nonsticky (face))))
-
- (defun slime-preserve-zmacs-region ()
- "In XEmacs, ensure that the zmacs-region stays active after this command."
- (when (boundp 'zmacs-region-stays)
- (set 'zmacs-region-stays t)))
-
- (defun slime-repl-in-input-area-p ()
- (<= slime-repl-input-start-mark (point)))
-
- (defun slime-repl-at-prompt-start-p ()
- ;; This will not work on non-current prompts.
- (= (point) slime-repl-input-start-mark))
-
- (defun slime-repl-beginning-of-defun ()
- "Move to beginning of defun."
- (interactive)
- ;; We call BEGINNING-OF-DEFUN if we're at the start of a prompt
- ;; already, to trigger SLIME-REPL-MODE-BEGINNING-OF-DEFUN by means
- ;; of the locally bound BEGINNING-OF-DEFUN-FUNCTION, in order to
- ;; jump to the start of the previous prompt.
- (if (and (not (slime-repl-at-prompt-start-p))
- (slime-repl-in-input-area-p))
- (goto-char slime-repl-input-start-mark)
- (beginning-of-defun))
- t)
-
- ;; FIXME: this looks very strange
- (defun slime-repl-end-of-defun ()
- "Move to next of defun."
- (interactive)
- ;; C.f. SLIME-REPL-BEGINNING-OF-DEFUN.
- (if (and (not (= (point) (point-max)))
- (slime-repl-in-input-area-p))
- (goto-char (point-max))
- (end-of-defun))
- t)
-
- (defun slime-repl-previous-prompt ()
- "Move backward to the previous prompt."
- (interactive)
- (slime-repl-find-prompt t))
-
- (defun slime-repl-next-prompt ()
- "Move forward to the next prompt."
- (interactive)
- (slime-repl-find-prompt))
-
- (defun slime-repl-find-prompt (&optional backward)
- (let ((origin (point))
- (prop 'slime-repl-prompt))
- (while (progn
- (slime-search-property-change prop backward)
- (not (or (slime-end-of-proprange-p prop) (bobp) (eobp)))))
- (unless (slime-end-of-proprange-p prop)
- (goto-char origin))))
-
- (defun slime-search-property-change (prop &optional backward)
- (cond (backward
- (goto-char (or (previous-single-char-property-change (point) prop)
- (point-min))))
- (t
- (goto-char (or (next-single-char-property-change (point) prop)
- (point-max))))))
-
- (defun slime-end-of-proprange-p (property)
- (and (get-char-property (max 1 (1- (point))) property)
- (not (get-char-property (point) property))))
-
- (defvar slime-repl-return-hooks)
-
- (defun slime-repl-return (&optional end-of-input)
- "Evaluate the current input string, or insert a newline.
- Send the current input only if a whole expression has been entered,
- i.e. the parenthesis are matched.
-
- With prefix argument send the input even if the parenthesis are not
- balanced."
- (interactive "P")
- (slime-check-connected)
- (cond (end-of-input
- (slime-repl-send-input))
- (slime-repl-read-mode ; bad style?
- (slime-repl-send-input t))
- ((and (get-text-property (point) 'slime-repl-old-input)
- (< (point) slime-repl-input-start-mark))
- (slime-repl-grab-old-input end-of-input)
- (slime-repl-recenter-if-needed))
- ((run-hook-with-args-until-success 'slime-repl-return-hooks end-of-input))
- ((slime-input-complete-p slime-repl-input-start-mark (point-max))
- (slime-repl-send-input t))
- (t
- (slime-repl-newline-and-indent)
- (message "[input not complete]"))))
-
- (defun slime-repl-recenter-if-needed ()
- "Make sure that (point) is visible."
- (unless (pos-visible-in-window-p (point-max))
- (save-excursion
- (goto-char (point-max))
- (recenter -1))))
-
- (defun slime-repl-send-input (&optional newline)
- "Goto to the end of the input and send the current input.
- If NEWLINE is true then add a newline at the end of the input."
- (unless (slime-repl-in-input-area-p)
- (error "No input at point."))
- (goto-char (point-max))
- (let ((end (point))) ; end of input, without the newline
- (slime-repl-add-to-input-history
- (buffer-substring slime-repl-input-start-mark end))
- (when newline
- (insert "\n")
- (slime-repl-show-maximum-output))
- (let ((inhibit-modification-hooks t))
- (add-text-properties slime-repl-input-start-mark
- (point)
- `(slime-repl-old-input
- ,(incf slime-repl-old-input-counter))))
- (let ((overlay (make-overlay slime-repl-input-start-mark end)))
- ;; These properties are on an overlay so that they won't be taken
- ;; by kill/yank.
- (overlay-put overlay 'face 'slime-repl-input-face)))
- (let ((input (slime-repl-current-input)))
- (goto-char (point-max))
- (slime-mark-input-start)
- (slime-mark-output-start)
- (slime-repl-send-string input)))
-
- (defun slime-repl-grab-old-input (replace)
- "Resend the old REPL input at point.
- If replace is non-nil the current input is replaced with the old
- input; otherwise the new input is appended. The old input has the
- text property `slime-repl-old-input'."
- (multiple-value-bind (beg end) (slime-property-bounds 'slime-repl-old-input)
- (let ((old-input (buffer-substring beg end)) ;;preserve
- ;;properties, they will be removed later
- (offset (- (point) beg)))
- ;; Append the old input or replace the current input
- (cond (replace (goto-char slime-repl-input-start-mark))
- (t (goto-char (point-max))
- (unless (eq (char-before) ?\ )
- (insert " "))))
- (delete-region (point) (point-max))
- (save-excursion
- (insert old-input)
- (when (equal (char-before) ?\n)
- (delete-char -1)))
- (forward-char offset))))
-
- (defun slime-repl-closing-return ()
- "Evaluate the current input string after closing all open lists."
- (interactive)
- (goto-char (point-max))
- (save-restriction
- (narrow-to-region slime-repl-input-start-mark (point))
- (while (ignore-errors (save-excursion (backward-up-list 1)) t)
- (insert ")")))
- (slime-repl-return))
-
- (defun slime-repl-newline-and-indent ()
- "Insert a newline, then indent the next line.
- Restrict the buffer from the prompt for indentation, to avoid being
- confused by strange characters (like unmatched quotes) appearing
- earlier in the buffer."
- (interactive)
- (save-restriction
- (narrow-to-region slime-repl-prompt-start-mark (point-max))
- (insert "\n")
- (lisp-indent-line)))
-
- (defun slime-repl-delete-current-input ()
- "Delete all text from the prompt."
- (interactive)
- (delete-region (slime-repl-history-yank-start) (point-max)))
-
- (defun slime-eval-last-expression-in-repl (prefix)
- "Evaluates last expression in the Slime REPL.
-
- Switches REPL to current package of the source buffer for the duration. If
- used with a prefix argument (C-u), doesn't switch back afterwards."
- (interactive "P")
- (let ((expr (slime-last-expression))
- (buffer-name (buffer-name (current-buffer)))
- (new-package (slime-current-package))
- (old-package (slime-lisp-package))
- (slime-repl-suppress-prompt t)
- (yank-back nil))
- (with-current-buffer (slime-output-buffer)
- (unless (eq (current-buffer) (window-buffer))
- (pop-to-buffer (current-buffer) t))
- (goto-char (point-max))
- ;; Kill pending input in the REPL
- (when (< (marker-position slime-repl-input-start-mark) (point))
- (kill-region slime-repl-input-start-mark (point))
- (setq yank-back t))
- (unwind-protect
- (progn
- (insert-before-markers (format "\n;;; from %s\n" buffer-name))
- (when new-package
- (slime-repl-set-package new-package))
- (let ((slime-repl-suppress-prompt nil))
- (slime-repl-insert-prompt))
- (insert expr)
- (slime-repl-return))
- (unless (or prefix (equal (slime-lisp-package) old-package))
- ;; Switch back.
- (slime-repl-set-package old-package)
- (let ((slime-repl-suppress-prompt nil))
- (slime-repl-insert-prompt))))
- ;; Put pending input back.
- (when yank-back
- (yank)))))
-
- (defun slime-repl-kill-input ()
- "Kill all text from the prompt to point."
- (interactive)
- (cond ((< (marker-position slime-repl-input-start-mark) (point))
- (kill-region slime-repl-input-start-mark (point)))
- ((= (point) (marker-position slime-repl-input-start-mark))
- (slime-repl-delete-current-input))))
-
- (defun slime-repl-replace-input (string)
- (slime-repl-delete-current-input)
- (insert-and-inherit string))
-
- (defun slime-repl-input-line-beginning-position ()
- (save-excursion
- (goto-char slime-repl-input-start-mark)
- (let ((inhibit-field-text-motion t))
- (line-beginning-position))))
-
- (defun slime-clear-repl-variables ()
- (interactive)
- (slime-eval-async `(swank-repl:clear-repl-variables)))
-
- (defvar slime-repl-clear-buffer-hook)
-
- (add-hook 'slime-repl-clear-buffer-hook 'slime-clear-repl-variables)
-
- (defun slime-repl-clear-buffer ()
- "Delete the output generated by the Lisp process."
- (interactive)
- (let ((inhibit-read-only t))
- (delete-region (point-min) slime-repl-prompt-start-mark)
- (delete-region slime-output-start slime-output-end)
- (when (< (point) slime-repl-input-start-mark)
- (goto-char slime-repl-input-start-mark))
- (recenter t))
- (run-hooks 'slime-repl-clear-buffer-hook))
-
- (defun slime-repl-clear-output ()
- "Delete the output inserted since the last input."
- (interactive)
- (let ((start (save-excursion
- (when (>= (point) slime-repl-input-start-mark)
- (goto-char slime-repl-input-start-mark))
- (slime-repl-previous-prompt)
- (ignore-errors (forward-sexp))
- (forward-line)
- (point)))
- (end (1- (slime-repl-input-line-beginning-position))))
- (when (< start end)
- (let ((inhibit-read-only t))
- (delete-region start end)
- (save-excursion
- (goto-char start)
- (insert ";;; output flushed"))))))
-
- (defun slime-repl-set-package (package)
- "Set the package of the REPL buffer to PACKAGE."
- (interactive (list (let* ((p (slime-current-package))
- (p (and p (slime-pretty-package-name p)))
- (p (and (not (equal p (slime-lisp-package))) p)))
- (slime-read-package-name "Package: " p))))
- (with-current-buffer (slime-output-buffer)
- (let ((previouse-point (- (point) slime-repl-input-start-mark))
- (previous-prompt (slime-lisp-package-prompt-string)))
- (destructuring-bind (name prompt-string)
- (slime-repl-shortcut-eval `(swank:set-package ,package))
- (setf (slime-lisp-package) name)
- (setf slime-buffer-package name)
- (unless (equal previous-prompt prompt-string)
- (setf (slime-lisp-package-prompt-string) prompt-string)
- (slime-repl-insert-prompt))
- (when (plusp previouse-point)
- (goto-char (+ previouse-point slime-repl-input-start-mark)))))))
-
- ;;;;; History
-
- (defcustom slime-repl-wrap-history nil
- "*T to wrap history around when the end is reached."
- :type 'boolean
- :group 'slime-repl)
-
- (make-variable-buffer-local
- (defvar slime-repl-input-history '()
- "History list of strings read from the REPL buffer."))
-
- (defun slime-repl-add-to-input-history (string)
- "Add STRING to the input history.
- Empty strings and duplicates are ignored."
- (setq string (slime-trim-whitespace string))
- (unless (equal string "")
- (setq slime-repl-input-history
- (remove string slime-repl-input-history))
- (unless (equal string (car slime-repl-input-history))
- (push string slime-repl-input-history))))
-
- ;; These two vars contain the state of the last history search. We
- ;; only use them if `last-command' was 'slime-repl-history-replace,
- ;; otherwise we reinitialize them.
-
- (defvar slime-repl-input-history-position -1
- "Newer items have smaller indices.")
-
- (defvar slime-repl-history-pattern nil
- "The regexp most recently used for finding input history.")
-
- (defun slime-repl-history-replace (direction &optional regexp)
- "Replace the current input with the next line in DIRECTION.
- DIRECTION is 'forward' or 'backward' (in the history list).
- If REGEXP is non-nil, only lines matching REGEXP are considered."
- (setq slime-repl-history-pattern regexp)
- (let* ((min-pos -1)
- (max-pos (length slime-repl-input-history))
- (pos0 (cond ((slime-repl-history-search-in-progress-p)
- slime-repl-input-history-position)
- (t min-pos)))
- (pos (slime-repl-position-in-history pos0 direction (or regexp "")
- (slime-repl-current-input)))
- (msg nil))
- (cond ((and (< min-pos pos) (< pos max-pos))
- (slime-repl-replace-input (nth pos slime-repl-input-history))
- (setq msg (format "History item: %d" pos)))
- ((not slime-repl-wrap-history)
- (setq msg (cond ((= pos min-pos) "End of history")
- ((= pos max-pos) "Beginning of history"))))
- (slime-repl-wrap-history
- (setq pos (if (= pos min-pos) max-pos min-pos))
- (setq msg "Wrapped history")))
- (when (or (<= pos min-pos) (<= max-pos pos))
- (when regexp
- (setq msg (concat msg "; no matching item"))))
- ;;(message "%s [%d %d %s]" msg start-pos pos regexp)
- (message "%s%s" msg (cond ((not regexp) "")
- (t (format "; current regexp: %s" regexp))))
- (setq slime-repl-input-history-position pos)
- (setq this-command 'slime-repl-history-replace)))
-
- (defun slime-repl-history-search-in-progress-p ()
- (eq last-command 'slime-repl-history-replace))
-
- (defun slime-repl-terminate-history-search ()
- (setq last-command this-command))
-
- (defun slime-repl-position-in-history (start-pos direction regexp
- &optional exclude-string)
- "Return the position of the history item matching REGEXP.
- Return -1 resp. the length of the history if no item matches.
- If EXCLUDE-STRING is specified then it's excluded from the search."
- ;; Loop through the history list looking for a matching line
- (let* ((step (ecase direction
- (forward -1)
- (backward 1)))
- (history slime-repl-input-history)
- (len (length history)))
- (loop for pos = (+ start-pos step) then (+ pos step)
- if (< pos 0) return -1
- if (<= len pos) return len
- for history-item = (nth pos history)
- if (and (string-match regexp history-item)
- (not (equal history-item exclude-string)))
- return pos)))
-
- (defun slime-repl-previous-input ()
- "Cycle backwards through input history.
- If the `last-command' was a history navigation command use the
- same search pattern for this command.
- Otherwise use the current input as search pattern.
-
- With a prefix-arg, do replacement from the mark."
- (interactive)
- (let ((slime-repl-history-use-mark (or slime-repl-history-use-mark
- current-prefix-arg)))
- (slime-repl-history-replace 'backward (slime-repl-history-pattern t))))
-
- (defun slime-repl-next-input ()
- "Cycle forwards through input history.
- See `slime-repl-previous-input'.
-
- With a prefix-arg, do replacement from the mark."
- (interactive)
- (let ((slime-repl-history-use-mark (or slime-repl-history-use-mark
- current-prefix-arg)))
- (slime-repl-history-replace 'forward (slime-repl-history-pattern t))))
-
- (defun slime-repl-forward-input ()
- "Cycle forwards through input history."
- (interactive)
- (slime-repl-history-replace 'forward (slime-repl-history-pattern)))
-
- (defun slime-repl-backward-input ()
- "Cycle backwards through input history."
- (interactive)
- (slime-repl-history-replace 'backward (slime-repl-history-pattern)))
-
- (defun slime-repl-previous-matching-input (regexp)
- "Insert the previous matching input.
-
- With a prefix-arg, do the insertion at the mark."
- (interactive (list (slime-read-from-minibuffer
- "Previous element matching (regexp): ")))
- (slime-repl-terminate-history-search)
- (let ((slime-repl-history-use-mark (or slime-repl-history-use-mark
- current-prefix-arg)))
- (slime-repl-history-replace 'backward regexp)))
-
- (defun slime-repl-next-matching-input (regexp)
- "Insert the next matching input.
-
- With a prefix-arg, do the insertion at the mark."
- (interactive (list (slime-read-from-minibuffer
- "Next element matching (regexp): ")))
- (slime-repl-terminate-history-search)
- (let ((slime-repl-history-use-mark (or slime-repl-history-use-mark
- current-prefix-arg)))
- (slime-repl-history-replace 'forward regexp)))
-
- (defun slime-repl-history-pattern (&optional use-current-input)
- "Return the regexp for the navigation commands."
- (cond ((slime-repl-history-search-in-progress-p)
- slime-repl-history-pattern)
- (use-current-input
- (goto-char (max (slime-repl-history-yank-start) (point)))
- (let ((str (slime-repl-current-input t)))
- (cond ((string-match "^[ \t\n]*$" str) nil)
- (t (concat "^" (regexp-quote str))))))
- (t nil)))
-
- (defun slime-repl-delete-from-input-history (string)
- "Delete STRING from the repl input history.
-
- When string is not provided then clear the current repl input and
- use it as an input. This is useful to get rid of unwanted repl
- history entries while navigating the repl history."
- (interactive (list (slime-repl-current-input)))
- (let ((merged-history
- (slime-repl-merge-histories (slime-repl-read-history nil t)
- slime-repl-input-history)))
- (setq slime-repl-input-history
- (cl-delete string merged-history :test #'string=))
- (slime-repl-save-history))
- (slime-repl-delete-current-input))
-
- ;;;;; Persistent History
-
- (defun slime-repl-merge-histories (old-hist new-hist)
- "Merge entries from OLD-HIST and NEW-HIST."
- ;; Newer items in each list are at the beginning.
- (let* ((ht (make-hash-table :test #'equal))
- (test (lambda (entry)
- (or (gethash entry ht)
- (progn (setf (gethash entry ht) t)
- nil)))))
- (append (cl-remove-if test new-hist)
- (cl-remove-if test old-hist))))
-
- (defun slime-repl-load-history (&optional filename)
- "Set the current SLIME REPL history.
- It can be read either from FILENAME or `slime-repl-history-file' or
- from a user defined filename."
- (interactive (list (slime-repl-read-history-filename)))
- (let ((file (or filename slime-repl-history-file)))
- (setq slime-repl-input-history (slime-repl-read-history file t))))
-
- (defun slime-repl-read-history (&optional filename noerrer)
- "Read and return the history from FILENAME.
- The default value for FILENAME is `slime-repl-history-file'.
- If NOERROR is true return and the file doesn't exits return nil."
- (let ((file (or filename slime-repl-history-file)))
- (cond ((not (file-readable-p file)) '())
- (t (with-temp-buffer
- (insert-file-contents file)
- (read (current-buffer)))))))
-
- (defun slime-repl-read-history-filename ()
- (read-file-name "Use SLIME REPL history from file: "
- slime-repl-history-file))
-
- (defun slime-repl-save-merged-history (&optional filename)
- "Read the history file, merge the current REPL history and save it.
- This tries to be smart in merging the history from the file and the
- current history in that it tries to detect the unique entries using
- `slime-repl-merge-histories'."
- (interactive (list (slime-repl-read-history-filename)))
- (let ((file (or filename slime-repl-history-file)))
- (with-temp-message "saving history..."
- (let ((hist (slime-repl-merge-histories (slime-repl-read-history file t)
- slime-repl-input-history)))
- (slime-repl-save-history file hist)))))
-
- (defun slime-repl-save-history (&optional filename history)
- "Simply save the current SLIME REPL history to a file.
- When SLIME is setup to always load the old history and one uses only
- one instance of slime all the time, there is no need to merge the
- files and this function is sufficient.
-
- When the list is longer than `slime-repl-history-size' it will be
- truncated. That part is untested, though!"
- (interactive (list (slime-repl-read-history-filename)))
- (let ((file (or filename slime-repl-history-file))
- (hist (or history slime-repl-input-history)))
- (unless (file-writable-p file)
- (error (format "History file not writable: %s" file)))
- (let ((hist (cl-subseq hist 0 (min (length hist) slime-repl-history-size))))
- ;;(message "saving %s to %s\n" hist file)
- (with-temp-file file
- (let ((cs slime-repl-history-file-coding-system)
- (print-length nil) (print-level nil))
- (setq buffer-file-coding-system cs)
- (insert (format ";; -*- coding: %s -*-\n" cs))
- (insert ";; History for SLIME REPL. Automatically written.\n"
- ";; Edit only if you know what you're doing\n")
- (prin1 (mapcar #'substring-no-properties hist) (current-buffer)))))))
-
- (defun slime-repl-save-all-histories ()
- "Save the history in each repl buffer."
- (dolist (b (buffer-list))
- (with-current-buffer b
- (when (eq major-mode 'slime-repl-mode)
- (slime-repl-safe-save-merged-history)))))
-
- (defun slime-repl-safe-save-merged-history ()
- (slime-repl-call-with-handler
- #'slime-repl-save-merged-history
- "%S while saving the history. Continue? "))
-
- (defun slime-repl-safe-load-history ()
- (slime-repl-call-with-handler
- #'slime-repl-load-history
- "%S while loading the history. Continue? "))
-
- (defun slime-repl-call-with-handler (fun query)
- "Call FUN in the context of an error handler.
- The handler will use qeuery to ask the use if the error should be ingored."
- (condition-case err
- (funcall fun)
- (error
- (if (y-or-n-p (format query (error-message-string err)))
- nil
- (signal (car err) (cdr err))))))
-
- ;;;;; REPL Read Mode
-
- (defvar slime-repl-read-mode-map
- (let ((map (make-sparse-keymap)))
- (define-key map "\C-m" 'slime-repl-return)
- (define-key map [return] 'slime-repl-return)
- (define-key map (kbd "TAB") 'self-insert-command)
- (define-key map "\C-c\C-b" 'slime-repl-read-break)
- (define-key map "\C-c\C-c" 'slime-repl-read-break)
- (define-key map [remap slime-indent-and-complete-symbol] 'ignore)
- (define-key map [remap slime-handle-repl-shortcut] 'self-insert-command)
- map))
-
- (define-minor-mode slime-repl-read-mode
- "Mode to read input from Emacs
- \\{slime-repl-read-mode-map}"
- nil
- "[read]")
-
- (make-variable-buffer-local
- (defvar slime-read-string-threads nil))
-
- (make-variable-buffer-local
- (defvar slime-read-string-tags nil))
-
- (defun slime-repl-read-string (thread tag)
- (slime-switch-to-output-buffer)
- (push thread slime-read-string-threads)
- (push tag slime-read-string-tags)
- (goto-char (point-max))
- (slime-mark-output-end)
- (slime-mark-input-start)
- (slime-repl-read-mode 1))
-
- (defun slime-repl-return-string (string)
- (slime-dispatch-event `(:emacs-return-string
- ,(pop slime-read-string-threads)
- ,(pop slime-read-string-tags)
- ,string))
- (slime-repl-read-mode -1))
-
- (defun slime-repl-read-break ()
- (interactive)
- (slime-dispatch-event `(:emacs-interrupt ,(car slime-read-string-threads))))
-
- (defun slime-repl-abort-read (thread tag)
- (with-current-buffer (slime-output-buffer)
- (pop slime-read-string-threads)
- (pop slime-read-string-tags)
- (slime-repl-read-mode -1)
- (message "Read aborted")))
-
- ;;;;; REPL handlers
-
- (cl-defstruct (slime-repl-shortcut (:conc-name slime-repl-shortcut.))
- symbol names handler one-liner)
-
- (defvar slime-repl-shortcut-table nil
- "A list of slime-repl-shortcuts")
-
- (defvar slime-repl-shortcut-history '()
- "History list of shortcut command names.")
-
- (defvar slime-within-repl-shortcut-handler-p nil
- "Bound to T if we're in a REPL shortcut handler invoked from the REPL.")
-
- (defun slime-handle-repl-shortcut ()
- (interactive)
- (if (> (point) slime-repl-input-start-mark)
- (insert (string slime-repl-shortcut-dispatch-char))
- (let ((shortcut (slime-lookup-shortcut
- (completing-read "Command: "
- (slime-bogus-completion-alist
- (slime-list-all-repl-shortcuts))
- nil t nil
- 'slime-repl-shortcut-history))))
- (with-struct (slime-repl-shortcut. handler) shortcut
- (let ((slime-within-repl-shortcut-handler-p t))
- (call-interactively handler))))))
-
- (defun slime-list-all-repl-shortcuts ()
- (loop for shortcut in slime-repl-shortcut-table
- append (slime-repl-shortcut.names shortcut)))
-
- (defun slime-lookup-shortcut (name)
- (cl-find-if (lambda (s) (member name (slime-repl-shortcut.names s)))
- slime-repl-shortcut-table))
-
- (defmacro defslime-repl-shortcut (elisp-name names &rest options)
- "Define a new repl shortcut. ELISP-NAME is a symbol specifying
- the name of the interactive function to create, or NIL if no
- function should be created.
-
- NAMES is a list of \(full-name . aliases\).
-
- OPTIONS is an plist specifying the handler doing the actual work
- of the shortcut \(`:handler'\), and a help text \(`:one-liner'\)."
- `(progn
- ,(when elisp-name
- `(defun ,elisp-name ()
- (interactive)
- (call-interactively ,(second (assoc :handler options)))))
- (let ((new-shortcut (make-slime-repl-shortcut
- :symbol ',elisp-name
- :names (list ,@names)
- ,@(apply #'append options))))
- (setq slime-repl-shortcut-table
- (cl-remove-if (lambda (s)
- (member ',(car names) (slime-repl-shortcut.names s)))
- slime-repl-shortcut-table))
- (push new-shortcut slime-repl-shortcut-table)
- ',elisp-name)))
-
- (defun slime-repl-shortcut-eval (sexp &optional package)
- "This function should be used by REPL shortcut handlers instead
- of `slime-eval' to evaluate their final expansion. (This
- expansion will be added to the REPL's history.)"
- (when slime-within-repl-shortcut-handler-p ; were we invoked via ,foo?
- (slime-repl-add-to-input-history (prin1-to-string sexp)))
- (slime-eval sexp package))
-
- (defun slime-repl-shortcut-eval-async (sexp &optional cont package)
- "This function should be used by REPL shortcut handlers instead
- of `slime-eval-async' to evaluate their final expansion. (This
- expansion will be added to the REPL's history.)"
- (when slime-within-repl-shortcut-handler-p ; were we invoked via ,foo?
- (slime-repl-add-to-input-history (prin1-to-string sexp)))
- (slime-eval-async sexp cont package))
-
- (defun slime-list-repl-short-cuts ()
- (interactive)
- (slime-with-popup-buffer ((slime-buffer-name :repl-help))
- (let ((table (cl-sort (cl-copy-list slime-repl-shortcut-table) #'string<
- :key (lambda (x)
- (car (slime-repl-shortcut.names x))))))
- (save-excursion
- (dolist (shortcut table)
- (let ((names (slime-repl-shortcut.names shortcut)))
- (insert (pop names)) ;; first print the "full" name
- (when names
- ;; we also have aliases
- (insert " (aka ")
- (while (cdr names)
- (insert (pop names) ", "))
- (insert (car names) ")"))
- (when (slime-repl-shortcut.one-liner shortcut)
- (insert "\n " (slime-repl-shortcut.one-liner shortcut)))
- (insert "\n")))))))
-
- (defun slime-save-some-lisp-buffers ()
- (if slime-repl-only-save-lisp-buffers
- (save-some-buffers nil (lambda ()
- (and (memq major-mode slime-lisp-modes)
- (not (null buffer-file-name)))))
- (save-some-buffers)))
-
- (defun slime-kill-all-buffers ()
- "Kill all the SLIME-related buffers."
- (dolist (buf (buffer-list))
- (when (or (string= (buffer-name buf) slime-event-buffer-name)
- (string-match "^\\*inferior-lisp*" (buffer-name buf))
- (string-match "^\\*slime-repl .*\\*$" (buffer-name buf))
- (string-match "^\\*sldb .*\\*$" (buffer-name buf))
- (string-match "^\\*SLIME.*\\*$" (buffer-name buf)))
- (kill-buffer buf))))
-
- (defslime-repl-shortcut slime-repl-shortcut-help ("help")
- (:handler 'slime-list-repl-short-cuts)
- (:one-liner "Display the help."))
-
- (defslime-repl-shortcut nil ("change-directory" "!d" "cd")
- (:handler 'slime-set-default-directory)
- (:one-liner "Change the current directory."))
-
- (defslime-repl-shortcut nil ("pwd")
- (:handler (lambda ()
- (interactive)
- (let ((dir (slime-eval `(swank:default-directory))))
- (message "Directory %s" dir))))
- (:one-liner "Show the current directory."))
-
- (defslime-repl-shortcut slime-repl-push-directory
- ("push-directory" "+d" "pushd")
- (:handler (lambda (directory)
- (interactive
- (list (read-directory-name
- "Push directory: "
- (slime-eval '(swank:default-directory))
- nil nil "")))
- (push (slime-eval '(swank:default-directory))
- slime-repl-directory-stack)
- (slime-set-default-directory directory)))
- (:one-liner "Save the current directory and set it to a new one."))
-
- (defslime-repl-shortcut slime-repl-pop-directory
- ("pop-directory" "-d" "popd")
- (:handler (lambda ()
- (interactive)
- (if (null slime-repl-directory-stack)
- (message "Directory stack is empty.")
- (slime-set-default-directory
- (pop slime-repl-directory-stack)))))
- (:one-liner "Restore the last saved directory."))
-
- (defslime-repl-shortcut nil ("change-package" "!p" "in-package" "in")
- (:handler 'slime-repl-set-package)
- (:one-liner "Change the current package."))
-
- (defslime-repl-shortcut slime-repl-push-package ("push-package" "+p")
- (:handler (lambda (package)
- (interactive (list (slime-read-package-name "Package: ")))
- (push (slime-lisp-package) slime-repl-package-stack)
- (slime-repl-set-package package)))
- (:one-liner "Save the current package and set it to a new one."))
-
- (defslime-repl-shortcut slime-repl-pop-package ("pop-package" "-p")
- (:handler (lambda ()
- (interactive)
- (if (null slime-repl-package-stack)
- (message "Package stack is empty.")
- (slime-repl-set-package
- (pop slime-repl-package-stack)))))
- (:one-liner "Restore the last saved package."))
-
- (defslime-repl-shortcut slime-repl-resend ("resend-form")
- (:handler (lambda ()
- (interactive)
- (insert (car slime-repl-input-history))
- (insert "\n")
- (slime-repl-send-input)))
- (:one-liner "Resend the last form."))
-
- (defslime-repl-shortcut slime-repl-disconnect ("disconnect")
- (:handler 'slime-disconnect)
- (:one-liner "Disconnect the current connection."))
-
- (defslime-repl-shortcut slime-repl-disconnect-all ("disconnect-all")
- (:handler 'slime-disconnect-all)
- (:one-liner "Disconnect all connections."))
-
- (defslime-repl-shortcut slime-repl-sayoonara ("sayoonara")
- (:handler (lambda ()
- (interactive)
- (when (slime-connected-p)
- (slime-quit-lisp))
- (slime-kill-all-buffers)))
- (:one-liner "Quit all Lisps and close all SLIME buffers."))
-
- (defslime-repl-shortcut slime-repl-quit ("quit")
- (:handler (lambda ()
- (interactive)
- ;; `slime-quit-lisp' determines the connection to quit
- ;; on behalf of the REPL's `slime-buffer-connection'.
- (let ((repl-buffer (slime-output-buffer)))
- (slime-quit-lisp)
- (kill-buffer repl-buffer))))
- (:one-liner "Quit the current Lisp."))
-
- (defslime-repl-shortcut slime-repl-defparameter ("defparameter" "!")
- (:handler (lambda (name value)
- (interactive (list (slime-read-symbol-name "Name (symbol): " t)
- (slime-read-from-minibuffer "Value: " "*")))
- (insert "(cl:defparameter " name " " value
- " \"REPL generated global variable.\")")
- (slime-repl-send-input t)))
- (:one-liner "Define a new global, special, variable."))
-
- (defslime-repl-shortcut slime-repl-compile-and-load ("compile-and-load" "cl")
- (:handler (lambda (filename)
- (interactive (list (expand-file-name
- (read-file-name "File: " nil nil nil nil))))
- (slime-save-some-lisp-buffers)
- (slime-repl-shortcut-eval-async
- `(swank:compile-file-if-needed
- ,(slime-to-lisp-filename filename) t)
- #'slime-compilation-finished)))
- (:one-liner "Compile (if neccessary) and load a lisp file."))
-
- (defslime-repl-shortcut nil ("restart-inferior-lisp")
- (:handler 'slime-restart-inferior-lisp)
- (:one-liner "Restart *inferior-lisp* and reconnect SLIME."))
-
- (defun slime-redirect-inferior-output (&optional noerror)
- "Redirect output of the inferior-process to the REPL buffer."
- (interactive)
- (let ((proc (slime-inferior-process)))
- (cond (proc
- (let ((filter (slime-rcurry #'slime-inferior-output-filter
- (slime-current-connection))))
- (set-process-filter proc filter)))
- (noerror)
- (t (error "No inferior lisp process")))))
-
- (defun slime-inferior-output-filter (proc string conn)
- (cond ((eq (process-status conn) 'closed)
- (message "Connection closed. Removing inferior output filter.")
- (message "Lost output: %S" string)
- (set-process-filter proc nil))
- (t
- (slime-output-filter conn string))))
-
- (defun slime-redirect-trace-output ()
- "Redirect the trace output to a separate Emacs buffer."
- (interactive)
- (let ((buffer (get-buffer-create (slime-buffer-name :trace))))
- (with-current-buffer buffer
- (let ((marker (copy-marker (buffer-size)))
- (target (incf slime-last-output-target-id)))
- (puthash target marker slime-output-target-to-marker)
- (slime-eval `(swank-repl:redirect-trace-output ,target))))
- ;; Note: We would like the entries in
- ;; slime-output-target-to-marker to disappear when the buffers are
- ;; killed. We cannot just make the hash-table ":weakness 'value"
- ;; -- there is no reference from the buffers to the markers in the
- ;; buffer, so entries would disappear even though the buffers are
- ;; alive. Best solution might be to make buffer-local variables
- ;; that keep the markers. --mkoeppe
- (pop-to-buffer buffer)))
-
- (defun slime-call-defun ()
- "Insert a call to the toplevel form defined around point into the REPL."
- (interactive)
- (cl-labels ((insert-call
- (name &key (function t)
- defclass)
- (let* ((setf (and function
- (consp name)
- (= (length name) 2)
- (eql (car name) 'setf)))
- (symbol (if setf
- (cadr name)
- name))
- (qualified-symbol-name
- (slime-qualify-cl-symbol-name symbol))
- (symbol-name (slime-cl-symbol-name qualified-symbol-name))
- (symbol-package (slime-cl-symbol-package
- qualified-symbol-name))
- (call (if (cl-equalp (slime-lisp-package) symbol-package)
- symbol-name
- qualified-symbol-name)))
- (slime-switch-to-output-buffer)
- (goto-char slime-repl-input-start-mark)
- (insert (if function
- "("
- " "))
- (when setf
- (insert "setf ("))
- (if defclass
- (insert "make-instance '"))
- (insert call)
- (cond (setf
- (insert " ")
- (save-excursion (insert ") )")))
- (function
- (insert " ")
- (save-excursion (insert ")"))))
- (unless function
- (goto-char slime-repl-input-start-mark)))))
- (let ((toplevel (slime-parse-toplevel-form)))
- (if (symbolp toplevel)
- (error "Not in a function definition")
- (slime-dcase toplevel
- (((:defun :defgeneric :defmacro :define-compiler-macro) symbol)
- (insert-call symbol))
- ((:defmethod symbol &rest args)
- (declare (ignore args))
- (insert-call symbol))
- (((:defparameter :defvar :defconstant) symbol)
- (insert-call symbol :function nil))
- (((:defclass) symbol)
- (insert-call symbol :defclass t))
- (t
- (error "Not in a function definition")))))))
-
- (defun slime-repl-copy-down-to-repl (slimefun &rest args)
- (slime-eval-async `(swank-repl:listener-save-value ',slimefun ,@args)
- #'(lambda (_ignored)
- (with-current-buffer (slime-repl)
- (slime-eval-async '(swank-repl:listener-get-value)
- #'(lambda (_ignored)
- (slime-repl-insert-prompt)))))))
-
- (defun slime-inspector-copy-down-to-repl (number)
- "Evaluate the inspector slot at point via the REPL (to set `*')."
- (interactive (list (or (get-text-property (point) 'slime-part-number)
- (error "No part at point"))))
- (slime-repl-copy-down-to-repl 'swank:inspector-nth-part number))
-
- (defun sldb-copy-down-to-repl (frame-id var-id)
- "Evaluate the frame var at point via the REPL (to set `*')."
- (interactive (list (sldb-frame-number-at-point) (sldb-var-number-at-point)))
- (slime-repl-copy-down-to-repl 'swank/backend:frame-var-value frame-id var-id))
-
- (defun sldb-insert-frame-call-to-repl ()
- "Insert a call to a frame at point."
- (interactive)
- (let ((call (slime-eval `(swank/backend::frame-call
- ,(sldb-frame-number-at-point)))))
- (slime-switch-to-output-buffer)
- (if (>= (point) slime-repl-prompt-start-mark)
- (insert call)
- (save-excursion
- (goto-char (point-max))
- (insert call))))
- (slime-repl))
-
- (defun slime-set-default-directory (directory)
- "Make DIRECTORY become Lisp's current directory."
- (interactive (list (read-directory-name "Directory: " nil nil t)))
- (let ((dir (expand-file-name directory)))
- (message "default-directory: %s"
- (slime-from-lisp-filename
- (slime-repl-shortcut-eval `(swank:set-default-directory
- ,(slime-to-lisp-filename dir)))))
- (with-current-buffer (slime-output-buffer)
- (setq default-directory dir))))
-
- (defun slime-sync-package-and-default-directory ()
- "Set Lisp's package and directory to the values in current buffer."
- (interactive)
- (let* ((package (slime-current-package))
- (exists-p (or (null package)
- (slime-eval `(cl:packagep
- (swank::guess-package ,package)))))
- (directory default-directory))
- (when (and package exists-p)
- (slime-repl-set-package package))
- (slime-set-default-directory directory)
- ;; Sync *inferior-lisp* dir
- (let* ((proc (slime-process))
- (buffer (and proc (process-buffer proc))))
- (when (buffer-live-p buffer)
- (with-current-buffer buffer
- (setq default-directory directory))))
- (message "package: %s%s directory: %s"
- (with-current-buffer (slime-output-buffer)
- (slime-lisp-package))
- (if exists-p "" (format " (package %s doesn't exist)" package))
- directory)))
-
- (defun slime-goto-connection ()
- "Switch to the REPL buffer for the connection at point."
- (interactive)
- (let ((slime-dispatching-connection (slime-connection-at-point)))
- (switch-to-buffer (slime-output-buffer))))
-
- (defun slime-repl-inside-string-or-comment-p ()
- (save-restriction
- (when (and (boundp 'slime-repl-input-start-mark)
- slime-repl-input-start-mark
- (>= (point) slime-repl-input-start-mark))
- (narrow-to-region slime-repl-input-start-mark (point)))
- (slime-inside-string-or-comment-p)))
-
- (defvar slime-repl-easy-menu
- (let ((C '(slime-connected-p)))
- `("REPL"
- [ "Send Input" slime-repl-return ,C ]
- [ "Close and Send Input " slime-repl-closing-return ,C ]
- [ "Interrupt Lisp process" slime-interrupt ,C ]
- "--"
- [ "Previous Input" slime-repl-previous-input t ]
- [ "Next Input" slime-repl-next-input t ]
- [ "Goto Previous Prompt " slime-repl-previous-prompt t ]
- [ "Goto Next Prompt " slime-repl-next-prompt t ]
- [ "Clear Last Output" slime-repl-clear-output t ]
- [ "Clear Buffer " slime-repl-clear-buffer t ]
- [ "Kill Current Input" slime-repl-kill-input t ])))
-
- (defun slime-repl-add-easy-menu ()
- (easy-menu-define menubar-slime-repl slime-repl-mode-map
- "REPL" slime-repl-easy-menu)
- (easy-menu-define menubar-slime slime-repl-mode-map
- "SLIME" slime-easy-menu)
- (easy-menu-add slime-repl-easy-menu 'slime-repl-mode-map))
-
- (add-hook 'slime-repl-mode-hook 'slime-repl-add-easy-menu)
-
- (defun slime-hide-inferior-lisp-buffer ()
- "Display the REPL buffer instead of the *inferior-lisp* buffer."
- (let* ((buffer (if (slime-process)
- (process-buffer (slime-process))))
- (window (if buffer (get-buffer-window buffer t)))
- (repl-buffer (slime-output-buffer t))
- (repl-window (get-buffer-window repl-buffer)))
- (when buffer
- (bury-buffer buffer))
- (cond (repl-window
- (when window
- (delete-window window)))
- (window
- (set-window-buffer window repl-buffer))
- (t
- (pop-to-buffer repl-buffer)
- (goto-char (point-max))))))
-
- (defun slime-repl-choose-coding-system ()
- (let ((candidates (slime-connection-coding-systems)))
- (or (cl-find (symbol-name (car default-process-coding-system))
- candidates
- :test (lambda (s1 s2)
- (if (fboundp 'coding-system-equal)
- (coding-system-equal (intern s1) (intern s2)))))
- (car candidates)
- (error "Can't find suitable coding-system"))))
-
- (defun slime-repl-connected-hook-function ()
- (destructuring-bind (package prompt)
- (let ((slime-current-thread t)
- (cs (slime-repl-choose-coding-system)))
- (slime-eval `(swank-repl:create-repl nil :coding-system ,cs)))
- (setf (slime-lisp-package) package)
- (setf (slime-lisp-package-prompt-string) prompt))
- (slime-hide-inferior-lisp-buffer)
- (slime-init-output-buffer (slime-connection)))
-
- (defun slime-repl-event-hook-function (event)
- (slime-dcase event
- ((:write-string output &optional target)
- (slime-write-string output target)
- t)
- ((:read-string thread tag)
- (assert thread)
- (slime-repl-read-string thread tag)
- t)
- ((:read-aborted thread tag)
- (slime-repl-abort-read thread tag)
- t)
- ((:open-dedicated-output-stream port coding-system)
- (slime-open-stream-to-lisp port coding-system)
- t)
- ((:new-package package prompt-string)
- (setf (slime-lisp-package) package)
- (setf (slime-lisp-package-prompt-string) prompt-string)
- (let ((buffer (slime-connection-output-buffer)))
- (when (buffer-live-p buffer)
- (with-current-buffer buffer
- (setq slime-buffer-package package))))
- t)
- (t nil)))
-
- (defun slime-change-repl-to-default-connection ()
- "Change current REPL to the REPL of the default connection.
- If the current buffer is not a REPL, don't do anything."
- (when (equal major-mode 'slime-repl-mode)
- (let ((slime-buffer-connection slime-default-connection))
- (pop-to-buffer-same-window (slime-connection-output-buffer)))))
-
- (defun slime-repl-find-buffer-package ()
- (or (slime-search-buffer-package)
- (slime-lisp-package)))
-
- (defun slime-repl-add-hooks ()
- (add-hook 'slime-event-hooks 'slime-repl-event-hook-function)
- (add-hook 'slime-connected-hook 'slime-repl-connected-hook-function)
- (add-hook 'slime-cycle-connections-hook
- 'slime-change-repl-to-default-connection))
-
- (defun slime-repl-remove-hooks ()
- (remove-hook 'slime-event-hooks 'slime-repl-event-hook-function)
- (remove-hook 'slime-connected-hook 'slime-repl-connected-hook-function)
- (remove-hook 'slime-cycle-connections-hook
- 'slime-change-repl-to-default-connection))
-
- (defun slime-repl-sexp-at-point ()
- "Returns the current sexp at point (or NIL if none is found)
- while ignoring the repl prompt text."
- (if (<= slime-repl-input-start-mark (point))
- (save-restriction
- (narrow-to-region slime-repl-input-start-mark (point-max))
- (slime-sexp-at-point))
- (slime-sexp-at-point)))
-
- (defun slime-repl-inspect (string)
- (interactive
- (list (slime-read-from-minibuffer "Inspect value (evaluated): "
- (slime-repl-sexp-at-point))))
- (slime-inspect string))
-
- (require 'bytecomp)
-
- ;; (mapc (lambda (sym)
- ;; (cond ((fboundp sym)
- ;; (unless (byte-code-function-p (symbol-function sym))
- ;; (byte-compile sym)))
- ;; (t (error "%S is not fbound" sym))))
- ;; '(slime-repl-event-hook-function
- ;; slime-write-string
- ;; slime-repl-write-string
- ;; slime-repl-emit
- ;; slime-repl-show-maximum-output))
-
- (provide 'slime-repl)
|