I Wrote a Macro That Will Expand Python Imports
Tags: python, vim, macros
Published: 2020-11-15 23:21:46

## Intro

We've all been there. You just added yet _another_ item to the list of imports. You passed the 80 character limit long ago, and the list is still growing. Refactoring now would take a lot of effort, but the longer you wait, the worse it will get. You are doomed to be forever trapped in the never-ending downward spiral of your worst python import nightmare.

Never fear! Vim macros are here!

That's right. I'm here to tell you that there exists a vim macro that will turn your ugly python import statement that looks something like this:

```python
from random import random, randint, choice, randrange, shuffle
```

...into a beautiful piece of idiomatic and readable code that looks a bit more like this:

```python
from random import (
    random,
    randint,
    choice,
    randrange,
    shuffle
)
```

## The macro

So here it is, folks - the macro you've all been waiting for:
`0f,F a()<ESC>lDPa<CR><ESC>d0kf(a<CR><ESC>^@q`

Because of the limitations of pasting metacharacters into a web browser, I couldn't paste the actual macro in all of its `^M`- and `^[`-riddled glory, so be sure that you replace all of the `<CR>`s, `<ESC>`s, etc. with their respective metacharacters when you paste this into your editor.

The perceptive among you may have noticed that this macro actually calls another macro at the end. Without that call, the code would end up looking like this:

```python
from random import (
    random, randint, choice, randrange, shuffle
)
```

...which isn't much better than before.

Here's what that other macro looks like:
`f r<CR>@q`

This is a recursive macro, so be sure that you either record this macro into register 'q' or replace the `@q` in both the macro bodies with the register that you do record it into.

What this second macro does is it finds the next space, replaces it with a carriage return, and then recurses. This will continue until it hits a line that does not contain a space, which in this case will be the line with the closing parenthesis. So in effect, this does the opposite of `<S-j>`, breaking lines at each space.

For those of you who are still scratching your heads trying to figure out what in the world the macro is doing, here it is broken down into bite-sized pieces:

`0f,F ` - Find the first comma and jump back to the previous space. This puts the cursor right in front of the first imported variable.
`a()<ESC>` - Pretty self-explainitory. Appends a pair of parentheses.
`lDP` - Goes to the right one char and deletes to the end of the line, then pastes it before the cursor. This puts the entire line after the parentheses between them.
`a<CR><ESC>` - Inserts a new line before the closing paren.
`d0` - Gets rid of the whitespace added after adding the newline (this macro assumes you have `autoindent` enabled).
`kf(a<CR><ESC>` - Goes up one line, jumps to the opening paren, and adds a newline after it.
`^@q` - Goes to the first non-whitespace char and executes the 'q' macro

Here's a GIF of what the macro would look like if you could see it one keypress at a time:
![Macro GIF](https://dev-to-uploads.s3.amazonaws.com/i/2prisgpy149f1qpfsuyx.gif)