physicsmichael
physicsmichael

Reputation: 4963

Vim copy and paste line with a search and replace

Say I've written code that references the x dimension. What is the best way to get vim to duplicate a line of code replacing all references to x to y and to z (best being the most clear method).

Input:

length_x = X_vec.dot(X_vec)**.5

Desired Output:

length_x = X_vec.dot(X_vec)**.5
length_y = Y_vec.dot(Y_vec)**.5
length_z = Z_vec.dot(Z_vec)**.5

Here's my best so far.

function SwitchXtoYZ()
  :normal yy
  :normal p
  :normal! V
  :s/X/Y/ge
  :normal! V
  :s/x/y/ge
  :normal p
  :normal! V
  :s/X/Z/ge
  :normal! V
  :s/x/z/ge
endfunction

command XtoYZ exec SwitchXtoYZ() | :normal `.

It works, but I feel this is not very vim-y. Bonus points if the cursor returns to where it was before the command XtoYZ was issued (it currently goes the beginning of the second inserted line).

Upvotes: 1

Views: 271

Answers (2)

benjifisher
benjifisher

Reputation: 5112

The : at the beginning of each line is optional, as are the :normal! V lines.

You are leveraging the Normal commands that you know, which is a good way to start, but IMHO you get cleaner code if you use more Command-mode (ex) commands and functions. I would do something like this:

function! SwitchXtoYZ()
  let save_cursor = getpos(".")
  copy .
  s/X/Y/ge
  s/x/y/ge
  -copy .
  s/X/Z/ge
  s/x/z/ge
  call setpos('.', save_cursor)
endfun
command! XtoYZ call SwitchXtoYZ()

:help function-list
:help getpos()
:help :call
:help :exec

Upvotes: 1

Kent
Kent

Reputation: 195029

You don't need a function to do that, a macro would be fine for your requirement. Also you can define a macro in your vimrc too, if you like, so that you can have it everytime you open vim.

here is the macro:

qqv<Esc>Y2p:s/x/y/gi<Enter>n:s//z/gi<Enter>`<q

so it was recorded and saved in register q, you can @q to replay it.

explain it a little:

qq               " start recording into q
v<esc>           " enter visual mode and exit. to let `< work
Y2p              " yank current line and paste twice below
:s/x/y/gi<Enter> " x->y sub, case insensitive
n                " go to next x (here we could use j too)
:s//z/gi<Enter>  " do another sub x->z
`<               " back to the old cursor position
q                " end recording

if you want to X->Y and x->y, just remove the i flag and add two more :s

Upvotes: 1

Related Questions