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))))
 |