36
\$\begingroup\$

I've recently realized how vim works great for golfing, especially for . Also, according to meta vim is a perfectly acceptable 'programming language' at least, for the scope of this site, that is.

What general tips do you have for golfing in Vim? I'm looking for ideas which can be applied to code-golf problems and which are also at least somewhat specific to Vim (e.g. "remove comments" is not an answer).

Please post one tip per answer.

\$\endgroup\$
1
  • 12
    \$\begingroup\$ This is a particularly helpful tips question because "Tips for golfing in vim" and "Tips for using vim efficiently as a text editor" are basically the same thing. \$\endgroup\$
    – DLosc
    Oct 28, 2016 at 18:00

13 Answers 13

20
\$\begingroup\$

Be aware of the uppercase variants of common commands. This can trivially save one keystroke in a lot of contexts. For example:

A = $a
C = c$
D = d$
I = ^i
S = cc
X = dh
Y = yy

Another one is to use G instead of gg, but only if you use a count! For example, without a count, gg moves to the beginning, and G moves to the end. However, with a count, they both move to the line you specify, so 5G is shorter than but equivalent to 5gg .

Another one, is to use H in place of gg, but it's important to note that this only works if you can guarantee that any input will not take up more than the default number of rows (I believe this is 24, but it may vary).

\$\endgroup\$
5
  • 1
    \$\begingroup\$ If a challenge specifically editor golf (and therefore usually scored by keystrokes) an upper-case letter is two keystrokes as well (so is $ though, so at least C and D would save something). \$\endgroup\$ Feb 4, 2016 at 8:26
  • 1
    \$\begingroup\$ Something I'm not clear about when scoring in keystrokes is whether consecutive letters that require the shift key only count the shift key once per group, since you could (at least for practical purposes) hold it down while you hit the other keys. \$\endgroup\$
    – Alex A.
    Feb 4, 2016 at 20:18
  • 6
    \$\begingroup\$ @AlexA. Personallly, I think modifiers shouldn't count. Maybe we need a meta post on this... \$\endgroup\$
    – DJMcMayhem
    Feb 4, 2016 at 20:25
  • \$\begingroup\$ G and gg are not equivalent. The former goes to the end of the buffer and the latter goes to the beginning. \$\endgroup\$
    – Jordan
    Sep 23, 2016 at 15:43
  • \$\begingroup\$ @Jordan That's a good point. I meant they're the same when given a count, e.g. 5G is equivalent to 5gg. I'll add some more details about that point. \$\endgroup\$
    – DJMcMayhem
    Sep 23, 2016 at 15:46
13
\$\begingroup\$

Anytime a command fails, it will make a ding noise, which will cancel the current macro. You can (ab)use this to create a crude form of loop. For example, if you want to repeat the keystrokes <foobar> until your buffer has less than 3 lines in it. You can do

qq<foobar>3G``@qq

which means:

qq                 'Start recording in register q
  <foobar>         'Keystrokes
          3G       'Go to line 3. If line 3 doesn't exist, this is effectively a "break" statement.
            ``     'Jump back to previous cursor location
              @q   'Call macro q
                q  'Stop recording

It's important to note that if you are testing this in a vim session, the @q might have unintended side effects, since macros are loaded from .vim_profile. You can get around this a couple different ways. Probably the best workaround is to launch vim with

vim -i NONE

You could also delete your .viminfo.

If you have already launched vim, you can type

qqq

at the beginning to zero the macro out.

Other conditions you can replace the 3G with are

f<char>    'Jump to first occurence of <char>. Also see F, t and T

or

<number>|  'Go to the <number>'th character on the current line.
\$\endgroup\$
0
11
\$\begingroup\$

The alphabet in 10 keystrokes

All credit for this technique goes to Lynn, who first used it in this answer. I'm writing it up here for posterity.

Use the following ten keystrokes to yank the lowercase alphabet:

:h<_␍jjYZZ

(Where is a carriage return, i.e. the Enter key.)

Explanation

:h<_␍ opens the help section v_b_<_example (an example of visual blockwise selection), which happens to contain the text abcdefghijklmnopqrstuvwyxz. jjY moves down to that line and yanks it, then ZZ closes the help window.

\$\endgroup\$
1
  • \$\begingroup\$ Wow that's creative \$\endgroup\$
    – PkmnQ
    Jun 30, 2020 at 16:53
8
\$\begingroup\$
:%s/\n//g

is always equivalent to

:%s/\n//

which happens to also be equivalent to

:%s/\n

surprisingly enough. (In general, without any flags, the last slash in a :s expression is never necessary.)

\$\endgroup\$
2
  • 2
    \$\begingroup\$ This works for newline, but doesn't work for any character that could appear more than once in a line. For example :%s/x doesn't remove every occurrence of x, just the first one on each line. \$\endgroup\$
    – DJMcMayhem
    Mar 6, 2016 at 0:32
  • 1
    \$\begingroup\$ You can also use :&& to repeat a substitute command and it's flags. \$\endgroup\$
    – DJMcMayhem
    Sep 23, 2016 at 16:46
8
\$\begingroup\$

There are more shortened versions of ex commands that you think. For example, you probably already knew that :global can be shortened to :g, but did you know that :nnoremap is equivalent to :nn?

It's a good idea to run :h :foo on all the ex commands you're using in your answer to see if there's a shorter version.

\$\endgroup\$
7
\$\begingroup\$

Let's say you're in insert mode, and you want to make a single normal mode command. You might write that like this:

isome_text<esc><foobar>gisome_more_text

Don't do it that way. Instead, use <C-o>, which executes a single normal command then immediately returns to insert mode.

isome_text<C-o><foobar>some_more_text
\$\endgroup\$
8
  • \$\begingroup\$ I don't think those spaces are necessary either \$\endgroup\$
    – anon
    May 5, 2016 at 0:13
  • \$\begingroup\$ @QPaysTaxes You're right, they aren't. I thought it would make it more readable. I'll edit that. \$\endgroup\$
    – DJMcMayhem
    May 5, 2016 at 0:24
  • \$\begingroup\$ You don't need a <CR> after <foobar> to send the command? \$\endgroup\$
    – anon
    May 5, 2016 at 3:04
  • \$\begingroup\$ @QPaysTaxes Only if it's an ex command or a search. (E.g. any command starting with a :, /, or ?) \$\endgroup\$
    – DJMcMayhem
    May 5, 2016 at 3:41
  • 1
    \$\begingroup\$ I believe <M-<foobar>> is system dependent. Which makes me curious if it's allowed over at VimGolf.com \$\endgroup\$
    – B Layer
    Jun 19, 2020 at 21:19
6
\$\begingroup\$

Golfier movements

Frequently if you're working with a block of text (especially in challenges), you'll need to move to the first or last character in the buffer. G and gg are pretty good, but there are some annoying things. For example, to get the last character, you need G$ and gg will not necessarily put you on the first column if there's leading whitespace. Here are some nicer movements, although note that they only work if your text doesn't have empty lines in the middle of it:

  • First character of the buffer: { is better than gg0. If there are empty lines in the middle of the text, you can also use go which brings you to the first character in the buffer no matter what.

  • Last character of the buffer: } is better than G$

These also work as an argument to an operator, but note that they are character-wise movements, not line-wise movements!

\$\endgroup\$
2
  • \$\begingroup\$ H is golfier than gg \$\endgroup\$
    – user41805
    Dec 20, 2016 at 17:52
  • 1
    \$\begingroup\$ @KritixiLithos except that H is not reliable. It's behavior depends on the size of the visible window and where you are in the buffer. (It's remapped in V to avoid this confusion) \$\endgroup\$
    – DJMcMayhem
    Dec 20, 2016 at 17:54
5
\$\begingroup\$

There are three "change case" operators,

gu    "Convert to lowercase
gU    "Convert to uppercase
g~    "Toggle case

Since these are operators, they take a motion, so to convert the current character to lowercase you would use

gul

Here's where the fun trick comes in. :) If you want to change the case of a single character or the current line, it's a byte shorter in visual mode. For example gul is equivalent to

vu

And gu_ (_ is current line) is equivalent to

Vu

This trick does not work for toggling, because v? triggers a backwards search in visual mode, (just like v/ but moving in the opposite direction) so you need vg? instead. However, here's where you can use the ~ operator, to save even more bytes! (Thanks @Doorknob for pointing this out)

You can see this trick in action on my vim answer here, and on the equivalent V answer on the and post.

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Don't forget ~, which toggles the case of the character under the cursor and moves one to the right! \$\endgroup\$
    – Doorknob
    Aug 17, 2016 at 2:50
5
\$\begingroup\$

Know your registers

Yanking or deleting text into a specific register (e.g. "aY) to use it later isn't always necessary. Vim has several registers that are automatically populated with the subjects of various commands. Type :help registers to see them all, but here's a few of particular note:

  • Unnamed register: "" - The last text that was yanked or deleted.

  • Numbered register "0 - The last text that was yanked.

  • Numbered registers "1"9 - The last text that was deleted unless it was less than one line. Each time "1 is filled its previous contents are shifted to "2 and so on.

  • Small delete register: "- - The last text shorter than one line that was deleted.

  • Last inserted text: ".

In particular, the "- and "1 registers can be useful together, enabling you to delete some text with e.g. dd into the "1 register and delete some other text with D into the "- (of course, the semantics aren't identical, but often they don't need to be).

All of the registers can be a little tough to keep track of, but you can see the contents of all of your registers at any time by typing :registers.

\$\endgroup\$
5
\$\begingroup\$

This came up in chat earlier, so I thought I might as well post it as a full tip.

<C-a> and <C-x> increment and decrement the next number on the line. This is an extremely useful feature for some simple math, but it also allows for a neat little hack.

Let's say you need to jump to the next number. For example, we need to change

I need 2 spell better

to

I need to spell better

The obvious way is to jump to the 2, then do:

sto                "Synonymous with 'clto'

Since this is shorter than

:s/2/to<cr>

There are many different ways to jump to the 2. For example,

/2<cr>
ww 
2w
ee
2e
8|
f2

As well as many other ways.

These are all 2 (or more) keystrokes. However, since <C-a> and <C-x> not only increment/decrement numbers, but also jump to the next number, we can take a byte off with

<C-a>sto
\$\endgroup\$
1
  • \$\begingroup\$ Just make sure you didn't add 'alpha' to 'nrformats' and forget, like I did...major confusion! (Though, using vimgolf.com's required vimrc will prevent this from happening.) \$\endgroup\$
    – B Layer
    Jun 19, 2020 at 21:44
3
\$\begingroup\$

Do calculations with the expression register

You can do calculations in both normal mode and insert mode.

Normal mode

In normal mode, if you type @= your cursor will move to the command line, where you can enter any expression. When you press enter, the result of the expression will be executed as normal mode commands.

For example, suppose you want to go to the middle column of the current line. The function call col('$') returns the number of columns in the line, so we can accomplish what by typing the following:

@=col('$')/2<CR>|

When you press enter, the cursor returns to the buffer and vim waits for an operator (like |) as though you had just entered a number. Alternatively, you could have entered this:

@=col('$')/2.'|'

...but of course that's more bytes.

Insert mode

You can use the expression register in insert mode, too, by pressing <Ctrl-r>= instead of @=. It works the same in normal mode, except the result of the expression you enter will be executed in insert mode. For example, if you typed <Ctrl-r>=col('$')<CR>, the number of columns in the current line would be inserted at the cursor as though you had typed it.

For more information on the expression register, type :help "=.

Reusing expressions

The last expression you used is stored in the expression register, "=. Typing @=<CR> in normal mode or <Ctrl-r>=<CR> in insert mode will evaluate the expression again, allowing you to use them much like macros.

Do calculations in substitutions

You can evaluate expressions when doing regular expression substitutions, too. All you have to do is begin your substitution with \=. For example, suppose you wanted to number the lines in this file:

foo
bar
baz

The function call line('.') returns the current line number, so the job is easy. Entering this:

:s/^/\=line('.').' '/g<CR>

...yields the desired result:

1 foo
2 bar
3 baz

To use captured groups in such an expression you can use the submatch() function, where e.g. submatch(0) is equivalent to \0 in an ordinary substitution, submatch(1) is equivalent to \1, etc. This eats up a lot of keystrokes, unfortunately.

For more information on expression substitution, type :help sub-replace-expression.

\$\endgroup\$
2
  • \$\begingroup\$ For your last example there are shorter ways. For example i1 <esc>bqqywjPb<C-a>@qq@q is a pure normal mode solution that is 5 bytes shorter. And if you're on unix, you could even do :%!nl. Still good tips though! \$\endgroup\$
    – DJMcMayhem
    Sep 23, 2016 at 18:53
  • 1
    \$\begingroup\$ My goal was to demonstrate how to use the feature, not how to number lines in the fewest keystrokes. \$\endgroup\$
    – Jordan
    Sep 23, 2016 at 18:55
2
\$\begingroup\$

Rotate your loops

Rotating loops is a golfing trick in many languages but I've found it to be more frequently useful in vim, largely due to the fact that loops usually exit by failure. This allows you to determine a subset of code to be executed on the first and/or last iteration.

Thus, when you have code like <init>qq<loop>@qq@q<finalize>, try to see if you can rotate the loop in order to overlap with either the init code or the finalize code so that they can be merged. Here's an example I used in a recent golf.

A straightforward 16 byte solution might look like this:

x$pqqF xp@qq@qZZ

This moves the first character to the end, and then in the loop moves backwards to the previous space character and swaps it with the letter that follows it, once it reaches the first space character. One can check that this algorithm solves the challenge in general.

The observation we can make here is that we wrote p twice: once just before the loop and once just at the end of it. It turns out we can merge them together like so:

x$qqpF x@qq@qZZ

Notice how we managed to save a character by only writing p once: we rotated the loop by one character and had the first iteration of the loop do us the favor of finishing up the init work of pasting the first character at the end of the string. Note that the total execution pattern of this solution is exactly the same as the previous one, since we will still exit the loop on failure of the final F command.

(Technical NB: there's some added subtleties on the Anarchy Golf server since macro definitions are saved between test cases, and because of this the solution above won't work on the 2nd and 3rd test cases when q is already defined. My linked solution is roughly equivalent but loses a bit of generality to get around this.)

\$\endgroup\$
1
\$\begingroup\$

You can directly modify the text of a macro, which can be significantly shorter than a convoluted conditional. For example, let's say you want to paste something n times, where n is user input. I first tried to do this:

qq/[1-9]<enter><c-x>``p@qq@q

Explanation:

qq                             #Start Recording
  /[1-9]<enter>                #Search for a number that isn't 0.
                <c-x>          #Decrement that number
                     ``p       #jump back to your previous location and paste.
                        @q     #Recursive call to macro 'q'
                          q@q  #Stop recording, and run the macro

This is 18 keystrokes, and a really ugly way to do it. Instead, go to the location of that number, and do this:

Ap<esc>"qdd@q

Explanation:

Ap<esc>                 #Append a 'p' to the end of the current line.
       "qdd             #Delete the current line into register q
           @q           #Run macro q.

This is 9 keystrokes, a huge improvement.

Edit:

Even shorter is to run the macro for your number, and then type your command. For example:

"qdd@qp

Explanation:

"qdd         #Delete the current line into register q
    @q       #Run register q
      p      #Paste

7 keytstrokes.

\$\endgroup\$
1
  • 4
    \$\begingroup\$ 1. Use D, not dd. 2. If the command doesn't involve registers, you can make it even shorter. For example, if you want to repeat the character x as many times as the number under the cursor, instead of "qD@qix<esc>, use D@"ix<esc>. \$\endgroup\$
    – Doorknob
    May 29, 2016 at 16:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.