Copy block of code with increasing index - vim

I have the following function declaration:
function f1(s)
real f1,s
f1 = 1/s
end
I would like to copy the same block but with increasing function name, i.e. f1, f2, f3, ... in order to get this:
function f1(s)
real f1,s
f1 = 1/s
end
function f2(s)
real f2,s
f2 = 1/s
end
function f3(s)
real f3,s
f3 = 1/s
end
My present approach is to copy the block in visual mode, paste it several times and then rename the functions manually. I would like to know how to achieve this faster using Vim. Thanks.

You can always use a recording (see :h recording). Assuming you have a blank line before and after your function and no blank lines in between
// empty line here, cursor here
function f1(s)
real f1,s
f1 = 1/s
end
// empty line here
With the cursor on the empty line above, make a recording to any register you like. Here I'm using register c. Press qc then press y}}Pjw*Ne^Ane^Ane^A{ and exit with q.
Explanation
y} - yank next paragraph
} - move down one paragraph
P - put above this line
j - move one line done
w - move to next word
* - search for word under cursor ( this is the function name here)
N - search backwards ( we moved with * to get the pattern into the register )
e - go to end of word
^A - Ctrl a to increase the number
n - go to next match / search forward ( this is the function name )
e - go to end of word
^A - increase the number
n - go to next match / search forward
e - go to end of word
^A - increase the number
{ - move up one paragraph (same relative position as in the beginning, but at the inserted function f2 )
Now you can use #c to copy the function and increase all numbers. Prefix with the count you want, e.g. 5#c will copy the function 5 times and adjust the numbering.
In case you don't want to remember the string y}}Pjw*Ne^Ane^Ane^A{ you can paste it in the vim buffer. You will have to replace the ^A before yanking though. Delete ^A and when in insert mode press Ctrl va. ( If you are inside a screen session you will have to press Ctrl aa, this is CTRL-a and a)
With the cursor on the line in normal mode press "cY to yank it in register c.
Then you can replay it with #c.
This way you can also modify it or yank it to another register.
Use let #c=y}}Pjw*Ne^Ane^Ane^A{ in your .vimrc to have it always load to register c when starting vim.

I figured out the solution. It may seems complex but does not, cause you have just to copy this function to the clipboard and load the function with:
:#+
Below the function with additional features
fun! CopyAndIncrease()
try
let l:old_copy = getreg('0')
normal yip
let #0 = substitute(#0,'\d\+','\=submatch(0) + 1','g')
exec "normal }O\<Esc>p"
finally
call setreg('0', l:old_copy)
endtry
endfun
command! -nargs=0 CopyIncrease silent call CopyAndIncrease() | exec "normal \<Esc>"
let mapleader = ','
nnoremap <Leader>c :CopyIncrease<CR>
Now pressing ,c Now you can use the keybinding and commands defined in the function.
Function's explanation:
let l:old_copy = getreg('0') -> save copy register
normal yip -> copy block
let #0 ... increase numbers on copy register
exec "normal }O\<Esc>p" -> paste increased block

Related

variable search and replace in vim

I have a list of items that I want to add a method call to. An example is easiest. Here's what I have now:
assists: 12,
level: 14,
deaths: 5,
...
I want to change that list to look like this:
assists: build_average(:assists),
level: build_average(:level),
deaths: build_average(:deaths),
...
Is it possible to add that method call to the end of every line with the name of the key as the argument with a neat Vim expression?
More of a regular expression:
:%s/\(\w\+\):.\+/\1: build_average(:\1),/
Note that this applies to all lines in your file. To only replace in a region, select the region (using V) and then use :s (which results in :<,>s/...).
Using more complex regular expressions in VIM can be tricky, because metacharacters are different from "normal" regular expression syntax (you need to write \+ instead of +, but can use . without escaping it, for example). I found this guide very handy to refer to the special VIM-syntax of regular expressions: http://vimregex.com/#pattern
Alternatively you can record a macro:
q // record macro
q // assign it to letter 'q'
0 // go to start of line
/:<ENTER> // search for ':'
l // move cursor 1 position to the right
d$ // delete to end of line (line is now 'assists:')
yyp // duplicate current line (cursor moves 1 line down)
k // move cursor up
A build_average( // append " build_average("
<ESC> // exit edit mode
J // join next line
A ), // append " ),"
<ESC> // exit edit mode
j // move 1 line down
q // stop recording macro
2#q // execute macro 'q' 2 times
More regexp gymnastics:
:%s/\v(\w+):\s+\zs.*\ze,/build_average(:\1)/
Decrypting it:
:help \v
:help \w
:help \s
:help \zs
:help \ze
:help \1

Jump to the end of a long list of repeated pattern

I have a big file with a lot of lines that share the same pattern, something like this:
dbn.py:206 ... (some other text) <-- I am here
dbn.py:206 ... (some other text)
...
(something I don't know) <-- I want to jump here
Is there a quick way in Vim to jump to the place where the succession of dbp.py:206 ends?
/^\(dbn.py\)\#!
Matches first line which does not start with the text inside the escaped parentheses.
If you want quick access to this you could add a vmap which yanks the visually selected text and inserts it in the right spot (but first escaping it with escape(var, '/').
Try this vmap: vmap <leader>n "hy<Esc>/^\(<C-R>=escape(#h,'/')<CR>\)\#!<CR>
Press n when visually selecting the text you wish to skip and you should be placed on the next first line which does not begin with the selection.
I just write a function to select identical lines:
nnoremap vii :call SelectIdenticalLines()<CR>
fun! SelectIdenticalLines()
let s = getline('.')
let n = line('.')
let i = n
let j = n
while getline(i)==s && i>0
let i-=1
endwhile
while getline(j)==s && j<=line('$')
let j+=1
endwhile
call cursor(i+1, 0)
norm V
call cursor(j-1, 0)
endfun
type vii to select identical lines (feel free to change the key-binding)
type zf to fold them.
type za to toggle folding
It's handy when you want to squeeze several empty line.
It acts like C-x C-o in emacs.
One option is to go to the bottom of the file and search backwards for the last line you want, then go down one:
G ?^dbn\.py:206?+1

Mark a block of characters in VI/M ex command

It's quite straitforward for VI/M to mark a block of lines from Mth line to Nth line ready to delete, cut & past, or copy & paste.
:M,N d
:M,N m p
:M,N t p
If it's further required for VI/M to mark a block of characters from Ith character of Mth line to Jth character of Nth line, is it possible to accomplish similarly to the above?
#EDIT
Except the next answer asked for visual block mode, how about the option on typing a succinct ex command?
#EDIT 2
To clarify the meaning of a block of characters:
a square block of characters, addressed by visual block mode, directly called upon by pressing Ctrl-v in normal mode
a zipzag area of successive characters, addressed by visual character mode, directly called upon by pressing v in normal mode
a rows region of successive lines, addressed by visual line mode, directly called upon by pressing V in normal mode. In this case, the handy solution in ex mode has been illustrated above when this topic was originally raised.
#SOLUTION
Selecting abitrary zipzag area of successive characters from line M, column I to line N, column J in ex mode exactly like in visual character mode :
mark:
:normal! MggI|vNggJ|
delete:
:normal! MggI|vNggJ|d
yank:
:normal! MggI|vNggJ|y
move to line X column Y
:normal! MggI|vNggJ|dXggY|p
copy to line X column Y
:normal! MggI|vNggJ|yXggY|p
#SOLUTION 2
Selecting abitrary square block of characters from line M, column I to line N, column J in ex mode exactly like in visual block mode :
mark:
:execute "normal! MggI|\<C-v>NggJ|"
delete:
:execute "normal! MggI|\<C-v>NggJ|d"
yank:
:execute "normal! MggI|\<C-v>NggJ|y"
move to line X column Y
:execute "normal! MggI|\<C-v>NggJ|dXggY|p"
copy to line X column Y
:execute "normal! MggI|\<C-v>NggJ|yXggY|p"
You can use visual block mode from an ex command mode using normal!: for example, to select a block (line, column) from (42, 10) to (54, 20) and yank it (both lines must have at least 20 characters or virtualedit=block should be set):
execute "normal! 42gg10|\<C-v>54gg20|y"
. It is very straightforward way to do this, useful only in scripts.
Note that this command has at least following side-effects:
Setting marks '<, '>, '[, '], ''.
Moving a cursor.
Changing registers #", #0.
Adding one item to the jumplist.
Overwriting previous visual selection.
Altering v:count and v:count1 variables.
Ctrl+V enables visual block mode, then you can use the arrow keys to select the block.

How to repeatedly add text on both sides of a word in vim?

I have a bunch of local variable references in a Python script that I want to pull from a dictionary instead. So, I need to essentially change foo, bar, and others into env['foo'], env['bar'] and so on. Do I need to write a regular expression and match each variable name to transform, or is there a more direct approach that I could just repeat with the . command?
You can use a macro: type these commands in one go (with spacing just to insert comments)
" first move to start of the relevant word (ie via search)
qa " record macro into the a register.
ienv['<esc> " insert relevant piece
ea'] " move to end of word and insert relevant piece
q " stop recording
then, when you're on the next word, just hit #a to replay the macro (or even ## to repeat the last replay after that).
There's an easier way - you can use a regex search and replace. Go into cmdline mode by typing a colon and then run this command:
%s/\\(foo\|bar\|baz\\)/env['\1']/
Replacing foo, bar, and baz with whatever your actual variable names are. You can add as many additional variables as you'd like, just be sure to escape your OR pipes with a backslash. Hope that helps.
you could write a function that would do this pretty well, add this to your .vimrc file:
function! s:surround()
let word = expand("<cword>")
let command = "%s/".word."/env[\'".word."\']/g"
execute command
endfunction
map cx :call <SID>surround()<CR>
This will surround every occurance of the word currently under the cursor.
If you wanted to specify what went before and after each instance you could use this:
function! s:surround()
let word = expand("<cword>")
let before = input("what should go before? ")
let after = input("what should go after? ")
let command = "%s/".word."/".before.word.after."/g"
execute command
endfunction
map cx :call <SID>surround()<CR>
If you only want to confirm each instance of the variable you could use this:
function! s:surround()
let word = expand("<cword>")
let before = input("what should go before? ")
let after = input("what should go after? ")
let command = "%s/".word."/".before.word.after."/c"
execute command
endfunction
map cx :call <SID>surround()<CR>
I figured out one way to do what I need. Use q{0-9a-zA-Z"} to record key strokes into a buffer. Position the cursor at the begging of the variable name, then cw and type env['']. Next move the cursor back one space to the last quote and paste the buffer filled from the cw command with P. Finally, reuse the recording with #{0-9a-z".=*} for each variable.

How to append to the clipboard

I know how to copy to the clipboard but how can I append to it?
I use this in my code:
let #+ = my_expression
but that overwrites the clipboard.
I know that I can use the registers a-z to append to:
let #B = my_expression
which appends to register b, but what do I when I want to append to the clipboard?
use:
let #+ = #+ . my_expression
or shorter:
let #+ .= my_expression
Reference: :help :let.=
If you're not macro-ing, it's probably worth checking out registers as well. :help registers was mind-blowing.
In an oversimplified nutshell, there are 26 additional "customizable clipboards", called registers, where you can store text, starting with a and going through z.
You add text to a register in command mode by hitting ", naming the register (say f), and then typing the "motion" you want to select text.
NOTE: We're using the named f register here b/c it's probably under your left index finger. That is, f is picked just b/c it's handy. You could replace f with a or u or z or whatever throughout if you wanted.
Copying with a register (cursor at [T]):
Initial File State
This is my first line.
[T]his is my second line.
This is my third line.
Type "fyy in command mode to fill the register with one line (yy).
Type p (* see below) to immediately paste it from the default register.
Type "f to pick the f register and then p to paste from the f register directly. Right now f and default as the same.
So the result of typing "fyyp is exactly the same as having typed yyp with the default clipboard.
Result
This is my first line.
This is my second line.
[T]his is my second line.
This is my third line.
Appending to a register:
Use the capital letter to append to your existing register.
In the above example after pasting, press j to go down a line and then "Fyy. Then type p to paste. You've appended "This is my third line." to f's contents.
Result
This is my first line.
This is my second line.
This is my second line.
This is my third line.
This is my second line.
[T]his is my third line.
(Using a lower case f would have cleared out f's contents and ended up with it only holding "This is my third line.")
Why does p paste what's in register f immediately after you yanked into f? Because your default register holds a pointer to the last selection, and apparently doesn't simply hold what you added to f, but pulls everything that's in f when you append. It might be more expository to say, in the first case, "the result of typing "fyy"fp is exactly the same as having typed yyp with the default clipboard."
But if you were now to yy a new line into the default register, you can hit "f to select the f register and then p to paste that previous value.

Resources