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

> This means using :normal and macros instead of :s for most of my search/replace actions

Interesting. Can you please elaborate more on this?




Suppose a range of lines contains function calls in some programming language:

    i = foo(a, 42, 1)
    j = bar(b, c, 100, 2)  # this is a comment (well, duh)
    bazbar(etc, 3)
You want to edit the last parameter in each function call -- change it to 'hello'. I have two ways of approaching this: macros (interactive) or :norm (less interactive).

1. Put your cursor on the first byte in the first line, type qq to record a macro of your change, q to finish. Select the rest of the lines in visual line mode and type :'<,'>norm @q to run the macro on each of the remaining lines.

2. Select the lines in visual line mode and type, for instance, :'<,'>norm %F,ws'hello' This is the "non-interactive" version of the above since the macro keystrokes are given directly to :normal. Pro: ability to undo keystrokes unlike when recording a macro. Con: cannot actually see the effect of the keystrokes (but with practice, this is not a problem).

(Note that colon in visual mode automatically inserts the :'<,'> so you only have to type :norm ...)

I find the declarative nature of regexes/:s to be too restrictive -- I much prefer the operational nature of macros/:normal, since that lets me make the change I want directly, without me having to phrase the change as a regular expression.

I could also use CTRL-A in normal mode to increment the last argument in each line: :'<,'>norm %b^A (where ^A is the literal, typed CTRL-V CTRL-A)

I feel like a wizard whenever I use :normal in a non-trivial manner.


Lord, that's so useful.

I normally include a move to next line part in my macro and then guess at how many times to run it (20@q, repeat until I get it right). It's actually the main reason I often don't bother using macros.

This is such a great tip. Thanks!


You can combine regexes with macros if you only wish to run your macro on a particular set of lines:

    :'<,'>g/foobar/norm @q
will run the macro only on the subset of selected lines that match the regex "foobar". :g/.../ will match the regex against all lines in the file.


I love the g command. Very useful to quickly cull out lines of stuff that you don't want in your file. g/foobar/d


@@ applies the previously applied macro, so after applying the macro (including the move to next line, but see below too) you can you just hold down @ and quickly go through as many lines as you need. If you overshoot, you can just press u a few times.

Another tip: when you forget to add the move to next line to your macro, you don't have to start over. You can append to a macro, just use the upper-case register name instead. So to append a down and move to beginning of line to macro 'a', you'd type qAj0


Both useful tips. I knew the macro is really just replaying whatever is in the register but it's good to connect the append dots to the equation.


Isn't

:%s/.)/hello)/

simpler? Maybe that's because I know nothing about macros. Thanks for the explanation.


Pardon, it might have been a trivial example. I managed to find a real-world example I encountered in February this year.

The PAPI library has a utility program papi_avail which prints a table of supported performance counters. The lines may look like this:

    PAPI_VEC_SP  0x80000069  Yes  Single precision vector/SIMD instructions
    PAPI_VEC_DP  0x8000006a  Yes  Double precision vector/SIMD instructions
    PAPI_REF_CYC 0x8000006b  No   Reference clock cycles
For a course project, I wanted to turn this into a C-array like this:

    {PAPI_VEC_SP, "Single precision vector/SIMD instructions"},
    {PAPI_VEC_DP, "Double precision vector/SIMD instructions"},
    {PAPI_REF_CYC, "Reference clock cycles"},
Using :norm, this transformation can be achieved in the following way:

    :'<,'>norm I{^[eldedecw, "^[A"},
(where ^[ is a literal escape, typed CTRL-V ESC.)

Anecdotally, it is faster for me to type up such a line than an equivalent regular expression, since I use vi normal mode commands much more than regular expressions.


  /^\(\S*\)\s*\S*\s*\S*\s*\(\p*\)
  :%s//{\1, "\2"},
"Now they have two problems."


I would have piped this through awk, but this is probably better done with macros. Thanks for taking the time to elaborate. I will definitely give macros a try.


I just spent minutes to parse that instruction. Mind blown. Thank you for this enlightenment.


You need to know how dw, de, cw, ce relate to each other, when the cursor is on a token character vs. a whitespace character, and so on, in order for this to be truly useful -- but luckily, ordinary practice with those commands in Vim can lead to an efficient internal idea of how it works. "You get used to it, though. Your brain does the translating. I don't even see the code. All I see is blonde, brunette, redhead."


this is awesome, can you please suggest some resources for me, a long-time basic user of vim, ( know a lot of commands for actions, but not the underlying relationships?


Something i've been using more lately is Ctrl-R + Register. Great for inserting text in command mode. For instance, you can do this to insert your recorded macro (if it's in register q) as a norm command: :norm Ctrl-R q


I would have almost simply started recording a macro, convert the first one using as many idempotent normal mode commands in place of insert mode commands as possible, and simply replayed it. Thanks for the idea.

As a side note, I love how Vim is basically a functional programming language for text editing.


You can do this with a few shortcuts in sublime. It requires about the same number of keystrokes, but far less cognitive overhead.

    { down { down {
to insert all three leftmost brackets.

    up up up
to go to first bracket.

    shift+left
to select first bracket.

    cmd+d cmd+d cmd+d
to select all three next brackets including the first.

    ctrl+shift right
to move all three cursors to the end of word.

    comma space "
to type your characters with all three cursors.

    cmd+right
to go to end of line with all three cursors.

    " } ,
to type your characters with all three cursors.

21 presses (29 keys), compared to your 32. And all I had to think about was where the cursor was.

(Not to be pedantic, of course!)


Well you're leaving the middle columns there, then on your first keystrokes it would be acutally {+down+left as Sublime does not put cursor back at beginning of line when going down. In all I used 26 keystrokes in Sublime, and 27 in Vim. Also, if you understand Vim's vocabulary, the "cognitive overhead" is, as you put it "far less" in Vim's favor. IMO.


I guess that would change the comment : "duh)" would become "hello)", whereas the macro does not.


No, not without a 'g' at the end.


care to suggest any resources that helped you reach wizard-like levels of usage? I very much wish to work towards that kind of ability in vi/vim.


I am sorry, but I don't know any resources... I have collected a few things on the following page: http://users-cs.au.dk/rav/vim/

Basically, I achieved my wizard-level C++, Vim and Git knowledge during my three years as a part-time student programmer in the basic research center MADALGO. I took the time to study the documentation (respectively the C++11 draft, :help and the git man pages) whenever I was curious. For Vim in particular, I made sure to eliminate all repeated keysmashing in my daily workflow, which "unfortunately" required me to learn about macro wizardry.

My only advice is to keep practicing, to keep trying to spot inefficiencies in your own workflows, and to actively eliminate these inefficiencies by studying the Vim help pages.


Over the years, I've got a lot of mileage out of "Learning the vi and vim Editors" from O'Reilly, and web pages like http://www.lagmonster.org/docs/vi.html. Practicing and testing compositions based on what you learn from these will go a _long_ way. Try new ways to move (by screens, by sentences, by blocks), test using your buffers, experiment w/ transforms. Then, one day, you realize you're pretty good with vi.


I like http://learnvimscriptthehardway.stevelosh.com/ , which covers a different set of features.


Another little "trick" for replacing:

  " search. cw (or cs, c whatever) to replace/fix. esc. n.n.n.n.
  vnoremap <silent> s //e<C-r>=&selection=='exclusive'?'+1':''<CR><CR>
      \:<C-u>call histdel('search',-1)<Bar>let @/=histget('search',-1)<CR>gv
  omap s :normal vs<CR>
A really simple, dumb example: http://showterm.io/040c49ac158cdda15fbca


Can you explain what the mappings do?

I usually use the asterisk to search for the token under the cursor, edit the sought token using cw and then n.n.n.n. to replace later occurrences. I don't see how the mapping makes this more efficient.




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

Search: