variable search and replace in vim - 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

Related

Jump to current function declaration from middle of function

Is there a way to jump to the signature of the function my cursor is currently in, then jump back to where I was?
For example, when I have a 1000 line function, where the prefix x + y: refers to line numbers, is there a way from me to jump from my cursor location at x + 555 to the signature at x + 0 then back to where I was at (x + 555):
x + 000: void theFn(int arg) {
x + ...: ...
x + 555: /// where my cursor starts
x + ...: ...
x + 999: }
And, yes, I couldn't agree with you more that there shouldn't be 1000 line functions.
Also, is there a way to automatically jump to the end of function without being at the opening bracket of the function?
Useful motions in such case are [[, ][ and <C-o>.
As we can read in help:
*[[*
[[ [count] sections backward or to the previous '{' in
the first column. |exclusive|
Note that |exclusive-linewise| often applies.
*][*
][ [count] sections forward or to the next '}' in the
first column. |exclusive|
Note that |exclusive-linewise| often applies.
*CTRL-O*
CTRL-O Go to [count] Older cursor position in jump list
(not a motion command).
{not available without the |+jumplist| feature}
In short:
[[ to got to the beginning
<C-o> to go back to previous place
][ to go to end
Those motions will have the desired effect only when braces are in the first column, but from your example seems like this requirement is not met.
In such case at the end of :h section we can read:
If your '{' or '}' are not in the first column, and you would like to use "[["
and "]]" anyway, try these mappings: >
:map [[ ?{<CR>w99[{
:map ][ /}<CR>b99]}
:map ]] j0[[%/{<CR>
:map [] k$][%?}<CR>
Unfortunately, Vim doesn't offer better solution as it doesn't parse syntax.
It may change though as Neovim experiments with Tree-sitter.
It also wouldn't be surprising if there was a plugin which provides better support for such motion.
Tagbar could fit this role:
Toggle Tagbar window
Switch to it
Cursor should be already over the current tag
Press enter
Toggle window
You are at beginning of the function
Use <C-o> to get back
I also once found and had in my config a mapping which could also be useful in such case:
nnoremap <Leader>gd ?\v%(%(if|while|for|switch)\_s*)#<!\([^)]*\)\_[^;(){}]*\zs\{

How to multi-line the import functions in {} in VIM?

Sorry for the bad title but
With this code,,,
import { amethod, methodb, methodc } from '../../utils/mockData';
how do you make this
import {
amethod,
methodb,
methodc
} from '../../utils/mockData';
with VIM? I mean how can I do it fast?
What I do is to
go to the first method.
press Enter
go to the end of the method..
press Enter...
go to the second method.
6....
...
It's so slow with VIM. Can we do this fast in VIM? I think I can this much faster with my mouse :(
import { amethod, methodb, methodc } from '../...';
^
f ;r<CR>;.;.;.
doesn't strike me as particularly slow. A tad too repetitive, maybe?
Here is a slightly smarter (but probably not that smart) approach:
ciB " change in brackets
<CR><CR> " insert two carriage returns
<Up> " move up one line
<C-r>" " insert previous content of brackets
<Esc> " leave insert mode
:s/,/,\r/g<CR> " put each symbol on its own line
=iB " re-indent the content of brackets
that can be mapped for convenience:
nnoremap <key> ciB<CR><CR><Up><C-r>"<Esc>:s/,/,\r/g<CR>=iB
Or you can look for a proper plugin that handles corner cases gracefully.
#romainl had a good answer, but you can also use replace for this particular case:
:%s/\([,{]\)/\1\n/g
With the cursor on line1 and col1, you can press this in normal mode:
f 4#=';r^M'
then press ENTER.
Note: for the ^M you press Ctrl-v then Enter
I have got this
import {
amethod,
methodb,
methodc
} from '../../utils/mockData';
Using this:
:%s/\v\{\zs( \w+,?)+ \ze}/\=substitute(submatch(0), " ", "\n\t", "g")
\v ............ very magic regex (avoid many backslashes)
{ ............ literal {
\zs ........... vim trick that marks the start of the pattern
( ............ start of regex group 1
<Space> ....... literal space inside group 1
\w+ ........... one word or more
,? ........... optional coma
+ ............ quantifier for the group (at least one)
<Space>
\ze ........... end of our vim search
The substitute function has three parts like a normal vim substitution and the submatch(0) corresponds to our regex, hence we are substituting in our regex one space for one line breake and two tabs.

Copy block of code with increasing index

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

Context sensitivity in commentstring

When setting folds manually, it would be handy if it were possible to set the commentstring in a context sensitive manner. Consider the case in which the language uses BCPL-style comment markers (i.e., comments begin with // and are terminated by a newline), the first line of the visual block contains a comment, and the last line does not. Currently, if commentstring is set to //%s, redundant // characters will be appended to the first line when zf is used to create a fold.
Is it possible to set commentstring so that the // characters are added only if they do not already appear on the line?
According to :help fold-create-marker, automatic folding marker insertion
does not work properly when:
The line already contains a marker with a level number. Vim then doesn't
know what to do.
Folds nearby use a level number in their marker which gets in the way.
The line is inside a comment, commentstring isn't empty and nested
comments don't work. For example with C: adding /* {{{ */ inside
a comment will truncate the existing comment.
Thus, it is not possible to change the default zf behavior by modifying the
commentstring setting.
However, it is possible to create a custom version of the zf command that
would take into consideration that starting or ending (or both) lines of
a fold could have comments. For example, consider the following mappings, one
for marking a fold by visual selection, another for using with a motion
command.
nnoremap <silent> <leader>zf :set opfunc=CreateMarkerFold<cr>g#
vnoremap <silent> <leader>zf :<c-u>call CreateMarkerFold(visualmode(), 1)<cr>
function! CreateMarkerFold(vt, ...)
let range = map(['[<', ']>'], 'line("''".v:val[a:0])')
let mark = split(&foldmarker, ',')
let pat = escape(&commentstring, '\')
let pat = '\V' . substitute(pat, '\\\#<!%s', '\\zs\\ze\\.\\{-}', '')
for i in [1, 0]
let line = getline(range[i])
if line =~ pat
let line = substitute(line, pat, escape(mark[i], '\'), '')
else
let line .= printf(&commentstring, mark[i])
endif
call setline(range[i], line)
endfor
endfunction
Both of the mappings follow the same routine. Before adding starting and
ending folding markers, it checks separately whether the first and the last
lines of the block to fold match the commentstring pattern. For each of the
two that do match, it inserts the corresponding marker inside the first found
comment, at the very beginning of its text. Otherwise, the marker is
decorated according to the commentstring template and added at the end of
the line.
If in the latter case it is preferable to separate the marker on its own line,
one can change the for loop as shown below.
for i in [1, 0]
let line = getline(range[i])
if line =~ pat
let line = substitute(line, pat, escape(mark[i], '\'), '')
call setline(range[i], line)
else
call append(range[i] - !i, printf(&commentstring, mark[i]))
endif
endfor
Unlike the previous version of the loop, the order of processing the two
lines is important: The ending marker line should be added first, if
necessary, because inserting a line for the beginning marker would shift the
following lines changing their numbers.

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.

Resources