gera2ld.space

How I Align Contents in Neovim


Background

When writing documentation in plain text, I struggled to make the text look better, including Markdown tables, key-value pairs with different lengths, etc.

Who doesn’t want to make a table like this look better?

| header 1 | header 2 | header 3 |
|-|-|-|
| long content 1 | long content 2 | long content 3 |
| 4 | 5 | 6 |

Then I found this plugin. It is quite straightforward to use after understanding how it works. Let’s see how it helps.

mini.align to the Rescue

mini.align is one of the powerful Neovim plugins collected in mini.nvim.

Basically, it aligns contents in 3 steps:

  1. Split the content into columns with a pattern. Note that the delimiters are preserved.
  2. Justify the columns, align the contents of each column with paddings.
  3. Merge the columns back into rows.

Installation

I installed mini.nvim with lazy.nvim:

return {
  {
    "echasnovski/mini.nvim",
    version = "*",
    event = 'VeryLazy',
    config = function()
      require('mini.align').setup { mappings = { start = '', start_with_preview = 'g=' } }
    end,
  },
}

Note that I set a keymap of g= to start alignment with preview.

Examples

Space Separated Columns

Let’s take an example, to format a crontab file like this:

*/3 * * * * echo 1
* */3 * * * echo 2
* 1,3,5 * * * echo 3

I really hope to see the columns aligned so I can easily tell which time unit each column belongs to and how the command works.

I select the content to be aligned in visual mode, and press g= to start alignment.

If everything goes well, I enter the alignment process and see the prompt in the status bar:

(mini.align) Split: "" | Justify: "left" | Merge: "" | Enter modifier

Now I go through the 3 steps mentioned above.

  • Firstly, press s for Split, enter the delimiter: %s+, meaning one or more whitespaces. I will see all the whitespaces disappear and everything collapses.
  • Press t to trim the columns to remove extra whitespaces before merging. Justify of left looks good to me, so I won’t change it.
  • Finally press m and set Merge delimiter as , exactly one whitespace.

Now the content should look like this:

*/3 *     * * * echo 1
*   */3   * * * echo 2
*   1,3,5 * * * echo 3

Each column is aligned to the left, and separated with exactly one whitespace.

A Markdown Table

Another example is to format Markdown tables, for example:

| header 1 | header 2 | header 3 |
|-|-|-|
| long content 1 | long content 2 | long content 3 |
| 4 | 5 | 6 |

This is a valid Markdown table but it really looks bad in the source. So I am going to format it with mini.align. To demonstrate some advanced features, I am going to align the first column to the left, the second in the center, and the third column to the right.

To make it clear, I will get the following structure after splitting (pseudo-code for better understanding):

[
  ['|', ' header 1 ', '|', ' header 2 ', '|', ' header 3 ', '|'],
  ['|', '-', '|', '-', '|', '-', '|'],
  ['|', ' long content 1 ', '|', ' long content 2 ', '|', ' long content 3 ', '|'],
  ['|', ' 4 ', '|', ' 5 ', '|', ' 6 ', '|'],
]

Note that the delimiters (|) and whitespaces are all preserved.

Now I will align the first column to the left, using the filter feature.

  • Select the table, press g= to start alignment, press s to set the delimiter to |.
  • Press f for filter, set the expression to col<4 so we only modify the first column, taking the delimiters into account.
  • Press t to trim the filtered columns, then jl to justify the contents to the left.
  • Press m and merge the columns with a whitespace .

Then the second column:

  • Select the table, press g= to start alignment, press s to set the delimiter to |.
  • Press f for filter, set the expression to col>=4 and col<6 so we only modify the second column and the following delimiter.
  • Press t to trim the filtered columns, then jc to justify the contents in the center.
  • Press m and merge the columns with a whitespace .

Then the third column:

  • Select the table, press g= to start alignment, press s to set the delimiter to |.
  • Press f for filter, set the expression to col>=6 so we only modify the third column and the following delimiter.
  • Press t to trim the filtered columns, then jr to justify the contents to the right.
  • Press m and merge the columns with a whitespace .

Here is what I get:

| header 1       |    header 2    |       header 3 |
| -              |        -       |              - |
| long content 1 | long content 2 | long content 3 |
| 4              |        5       |              6 |

Limitations

  • The filter is global. All changes apply to the filtered columns no matter when the filter is applied. So if we want to align the columns in 3 ways, we have to go through the process 3 times.
  • It is not perfect for Markdown table formatting. Here I just use it to demonstrate what we can do. Consider using marksman for better Markdown formatting.