SLIME, LispWorks and UTF-8 revisited
It seems that this problem was discussed before
(http://article.gmane.org/gmane.lisp.lispworks.general/5559)
and this problem had bitten me on day one with Lispworks. Short
description: start SLIME with
:coding-system "utf-8-unix", type at the SLIME REPL something like
(code-char 3000) and get "Lisp
connection terminated". Seems that comm:socket-stream is not working
with external formats, so
slime-lispworks just ignores it.
The solution involving using flexi-streams in the article mentioned
above probably wouldn't be acceptable
for SLIME (it also for some reason didn't work for me on the first try,
but I didn't investigate further), so
I thought that something lightweight might do the trick. This is the
first time I'm seeing LW's user-defined
streams and not everything is clear from the documentation, so I'd very
much like people with more
experience in this area to have look and tell me if I'm doing something
stupid.
The idea is to subclass the comm:socket-stream and provide
external-format processing on just the methods
that are used by SLIME. At the moment it seems to work (checked only
with the utf-8 encoding) both with
and without swank:*use-dedicated-output-stream*.
I do not post this in patch format since it's mostly a request for
comments at the moment and the change is
located in one place only. In file swank-lispworks.lisp the function
accept-connection is replaced by the following
code:
(defclass ef-socket-stream (comm:socket-stream)
((ext-format :reader ext-format :initarg :ext-format)))
;; This seems to greatly affect which generic stream functions
;; are called by the standard ones in CL:
;; (defmethod stream-element-type ((stream ef-socket-stream))
;; ;;'lw:simple-char
;; 'character
;; )
(defun decode-external-string-or-nil (buffer external-format &key (start
0) end)
(handler-bind ((arithmetic-error #'(lambda (condition)
(declare (ignore condition))
(return-from
decode-external-string-or-nil nil) )))
(ef:decode-external-string buffer external-format
:start start :end end)))
(defmethod stream:stream-read-sequence ((stream ef-socket-stream)
sequence start end)
(setf end (or end (length sequence)))
(setf start (or start 0))
(let* ((charlen-to-read (- end start))
(buf (make-array (list (* 4 charlen-to-read))
:element-type '(unsigned-byte 8)))
(pos (call-next-method stream buf 0 end)))
(loop for decoded = (decode-external-string-or-nil buf (ext-format
stream)
:start 0 :end pos)
until (and decoded
(= (length decoded) charlen-to-read))
do
(setf (aref buf pos) (stream:stream-read-byte stream))
(incf pos)
finally
(replace sequence decoded :start1 start :end1 end)
(return end))))
(defmethod stream:stream-write-char ((stream ef-socket-stream) char)
(write-sequence (ef:encode-lisp-string (string char)
(ext-format stream))
stream))
(defmethod stream:stream-write-string ((stream ef-socket-stream) string
&optional start end)
(write-sequence
(ef:encode-lisp-string string (ext-format stream)
:start start :end end)
stream))
(defmethod stream-external-format ((stream ef-socket-stream))
(ext-format stream))
(defimplementation accept-connection (socket
&key external-format buffering
timeout)
(declare (ignore buffering timeout))
(let* ((fd (comm::get-fd-from-socket socket)))
(assert (/= fd -1))
(make-instance 'ef-socket-stream :socket fd :direction :io
:element-type
;;'base-char
'(unsigned-byte 8)
;;'simple-char
:ext-format external-format)))
These changes should probably be protected by #+lispworks5 - when were
the user-defined streams introduced?
Of course it was only meant to be just enough to get SLIME working but
it's still interesting to know what's missing,
what should be done differently, starting with which LW version this is
supposed to work at all, maybe other comments.
Ury