DEV Community

Cover image for Vim to the rescue: Repetition Made Easy
Angad Sharma
Angad Sharma

Posted on

Vim to the rescue: Repetition Made Easy

The What

A month ago I was working on a script for sending bulk emails to the participants of an event. The plan was to load a JSON file with a list of emails and generate usernames for sending people a greeting email. But the file that was provided to me looked something like this (the emails have been changed of course):

abc@xyz.com
def@uvw.com
ghi@rst.com
jkl@opq.com
mno@lmn.com
Enter fullscreen mode Exit fullscreen mode

The format required for the script to work was JSON, with email and username as keys. The usernames were supposed to be generated from the string preceding the '@' symbol in the email. For example: the username for abc@xyz will be abc.

Expected output:

[
  {
    "email": "abc@xyz.com",
    "username": "abc"
  },
  {
    "email": "def@uvw.com",
    "username": "def"
  },
  {
    "email": "ghi@rst.com",
    "username": "ghi"
  },
  {
    "email": "jkl@opq.com",
    "username": "jkl"
  },
  {
    "email": "mno@lmn.com",
    "username": "mno"
  }
]
Enter fullscreen mode Exit fullscreen mode

The How

Vim has a feature called macros, which are essentially an array of subsequent functions that can be recorded and replayed at will. This task was repetitive. To do it by hand would have consumed a lot of time, since the list emails contained about 5,000 entries! Vim simplified the flow:

Flow


Recording a Macro in Vim

In vim, a macro can be bound to a key, and can be recorded by pressing the q button. For example: q + a will record a macro in the key a. Now whatever functions you perform are being tracked. To stop recording, simply press q again.

This macro can now be played using @a. To run it 5000 times just input:

5000@a
Enter fullscreen mode Exit fullscreen mode

Recording a macro for our purpose

Before recording our macro, enclose the file in square brackets (for making an array):

[
{CURSOR_HERE}
abc@xyz.com
def@uvw.com
ghi@rst.com
jkl@opq.com
mno@lmn.com
]
Enter fullscreen mode Exit fullscreen mode

Press q + a to start recording. Then input { "email": ":

[
{
"email: "{CURSOR_HERE}abc@xyz.com
def@uvw.com
ghi@rst.com
jkl@opq.com
mno@lmn.com
]
Enter fullscreen mode Exit fullscreen mode

Yank (copy) till the @ symbol by typing: y + f + @. Then append '",' to the end of the line using: A + ",:

[
{
"email: "abc@xyz.com",
{CURSOR_HERE}
def@uvw.com
ghi@rst.com
jkl@opq.com
mno@lmn.com
]
Enter fullscreen mode Exit fullscreen mode

Now for adding our username, type {"username": ""}, and come back between the quotes:

[
{
"email: "abc@xyz.com",
"username": "{CURSOR_HERE}" },
def@uvw.com
ghi@rst.com
jkl@opq.com
mno@lmn.com
]
Enter fullscreen mode Exit fullscreen mode

Remember we yanked the email (till @) earlier? Paste it by pressing p and remove the @ symbol using x.

[
{
"email: "abc@xyz.com",
"username": "abc" },
{CURSOR_HERE}
def@uvw.com
ghi@rst.com
jkl@opq.com
mno@lmn.com
]
Enter fullscreen mode Exit fullscreen mode

You can stop recording the macro now using q.

Now that we have made our JSON for the first email, we don't have to do anything else. Simply rerun the macro 5,000 times to format all the emails just like you formatted the first one to get this output:

[
  {
    "email": "abc@xyz.com",
    "username": "abc"
  },
  {
    "email": "def@uvw.com",
    "username": "def"
  },
  {
    "email": "ghi@rst.com",
    "username": "ghi"
  },
  {
    "email": "jkl@opq.com",
    "username": "jkl"
  },
  {
    "email": "mno@lmn.com",
    "username": "mno"
  }
]
Enter fullscreen mode Exit fullscreen mode

Don't know how many emails there are? Just enter a huge number for a macro to repeat, when it can't find the next line, it'll exit automatically.

Here is a recording of the same:


Fun facts about vim macros and registers

  • You can create 26 macros, one for each letter of the alphabet.
  • Each macro is stored in a register and can be accessed using the " symbol.
  • To get the keystrokes of a macro 'a' in our current buffer, you can press: " + a + p, where '"' is the macro selector, 'a' is the register and 'p' specifies paste action.
  • There are some default registers in vim:
    • Whenever you delete something, it is pasted in the '"' register (can be accessed by "")
    • Yanked text can be accessed using "0 to "9 registers, from latest to oldest.
    • ". is a readonly register which stores the last inserted text
    • "% is a readonly register which stores the current file path
    • "# is a readonly register which stores the name of the last edited file
    • ": is a readonly register which stores the last executed command

Top comments (2)

Collapse
 
coder_h profile image
Coder_H

Will be waiting eagerly for the next part. πŸ™ŒπŸ™Œ

Collapse
 
l04db4l4nc3r profile image
Angad Sharma

Coming soon.... :)