Klimi's new dotfiles with stow.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

334 lignes
12 KiB

il y a 5 ans
  1. ;;; swank-presentation-streams.lisp --- Streams that allow attaching object identities
  2. ;;; to portions of output
  3. ;;;
  4. ;;; Authors: Alan Ruttenberg <alanr-l@mumble.net>
  5. ;;; Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
  6. ;;; Helmut Eller <heller@common-lisp.net>
  7. ;;;
  8. ;;; License: This code has been placed in the Public Domain. All warranties
  9. ;;; are disclaimed.
  10. (in-package :swank)
  11. (eval-when (:compile-toplevel :load-toplevel :execute)
  12. (swank-require :swank-presentations))
  13. ;; This file contains a mechanism for printing to the slime repl so
  14. ;; that the printed result remembers what object it is associated
  15. ;; with. This extends the recording of REPL results.
  16. ;;
  17. ;; There are two methods:
  18. ;;
  19. ;; 1. Depends on the ilisp bridge code being installed and ready to
  20. ;; intercept messages in the printed stream. We encode the
  21. ;; information with a message saying that we are starting to print
  22. ;; an object corresponding to a given id and another when we are
  23. ;; done. The process filter notices these and adds the necessary
  24. ;; text properties to the output.
  25. ;;
  26. ;; 2. Use separate protocol messages :presentation-start and
  27. ;; :presentation-end for sending presentations.
  28. ;;
  29. ;; We only do this if we know we are printing to a slime stream,
  30. ;; checked with the method slime-stream-p. Initially this checks for
  31. ;; the knows slime streams looking at *connections*. In cmucl, sbcl, and
  32. ;; openmcl it also checks if it is a pretty-printing stream which
  33. ;; ultimately prints to a slime stream.
  34. ;;
  35. ;; Method 1 seems to be faster, but the printed escape sequences can
  36. ;; disturb the column counting, and thus the layout in pretty-printing.
  37. ;; We use method 1 when a dedicated output stream is used.
  38. ;;
  39. ;; Method 2 is cleaner and works with pretty printing if the pretty
  40. ;; printers support "annotations". We use method 2 when no dedicated
  41. ;; output stream is used.
  42. ;; Control
  43. (defvar *enable-presenting-readable-objects* t
  44. "set this to enable automatically printing presentations for some
  45. subset of readable objects, such as pathnames." )
  46. ;; doing it
  47. (defmacro presenting-object (object stream &body body)
  48. "What you use in your code. Wrap this around some printing and that text will
  49. be sensitive and remember what object it is in the repl"
  50. `(presenting-object-1 ,object ,stream #'(lambda () ,@body)))
  51. (defmacro presenting-object-if (predicate object stream &body body)
  52. "What you use in your code. Wrap this around some printing and that text will
  53. be sensitive and remember what object it is in the repl if predicate is true"
  54. (let ((continue (gensym)))
  55. `(let ((,continue #'(lambda () ,@body)))
  56. (if ,predicate
  57. (presenting-object-1 ,object ,stream ,continue)
  58. (funcall ,continue)))))
  59. ;;; Get pretty printer patches for SBCL at load (not compile) time.
  60. #+#:disable-dangerous-patching ; #+sbcl
  61. (eval-when (:load-toplevel)
  62. (handler-bind ((simple-error
  63. (lambda (c)
  64. (declare (ignore c))
  65. (let ((clobber-it (find-restart 'sb-kernel::clobber-it)))
  66. (when clobber-it (invoke-restart clobber-it))))))
  67. (sb-ext:without-package-locks
  68. (swank/sbcl::with-debootstrapping
  69. (load (make-pathname
  70. :name "sbcl-pprint-patch"
  71. :type "lisp"
  72. :directory (pathname-directory
  73. swank-loader:*source-directory*)))))))
  74. (let ((last-stream nil)
  75. (last-answer nil))
  76. (defun slime-stream-p (stream)
  77. "Check if stream is one of the slime streams, since if it isn't we
  78. don't want to present anything.
  79. Two special return values:
  80. :DEDICATED -- Output ends up on a dedicated output stream
  81. :REPL-RESULT -- Output ends up on the :repl-results target.
  82. "
  83. (if (eq last-stream stream)
  84. last-answer
  85. (progn
  86. (setq last-stream stream)
  87. (if (eq stream t)
  88. (setq stream *standard-output*))
  89. (setq last-answer
  90. (or #+openmcl
  91. (and (typep stream 'ccl::xp-stream)
  92. ;(slime-stream-p (ccl::xp-base-stream (slot-value stream 'ccl::xp-structure)))
  93. (slime-stream-p (ccl::%svref (slot-value stream 'ccl::xp-structure) 1)))
  94. #+cmu
  95. (or (and (typep stream 'lisp::indenting-stream)
  96. (slime-stream-p (lisp::indenting-stream-stream stream)))
  97. (and (typep stream 'pretty-print::pretty-stream)
  98. (fboundp 'pretty-print::enqueue-annotation)
  99. (let ((slime-stream-p
  100. (slime-stream-p (pretty-print::pretty-stream-target stream))))
  101. (and ;; Printing through CMUCL pretty
  102. ;; streams is only cleanly
  103. ;; possible if we are using the
  104. ;; bridge-less protocol with
  105. ;; annotations, because the bridge
  106. ;; escape sequences disturb the
  107. ;; pretty printer layout.
  108. (not (eql slime-stream-p :dedicated-output))
  109. ;; If OK, return the return value
  110. ;; we got from slime-stream-p on
  111. ;; the target stream (could be
  112. ;; :repl-result):
  113. slime-stream-p))))
  114. #+sbcl
  115. (let ()
  116. (declare (notinline sb-pretty::pretty-stream-target))
  117. (and (typep stream (find-symbol "PRETTY-STREAM" 'sb-pretty))
  118. (find-symbol "ENQUEUE-ANNOTATION" 'sb-pretty)
  119. (not *use-dedicated-output-stream*)
  120. (slime-stream-p (sb-pretty::pretty-stream-target stream))))
  121. #+allegro
  122. (and (typep stream 'excl:xp-simple-stream)
  123. (slime-stream-p (excl::stream-output-handle stream)))
  124. (loop for connection in *connections*
  125. thereis (or (and (eq stream (connection.dedicated-output connection))
  126. :dedicated)
  127. (eq stream (connection.socket-io connection))
  128. (eq stream (connection.user-output connection))
  129. (eq stream (connection.user-io connection))
  130. (and (eq stream (connection.repl-results connection))
  131. :repl-result)))))))))
  132. (defun can-present-readable-objects (&optional stream)
  133. (declare (ignore stream))
  134. *enable-presenting-readable-objects*)
  135. ;; If we are printing to an XP (pretty printing) stream, printing the
  136. ;; escape sequences directly would mess up the layout because column
  137. ;; counting is disturbed. Use "annotations" instead.
  138. #+allegro
  139. (defun write-annotation (stream function arg)
  140. (if (typep stream 'excl:xp-simple-stream)
  141. (excl::schedule-annotation stream function arg)
  142. (funcall function arg stream nil)))
  143. #+cmu
  144. (defun write-annotation (stream function arg)
  145. (if (and (typep stream 'pp:pretty-stream)
  146. (fboundp 'pp::enqueue-annotation))
  147. (pp::enqueue-annotation stream function arg)
  148. (funcall function arg stream nil)))
  149. #+sbcl
  150. (defun write-annotation (stream function arg)
  151. (let ((enqueue-annotation
  152. (find-symbol "ENQUEUE-ANNOTATION" 'sb-pretty)))
  153. (if (and enqueue-annotation
  154. (typep stream (find-symbol "PRETTY-STREAM" 'sb-pretty)))
  155. (funcall enqueue-annotation stream function arg)
  156. (funcall function arg stream nil))))
  157. #-(or allegro cmu sbcl)
  158. (defun write-annotation (stream function arg)
  159. (funcall function arg stream nil))
  160. (defstruct presentation-record
  161. (id)
  162. (printed-p)
  163. (target))
  164. (defun presentation-start (record stream truncatep)
  165. (unless truncatep
  166. ;; Don't start new presentations when nothing is going to be
  167. ;; printed due to *print-lines*.
  168. (let ((pid (presentation-record-id record))
  169. (target (presentation-record-target record)))
  170. (case target
  171. (:dedicated
  172. ;; Use bridge protocol
  173. (write-string "<" stream)
  174. (prin1 pid stream)
  175. (write-string "" stream))
  176. (t
  177. (finish-output stream)
  178. (send-to-emacs `(:presentation-start ,pid ,target)))))
  179. (setf (presentation-record-printed-p record) t)))
  180. (defun presentation-end (record stream truncatep)
  181. (declare (ignore truncatep))
  182. ;; Always end old presentations that were started.
  183. (when (presentation-record-printed-p record)
  184. (let ((pid (presentation-record-id record))
  185. (target (presentation-record-target record)))
  186. (case target
  187. (:dedicated
  188. ;; Use bridge protocol
  189. (write-string ">" stream)
  190. (prin1 pid stream)
  191. (write-string "" stream))
  192. (t
  193. (finish-output stream)
  194. (send-to-emacs `(:presentation-end ,pid ,target)))))))
  195. (defun presenting-object-1 (object stream continue)
  196. "Uses the bridge mechanism with two messages >id and <id. The first one
  197. says that I am starting to print an object with this id. The second says I am finished"
  198. ;; this declare special is to let the compiler know that *record-repl-results* will eventually be
  199. ;; a global special, even if it isn't when this file is compiled/loaded.
  200. (declare (special *record-repl-results*))
  201. (let ((slime-stream-p
  202. (and *record-repl-results* (slime-stream-p stream))))
  203. (if slime-stream-p
  204. (let* ((pid (swank::save-presented-object object))
  205. (record (make-presentation-record :id pid :printed-p nil
  206. :target (if (eq slime-stream-p :repl-result)
  207. :repl-result
  208. nil))))
  209. (write-annotation stream #'presentation-start record)
  210. (multiple-value-prog1
  211. (funcall continue)
  212. (write-annotation stream #'presentation-end record)))
  213. (funcall continue))))
  214. (defun present-repl-results-via-presentation-streams (values)
  215. ;; Override a function in swank.lisp, so that
  216. ;; nested presentations work in the REPL result.
  217. (let ((repl-results (connection.repl-results *emacs-connection*)))
  218. (flet ((send (value)
  219. (presenting-object value repl-results
  220. (prin1 value repl-results))
  221. (terpri repl-results)))
  222. (if (null values)
  223. (progn
  224. (princ "; No value" repl-results)
  225. (terpri repl-results))
  226. (mapc #'send values)))
  227. (finish-output repl-results)))
  228. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  229. #+openmcl
  230. (in-package :ccl)
  231. #+openmcl
  232. (defun monkey-patch-stream-printing ()
  233. (let ((*warn-if-redefine-kernel* nil)
  234. (*warn-if-redefine* nil))
  235. (defun %print-unreadable-object (object stream type id thunk)
  236. (cond ((null stream) (setq stream *standard-output*))
  237. ((eq stream t) (setq stream *terminal-io*)))
  238. (swank::presenting-object object stream
  239. (write-unreadable-start object stream)
  240. (when type
  241. (princ (type-of object) stream)
  242. (stream-write-char stream #\space))
  243. (when thunk
  244. (funcall thunk))
  245. (if id
  246. (%write-address object stream #\>)
  247. (pp-end-block stream ">"))
  248. nil))
  249. (defmethod print-object :around ((pathname pathname) stream)
  250. (swank::presenting-object-if
  251. (swank::can-present-readable-objects stream)
  252. pathname stream (call-next-method))))
  253. (ccl::def-load-pointers clear-presentations ()
  254. (swank::clear-presentation-tables)))
  255. (in-package :swank)
  256. #+cmu
  257. (progn
  258. (fwrappers:define-fwrapper presenting-unreadable-wrapper (object stream type identity body)
  259. (presenting-object object stream
  260. (fwrappers:call-next-function)))
  261. (fwrappers:define-fwrapper presenting-pathname-wrapper (pathname stream depth)
  262. (presenting-object-if (can-present-readable-objects stream) pathname stream
  263. (fwrappers:call-next-function)))
  264. (defun monkey-patch-stream-printing ()
  265. (fwrappers::fwrap 'lisp::%print-pathname #'presenting-pathname-wrapper)
  266. (fwrappers::fwrap 'lisp::%print-unreadable-object #'presenting-unreadable-wrapper)))
  267. #+sbcl
  268. (progn
  269. (defvar *saved-%print-unreadable-object*
  270. (fdefinition 'sb-impl::%print-unreadable-object))
  271. (defun monkey-patch-stream-printing ()
  272. (sb-ext:without-package-locks
  273. (when (eq (fdefinition 'sb-impl::%print-unreadable-object)
  274. *saved-%print-unreadable-object*)
  275. (setf (fdefinition 'sb-impl::%print-unreadable-object)
  276. (lambda (object stream &rest args)
  277. (presenting-object object stream
  278. (apply *saved-%print-unreadable-object*
  279. object stream args)))))
  280. (defmethod print-object :around ((object pathname) stream)
  281. (presenting-object object stream
  282. (call-next-method))))))
  283. #+allegro
  284. (progn
  285. (excl:def-fwrapper presenting-unreadable-wrapper (object stream type identity continuation)
  286. (swank::presenting-object object stream (excl:call-next-fwrapper)))
  287. (excl:def-fwrapper presenting-pathname-wrapper (pathname stream depth)
  288. (presenting-object-if (can-present-readable-objects stream) pathname stream
  289. (excl:call-next-fwrapper)))
  290. (defun monkey-patch-stream-printing ()
  291. (excl:fwrap 'excl::print-unreadable-object-1
  292. 'print-unreadable-present 'presenting-unreadable-wrapper)
  293. (excl:fwrap 'excl::pathname-printer
  294. 'print-pathname-present 'presenting-pathname-wrapper)))
  295. #-(or allegro sbcl cmu openmcl)
  296. (defun monkey-patch-stream-printing ()
  297. (values))
  298. ;; Hook into SWANK.
  299. (defslimefun init-presentation-streams ()
  300. (monkey-patch-stream-printing)
  301. ;; FIXME: import/use swank-repl to avoid package qualifier.
  302. (setq swank-repl:*send-repl-results-function*
  303. 'present-repl-results-via-presentation-streams))
  304. (provide :swank-presentation-streams)