|
|
- ;;; swank-mrepl.lisp
- ;;
- ;; Licence: public domain
-
- (in-package :swank)
- (eval-when (:compile-toplevel :load-toplevel :execute)
- (let ((api '(
- *emacs-connection*
- channel
- channel-id
- define-channel-method
- defslimefun
- dcase
- log-event
- process-requests
- send-to-remote-channel
- use-threads-p
- wait-for-event
- with-bindings
- with-connection
- with-top-level-restart
- with-slime-interrupts
- )))
- (eval `(defpackage #:swank-api
- (:use)
- (:import-from #:swank . ,api)
- (:export . ,api)))))
-
- (defpackage :swank-mrepl
- (:use :cl :swank-api)
- (:export #:create-mrepl))
-
- (in-package :swank-mrepl)
-
- (defclass listener-channel (channel)
- ((remote :initarg :remote)
- (env :initarg :env)
- (mode :initform :eval)
- (tag :initform nil)))
-
- (defun package-prompt (package)
- (reduce (lambda (x y) (if (<= (length x) (length y)) x y))
- (cons (package-name package) (package-nicknames package))))
-
- (defslimefun create-mrepl (remote)
- (let* ((pkg *package*)
- (conn *emacs-connection*)
- (thread (if (use-threads-p)
- (spawn-listener-thread conn)
- nil))
- (ch (make-instance 'listener-channel :remote remote :thread thread)))
- (setf (slot-value ch 'env) (initial-listener-env ch))
- (when thread
- (swank/backend:send thread `(:serve-channel ,ch)))
- (list (channel-id ch)
- (swank/backend:thread-id (or thread (swank/backend:current-thread)))
- (package-name pkg)
- (package-prompt pkg))))
-
- (defun initial-listener-env (listener)
- `((*package* . ,*package*)
- (*standard-output* . ,(make-listener-output-stream listener))
- (*standard-input* . ,(make-listener-input-stream listener))))
-
- (defun spawn-listener-thread (connection)
- (swank/backend:spawn
- (lambda ()
- (with-connection (connection)
- (dcase (swank/backend:receive)
- ((:serve-channel c)
- (loop
- (with-top-level-restart (connection (drop-unprocessed-events c))
- (process-requests nil)))))))
- :name "mrepl thread"))
-
- (defun drop-unprocessed-events (channel)
- (with-slots (mode) channel
- (let ((old-mode mode))
- (setf mode :drop)
- (unwind-protect
- (process-requests t)
- (setf mode old-mode)))
- (send-prompt channel)))
-
- (define-channel-method :process ((c listener-channel) string)
- (log-event ":process ~s~%" string)
- (with-slots (mode remote) c
- (ecase mode
- (:eval (mrepl-eval c string))
- (:read (mrepl-read c string))
- (:drop))))
-
- (defun mrepl-eval (channel string)
- (with-slots (remote env) channel
- (let ((aborted t))
- (with-bindings env
- (unwind-protect
- (let ((result (with-slime-interrupts (read-eval-print string))))
- (send-to-remote-channel remote `(:write-result ,result))
- (setq aborted nil))
- (setf env (loop for (sym) in env
- collect (cons sym (symbol-value sym))))
- (cond (aborted
- (send-to-remote-channel remote `(:evaluation-aborted)))
- (t
- (send-prompt channel))))))))
-
- (defun send-prompt (channel)
- (with-slots (env remote) channel
- (let ((pkg (or (cdr (assoc '*package* env)) *package*))
- (out (cdr (assoc '*standard-output* env)))
- (in (cdr (assoc '*standard-input* env))))
- (when out (force-output out))
- (when in (clear-input in))
- (send-to-remote-channel remote `(:prompt ,(package-name pkg)
- ,(package-prompt pkg))))))
-
- (defun mrepl-read (channel string)
- (with-slots (tag) channel
- (assert tag)
- (throw tag string)))
-
- (defun read-eval-print (string)
- (with-input-from-string (in string)
- (setq / ())
- (loop
- (let* ((form (read in nil in)))
- (cond ((eq form in) (return))
- (t (setq / (multiple-value-list (eval (setq + form))))))))
- (force-output)
- (if /
- (format nil "~{~s~%~}" /)
- "; No values")))
-
- (defun make-listener-output-stream (channel)
- (let ((remote (slot-value channel 'remote)))
- (swank/backend:make-output-stream
- (lambda (string)
- (send-to-remote-channel remote `(:write-string ,string))))))
-
- (defun make-listener-input-stream (channel)
- (swank/backend:make-input-stream (lambda () (read-input channel))))
-
- (defun set-mode (channel new-mode)
- (with-slots (mode remote) channel
- (unless (eq mode new-mode)
- (send-to-remote-channel remote `(:set-read-mode ,new-mode)))
- (setf mode new-mode)))
-
- (defun read-input (channel)
- (with-slots (mode tag remote) channel
- (force-output)
- (let ((old-mode mode)
- (old-tag tag))
- (setf tag (cons nil nil))
- (set-mode channel :read)
- (unwind-protect
- (catch tag (process-requests nil))
- (setf tag old-tag)
- (set-mode channel old-mode)))))
-
- (provide :swank-mrepl)
|