Klimi's new dotfiles with stow.
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

246 řádky
8.3 KiB

před 5 roky
  1. ;;; swank-presentations.lisp --- imitate LispM's presentations
  2. ;;
  3. ;; Authors: Alan Ruttenberg <alanr-l@mumble.net>
  4. ;; Luke Gorrie <luke@synap.se>
  5. ;; Helmut Eller <heller@common-lisp.net>
  6. ;; Matthias Koeppe <mkoeppe@mail.math.uni-magdeburg.de>
  7. ;;
  8. ;; License: This code has been placed in the Public Domain. All warranties
  9. ;; are disclaimed.
  10. ;;
  11. (in-package :swank)
  12. (eval-when (:compile-toplevel :load-toplevel :execute)
  13. (swank-require :swank-repl))
  14. ;;;; Recording and accessing results of computations
  15. (defvar *record-repl-results* t
  16. "Non-nil means that REPL results are saved for later lookup.")
  17. (defvar *object-to-presentation-id*
  18. (make-weak-key-hash-table :test 'eq)
  19. "Store the mapping of objects to numeric identifiers")
  20. (defvar *presentation-id-to-object*
  21. (make-weak-value-hash-table :test 'eql)
  22. "Store the mapping of numeric identifiers to objects")
  23. (defun clear-presentation-tables ()
  24. (clrhash *object-to-presentation-id*)
  25. (clrhash *presentation-id-to-object*))
  26. (defvar *presentation-counter* 0 "identifier counter")
  27. (defvar *nil-surrogate* (make-symbol "nil-surrogate"))
  28. ;; XXX thread safety? [2006-09-13] mb: not in the slightest (fwiw the
  29. ;; rest of slime isn't thread safe either), do we really care?
  30. (defun save-presented-object (object)
  31. "Save OBJECT and return the assigned id.
  32. If OBJECT was saved previously return the old id."
  33. (let ((object (if (null object) *nil-surrogate* object)))
  34. ;; We store *nil-surrogate* instead of nil, to distinguish it from
  35. ;; an object that was garbage collected.
  36. (or (gethash object *object-to-presentation-id*)
  37. (let ((id (incf *presentation-counter*)))
  38. (setf (gethash id *presentation-id-to-object*) object)
  39. (setf (gethash object *object-to-presentation-id*) id)
  40. id))))
  41. (defslimefun lookup-presented-object (id)
  42. "Retrieve the object corresponding to ID.
  43. The secondary value indicates the absence of an entry."
  44. (etypecase id
  45. (integer
  46. ;;
  47. (multiple-value-bind (object foundp)
  48. (gethash id *presentation-id-to-object*)
  49. (cond
  50. ((eql object *nil-surrogate*)
  51. ;; A stored nil object
  52. (values nil t))
  53. ((null object)
  54. ;; Object that was replaced by nil in the weak hash table
  55. ;; when the object was garbage collected.
  56. (values nil nil))
  57. (t
  58. (values object foundp)))))
  59. (cons
  60. (dcase id
  61. ((:frame-var thread-id frame index)
  62. (declare (ignore thread-id)) ; later
  63. (handler-case
  64. (frame-var-value frame index)
  65. (t (condition)
  66. (declare (ignore condition))
  67. (values nil nil))
  68. (:no-error (value)
  69. (values value t))))
  70. ((:inspected-part part-index)
  71. (inspector-nth-part part-index))))))
  72. (defslimefun lookup-presented-object-or-lose (id)
  73. "Get the result of the previous REPL evaluation with ID."
  74. (multiple-value-bind (object foundp) (lookup-presented-object id)
  75. (cond (foundp object)
  76. (t (error "Attempt to access unrecorded object (id ~D)." id)))))
  77. (defslimefun lookup-and-save-presented-object-or-lose (id)
  78. "Get the object associated with ID and save it in the presentation tables."
  79. (let ((obj (lookup-presented-object-or-lose id)))
  80. (save-presented-object obj)))
  81. (defslimefun clear-repl-results ()
  82. "Forget the results of all previous REPL evaluations."
  83. (clear-presentation-tables)
  84. t)
  85. (defun present-repl-results (values)
  86. ;; Override a function in swank.lisp, so that
  87. ;; presentations are associated with every REPL result.
  88. (flet ((send (value)
  89. (let ((id (and *record-repl-results*
  90. (save-presented-object value))))
  91. (send-to-emacs `(:presentation-start ,id :repl-result))
  92. (send-to-emacs `(:write-string ,(prin1-to-string value)
  93. :repl-result))
  94. (send-to-emacs `(:presentation-end ,id :repl-result))
  95. (send-to-emacs `(:write-string ,(string #\Newline)
  96. :repl-result)))))
  97. (fresh-line)
  98. (finish-output)
  99. (if (null values)
  100. (send-to-emacs `(:write-string "; No value" :repl-result))
  101. (mapc #'send values))))
  102. ;;;; Presentation menu protocol
  103. ;;
  104. ;; To define a menu for a type of object, define a method
  105. ;; menu-choices-for-presentation on that object type. This function
  106. ;; should return a list of two element lists where the first element is
  107. ;; the name of the menu action and the second is a function that will be
  108. ;; called if the menu is chosen. The function will be called with 3
  109. ;; arguments:
  110. ;;
  111. ;; choice: The string naming the action from above
  112. ;;
  113. ;; object: The object
  114. ;;
  115. ;; id: The presentation id of the object
  116. ;;
  117. ;; You might want append (when (next-method-p) (call-next-method)) to
  118. ;; pick up the Menu actions of superclasses.
  119. ;;
  120. (defvar *presentation-active-menu* nil)
  121. (defun menu-choices-for-presentation-id (id)
  122. (multiple-value-bind (ob presentp) (lookup-presented-object id)
  123. (cond ((not presentp) 'not-present)
  124. (t
  125. (let ((menu-and-actions (menu-choices-for-presentation ob)))
  126. (setq *presentation-active-menu* (cons id menu-and-actions))
  127. (mapcar 'car menu-and-actions))))))
  128. (defun swank-ioify (thing)
  129. (cond ((keywordp thing) thing)
  130. ((and (symbolp thing)(not (find #\: (symbol-name thing))))
  131. (intern (symbol-name thing) 'swank-io-package))
  132. ((consp thing) (cons (swank-ioify (car thing))
  133. (swank-ioify (cdr thing))))
  134. (t thing)))
  135. (defun execute-menu-choice-for-presentation-id (id count item)
  136. (let ((ob (lookup-presented-object id)))
  137. (assert (equal id (car *presentation-active-menu*)) ()
  138. "Bug: Execute menu call for id ~a but menu has id ~a"
  139. id (car *presentation-active-menu*))
  140. (let ((action (second (nth (1- count) (cdr *presentation-active-menu*)))))
  141. (swank-ioify (funcall action item ob id)))))
  142. (defgeneric menu-choices-for-presentation (object)
  143. (:method (ob) (declare (ignore ob)) nil)) ; default method
  144. ;; Pathname
  145. (defmethod menu-choices-for-presentation ((ob pathname))
  146. (let* ((file-exists (ignore-errors (probe-file ob)))
  147. (lisp-type (make-pathname :type "lisp"))
  148. (source-file (and (not (member (pathname-type ob) '("lisp" "cl")
  149. :test 'equal))
  150. (let ((source (merge-pathnames lisp-type ob)))
  151. (and (ignore-errors (probe-file source))
  152. source))))
  153. (fasl-file (and file-exists
  154. (equal (ignore-errors
  155. (namestring
  156. (truename
  157. (compile-file-pathname
  158. (merge-pathnames lisp-type ob)))))
  159. (namestring (truename ob))))))
  160. (remove nil
  161. (list*
  162. (and (and file-exists (not fasl-file))
  163. (list "Edit this file"
  164. (lambda(choice object id)
  165. (declare (ignore choice id))
  166. (ed-in-emacs (namestring (truename object)))
  167. nil)))
  168. (and file-exists
  169. (list "Dired containing directory"
  170. (lambda (choice object id)
  171. (declare (ignore choice id))
  172. (ed-in-emacs (namestring
  173. (truename
  174. (merge-pathnames
  175. (make-pathname :name "" :type "")
  176. object))))
  177. nil)))
  178. (and fasl-file
  179. (list "Load this fasl file"
  180. (lambda (choice object id)
  181. (declare (ignore choice id object))
  182. (load ob)
  183. nil)))
  184. (and fasl-file
  185. (list "Delete this fasl file"
  186. (lambda (choice object id)
  187. (declare (ignore choice id object))
  188. (let ((nt (namestring (truename ob))))
  189. (when (y-or-n-p-in-emacs "Delete ~a? " nt)
  190. (delete-file nt)))
  191. nil)))
  192. (and source-file
  193. (list "Edit lisp source file"
  194. (lambda (choice object id)
  195. (declare (ignore choice id object))
  196. (ed-in-emacs (namestring (truename source-file)))
  197. nil)))
  198. (and source-file
  199. (list "Load lisp source file"
  200. (lambda(choice object id)
  201. (declare (ignore choice id object))
  202. (load source-file)
  203. nil)))
  204. (and (next-method-p) (call-next-method))))))
  205. (defmethod menu-choices-for-presentation ((ob function))
  206. (list (list "Disassemble"
  207. (lambda (choice object id)
  208. (declare (ignore choice id))
  209. (disassemble object)))))
  210. (defslimefun inspect-presentation (id reset-p)
  211. (let ((what (lookup-presented-object-or-lose id)))
  212. (when reset-p
  213. (reset-inspector))
  214. (inspect-object what)))
  215. (defslimefun init-presentations ()
  216. ;; FIXME: import/use swank-repl to avoid package qualifier.
  217. (setq swank-repl:*send-repl-results-function* 'present-repl-results))
  218. (provide :swank-presentations)