c541273d22
Creates a simple dot graph that shows the internal dependencies of an elisp module. Useful for certain kinds of refactoring ... Change-Id: Id7a7756b866751d0e7288e0d22b72ec8056a9eef Reviewed-on: https://cl.tvl.fyi/c/depot/+/10591 Reviewed-by: tazjin <tazjin@tvl.su> Autosubmit: tazjin <tazjin@tvl.su> Tested-by: BuildkiteCI
83 lines
2.7 KiB
EmacsLisp
83 lines
2.7 KiB
EmacsLisp
;; Visualise the internal structure of an Emacs Lisp file using
|
|
;; Graphviz.
|
|
;;
|
|
;; Entry point is the function `edeps-analyse-file'.
|
|
|
|
(require 'map)
|
|
|
|
(defun edeps-read-defs (file-name)
|
|
"Stupidly read all definitions from an Emacs Lisp file. This only
|
|
considers top-level forms, where the first element of the form is
|
|
a symbol whose name contains the string `def', and where the
|
|
second element is a symbol.
|
|
|
|
Returns a hashmap of all these symbols with the remaining forms
|
|
in their bodies."
|
|
|
|
(with-temp-buffer
|
|
(insert-file-contents file-name)
|
|
(goto-char (point-min))
|
|
|
|
(let ((symbols (make-hash-table)))
|
|
(condition-case _err
|
|
(while t
|
|
(let ((form (read (current-buffer))))
|
|
(when (and (listp form)
|
|
(symbolp (car form))
|
|
(string-match "def" (symbol-name (car form)))
|
|
(symbolp (cadr form)))
|
|
(when (and (map-contains-key symbols (cadr form))
|
|
;; generic methods have multiple definitions
|
|
(not (eq (car form) 'cl-defmethod)))
|
|
(error "Duplicate symbol: %s" (symbol-name (cadr form))))
|
|
|
|
(map-put! symbols (cadr form)
|
|
(cons (car form) (cddr form))))))
|
|
(end-of-file symbols)))))
|
|
|
|
(defun edeps-analyse-structure (symbols)
|
|
"Analyse the internal structure of the symbols found by
|
|
edeps-read-defs, and return a hashmap with the results of the
|
|
analysis. The hashmap uses the symbols as keys, "
|
|
(let ((deps (make-hash-table)))
|
|
(map-do
|
|
(lambda (sym val)
|
|
(dolist (expr (flatten-list (cdr val)))
|
|
(when (map-contains-key symbols expr)
|
|
(map-put! deps expr (cons sym (ht-get deps expr))))))
|
|
symbols)
|
|
deps))
|
|
|
|
(defun edeps-graph-deps (symbols deps)
|
|
(with-temp-buffer
|
|
(insert "digraph edeps {\n")
|
|
|
|
;; List all symbols first
|
|
(insert " subgraph {\n")
|
|
(map-do
|
|
(lambda (sym val)
|
|
(insert " " (format "\"%s\" [label=\"%s\\n(%s)\"];\n" sym sym (car val))))
|
|
symbols)
|
|
(insert " }\n\n")
|
|
|
|
;; Then drop all the edges in there ..
|
|
(insert " subgraph {\n")
|
|
(map-do
|
|
(lambda (sym deps)
|
|
(dolist (dep deps)
|
|
(insert " " (format "\"%s\" -> \"%s\";\n" dep sym))))
|
|
deps)
|
|
(insert " }\n")
|
|
|
|
(insert "}\n")
|
|
(buffer-string)))
|
|
|
|
(defun edeps-analyse-file (infile outfile)
|
|
"Produces a dot-graph in OUTFILE from an internal structural
|
|
analysis of INFILE. This can be graphed using the graphviz
|
|
package."
|
|
(let* ((symbols (edeps-read-defs infile))
|
|
(deps (edeps-analyse-structure symbols)))
|
|
(with-temp-buffer
|
|
(insert (edeps-graph-deps symbols deps))
|
|
(write-file outfile))))
|