(eval-and-compile
|
|
(require 'slime))
|
|
|
|
(define-slime-contrib slime-xref-browser
|
|
"Xref browsing with tree-widget"
|
|
(:authors "Rui Patrocínio <rui.patrocinio@netvisao.pt>")
|
|
(:license "GPL"))
|
|
|
|
|
|
;;;; classes browser
|
|
|
|
(defun slime-expand-class-node (widget)
|
|
(or (widget-get widget :args)
|
|
(let ((name (widget-get widget :tag)))
|
|
(cl-loop for kid in (slime-eval `(swank:mop :subclasses ,name))
|
|
collect `(tree-widget :tag ,kid
|
|
:expander slime-expand-class-node
|
|
:has-children t)))))
|
|
|
|
(defun slime-browse-classes (name)
|
|
"Read the name of a class and show its subclasses."
|
|
(interactive (list (slime-read-symbol-name "Class Name: ")))
|
|
(slime-call-with-browser-setup
|
|
(slime-buffer-name :browser) (slime-current-package) "Class Browser"
|
|
(lambda ()
|
|
(widget-create 'tree-widget :tag name
|
|
:expander 'slime-expand-class-node
|
|
:has-echildren t))))
|
|
|
|
(defvar slime-browser-map nil
|
|
"Keymap for tree widget browsers")
|
|
|
|
(require 'tree-widget)
|
|
(unless slime-browser-map
|
|
(setq slime-browser-map (make-sparse-keymap))
|
|
(set-keymap-parent slime-browser-map widget-keymap)
|
|
(define-key slime-browser-map "q" 'bury-buffer))
|
|
|
|
(defun slime-call-with-browser-setup (buffer package title fn)
|
|
(switch-to-buffer buffer)
|
|
(kill-all-local-variables)
|
|
(setq slime-buffer-package package)
|
|
(let ((inhibit-read-only t)) (erase-buffer))
|
|
(widget-insert title "\n\n")
|
|
(save-excursion
|
|
(funcall fn))
|
|
(lisp-mode-variables t)
|
|
(slime-mode t)
|
|
(use-local-map slime-browser-map)
|
|
(widget-setup))
|
|
|
|
|
|
;;;; Xref browser
|
|
|
|
(defun slime-fetch-browsable-xrefs (type name)
|
|
"Return a list ((LABEL DSPEC)).
|
|
LABEL is just a string for display purposes.
|
|
DSPEC can be used to expand the node."
|
|
(let ((xrefs '()))
|
|
(cl-loop for (_file . specs) in (slime-eval `(swank:xref ,type ,name)) do
|
|
(cl-loop for (dspec . _location) in specs do
|
|
(let ((exp (ignore-errors (read (downcase dspec)))))
|
|
(cond ((and (consp exp) (eq 'flet (car exp)))
|
|
;; we can't expand FLET references so they're useless
|
|
)
|
|
((and (consp exp) (eq 'method (car exp)))
|
|
;; this isn't quite right, but good enough for now
|
|
(push (list dspec (string (cl-second exp))) xrefs))
|
|
(t
|
|
(push (list dspec dspec) xrefs))))))
|
|
xrefs))
|
|
|
|
(defun slime-expand-xrefs (widget)
|
|
(or (widget-get widget :args)
|
|
(let* ((type (widget-get widget :xref-type))
|
|
(dspec (widget-get widget :xref-dspec))
|
|
(xrefs (slime-fetch-browsable-xrefs type dspec)))
|
|
(cl-loop for (label dspec) in xrefs
|
|
collect `(tree-widget :tag ,label
|
|
:xref-type ,type
|
|
:xref-dspec ,dspec
|
|
:expander slime-expand-xrefs
|
|
:has-children t)))))
|
|
|
|
(defun slime-browse-xrefs (name type)
|
|
"Show the xref graph of a function in a tree widget."
|
|
(interactive
|
|
(list (slime-read-from-minibuffer "Name: "
|
|
(slime-symbol-at-point))
|
|
(read (completing-read "Type: " (slime-bogus-completion-alist
|
|
'(":callers" ":callees" ":calls"))
|
|
nil t ":"))))
|
|
(slime-call-with-browser-setup
|
|
(slime-buffer-name :xref) (slime-current-package) "Xref Browser"
|
|
(lambda ()
|
|
(widget-create 'tree-widget :tag name :xref-type type :xref-dspec name
|
|
:expander 'slime-expand-xrefs :has-echildren t))))
|
|
|
|
(provide 'slime-xref-browser)
|