|
;; An experimental implementation of multiple REPLs multiplexed over a
|
|
;; single Slime socket. M-x slime-new-mrepl creates a new REPL buffer.
|
|
;;
|
|
(require 'slime)
|
|
(require 'inferior-slime) ; inferior-slime-indent-lime
|
|
(require 'cl-lib)
|
|
|
|
(define-slime-contrib slime-mrepl
|
|
"Multiple REPLs."
|
|
(:authors "Helmut Eller <heller@common-lisp.net>")
|
|
(:license "GPL")
|
|
(:swank-dependencies swank-mrepl))
|
|
|
|
(require 'comint)
|
|
|
|
(defvar slime-mrepl-remote-channel nil)
|
|
(defvar slime-mrepl-expect-sexp nil)
|
|
|
|
(define-derived-mode slime-mrepl-mode comint-mode "mrepl"
|
|
;; idea lifted from ielm
|
|
(unless (get-buffer-process (current-buffer))
|
|
(let* ((process-connection-type nil)
|
|
(proc (start-process "mrepl (dummy)" (current-buffer) "hexl")))
|
|
(set-process-query-on-exit-flag proc nil)))
|
|
(set (make-local-variable 'comint-use-prompt-regexp) nil)
|
|
(set (make-local-variable 'comint-inhibit-carriage-motion) t)
|
|
(set (make-local-variable 'comint-input-sender) 'slime-mrepl-input-sender)
|
|
(set (make-local-variable 'comint-output-filter-functions) nil)
|
|
(set (make-local-variable 'slime-mrepl-expect-sexp) t)
|
|
;;(set (make-local-variable 'comint-get-old-input) 'ielm-get-old-input)
|
|
(set-syntax-table lisp-mode-syntax-table)
|
|
)
|
|
|
|
(slime-define-keys slime-mrepl-mode-map
|
|
((kbd "RET") 'slime-mrepl-return)
|
|
([return] 'slime-mrepl-return)
|
|
;;((kbd "TAB") 'slime-indent-and-complete-symbol)
|
|
((kbd "C-c C-b") 'slime-interrupt)
|
|
((kbd "C-c C-c") 'slime-interrupt))
|
|
|
|
(defun slime-mrepl-process% () (get-buffer-process (current-buffer))) ;stupid
|
|
(defun slime-mrepl-mark () (process-mark (slime-mrepl-process%)))
|
|
|
|
(defun slime-mrepl-insert (string)
|
|
(comint-output-filter (slime-mrepl-process%) string))
|
|
|
|
(slime-define-channel-type listener)
|
|
|
|
(slime-define-channel-method listener :prompt (package prompt)
|
|
(with-current-buffer (slime-channel-get self 'buffer)
|
|
(slime-mrepl-prompt package prompt)))
|
|
|
|
(defun slime-mrepl-prompt (package prompt)
|
|
(setf slime-buffer-package package)
|
|
(slime-mrepl-insert (format "%s%s> "
|
|
(cl-case (current-column)
|
|
(0 "")
|
|
(t "\n"))
|
|
prompt))
|
|
(slime-mrepl-recenter))
|
|
|
|
(defun slime-mrepl-recenter ()
|
|
(when (get-buffer-window)
|
|
(recenter -1)))
|
|
|
|
(slime-define-channel-method listener :write-result (result)
|
|
(with-current-buffer (slime-channel-get self 'buffer)
|
|
(goto-char (point-max))
|
|
(slime-mrepl-insert result)))
|
|
|
|
(slime-define-channel-method listener :evaluation-aborted ()
|
|
(with-current-buffer (slime-channel-get self 'buffer)
|
|
(goto-char (point-max))
|
|
(slime-mrepl-insert "; Evaluation aborted\n")))
|
|
|
|
(slime-define-channel-method listener :write-string (string)
|
|
(slime-mrepl-write-string self string))
|
|
|
|
(defun slime-mrepl-write-string (self string)
|
|
(with-current-buffer (slime-channel-get self 'buffer)
|
|
(goto-char (slime-mrepl-mark))
|
|
(slime-mrepl-insert string)))
|
|
|
|
(slime-define-channel-method listener :set-read-mode (mode)
|
|
(with-current-buffer (slime-channel-get self 'buffer)
|
|
(cl-ecase mode
|
|
(:read (setq slime-mrepl-expect-sexp nil)
|
|
(message "[Listener is waiting for input]"))
|
|
(:eval (setq slime-mrepl-expect-sexp t)))))
|
|
|
|
(defun slime-mrepl-return (&optional end-of-input)
|
|
(interactive "P")
|
|
(slime-check-connected)
|
|
(goto-char (point-max))
|
|
(cond ((and slime-mrepl-expect-sexp
|
|
(or (slime-input-complete-p (slime-mrepl-mark) (point))
|
|
end-of-input))
|
|
(comint-send-input))
|
|
((not slime-mrepl-expect-sexp)
|
|
(unless end-of-input
|
|
(insert "\n"))
|
|
(comint-send-input t))
|
|
(t
|
|
(insert "\n")
|
|
(inferior-slime-indent-line)
|
|
(message "[input not complete]")))
|
|
(slime-mrepl-recenter))
|
|
|
|
(defun slime-mrepl-input-sender (proc string)
|
|
(slime-mrepl-send-string (substring-no-properties string)))
|
|
|
|
(defun slime-mrepl-send-string (string &optional command-string)
|
|
(slime-mrepl-send `(:process ,string)))
|
|
|
|
(defun slime-mrepl-send (msg)
|
|
"Send MSG to the remote channel."
|
|
(slime-send-to-remote-channel slime-mrepl-remote-channel msg))
|
|
|
|
(defun slime-new-mrepl ()
|
|
"Create a new listener window."
|
|
(interactive)
|
|
(let ((channel (slime-make-channel slime-listener-channel-methods)))
|
|
(slime-eval-async
|
|
`(swank-mrepl:create-mrepl ,(slime-channel.id channel))
|
|
(slime-rcurry
|
|
(lambda (result channel)
|
|
(cl-destructuring-bind (remote thread-id package prompt) result
|
|
(pop-to-buffer (generate-new-buffer (slime-buffer-name :mrepl)))
|
|
(slime-mrepl-mode)
|
|
(setq slime-current-thread thread-id)
|
|
(setq slime-buffer-connection (slime-connection))
|
|
(set (make-local-variable 'slime-mrepl-remote-channel) remote)
|
|
(slime-channel-put channel 'buffer (current-buffer))
|
|
(slime-channel-send channel `(:prompt ,package ,prompt))))
|
|
channel))))
|
|
|
|
(defun slime-mrepl ()
|
|
(let ((conn (slime-connection)))
|
|
(cl-find-if (lambda (x)
|
|
(with-current-buffer x
|
|
(and (eq major-mode 'slime-mrepl-mode)
|
|
(eq (slime-current-connection) conn))))
|
|
(buffer-list))))
|
|
|
|
(def-slime-selector-method ?m
|
|
"First mrepl-buffer"
|
|
(or (slime-mrepl)
|
|
(error "No mrepl buffer (%s)" (slime-connection-name))))
|
|
|
|
(provide 'slime-mrepl)
|