Поради щодо гольфу in vim


32

Нещодавно я зрозумів, як vim чудово з гольфом, особливо для . Крім того, згідно з мета vim - це цілком прийнятна «мова програмування», принаймні, для сфери використання цього веб-сайту, тобто.

Які загальні поради щодо гольфу у Vim? Я шукаю ідеї, які можна застосувати до проблем із гольф-кодом, які також принаймні дещо характерні для Vim (наприклад, "видалити коментарі" - це не відповідь).

Будь ласка, опублікуйте одну пораду на відповідь.


4
Це особливо корисне питання поради, оскільки "Поради щодо гольфу в vim" та "Поради щодо ефективного використання vim як текстового редактора" - це в основному те саме.
DLosc

Відповіді:


15

Будьте в курсі великих варіантів загальних команд. Це може тривіально зберегти один натискання клавіші у багатьох контекстах. Наприклад:

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

Інший - використовувати Gзамість gg, але тільки якщо ви використовуєте кількість! Наприклад, без рахунку ggрухається до початку і Gрухається до кінця. Однак, підрахувавши кількість, вони обидва рухаються до вказаної вами лінії, тому 5Gвона коротша, але еквівалентна5gg .

Ще один варіант - використовувати Hзамість gg, але важливо зазначити, що це працює лише в тому випадку, якщо ви можете гарантувати, що будь-який вхід не займе більше рядків за замовчуванням (я вважаю, що це 24, але він може змінюватися).


1
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).
Martin Ender

1
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.
Alex A.

6
@AlexA. Personallly, I think modifiers shouldn't count. Maybe we need a meta post on this...
DJMcMayhem

G and gg are not equivalent. The former goes to the end of the buffer and the latter goes to the beginning.
Jordan

@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.
DJMcMayhem

11

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.

8

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.


6
:%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.)


2
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.
DJMcMayhem

1
You can also use :&& to repeat a substitute command and it's flags.
DJMcMayhem

6

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

I don't think those spaces are necessary either
Fund Monica's Lawsuit

@QPaysTaxes You're right, they aren't. I thought it would make it more readable. I'll edit that.
DJMcMayhem

You don't need a <CR> after <foobar> to send the command?
Fund Monica's Lawsuit

@QPaysTaxes Only if it's an ex command or a search. (E.g. any command starting with a :, /, or ?)
DJMcMayhem

Oh, my bad. I confused command and normal modes. Cool :D
Fund Monica's Lawsuit

5

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.


1
Don't forget ~, which toggles the case of the character under the cursor and moves one to the right!
Doorknob

5

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.


4

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.


3

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

3

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!


H is golfier than gg
Kritixi Lithos

@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)
DJMcMayhem

2

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.


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!
DJMcMayhem

1
My goal was to demonstrate how to use the feature, not how to number lines in the fewest keystrokes.
Jordan

1

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.


1
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>.
Doorknob
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.