2013-11-23

Stairs Indenting

The challenge. Every line needs a number of spaces equal to the line number. There are lots of interesting solutions, so I'll show several.

Stairs Indenting

:%no<S-Tab> i ^A^MZZ for 12.

:%normal runs its argument as a macro on every line, and that macro is simply i ^A. There's no need to escape insert mode; that's automatic at the end of a :normal. The whole command means "insert a space and repeat the last insert (using ^A)". The last insert is stored in the ". register. Every time the command runs, ". is updated to contain both the spaces it had before (inserted by ^A), plus the one entered manually.

Insert mode ^A is actually equivalent to ^R., inserting the contents of the ". register as if typed (like an insert mode macro), and leaving those contents in the new copy of ". once the insert is complete. :help i^@

When ^A runs the first time, ". will not exist yet, and that will fail the macro. In fact, it will throw an E29: No inserted text yet error. (Most macro failures aren't caused by actual errors, but this one is.) Thing is, since we're running from :normal, most screen updates won't show, including this error. Instead, Vim hangs for a second, waiting to dismiss an invisible error.

The macro failure has an interesting effect if you do the insert in the other order (i^A ). In the first line, the failure comes before the space, so the space doesn't get inserted. However, insert mode was entered, so ". will exist for line 2, though it will be empty. In the end, each line has one less space than you'd expect, because the macro doesn't really start until line 2.

Alternate solutions

qqi ^@+q9@qZZ for 12.

Same stroke count as the last solution. In fact, it is the same solution, adapted for a recorded macro. You need + to move the cursor to the next line, because unlike :normal, nothing's going to do it for you. Insert mode escaping isn't automatic, but fortunately there's ^@, which is like ^A^[; it enters the text in "., and it escapes insert mode.

You can see the error message this time! The empty ". would still cause macro failure, but it only occurs during recording, where you can fail all you want. As long as the macro doesn't fail early when you actually run it, you're fine.

:g/^/s//~ ^MZZ for 13.

:g runs the :s command on every line that matches ^ (or every line that has a beginning, which is all of them). We use a blank search string for :s, which makes it default to the previous search string, which was set to ^ by :g. The ~ in the :s replace string means "last replace text". It starts empty on line 1, then picks up the manual space on line after.

If you just used :%s/^/~ , :s would run only once, and the same replace string would be used on every line. When you run it from :g however, it runs a separate :s on every line.

:%no<S-Tab>^V^VGI ^MZZ for 13.

On every line, use block visual to insert a space from that line to the end of the file. You can't just type ^V for block visual on the command line, so you need ^V to escape it. It's the same character both times, but they mean totally different things. :help c^V

^VG:no<S-Tab>1vI ^MZZ for 13.

Same idea, but using visual mode instead of % as the range to :normal, at the cost of a stroke. This lets you set the visual range with 1v instead of ^V^VG, saving the stroke back. 1v means "make a new visual area from the cursor, same size as the last one", but here it runs into the end of the file, so if effectively shrinks a line each time.

:g/^/,$s// ^MZZ for 14.

For every line, substitute the beginning of every line to the end of the file with a space.

:%no<S-Tab> y$@"i ^MZZ for 15.

Use the number on the line as an argument to i to determine how many spaces it adds.

:se et|%no<S-Tab>==i ^MZZ for 18.

Use == to match the indent of the previous line. Problem is, when it tries to match an 8-space indent, it merges the spaces into tabs, so you have to :set expandtab to stop that. (:ret99 also works by raising 'tabstop', for the same stroke count.) If 'expandtab' was already set, this could tie for the win at 12.

:se sw=1 et|%no<S-Tab>>G^MZZ for 21.

If you started with these (unusual) settings, this would be the winner at 10.

Similar challenges

No comments:

Post a Comment