Pretty Printing a Table in Emacs¶
Recently, I needed to output some relatively small tabular data in Emacs and
message was starting to be a bit long in the tooth. Finally, I’ve decided to
try my hand at upgrading the visuals for myself. I realize that there’s probably
dozens of different ways of pretty-printing tables in Emacs, but I was already
partial to the tabular output used by functions such as
plugins such as prodigy (Using org
mode’s tables also comes to mind for example). So I’ve decided to recreate this
experience for my own tables. The result has been convenient and aesthetically
pleasing enough to share.
Since this is just a little snippet, I’ll start with running the code and using it. Some brief explanations and customization options will follow.
(defun make-tabulated-headers (column-names rows) "column width are calculated by picking the max width of every cell under that column + the column name" (let ((widths (-reduce-from (lambda (acc x) (-zip-with (lambda (l r) (max l (length r))) acc (append x '()))) (-map #'length columns-names) rows))) (cl-map #'vector #'identity (-zip-with (lambda (col size) (list col size nil)) columns-names widths)))) (defun display-table-in-buffer (columns-names rows) (let ((headers (make-tabulated-headers columns-names rows)) (bname "*display table*")) (with-current-buffer (get-buffer-create bname) (tabulated-list-mode) (setq tabulated-list-format headers) (setq tabulated-list-padding 2) (tabulated-list-init-header) (setq tabulated-list-entries (-zip-with (lambda (i x) (list i x)) (-iterate '1+ 0 (length rows)) rows)) (tabulated-list-print t) (popwin:popup-buffer bname))))
And here’s a little demo to show how it works:
(display-table-in-buffer '("Header 1" "Header 2" "Header 3" "#") '(["" "foo" "bar" "123"] ["abc" "def" "hij" "900"]))
Notes & Customizations¶
First of all, the whole thing is based off [tabulated-list-mode](https://www.gnu.org/software/emacs/manual/html_node/elisp/Tabulated-List-Mode.html) - a mode which is capable of a lot more than just pretty printing simple tables. Any serious customization should start by looking at what’s available there.
My wrapper of this mode adds some conveniences, but also limitations. For example, I automatically calculate the column widths based on the widest cell in a column. This is convenient for the kind of data that I deal with, but will not work for all cases.
Also note the rows argument requires a list of strings vectors (no nulls!). A
vector per row is required by
tabulated-list-mode itself, so I felt little
need to change that.
Finally, I reuse the same
*display table* buffer to output my tables. This
works great for throwaway output, but not so well when you want to display 2
different tables for examples.