Hacker News new | past | comments | ask | show | jobs | submit login

Maybe you can cover macros since they are conceptually very simple and very powerful.

They may seem a bit complex but are quite simple. Basically you just record your editing movements and play it back.

How macros work: press q x to record and q to end. Then repeat with @ x, where x is the name of the macro you want to use (you can have several macros). Anything you do will be played back. That's it... Conceptually simple.

A nice trick is to record a macro for a single line and place the cursor on the start of the next line at the end of the recording. This way it's possible to operate upon large blocks of text in a similar way by just repeating the macro. The key then is to use generic movements like "go to the beginning of line" (^), "go to the end of line" ($), "skip word" (w), etc.

As a silly toy example, if you have a list of strings in a text file, you can turn it into a list of quotes with (from the top of my head):

  qx<Home>i"<Esc>$i<Right>",<Down><Home><Esc>q
(Record macro, go to beginning of line, insert mode, write a quote, exit insert mode, go to end of line, insert mode, move right, write a quote and a comma, move down to next line, move to the beginning of line, exit insert mode, end macro recording)

Then to process 100 lines, you'd do:

100@x

That turns these:

  alpha
  bravo
  charlie
  delta
  ...
into

  "alpha",
  "bravo",
  "charlie",
  "delta",
  ...
If you have hundreds of lines of that you'll start to appreciate the easy automation.

I use macros to do drive-by mass-edits without resorting to sed, scripting, regular expressions etc. since the cognitive load of those is higher for me.

Edit: formatting




My favorite newish-to-me macro trick is to use "recursive macros" -- macros that call themselves at the end, eg `qa[do something]@aq`. The idea (at least for me so far) is to record a line-wise action that goes to the next line before calling itself and will fail on the first non-matching line. For example, if you need to put some text after the first `0`, using commands like `t` or `f` to get there will fail if there is no `0`, but `/` will go to the next `0` on any line.

A macro like this will just keep calling itself until it fails, which is nice for files with thousands of lines and you want to make a change that is not conducive to a regex.

As a tip that took me a while to figure out, you can search within a visual selection using `\%V`, which may make it easier to reliably go to a specific part of the current line (compared to t or f like I mention above).

Also wanted to point out that iVim for iOS is pretty good, for those rare cases you want to do some text processing on the move that makes you miss Vim: https://itunes.apple.com/us/app/ivim/id1266544660?mt=8

A decent post on recursive macros: https://jovicailic.org/2018/06/recursive-macros-in-vim/


A slight alternative to recursive macros, macros are repeatable actions, so this works: 100@a

I use it so I can run the macro in batches a couple times and check if I made any mistakes, before picking an absurdly high number to hit the whole file.


Another alternative, visually select the region you're interested in then:

:'<,'>normal @q

to apply the macro (in q) to that region. You might want to remap the above command to some shortcut if you find it useful often.


I've used vim for a couple of decades, but never pushed to many advanced features. For your example above I'd do :%s/^/"/ and :%s/$/",/

Unless I use a feature regularly I find it's a higher cognative load. I use bash, sed and perl regexps on a near daily basis. Macros are very rare.


For that same example, you can accomplish the job in one substitute command:

    :%s/.*/"&",/
The & special character gets replaced with the whole matched pattern.


How do you escape the & character? I have been bit by this (not escaping &) a few times but never enough to have found the answer.


The same way you escape anything - with a \. E.g. if you wanted to substitute the character x with the sequence &/\ you would type

:s/x/\&\/\\


Likewise - with visual mode it's normally pretty quick to select an area and do a basic regex, even for things like re-indentation (auto-completes to something like :'<,'>s/^/\t/) even though I'm sure there's a 'proper' way to do that


> for indent, < for un-indent


For that example I would open the document in Sublime Text, select all, and split selection into lines so that I have an independent caret on every line. Then I'd just type the quote at the beginning and end of each line - the home/end keys will put each caret at the beginning or end of its respective line.


I've been using vim a lot recently. Multiple cursors for this exact scenario is the only thing I still miss about other editors.

Macros and other solutions are great, but for this very simple and surprisingly common task, multiple cursors are perfect.


Multiple cursors has been supported for quite some time now in VSCode, as well as related useful default editor shortcuts like jump cursor by word, beginning/end of line, etc, that are especially useful when moving around multiple cursors/selections simultaneously.


I was going to say that one annoying thing about these macros is you can't press . to repeat the last macro. It is annoying having to count out 143 lines and then do 143@x or what I tend to do is do it in batches of say 20 until I get to the end..

But this has prompted me to look this up and it turns out that you can use @@ to do this. This will make my life a lot easier, I can just smash <Shift 2> until all my lines are updated.


You can also visual select all the lines and then do

  :'<,'>norm @x


Another notable trick is that vim registers are case-insensitive -- and since macro's are just stored in registers, macro's are case-insenstitive as well. so @q == @Q, which alleviates some of the RSI problems with spamming @q.


I remember something about a capital register appending instead of overwriting -- is that only for saving (with no influence in execution)?


Yes, that's only for writing. For example, to collect all lines matching a pattern into a register with

  :g/pattern/y A
you need to use an uppercase register ("A" in this case) because it executes `y` once per match. With `y a` instead of `y A`, you would end up having only the last match in the register.


I also use a lot of macros. I just learned that macro registers are the same as regular registers, so you can actually paste the contents of a macro and examine it or save it to a file (e.g. "xp after recording your macro).


I use the . (dot) command a lot out of habit. It executes the last command. If the last command was a macro execution, dot doesn't execute the macro - it executes the last command of the macro. Is there a way to fix this?

edit I see someone else answered this - use @@ instead of .


@@ repeats the last macro


Yep, and you can just hold down @ to blaze through many lines. If you overshoot, just undo a bit.


Another simpler way (even simpler than macros) that works great for simple mass editing is visual blocks. In normal mode, press Ctrl + V and you get to select a literal block of text (not like visual mode, where the text is sequential). Then you can for example, do a "c" to change the selected text in each of the rows that your block covers. This is especially practical to remove a column. Just select the column, press d and voilà.


this is somewhat shorter:

    qq0i"<Esc>A",<Esc>jq@@
i agree this example is silly. i'd do something like

    :%s/.*/"&",/


Yeah, it is a toy example on purpose.

Macros really shine when you need to do a bit more complex stuff, there is certainly a cutoff between ease of regex "from the top of my head" vs. spending time formulating it.


This is really neat, thanks for rundown. Its easy to get far too comfortable with ad-hoc sed commands when there's far better solutions out there.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: