;; 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 ") (: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)