## title: Fun visualization of Emacs dependencies
## date: "2026-03-02"
## Introduction
One day, I was using my favorite application, GNU Emacs. I
wanted to have a simple visual representation of the
relationships between certain Emacs dependencies. I just
wanted to be able to create a simple image with a graph on
it. However, I couldn't find anything that matched what I
was looking for, i.e., an Emacs package that exposes
commands with the dependency to be analyzed as a parameter.
To meet this need, I decided to write my own Emacs package,
called dv.
## My goal
The goal is to create an ELisp project that provides code
that makes it easy to visualize the dependencies of other
dependencies. For example, I would like to see which
packages are dependencies of the fj package, which are
dependencies of certain ELisp files, etc.
## How it works
During execution, there are several main steps. First, we
retrieve the metadata for all the dependencies involved and
establish the relationships between them. Next, we generate
the dot code that expresses these relationships. Finally, we
create a dot process asynchronously and pass it the dot code
as standard input, specifying a destination path as a
parameter. We can choose to automatically open the image
generated by the process with Emacs when it finishes.
### Package metadata
Emacs package metadata retrieval is similar to the describe-
package function, in that the package cache is first
consulted before automatically installing the package from
the package archive if it isn't there. In practical terms,
Emacs package metadata, at runtime, is represented through
package-desc objects.
Below is the code responsible for retrieving this metadata.
(defun dv--get-package-desc (pkg)
"Returns `package-desc' object if PKG is available.
Otherwise it returns
nil."
(or
(if (package-desc-p pkg) pkg)
(cadr (assq pkg package-alist))
(let ((built-in (assq pkg package--builtins)))
(if built-in
(package--from-builtin built-in)
(cadr (assq pkg package-archive-contents))))
;; If the package was not in cache,
;; we can try to install it using `package-install'
;;
;; And then we should be able to get it from cache by
calling the
;; `dv--get-package-desc' function again
(unless (package-installed-p pkg)
;; Can signal an error
(package-install pkg)
(dv--get-package-desc pkg))))
(defun dv-get-package-desc (pkg)
"Wrapping `dv--get-package-desc'."
(unless package--initialized
(package-initialize t))
(dv--get-package-desc pkg))
### Node relationships
Several node relationships are possible with the current
implementation.
- package nodes can only have package child nodes.
- filepath nodes can have both filepath and package child
nodes.
- dirpath nodes cannot have parent nodes and can only have
filepath child nodes.
### The main data structure
The data structure chosen to represent the relationships
between Emacs dependencies is a hash table, which allowed me
to implement a directed graph. I find this more suited to my
needs and easier to implement than a tree with only root
access. It allows me to easily access any analyzed
dependency with a time complexity of O(1). This is quite
advantageous for handling circular imports and other edge
cases.
### Exposed commands
The main interactive functions exposed are dv-package, dv-
filepath, and dv-dirpath. They have the following signatures,
respectively.
(dv-package DEST-FILE &optional OPEN-FILE &rest SEQ)
(dv-filepath DEST-FILE &optional OPEN-FILE &rest SEQ)
(dv-dirpath DEST-FILE &optional OPEN-FILE &rest SEQ)
Knowing that these functions, if called non-interactively,
can manage multiple dependencies through the SEQ parameter.
We can imagine the following call.
;; The same package can be passed multiple times as an
argument.
(dv-package "/tmp/abc.jpg" t 'fj 'magit 'elfeed 'pq 'pq 'pq
'magit)
### Customizations
The package offers customizations, some of which are useful
for customizing the output image, such as dv-node-properties,
which defaults to the following expression.
`((,dv-type-package :color "red")
(,dv-type-filepath :color "blue")
(,dv-type-dirpath :color "green"))
It may seem complex, but it is very easy to edit using the
Emacs user interface.
## Results
I am happy with the result; it fully meets the requirements
set out at the beginning. Below is an SVG image of the
dependencies of the Emacs transient package.
/abc.svg
(IMG) /abc.svg
Below is a more complex SVG image involving all types of
nodes and circular imports, produced using the Emacs command
dv-dirpath.
/dirpath.svg
(IMG) /dirpath.svg
## Conclusion
This project was a lot of fun to implement, and now I can
easily get an overview of all the dependencies of an Emacs
package.