PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/emacs/emacs.d.symlink/elpa/slime-20140305.1205/contrib/swank-presentation-streams.lisp

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