Modal editing

22 Aug 2021

This is real-world example of something I had to do, something which probably would make me want to kill myself if it weren't for Vim's modal editing.

Here is the thing we have to edit, and what we should end up with:

// Start:
start.get('some-selector:contains("sometext")').chain('continues');
// Result:
start.get('some-selector').contains('sometext').chain('continues');

I have about ~200 of these lines scattered around the file, where some-selector, sometext and the exact functions in the chain vary. A search and replace (even a scary regex one) is therefore out of the question, since sometext needs to be preserved.

How would you go about doing this in your favorite tex editor? And keep in mind, you ought to do it 200 times, so better think up something smart.

Vim makes it easy

Let me mark the position of the cursor with for normal mode (putting it after the character it's currently on, since my technology is limited) and | for insert mode. The key presses will be shown in the comment above the line of code.

We shall start by looking for the first occurrence of :contains:

// /:contains<CR>
start.get('some-selector:▊contains("sometext")').chain('continues');
// Note: <CR> means "Enter" in vimspeak

which will position us on the :. Start by recording a macro into the "q" register:

// qq
start.get('some-selector:▊contains("sometext")').chain('continues');

Now I want to substitute the colon with a closing single quote, closing parenthesis and a dot:

// s').
start.get('some-selector').|contains("sometext")').chain('continues');

and leave the insert mode, since my job typing is done. We shall never enter it again.

// <Esc>
start.get('some-selector').c▊ontains("sometext")').chain('continues');

Now I want to find that pesky single quote messing up my syntax highlighting:

// f'
start.get('some-selector').contains("sometext")'▊).chain('continues');

and delete it. Once I do, the dangling closing parenthesis next to it is under my cursor, so I can delete it as well.

// xx
start.get('some-selector').contains("sometext")▊.chain('continues');

We're almost there, now just position the cursor on "

// h (or left arrow, but with h you stay on the home row)
start.get('some-selector').contains("sometext"▊).chain('continues');

and change surroundings form " to ' (it will also move my cursor to the opening quote, don't pay it any attention):

// cs"'
start.get('some-selector').contains('▊sometext').chain('continues');
// Note: this step technically requires the "vim-surround" plugin,
// but the plugin is so ubiquitous we may as well consider it part
// of Vim. It's also included by default in every Vim emulator
// (like the VS Code/Visual Studio plugins or Emacs' evil mode)

// Without any plugins one might just do: r'F".
// (replace current char with ', backwards-find " and repeat the
// replacement) BUT this will catch any escaped \" in "sometext"

The edit is done, jump to another occurrence of :contains by pressing n and end recording macro by pressing q.

The best part

This may seem like a lot of work and a lot of thought being but into something you could just change by selecting things with your mouse. But remember, we have to do it 200 times, so anything involving mouse is out of the question unless you like moving your hand from the keyboard to the mousepad and beck 200 times. And no one likes that.

This is why we have begun this whole adventure by recording a macro into the "q" register with qq. Now all we have to do is execute the macro form the "q" register with @q, which edits the line and jumps to the next occurrence of :contains, ready to be executed again. So we can just 10@q to fix 10 lines at a time or 100@q to edit 100 lines at a time (repeating it with . to edit as many 10's or 100's of lines as we need) and so on and so forth.

It's a language

Vim users don't swear by "The Vim", the program written by Bram Moolenaar. They swear by this "keystrokes as editing language" philosophy, because it makes their lives easier.

Notice how I never gave you the exact "formula" for the edit. Here it is:

s').<ESC>f'xxhcs"'n

If you include the initial search for :contains and replaying the macro 200 times it gets even worse:

/:contains<CR>qqs').<ESC>f'xxhcs"'nq200@q

Do you know why I didn't just show you this? Because no one thinks like that, and you are not supposed to think like that. It's a sequence of small steps which make total sense on their own, and those steps are what you remember. Not this absolute monstrosity. From those simple, (somewhat) logically named steps you compose the edit, just like composing a computer program from simple and logically named instructions. It's a language for editing code.

You don't have to use Vim (and maybe you shouldn't), but you should at least acknowledge what it brings to the table. People don't use it because they are old and crusty and "that's how it was done back in the day".

They use it because it does something that newer, more "user friendly" and "familiar" text editors simply cannot do.