Update of /project/mcclim/cvsroot/mcclim/Apps/Inspector In directory common-lisp.net:/tmp/cvs-serv5675
Modified Files: inspector.lisp Log Message: Added class-like inspection of structures, characters are inspected non-annoyingly, documentation strings are displayed where possible, the source code has been commented and documented more, and some other features and bug fixes have been added.
Date: Thu Feb 3 21:14:57 2005 Author: pscott
Index: mcclim/Apps/Inspector/inspector.lisp diff -u mcclim/Apps/Inspector/inspector.lisp:1.9 mcclim/Apps/Inspector/inspector.lisp:1.10 --- mcclim/Apps/Inspector/inspector.lisp:1.9 Wed Feb 2 11:16:59 2005 +++ mcclim/Apps/Inspector/inspector.lisp Thu Feb 3 21:14:57 2005 @@ -24,8 +24,11 @@
(in-package :clouseau)
+(define-modify-macro togglef () not) + (define-application-frame inspector () ((dico :initform (make-hash-table) :reader dico) + (cons-cell-dico :initform (make-hash-table) :reader cons-cell-dico) (obj :initarg :obj :reader obj)) (:pointer-documentation t) (:panes @@ -55,16 +58,23 @@ (run-frame-top-level (make-application-frame 'inspector :obj obj))))
-(defparameter *inspected-objects* '()) +(defparameter *inspected-objects* '() + "A list of objects which are currently being inspected with +INSPECT-OBJECT") + +(defgeneric inspect-object-briefly (object pane) + (:documentation "Inspect an object in a short form, displaying this +on PANE. For example, rather than displaying all the slots of a class, +only the class name would be shown."))
-(defgeneric inspect-object-briefly (object pane)) -(defgeneric inspect-object (object pane)) +(defgeneric inspect-object (object pane) + (:documentation "Inspect an object, displaying it on PANE"))
(defmethod inspect-object :around (object pane) (cond ((member object *inspected-objects*) (with-output-as-presentation (pane object (presentation-type-of object)) - (princ "==="))) + (princ "==="))) ; Prevent infinite loops ((not (gethash object (dico *application-frame*))) (inspect-object-briefly object pane)) (t @@ -84,6 +94,8 @@
(define-presentation-type settable-slot () :inherit-from t) +(define-presentation-type cons () + :inherit-from t)
(define-presentation-method present (object (type settable-slot) stream @@ -93,6 +105,11 @@ (format stream "~s" (cdr object)))
(defmacro inspector-table (header &body body) + "Present OBJECT (captured from environment) in tabular form, with +HEADER printed in a box at the top. BODY should output the rows of the +table using INSPECTOR-TABLE-ROW. Also capured from the macro's +environment is PANE, which is the pane on which the table will be +drawn." `(with-output-as-presentation (pane object (presentation-type-of object)) (formatting-table (pane) @@ -102,32 +119,54 @@ ,header)) (formatting-cell (pane) (formatting-table (pane) - ,@body)))))) + ,@body)))) + (print-documentation object pane)))
(defmacro inspector-table-row (left right) + "Output a table row with two items, LEFT and RIGHT, in the +environment created by INSPECTOR-TABLE." `(formatting-row (pane) (formatting-cell (pane :align-x :right) ,left) (formatting-cell (pane) ,right)))
+(defun print-documentation (object pane) + "Print OBJECT's documentation, if any, to PANE" + (when (handler-bind ((warning #'muffle-warning)) + (documentation object t)) + (format pane "~&Documentation: ~A" (documentation object t)))) + +(defun inspect-structure-or-object (object pane) + "Inspect a structure or an object. Since both can be inspected in +roughly the same way, the common code is in this function, which is +called by the INSPECT-OBJECT methods for both standard objects and +structure objects." + (let ((class (class-of object))) + (inspector-table + (print (class-name class) pane) + (loop for slot in (reverse (clim-mop:class-slots class)) + do (let ((slot-name (clim-mop:slot-definition-name slot))) + (inspector-table-row + (with-output-as-presentation + (pane (cons object slot-name) 'settable-slot) + (format pane "~a:" slot-name)) + (inspect-object (slot-value object slot-name) pane))))))) + (defmethod inspect-object-briefly ((object standard-object) pane) (with-output-as-presentation (pane object (presentation-type-of object)) (format pane "instance of ~S" (class-name (class-of object))))) (defmethod inspect-object ((object standard-object) pane) - (let ((class (class-of object))) - (inspector-table - (print (class-name class) pane) - (loop for slot in (reverse (clim-mop:class-slots class)) - do (let ((slot-name (clim-mop:slot-definition-name slot))) - (inspector-table-row - (with-output-as-presentation - (pane (cons object slot-name) 'settable-slot) - (format pane "~a:" slot-name)) - (inspect-object (slot-value object slot-name) pane))))))) + (inspect-structure-or-object object pane))
-(defmethod inspect-object ((object cons) pane) +(defmethod inspect-object ((object structure-object) pane) + (inspect-structure-or-object object pane)) + +(defun inspect-cons-as-cells (object pane) + "Inspect a cons cell in a fancy graphical way. The inconvenient part +is that this necessarily involves quite a bit of clicking to show a +moderately-sized list." (if (null (cdr object)) (formatting-table (pane) (formatting-column (pane) @@ -157,6 +196,44 @@ (formatting-cell (pane) (inspect-object (cdr object) pane))))))
+(defun inspect-cons-as-list (object pane) + "Inspect a cons cell in a traditional, plain-text format. The only +difference between this and simply using the Lisp printer is that this +code takes advantage of CLIM's tables and presentations to make the +list as interactive as you would expect." + (with-output-as-presentation + (pane object 'cons) + (formatting-table (pane) + (formatting-row (pane) + (formatting-cell (pane) + (princ "(" pane)) + (do + ((length 0 (1+ length)) + (cdr (cdr object) (cdr cdr)) + (car (car object) (car cdr))) + ((cond ((eq nil cdr) + (formatting-cell (pane) (inspect-object car pane)) + (formatting-cell (pane) (princ ")" pane)) + t) + ((not (consp cdr)) + (formatting-cell (pane) (inspect-object car pane)) + (formatting-cell (pane) (princ "." pane)) + (formatting-cell (pane) (inspect-object cdr pane)) + (formatting-cell (pane) (princ ")" pane)) + t) + ((>= length *print-length*) + (formatting-cell (pane) (inspect-object car pane)) + (formatting-cell (pane) (princ "..." pane)) + t) + (t nil))) + (formatting-cell (pane) (inspect-object car pane))))))) + +(defmethod inspect-object ((object cons) pane) + (if (gethash object (cons-cell-dico *application-frame*)) + (inspect-cons-as-cells object pane) + (inspect-cons-as-list object pane))) + + (defmethod inspect-object-briefly ((object hash-table) pane) (with-output-as-presentation (pane object (presentation-type-of object)) @@ -185,6 +262,33 @@ do (formatting-cell (pane) (format pane "~s " (class-name specializer)))))))))
+(defun pretty-print-function (fun) + "Print a function in a readable way, returning a string. On most +implementations this just uses the standard Lisp printer, but it can +use implementation-specific functions to be more informative." + (flet ((generic-print (fun) + (with-output-to-string (string) + (prin1 fun string)))) + ;; If we have SBCL, try to do fancy formatting. If anything goes + ;; wrong with that, fall back on ugly standard PRIN1. + #+sbcl (handler-case (format nil "~A ~S" + (sb-impl::%simple-fun-name fun) + (sb-impl::%simple-fun-arglist fun)) + (error () (generic-print fun))) + ;; FIXME: Other Lisp implementations have ways of getting this + ;; information. If you want a better inspector on a non-SBCL Lisp, + ;; please add code for it and send patches. + #-sbcl (generic-print fun))) + +(defmethod inspect-object ((object function) pane) + (with-output-as-presentation + (pane object (presentation-type-of object)) + (format pane "Function: ~A" + (pretty-print-function object)) + #+sbcl (format pane "~&Type: ~A" + (sb-impl::%simple-fun-type object)) + (print-documentation object pane))) + (defmethod inspect-object-briefly ((object package) pane) (with-output-as-presentation (pane object (presentation-type-of object)) @@ -231,7 +335,10 @@ (pane object (presentation-type-of object)) (print object)))
-(defmethod inspect-object ((object complex) pane) +(defun inspect-complex (object pane) + "Inspect a complex number. Since complex numbers should be inspected +the same way briefly and fully, this function can be called by both of +them." (with-output-as-presentation (pane object (presentation-type-of object)) (formatting-table (pane) @@ -245,6 +352,12 @@ (formatting-cell (pane) (princ ")" pane))))))
+(defmethod inspect-object-briefly ((object complex) pane) + (inspect-complex object pane)) + +(defmethod inspect-object ((object complex) pane) + (inspect-complex object pane)) + (defmethod inspect-object ((object float) pane) (inspector-table (format pane "float ~S" object) @@ -267,6 +380,7 @@ (with-output-as-presentation (pane object (presentation-type-of object)) (print object))) + (defmethod inspect-object ((object symbol) pane) (inspector-table (format pane "Symbol ~S" (symbol-name object)) @@ -287,8 +401,17 @@ (princ "propery list:") (dolist (property (symbol-plist object)) (inspect-object property pane))))) +(make-instance 'packrat) +;; Characters are so short that displaying them as "..." takes almost +;; as much space as just showing them, and this way is more +;; informative. +(defmethod inspect-object-briefly ((object character) pane) + (with-output-as-presentation + (pane object (presentation-type-of object)) + (print object pane)))
(defun display-app (frame pane) + "Display the APP frame of the inspector" (inspect-object (obj frame) pane))
(define-inspector-command (com-quit :name t) () @@ -297,13 +420,16 @@ (define-inspector-command (com-inspect :name t) () (let ((obj (accept t :prompt "Select an object"))) (clim-sys:make-process #'(lambda () (inspector obj)) - :name "inspector"))) + :name "Inspector Clouseau"))) + +(define-inspector-command (com-toggle-show-list-cells :name t) + ((obj 'cons :gesture :select :prompt "Select a cons or list")) + (togglef (gethash obj (cons-cell-dico *application-frame*))))
(define-inspector-command (com-toggle-inspect :name t) ((obj t :gesture :select :prompt "Select an object")) (unless (eq obj (obj *application-frame*)) - (setf (gethash obj (dico *application-frame*)) - (not (gethash obj (dico *application-frame*)))))) + (togglef (gethash obj (dico *application-frame*)))))
(define-inspector-command (com-remove-method :name t) ((obj 'method :gesture :delete :prompt "Remove method")) @@ -313,3 +439,33 @@ ((slot 'settable-slot :gesture :select :prompt "Set slot")) (setf (slot-value (car slot) (cdr slot)) (accept t :prompt "New slot value"))) + +(defun slot-documentation (class slot) + "Returns the documentation of a slot of a class, or nil. There is, +unfortunately, no portable way to do this, but the MOP is +semi-portable and we can use it. To complicate things even more, some +implementations have unpleasant oddities in the way they store slot +documentation. For example, in SBCL slot documentation is only +available in direct slots." + (let ((slot-object (find slot (clim-mop:class-direct-slots class) + :key #'clim-mop:slot-definition-name))) + (if slot-object + (documentation slot-object t) + (when (clim-mop:class-direct-superclasses class) + (find-if #'identity + (mapcar #'(lambda (class) + (slot-documentation class slot)) + (clim-mop:class-direct-superclasses class))))))) + +(define-inspector-command (com-describe-slot :name t) + ((slot 'settable-slot :gesture :describe :prompt "Describe slot")) + (destructuring-bind (object . slot-name) slot + (let* ((stream (get-frame-pane *application-frame* 'int)) + (class (class-of object)) + (documentation (slot-documentation class slot-name)) + (slot-object (find slot-name (clim-mop:class-slots class) + :key #'clim-mop:slot-definition-name))) + (when documentation + (format stream "~&Documentation: ~A~%" documentation)) + (format stream "~&Type: ~S~%" + (clim-mop:slot-definition-type slot-object))))) \ No newline at end of file