Sunset Beer

While writing some documentation this weekend, I saw a Markdown file with a table that looked like this:

| Foo | Bar | Detailed comment | Bla |
| --- | --- | -------- ------- | --- |
| First | Second | This is the first row of the table, ignoring the headers | % |
| O | O | Some very long sentence for this entry | :emoji: |
| Bla | bleeeeeeeeeeeeeeeeeeee | blii | x |

The columns are not aligned! This bothered me, although the rendered table looks fine.

We have one million ways to make this table look nice in the Markdown file. One of them, when using Vim/NeoVim is:

  • select the table, including the header and footer lines (with shift V, for example).
  • Prettify the table with :!column -t -s '|' -o '|'

It only takes these two steps to align the columns of the example table:

| Foo   | Bar                    | Detailed comment                                         | Bla     |
| ---   | ---                    | -------- -------                                         | ---     |
| First | Second                 | This is the first row of the table, ignoring the headers | %       |
| O     | O                      | Some very long sentence for this entry                   | :emoji: |
| Bla   | bleeeeeeeeeeeeeeeeeeee | blii                                                     | x       |

No plugin needed 🎉

How the hell this works?

  • shift V switches to Visual mode linewise. This is to select all the lines of the table. See :help visual-mode for details.
  • : switches to Command line mode, to type commands. See :help Cmdline for details.
  • ! specifies a filter command. This means we will send data to a command to modify it (or to filter) and replace the original lines. In this case we are in Visual mode, we defined the input text (the selected lines) and we will use an external command to modify the data.
  • column is the filter command we are using, from the util-linux package. column’s purpose is to “columnate”. The -t flag tells column to use the Table mode. The -s flag specifies the delimiters in the input data (the default is whitespace). And the -o flag is to specify the output delimiter to use (we need that because the default is two whitespaces).

🎃 🧙

Edit

2024-04-07

There may be an issue with this filter: if there are consecutive spaces at the beginning or end of any element in the cell, the columns in the pretty table get too long. For example:

| Foo | Bar | Detailed comment | Bla |
| --- | --- | -------- ------- | --- |
|    First | Second | This is the first row of the table, ignoring the headers | % |
| O | O | Some very long sentence for this entry | :emoji: |
| Bla | bleeeeeeeeeeeeeeeeeeee                   | blii | x |

Becomes:

| Foo      | Bar                                      | Detailed comment                                         | Bla     |
| ---      | ---                                      | -------- -------                                         | ---     |
|    First | Second                                   | This is the first row of the table, ignoring the headers | %       |
| O        | O                                        | Some very long sentence for this entry                   | :emoji: |
| Bla      | bleeeeeeeeeeeeeeeeeeee                   | blii                                                     | x       |

Thanks to Phil, I got the tip of first filtering with tr -s ' '. This neat command gets rid of all consecutive spaces and replace with only one. Like this:

$ echo "     a    a   !" | tr -s ' '
 a a !

So, the neatest filter is actually :! tr -s " " | column -t -s '|' -o '|'. This way we first apply tr to get rid of repeated spaces and pipe that to the column command.

But note: any internal sequence of spaces is replaced with only one. Careful if you need those spaces.

Danke schön, Phil!