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 list-processes and 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.

The Code

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.

To run the code you will need dash, popwin. If you use spacemacs, these batteries will already be included. If not, then it should be trivial to replace these. Here’s the code:

(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.

Comments

comments powered by Disqus