Path: blob/master/elisp/slime/contrib/swank-fancy-inspector.lisp
990 views
;;; swank-fancy-inspector.lisp --- Fancy inspector for CLOS objects1;;2;; Author: Marco Baringer <[email protected]> and others3;; License: Public Domain4;;56(in-package :swank)78(defmethod emacs-inspect ((symbol symbol))9(let ((package (symbol-package symbol)))10(multiple-value-bind (_symbol status)11(and package (find-symbol (string symbol) package))12(declare (ignore _symbol))13(append14(label-value-line "Its name is" (symbol-name symbol))15;;16;; Value17(cond ((boundp symbol)18(append19(label-value-line (if (constantp symbol)20"It is a constant of value"21"It is a global variable bound to")22(symbol-value symbol) :newline nil)23;; unbinding constants might be not a good idea, but24;; implementations usually provide a restart.25`(" " (:action "[unbind it]"26,(lambda () (makunbound symbol))))27'((:newline))))28(t '("It is unbound." (:newline))))29(docstring-ispec "Documentation" symbol 'variable)30(multiple-value-bind (expansion definedp) (macroexpand symbol)31(if definedp32(label-value-line "It is a symbol macro with expansion"33expansion)))34;;35;; Function36(if (fboundp symbol)37(append (if (macro-function symbol)38`("It a macro with macro-function: "39(:value ,(macro-function symbol)))40`("It is a function: "41(:value ,(symbol-function symbol))))42`(" " (:action "[unbind it]"43,(lambda () (fmakunbound symbol))))44`((:newline)))45`("It has no function value." (:newline)))46(docstring-ispec "Function Documentation" symbol 'function)47(when (compiler-macro-function symbol)4849(append50(label-value-line "It also names the compiler macro"51(compiler-macro-function symbol) :newline nil)52`(" " (:action "[remove it]"53,(lambda ()54(setf (compiler-macro-function symbol) nil)))55(:newline))))56(docstring-ispec "Compiler Macro Documentation"57symbol 'compiler-macro)58;;59;; Package60(if package61`("It is " ,(string-downcase (string status))62" to the package: "63(:value ,package ,(package-name package))64,@(if (eq :internal status)65`(" "66(:action "[export it]"67,(lambda () (export symbol package)))))68" "69(:action "[unintern it]"70,(lambda () (unintern symbol package)))71(:newline))72'("It is a non-interned symbol." (:newline)))73;;74;; Plist75(label-value-line "Property list" (symbol-plist symbol))76;;77;; Class78(if (find-class symbol nil)79`("It names the class "80(:value ,(find-class symbol) ,(string symbol))81" "82(:action "[remove]"83,(lambda () (setf (find-class symbol) nil)))84(:newline)))85;;86;; More package87(if (find-package symbol)88(label-value-line "It names the package" (find-package symbol)))89))))9091(defun docstring-ispec (label object kind)92"Return a inspector spec if OBJECT has a docstring of of kind KIND."93(let ((docstring (documentation object kind)))94(cond ((not docstring) nil)95((< (+ (length label) (length docstring))9675)97(list label ": " docstring '(:newline)))98(t99(list label ": " '(:newline) " " docstring '(:newline))))))100101(unless (find-method #'emacs-inspect '() (list (find-class 'function)) nil)102(defmethod emacs-inspect ((f function))103(inspect-function f)))104105(defun inspect-function (f)106(append107(label-value-line "Name" (function-name f))108`("Its argument list is: "109,(inspector-princ (arglist f)) (:newline))110(docstring-ispec "Documentation" f t)111(if (function-lambda-expression f)112(label-value-line "Lambda Expression"113(function-lambda-expression f)))))114115(defun method-specializers-for-inspect (method)116"Return a \"pretty\" list of the method's specializers. Normal117specializers are replaced by the name of the class, eql118specializers are replaced by `(eql ,object)."119(mapcar (lambda (spec)120(typecase spec121(swank-mop:eql-specializer122`(eql ,(swank-mop:eql-specializer-object spec)))123(t (swank-mop:class-name spec))))124(swank-mop:method-specializers method)))125126(defun method-for-inspect-value (method)127"Returns a \"pretty\" list describing METHOD. The first element128of the list is the name of generic-function method is129specialiazed on, the second element is the method qualifiers,130the rest of the list is the method's specialiazers (as per131method-specializers-for-inspect)."132(append (list (swank-mop:generic-function-name133(swank-mop:method-generic-function method)))134(swank-mop:method-qualifiers method)135(method-specializers-for-inspect method)))136137(defmethod emacs-inspect ((object standard-object))138(let ((class (class-of object)))139`("Class: " (:value ,class) (:newline)140,@(all-slots-for-inspector object))))141142(defvar *gf-method-getter* 'methods-by-applicability143"This function is called to get the methods of a generic function.144The default returns the method sorted by applicability.145See `methods-by-applicability'.")146147(defun specializer< (specializer1 specializer2)148"Return true if SPECIALIZER1 is more specific than SPECIALIZER2."149(let ((s1 specializer1) (s2 specializer2) )150(cond ((typep s1 'swank-mop:eql-specializer)151(not (typep s2 'swank-mop:eql-specializer)))152(t153(flet ((cpl (class)154(and (swank-mop:class-finalized-p class)155(swank-mop:class-precedence-list class))))156(member s2 (cpl s1)))))))157158(defun methods-by-applicability (gf)159"Return methods ordered by most specific argument types.160161`method-specializer<' is used for sorting."162;; FIXME: argument-precedence-order and qualifiers are ignored.163(labels ((method< (meth1 meth2)164(loop for s1 in (swank-mop:method-specializers meth1)165for s2 in (swank-mop:method-specializers meth2)166do (cond ((specializer< s2 s1) (return nil))167((specializer< s1 s2) (return t))))))168(stable-sort (copy-seq (swank-mop:generic-function-methods gf)) #'method<)))169170(defun abbrev-doc (doc &optional (maxlen 80))171"Return the first sentence of DOC, but not more than MAXLAN characters."172(subseq doc 0 (min (1+ (or (position #\. doc) (1- maxlen)))173maxlen174(length doc))))175176(defstruct (inspector-checklist (:conc-name checklist.)177(:constructor %make-checklist (buttons)))178(buttons nil :type (or null simple-vector))179(count 0))180181(defun make-checklist (n)182(%make-checklist (make-array n :initial-element nil)))183184(defun reinitialize-checklist (checklist)185;; Along this counter the buttons are created, so we have to186;; initialize it to 0 everytime the inspector page is redisplayed.187(setf (checklist.count checklist) 0)188checklist)189190(defun make-checklist-button (checklist)191(let ((buttons (checklist.buttons checklist))192(i (checklist.count checklist)))193(incf (checklist.count checklist))194`(:action ,(if (svref buttons i)195"[X]"196"[ ]")197,#'(lambda ()198(setf (svref buttons i) (not (svref buttons i))))199:refreshp t)))200201(defmacro do-checklist ((idx checklist) &body body)202"Iterate over all set buttons in CHECKLIST."203(let ((buttons (gensym "buttons")))204`(let ((,buttons (checklist.buttons ,checklist)))205(dotimes (,idx (length ,buttons))206(when (svref ,buttons ,idx)207,@body)))))208209(defun box (thing) (cons :box thing))210(defun ref (box)211(assert (eq (car box) :box))212(cdr box))213(defun (setf ref) (value box)214(assert (eq (car box) :box))215(setf (cdr box) value))216217(defvar *inspector-slots-default-order* :alphabetically218"Accepted values: :alphabetically and :unsorted")219220(defvar *inspector-slots-default-grouping* :all221"Accepted values: :inheritance and :all")222223(defgeneric all-slots-for-inspector (object))224225(defmethod all-slots-for-inspector ((object standard-object))226(let* ((class (class-of object))227(direct-slots (swank-mop:class-direct-slots class))228(effective-slots (swank-mop:class-slots class))229(longest-slot-name-length230(loop for slot :in effective-slots231maximize (length (symbol-name232(swank-mop:slot-definition-name slot)))))233(checklist234(reinitialize-checklist235(ensure-istate-metadata object :checklist236(make-checklist (length effective-slots)))))237(grouping-kind238;; We box the value so we can re-set it.239(ensure-istate-metadata object :grouping-kind240(box *inspector-slots-default-grouping*)))241(sort-order242(ensure-istate-metadata object :sort-order243(box *inspector-slots-default-order*)))244(sort-predicate (ecase (ref sort-order)245(:alphabetically #'string<)246(:unsorted (constantly nil))))247(sorted-slots (sort (copy-seq effective-slots)248sort-predicate249:key #'swank-mop:slot-definition-name))250(effective-slots251(ecase (ref grouping-kind)252(:all sorted-slots)253(:inheritance (stable-sort-by-inheritance sorted-slots class sort-predicate)))))254`("--------------------"255(:newline)256" Group slots by inheritance "257(:action ,(ecase (ref grouping-kind)258(:all "[ ]")259(:inheritance "[X]"))260,(lambda ()261;; We have to do this as the order of slots will262;; be sorted differently.263(fill (checklist.buttons checklist) nil)264(setf (ref grouping-kind)265(ecase (ref grouping-kind)266(:all :inheritance)267(:inheritance :all))))268:refreshp t)269(:newline)270" Sort slots alphabetically "271(:action ,(ecase (ref sort-order)272(:unsorted "[ ]")273(:alphabetically "[X]"))274,(lambda ()275(fill (checklist.buttons checklist) nil)276(setf (ref sort-order)277(ecase (ref sort-order)278(:unsorted :alphabetically)279(:alphabetically :unsorted))))280:refreshp t)281(:newline)282,@ (case (ref grouping-kind)283(:all284`((:newline)285"All Slots:"286(:newline)287,@(make-slot-listing checklist object class288effective-slots direct-slots289longest-slot-name-length)))290(:inheritance291(list-all-slots-by-inheritance checklist object class292effective-slots direct-slots293longest-slot-name-length)))294(:newline)295(:action "[set value]"296,(lambda ()297(do-checklist (idx checklist)298(query-and-set-slot class object299(nth idx effective-slots))))300:refreshp t)301" "302(:action "[make unbound]"303,(lambda ()304(do-checklist (idx checklist)305(swank-mop:slot-makunbound-using-class306class object (nth idx effective-slots))))307:refreshp t)308(:newline))))309310(defun list-all-slots-by-inheritance (checklist object class effective-slots311direct-slots longest-slot-name-length)312(flet ((slot-home-class (slot)313(slot-home-class-using-class slot class)))314(let ((current-slots '()))315(append316(loop for slot in effective-slots317for previous-home-class = (slot-home-class slot) then home-class318for home-class = previous-home-class then (slot-home-class slot)319if (eq home-class previous-home-class)320do (push slot current-slots)321else322collect '(:newline)323and collect (format nil "~A:" (class-name previous-home-class))324and collect '(:newline)325and append (make-slot-listing checklist object class326(nreverse current-slots) direct-slots327longest-slot-name-length)328and do (setf current-slots (list slot)))329(and current-slots330`((:newline)331,(format nil "~A:"332(class-name (slot-home-class-using-class333(car current-slots) class)))334(:newline)335,@(make-slot-listing checklist object class336(nreverse current-slots) direct-slots337longest-slot-name-length)))))))338339(defun make-slot-listing (checklist object class effective-slots direct-slots340longest-slot-name-length)341(flet ((padding-for (slot-name)342(make-string (- longest-slot-name-length (length slot-name))343:initial-element #\Space)))344(loop345for effective-slot :in effective-slots346for direct-slot = (find (swank-mop:slot-definition-name effective-slot)347direct-slots :key #'swank-mop:slot-definition-name)348for slot-name = (inspector-princ349(swank-mop:slot-definition-name effective-slot))350collect (make-checklist-button checklist)351collect " "352collect `(:value ,(if direct-slot353(list direct-slot effective-slot)354effective-slot)355,slot-name)356collect (padding-for slot-name)357collect " = "358collect (slot-value-for-inspector class object effective-slot)359collect '(:newline))))360361(defgeneric slot-value-for-inspector (class object slot)362(:method (class object slot)363(let ((boundp (swank-mop:slot-boundp-using-class class object slot)))364(if boundp365`(:value ,(swank-mop:slot-value-using-class class object slot))366"#<unbound>"))))367368(defun slot-home-class-using-class (slot class)369(let ((slot-name (swank-mop:slot-definition-name slot)))370(loop for class in (reverse (swank-mop:class-precedence-list class))371thereis (and (member slot-name (swank-mop:class-direct-slots class)372:key #'swank-mop:slot-definition-name :test #'eq)373class))))374375(defun stable-sort-by-inheritance (slots class predicate)376(stable-sort slots predicate377:key #'(lambda (s)378(class-name (slot-home-class-using-class s class)))))379380(defun query-and-set-slot (class object slot)381(let* ((slot-name (swank-mop:slot-definition-name slot))382(value-string (read-from-minibuffer-in-emacs383(format nil "Set slot ~S to (evaluated) : "384slot-name))))385(when (and value-string (not (string= value-string "")))386(with-simple-restart (abort "Abort setting slot ~S" slot-name)387(setf (swank-mop:slot-value-using-class class object slot)388(eval (read-from-string value-string)))))))389390391(defmethod emacs-inspect ((gf standard-generic-function))392(flet ((lv (label value) (label-value-line label value)))393(append394(lv "Name" (swank-mop:generic-function-name gf))395(lv "Arguments" (swank-mop:generic-function-lambda-list gf))396(docstring-ispec "Documentation" gf t)397(lv "Method class" (swank-mop:generic-function-method-class gf))398(lv "Method combination"399(swank-mop:generic-function-method-combination gf))400`("Methods: " (:newline))401(loop for method in (funcall *gf-method-getter* gf) append402`((:value ,method ,(inspector-princ403;; drop the name of the GF404(cdr (method-for-inspect-value method))))405" "406(:action "[remove method]"407,(let ((m method)) ; LOOP reassigns method408(lambda ()409(remove-method gf m))))410(:newline)))411`((:newline))412(all-slots-for-inspector gf))))413414(defmethod emacs-inspect ((method standard-method))415`("Method defined on the generic function "416(:value ,(swank-mop:method-generic-function method)417,(inspector-princ418(swank-mop:generic-function-name419(swank-mop:method-generic-function method))))420(:newline)421,@(docstring-ispec "Documentation" method t)422"Lambda List: " (:value ,(swank-mop:method-lambda-list method))423(:newline)424"Specializers: " (:value ,(swank-mop:method-specializers method)425,(inspector-princ (method-specializers-for-inspect method)))426(:newline)427"Qualifiers: " (:value ,(swank-mop:method-qualifiers method))428(:newline)429"Method function: " (:value ,(swank-mop:method-function method))430(:newline)431,@(all-slots-for-inspector method)))432433(defmethod emacs-inspect ((class standard-class))434`("Name: " (:value ,(class-name class))435(:newline)436"Super classes: "437,@(common-seperated-spec (swank-mop:class-direct-superclasses class))438(:newline)439"Direct Slots: "440,@(common-seperated-spec441(swank-mop:class-direct-slots class)442(lambda (slot)443`(:value ,slot ,(inspector-princ (swank-mop:slot-definition-name slot)))))444(:newline)445"Effective Slots: "446,@(if (swank-mop:class-finalized-p class)447(common-seperated-spec448(swank-mop:class-slots class)449(lambda (slot)450`(:value ,slot ,(inspector-princ451(swank-mop:slot-definition-name slot)))))452`("#<N/A (class not finalized)> "453(:action "[finalize]"454,(lambda () (swank-mop:finalize-inheritance class)))))455(:newline)456,@(let ((doc (documentation class t)))457(when doc458`("Documentation:" (:newline) ,(inspector-princ doc) (:newline))))459"Sub classes: "460,@(common-seperated-spec (swank-mop:class-direct-subclasses class)461(lambda (sub)462`(:value ,sub ,(inspector-princ (class-name sub)))))463(:newline)464"Precedence List: "465,@(if (swank-mop:class-finalized-p class)466(common-seperated-spec (swank-mop:class-precedence-list class)467(lambda (class)468`(:value ,class ,(inspector-princ (class-name class)))))469'("#<N/A (class not finalized)>"))470(:newline)471,@(when (swank-mop:specializer-direct-methods class)472`("It is used as a direct specializer in the following methods:" (:newline)473,@(loop474for method in (sort (copy-seq (swank-mop:specializer-direct-methods class))475#'string< :key (lambda (x)476(symbol-name477(let ((name (swank-mop::generic-function-name478(swank-mop::method-generic-function x))))479(if (symbolp name) name (second name))))))480collect " "481collect `(:value ,method ,(inspector-princ (method-for-inspect-value method)))482collect '(:newline)483if (documentation method t)484collect " Documentation: " and485collect (abbrev-doc (documentation method t)) and486collect '(:newline))))487"Prototype: " ,(if (swank-mop:class-finalized-p class)488`(:value ,(swank-mop:class-prototype class))489'"#<N/A (class not finalized)>")490(:newline)491,@(all-slots-for-inspector class)))492493(defmethod emacs-inspect ((slot swank-mop:standard-slot-definition))494`("Name: " (:value ,(swank-mop:slot-definition-name slot))495(:newline)496,@(when (swank-mop:slot-definition-documentation slot)497`("Documentation:" (:newline)498(:value ,(swank-mop:slot-definition-documentation slot))499(:newline)))500"Init args: " (:value ,(swank-mop:slot-definition-initargs slot)) (:newline)501"Init form: " ,(if (swank-mop:slot-definition-initfunction slot)502`(:value ,(swank-mop:slot-definition-initform slot))503"#<unspecified>") (:newline)504"Init function: " (:value ,(swank-mop:slot-definition-initfunction slot))505(:newline)506,@(all-slots-for-inspector slot)))507508509;; Wrapper structure over the list of symbols of a package that should510;; be displayed with their respective classification flags. This is511;; because we need a unique type to dispatch on in EMACS-INSPECT.512;; Used by the Inspector for packages.513(defstruct (%package-symbols-container (:conc-name %container.)514(:constructor %%make-package-symbols-container))515title ;; A string; the title of the inspector page in Emacs.516description ;; A list of renderable objects; used as description.517symbols ;; A list of symbols. Supposed to be sorted alphabetically.518grouping-kind ;; Either :SYMBOL or :CLASSIFICATION. Cf. MAKE-SYMBOLS-LISTING.519)520521(defun %make-package-symbols-container (&key title description symbols)522(%%make-package-symbols-container :title title :description description523:symbols symbols :grouping-kind :symbol))524525(defgeneric make-symbols-listing (grouping-kind symbols))526527(defmethod make-symbols-listing ((grouping-kind (eql :symbol)) symbols)528"Returns an object renderable by Emacs' inspector side that529alphabetically lists all the symbols in SYMBOLS together with a530concise string representation of what each symbol531represents (see SYMBOL-CLASSIFICATION-STRING)"532(let ((max-length (loop for s in symbols maximizing (length (symbol-name s))))533(distance 10)) ; empty distance between name and classification534(flet ((string-representations (symbol)535(let* ((name (symbol-name symbol))536(length (length name))537(padding (- max-length length)))538(values539(concatenate 'string540name541(make-string (+ padding distance) :initial-element #\Space))542(symbol-classification-string symbol)))))543`("" ; 8 is (length "Symbols:")544"Symbols:" ,(make-string (+ -8 max-length distance) :initial-element #\Space) "Flags:"545(:newline)546,(concatenate 'string ; underlining dashes547(make-string (+ max-length distance -1) :initial-element #\-)548" "549(symbol-classification-string '#:foo))550(:newline)551,@(loop for symbol in symbols appending552(multiple-value-bind (symbol-string classification-string)553(string-representations symbol)554`((:value ,symbol ,symbol-string) ,classification-string555(:newline)556)))))))557558(defmethod make-symbols-listing ((grouping-kind (eql :classification)) symbols)559"For each possible classification (cf. CLASSIFY-SYMBOL), group560all the symbols in SYMBOLS to all of their respective561classifications. (If a symbol is, for instance, boundp and a562generic-function, it'll appear both below the BOUNDP group and563the GENERIC-FUNCTION group.) As macros and special-operators are564specified to be FBOUNDP, there is no general FBOUNDP group,565instead there are the three explicit FUNCTION, MACRO and566SPECIAL-OPERATOR groups."567(let ((table (make-hash-table :test #'eq))568(+default-classification+ :misc))569(flet ((normalize-classifications (classifications)570(cond ((null classifications) `(,+default-classification+))571;; Convert an :FBOUNDP in CLASSIFICATIONS to :FUNCTION if possible.572((and (member :fboundp classifications)573(not (member :macro classifications))574(not (member :special-operator classifications)))575(substitute :function :fboundp classifications))576(t (remove :fboundp classifications)))))577(loop for symbol in symbols do578(loop for classification in (normalize-classifications (classify-symbol symbol))579;; SYMBOLS are supposed to be sorted alphabetically;580;; this property is preserved here except for reversing.581do (push symbol (gethash classification table)))))582(let* ((classifications (loop for k being each hash-key in table collect k))583(classifications (sort classifications584;; Sort alphabetically, except +DEFAULT-CLASSIFICATION+585;; which sort to the end.586#'(lambda (a b)587(cond ((eql a +default-classification+) nil)588((eql b +default-classification+) t)589(t (string< a b)))))))590(loop for classification in classifications591for symbols = (gethash classification table)592appending`(,(symbol-name classification)593(:newline)594,(make-string 64 :initial-element #\-)595(:newline)596,@(mapcan #'(lambda (symbol)597(list `(:value ,symbol ,(symbol-name symbol)) '(:newline)))598(nreverse symbols)) ; restore alphabetic orderness.599(:newline)600)))))601602(defmethod emacs-inspect ((%container %package-symbols-container))603(with-struct (%container. title description symbols grouping-kind) %container604`(,title (:newline) (:newline)605,@description606(:newline)607" " ,(ecase grouping-kind608(:symbol609`(:action "[Group by classification]"610,(lambda () (setf grouping-kind :classification))611:refreshp t))612(:classification613`(:action "[Group by symbol]"614,(lambda () (setf grouping-kind :symbol))615:refreshp t)))616(:newline) (:newline)617,@(make-symbols-listing grouping-kind symbols))))618619(defmethod emacs-inspect ((package package))620(let ((package-name (package-name package))621(package-nicknames (package-nicknames package))622(package-use-list (package-use-list package))623(package-used-by-list (package-used-by-list package))624(shadowed-symbols (package-shadowing-symbols package))625(present-symbols '()) (present-symbols-length 0)626(internal-symbols '()) (internal-symbols-length 0)627(inherited-symbols '()) (inherited-symbols-length 0)628(external-symbols '()) (external-symbols-length 0))629630(do-symbols* (sym package)631(let ((status (symbol-status sym package)))632(when (eq status :inherited)633(push sym inherited-symbols) (incf inherited-symbols-length)634(go :continue))635(push sym present-symbols) (incf present-symbols-length)636(cond ((eq status :internal)637(push sym internal-symbols) (incf internal-symbols-length))638(t639(push sym external-symbols) (incf external-symbols-length))))640:continue)641642(setf package-nicknames (sort (copy-list package-nicknames) #'string<)643package-use-list (sort (copy-list package-use-list) #'string< :key #'package-name)644package-used-by-list (sort (copy-list package-used-by-list) #'string< :key #'package-name)645shadowed-symbols (sort (copy-list shadowed-symbols) #'string<))646647(setf present-symbols (sort present-symbols #'string<) ; SORT + STRING-LESSP648internal-symbols (sort internal-symbols #'string<) ; conses on at least649external-symbols (sort external-symbols #'string<) ; SBCL 0.9.18.650inherited-symbols (sort inherited-symbols #'string<))651652653`("" ; dummy to preserve indentation.654"Name: " (:value ,package-name) (:newline)655656"Nick names: " ,@(common-seperated-spec package-nicknames) (:newline)657658,@(when (documentation package t)659`("Documentation:" (:newline) ,(documentation package t) (:newline)))660661"Use list: " ,@(common-seperated-spec662package-use-list663(lambda (package)664`(:value ,package ,(package-name package))))665(:newline)666667"Used by list: " ,@(common-seperated-spec668package-used-by-list669(lambda (package)670`(:value ,package ,(package-name package))))671(:newline)672673,@ ; ,@(flet ((...)) ...) would break indentation in Emacs.674(flet ((display-link (type symbols length &key title description)675(if (null symbols)676(format nil "0 ~A symbols." type)677`(:value ,(%make-package-symbols-container :title title678:description description679:symbols symbols)680,(format nil "~D ~A symbol~P." length type length)))))681682`(,(display-link "present" present-symbols present-symbols-length683:title (format nil "All present symbols of package \"~A\"" package-name)684:description685'("A symbol is considered present in a package if it's" (:newline)686"\"accessible in that package directly, rather than" (:newline)687"being inherited from another package.\"" (:newline)688"(CLHS glossary entry for `present')" (:newline)))689690(:newline)691,(display-link "external" external-symbols external-symbols-length692:title (format nil "All external symbols of package \"~A\"" package-name)693:description694'("A symbol is considered external of a package if it's" (:newline)695"\"part of the `external interface' to the package and" (:newline)696"[is] inherited by any other package that uses the" (:newline)697"package.\" (CLHS glossary entry of `external')" (:newline)))698(:newline)699,(display-link "internal" internal-symbols internal-symbols-length700:title (format nil "All internal symbols of package \"~A\"" package-name)701:description702'("A symbol is considered internal of a package if it's" (:newline)703"present and not external---that is if the package is" (:newline)704"the home package of the symbol, or if the symbol has" (:newline)705"been explicitly imported into the package." (:newline)706(:newline)707"Notice that inherited symbols will thus not be listed," (:newline)708"which deliberately deviates from the CLHS glossary" (:newline)709"entry of `internal' because it's assumed to be more" (:newline)710"useful this way." (:newline)))711(:newline)712,(display-link "inherited" inherited-symbols inherited-symbols-length713:title (format nil "All inherited symbols of package \"~A\"" package-name)714:description715'("A symbol is considered inherited in a package if it" (:newline)716"was made accessible via USE-PACKAGE." (:newline)))717(:newline)718,(display-link "shadowed" shadowed-symbols (length shadowed-symbols)719:title (format nil "All shadowed symbols of package \"~A\"" package-name)720:description nil))))))721722723(defmethod emacs-inspect ((pathname pathname))724`(,(if (wild-pathname-p pathname)725"A wild pathname."726"A pathname.")727(:newline)728,@(label-value-line*729("Namestring" (namestring pathname))730("Host" (pathname-host pathname))731("Device" (pathname-device pathname))732("Directory" (pathname-directory pathname))733("Name" (pathname-name pathname))734("Type" (pathname-type pathname))735("Version" (pathname-version pathname)))736,@ (unless (or (wild-pathname-p pathname)737(not (probe-file pathname)))738(label-value-line "Truename" (truename pathname)))))739740(defmethod emacs-inspect ((pathname logical-pathname))741(append742(label-value-line*743("Namestring" (namestring pathname))744("Physical pathname: " (translate-logical-pathname pathname)))745`("Host: "746,(pathname-host pathname)747" (" (:value ,(logical-pathname-translations748(pathname-host pathname)))749"other translations)"750(:newline))751(label-value-line*752("Directory" (pathname-directory pathname))753("Name" (pathname-name pathname))754("Type" (pathname-type pathname))755("Version" (pathname-version pathname))756("Truename" (if (not (wild-pathname-p pathname))757(probe-file pathname))))))758759(defmethod emacs-inspect ((n number))760`("Value: " ,(princ-to-string n)))761762(defun format-iso8601-time (time-value &optional include-timezone-p)763"Formats a universal time TIME-VALUE in ISO 8601 format, with764the time zone included if INCLUDE-TIMEZONE-P is non-NIL"765;; Taken from http://www.pvv.ntnu.no/~nsaa/ISO8601.html766;; Thanks, Nikolai Sandved and Thomas Russ!767(flet ((format-iso8601-timezone (zone)768(if (zerop zone)769"Z"770(multiple-value-bind (h m) (truncate (abs zone) 1.0)771;; Tricky. Sign of time zone is reversed in ISO 8601772;; relative to Common Lisp convention!773(format nil "~:[+~;-~]~2,'0D:~2,'0D"774(> zone 0) h (round (* 60 m)))))))775(multiple-value-bind (second minute hour day month year dow dst zone)776(decode-universal-time time-value)777(declare (ignore dow dst))778(format nil "~4,'0D-~2,'0D-~2,'0DT~2,'0D:~2,'0D:~2,'0D~:[~*~;~A~]"779year month day hour minute second780include-timezone-p (format-iso8601-timezone zone)))))781782(defmethod emacs-inspect ((i integer))783(append784`(,(format nil "Value: ~D = #x~8,'0X = #o~O = #b~,,' ,8:B~@[ = ~E~]"785i i i i (ignore-errors (coerce i 'float)))786(:newline))787(when (< -1 i char-code-limit)788(label-value-line "Code-char" (code-char i)))789(label-value-line "Integer-length" (integer-length i))790(ignore-errors791(label-value-line "Universal-time" (format-iso8601-time i t)))))792793(defmethod emacs-inspect ((c complex))794(label-value-line*795("Real part" (realpart c))796("Imaginary part" (imagpart c))))797798(defmethod emacs-inspect ((r ratio))799(label-value-line*800("Numerator" (numerator r))801("Denominator" (denominator r))802("As float" (float r))))803804(defmethod emacs-inspect ((f float))805(cond806((> f most-positive-long-float)807(list "Positive infinity."))808((< f most-negative-long-float)809(list "Negative infinity."))810((not (= f f))811(list "Not a Number."))812(t813(multiple-value-bind (significand exponent sign) (decode-float f)814(append815`("Scientific: " ,(format nil "~E" f) (:newline)816"Decoded: "817(:value ,sign) " * "818(:value ,significand) " * "819(:value ,(float-radix f)) "^" (:value ,exponent) (:newline))820(label-value-line "Digits" (float-digits f))821(label-value-line "Precision" (float-precision f)))))))822823(defun make-pathname-ispec (pathname position)824`("Pathname: "825(:value ,pathname)826(:newline) " "827,@(when position828`((:action "[visit file and show current position]"829,(lambda () (ed-in-emacs `(,pathname :charpos ,position)))830:refreshp nil)831(:newline)))))832833(defun make-file-stream-ispec (stream)834;; SBCL's socket stream are file-stream but are not associated to835;; any pathname.836(let ((pathname (ignore-errors (pathname stream))))837(when pathname838(make-pathname-ispec pathname (and (open-stream-p stream)839(file-position stream))))))840841(defmethod emacs-inspect ((stream file-stream))842(multiple-value-bind (content)843(call-next-method)844(append (make-file-stream-ispec stream) content)))845846(defmethod emacs-inspect ((condition stream-error))847(multiple-value-bind (content)848(call-next-method)849(let ((stream (stream-error-stream condition)))850(append (when (typep stream 'file-stream)851(make-file-stream-ispec stream))852content))))853854(defun common-seperated-spec (list &optional (callback (lambda (v)855`(:value ,v))))856(butlast857(loop858for i in list859collect (funcall callback i)860collect ", ")))861862(defun inspector-princ (list)863"Like princ-to-string, but don't rewrite (function foo) as #'foo.864Do NOT pass circular lists to this function."865(let ((*print-pprint-dispatch* (copy-pprint-dispatch)))866(set-pprint-dispatch '(cons (member function)) nil)867(princ-to-string list)))868869(provide :swank-fancy-inspector)870871872