Übersicht Letzte Änderungen Neuigkeiten Suchen ElispSektion KurzAnleitung Impressum
Georgien: Erklärung der Unabhängigkeit 1918

SimpleCallTree

This package creates a call-tree – a data structure saying which function calls which function.

Use ‘M-x simple-call-tree-analyze’ to run it on a buffer. This uses ‘font-lock-function-name’ to identify function names. Therefore, you might have to wait for a bit until the stealth font lock has actually fontified all of the buffer, not only the visible part. Alternatively you might want to call ‘jit-lock-fontify-now’ explicitly (e.g. by inserting `(when jit-lock-mode (jit-lock-fontify-now))’ right after `(interactive)’ in ‘simple-call-tree-alist’ below), especially if you use jit-lock-mode and your Jit Lock Stealth Time is “never” (what is the default, I think).

For CPerl, some keywords like -f are also hilighted using font-lock-function-name. In order to exclude these, use ‘M-x simple-call-tree-analyze-perl’.

For any kind of literate programs consider calling ‘simple-call-tree-analyze’ on the "tangled" code.

The output in ielm (see InferiorEmacsLispMode):

    ELISP> simple-call-tree-alist
    (("DoShowVisitors")
     ("AddUserToRecentVisitors" "T" "GetParam" "UpdateRecentVisitors")
     ("UpdateRecentVisitors" "T" "ReadRecentVisitors" "WriteRecentVisitors")
     ("WriteRecentVisitors" "T" "WriteStringToFile")
     ("ReadRecentVisitors" "T" "ReadFile")
     ("DoShowVersion" "T" "GetHeader" "GetCommonFooter")
     ...

To see which functions are called at all:

    ELISP> (setq simple-call-tree-used (mapcar 'car (simple-call-tree-invert simple-call-tree-alist)))
    ("DoOtherRequest" "DoBrowseRequest" "InitRequest" "DoCacheBrowse" "InitLinkPatterns" ...

To see which functions are not called:

    ELISP> (set-difference (mapcar 'car simple-call-tree-alist) simple-call-tree-used)
    ("DoWikiRequest")

Use with Anything

Anything is a candidate selection framework.

Start with ‘M-x anything-simple-call-tree’, narrow the list by typing some patterns(multiple patterns are space-delimited string), select with up/down/pgup/pgdown/C-p/C-n/C-v/M-v, choose with enter,

With C-z function definitions (by rotation) are displayed without quitting anything session.

Discussion

Hm, could be that the last function is not analyzed correctly… – AlexSchroeder

Somebody make it write out dotty files please! :-) (see http://www.research.att.com/sw/tools/graphviz/)

For a simple graphviz solution (with dot), see my suggestion below in the Visualization part – PeterTury?

I can’t get this to work properly for me. It will only recognize functions if they have already been displayed in a window. I suppose that emacs only updates the text-properties when it needs to (i.e. when the text is displayed in a window). Is there any way to force emacs to evaluate the text properties of a buffer? – Aleblanc

Aleblanc, please check my suggestion with ‘jit-lock-fontify-now’ above – PeterTury?

See ‘font-lock-support-mode’. Instant fontification could be slow for large buffers, though. That’s why it is not usually used as a source of syntax information. - anon

As we need to call ‘jit-lock-fontify-now’, why not adding it in simple-call-tree-analyse, just before the ‘let’ ? – DanielDehennin

Shouldn’t you call ‘font-lock-fontify-buffer’ if you want the buffer to be fontified right now? – LennartBorgman

Pretty Printing

To show a pretty-printed representation of functions and their callers, you could use something like:

    (defun simple-call-tree-list-functions-and-callers ()
      "List functions and callers in `simple-call-tree-alist'."
      (interactive)
      (let ((list (simple-call-tree-invert simple-call-tree-alist)))
        (switch-to-buffer (get-buffer-create "*simple-call-tree*"))
        (erase-buffer)
        (dolist (entry list)
          (let ((callers (mapconcat #'identity (cdr entry) ", ")))
            (insert (car entry) " is called by "
                    (if (string= callers "")
                        "no functions."
                        callers)
                    ".\n")))))

Alternatively, if you wanted to list which callers and the functions they call, you could use something like:

    (defun simple-call-tree-list-callers-and-functions ()
      "List callers and functions in `simple-call-tree-alist'."
      (interactive)
      (let ((list simple-call-tree-alist))
        (switch-to-buffer (get-buffer-create "*simple-call-tree*"))
        (erase-buffer)
        (dolist (entry list)
          (let ((functions (mapconcat #'identity (cdr entry) ", ")))
            (insert (car entry) " calls "
                    (if (string= functions "")
                        "no functions"
                        functions)
                    ".\n")))))

Visualization

A simple (and rather manual) way (using http://www.graphviz.org/) can be:

  1. make sure you have graphviz installed (use e.g. Synaptic in Ubuntu…)
  2. “tangle” your source file (in case of literal programs)
  3. fontify your whole buffer (maybe explicitly with ‘jit-lock-fontify-now’)
  4. call ‘simple-call-tree-analyze’
  5. create ‘sct-dot’ (see below)
  6. switch to an empty buffer
  7. evaluate this in the minibuffer (‘M-:’): `(insert (sct-dot))’
  8. save the buffer as (e.g.) ‘my-file.dot’
  9. run graphviz’s dot program. E.g. from command line: `dot -Tjpg /path/to/my-file.dot -o result.jpg’
  10. enjoy! ;)

Evaluate this expression (C-M-x) to create function ‘sct-dot’:

(defun sct-dot ()
  "Generate dot file for graphviz from `simple-call-tree-alist'.

After calling `simple-call-tree-analyze', use `sct-dot' in an
empty buffer via `(insert (sct-dot))'.

Then save the file as \"my-file.dot\" and run
\"dot -Tjpg /path/to/my-file.dot -o result.jpg\" from command line."
  (concat "digraph G {\n" ;; default beginning of a dot file
          (mapconcat 'identity ;; end each line with a ";"
                     (mapcar
                      (lambda (defun-list)
                        "Called for each elemet (list) of `simple-call-tree-alist',
                         create all the 'caller -> callee;' strings."
                        (let ((caller (car defun-list))
                              (callees (cdr defun-list)))
                          (if (null callees)
                              (concat "\"" caller "\"")
                            (mapconcat (lambda (callee)
                                         "Called with each callee, create 'caller -> callee' pairs."
                                         (concat "\"" caller "\"" " -> " "\"" callee "\""))
                                       callees
                                       ";\n"))))
                      simple-call-tree-alist)
                     ";\n")
          ";\n}"))

simple-call-tree+.el

The following library is based on simple-call-tree.el and provides a major mode for displaying and navigating the call tree. You can invert the tree, change the depth, narrow to a particular branch, perform query replacements on functions (useful for refactoring), and it also works with follow mode if you have that installed (http://www.damtp.cam.ac.uk/user/sje30/emacs/fm.el).

Feel free to help develop it further, here is the github page: https://github.com/vapniks/simple-call-tree-ext

JoeBloggs


CategoryCode CategoryProgrammerUtils CategoryNavigation