;;; sepia-tree.el -- tree-widget-based calle[re] navigation ;; Copyright (C) 2004-2008 Sean O'Rourke. All rights reserved, some ;; wrongs reversed. This code is distributed under the same terms as ;; Perl itself. ;;; Commentary: ;; See the README file that comes with the distribution. ;;; Code: (require 'tree-widget) (require 'cl) (defvar sepia-tree-use-image nil "*If non-nil, show tree-widget with icons.") (defun sepia-tree-button-cb (widget &rest blah) (let* ((pw (widget-get widget :parent)) (wid-name (widget-get pw :sepia-name)) (location (and wid-name (car (xref-location wid-name))))) (cond ((not location) (error "Can't find %s." wid-name)) (current-prefix-arg (find-file-other-window (car location)) (sepia-set-found (list location) 'function) (sepia-next)) ((widget-get widget :sepia-shown-p) (save-excursion (end-of-line) (let ((inhibit-read-only t)) (delete-region (point) (+ 1 (point) (widget-get widget :sepia-shown-p)))) (widget-put widget :sepia-shown-p nil))) (t (let ((str (apply #'sepia-extract-def location))) (if str (save-excursion (end-of-line) (widget-put widget :sepia-shown-p (length str)) (widget-insert "\n" str)) (message "(not found)"))))))) (defun sepia-tree-node-cb (widget &rest blah) (let ((func (widget-get widget :sepia-func))) (or (widget-get widget :args) (let ((children (funcall func widget))) (if children (mapcar (lambda (x) (sepia-tree-node func x)) children) (widget-put widget :has-children nil)))))) (defun sepia-tree-node (func name) "Make a tree node for the object specified by FILE, LINE, OBJ, and MOD. The new node will have a property :sepia-X corresponding to each of these values. FUNC is a function that will, given a widget, generate its children." `(tree-widget :node (push-button :tag ,name :format "%[%t%]\n" :notify sepia-tree-button-cb) :dynargs sepia-tree-node-cb :has-children t :sepia-name ,name :sepia-func ,func)) (defun sepia-tree-tidy-buffer (name) "Get/create a new, tidy buffer for the tree widget." (switch-to-buffer name) (kill-all-local-variables) ;; because the widget images are ugly. (set (make-local-variable 'widget-image-enable) sepia-tree-use-image) (let ((inhibit-read-only t)) (erase-buffer)) (let ((all (overlay-lists))) (mapcar #'delete-overlay (car all)) (mapcar #'delete-overlay (cdr all))) (toggle-read-only 1) (view-mode -1)) (defun sepia-build-tree-buffer (func defs bufname) (if defs (lexical-let ((func func)) (sepia-tree-tidy-buffer bufname) (with-current-buffer bufname (dolist (x defs) (widget-create (sepia-tree-node (lambda (widget) (funcall func (widget-get widget :sepia-name))) x))) (use-local-map (copy-keymap widget-keymap)) ;; (local-set-key "\M-." sepia-keymap) ;; (sepia-install-keys) (let ((view-read-only nil)) (toggle-read-only 1)) (goto-char (point-min)) (message "Type C-h m for usage information"))) (message "No items for %s" bufname))) ;;;###autoload (defun sepia-callee-tree (name) "Create a tree view of a function's callees. Pressing RET on a function's name displays its definition. With prefix argument, RET instead visits in another window." (interactive (let ((func (sepia-interactive-arg 'function)) (mod (sepia-interactive-module))) (list (if mod (format "%s::%s" mod func) func)))) (let* ((defs (xref-apropos name))) (sepia-build-tree-buffer #'xref-callees defs (format "*%s callees*" name)))) (defun sepia-caller-tree (name) "Create a tree view of a function's callers. Pressing RET on a function's name displays its definition. With prefix argument, RET instead visits in another window." (interactive (let ((func (sepia-interactive-arg 'function)) (mod (sepia-interactive-module))) (list (if mod (format "%s::%s" mod func) func)))) (let* ((defs (xref-apropos name))) (sepia-build-tree-buffer #'xref-callers defs (format "*%s callers*" name)))) ;;;###autoload (defun sepia-module-callee-tree (mod) "Display a callee tree for each of MOD's subroutines. Pressing RET on a function's name displays its definition. With prefix argument, RET instead visits in another window." (interactive (list (sepia-interactive-arg 'module))) (let ((defs (xref-mod-subs mod))) (sepia-build-tree-buffer #'xref-callees defs (format "*%s subs*" mod)))) (provide 'sepia-tree) ;;; sepia.el ends here