incremental change the pattern in vim - vim

Is it possible to search pattern like this: some_name{number}: for example r001. And then incrementally increment each search result number by one. For example for such input:
r001
...
r001
...
r001
...
Desired output will be:
r002
...
r003
...
r004

Following would replace each number in the entire file with an incrementing number starting from two.
let i=2|g/\v\d+$/s//\=i/|let i=i+1
Output
r2
...
r3
...
r4
...
Breakdown
let i=2 : Initializes variable i to 2
g/\v\d+$/ : a global command to search for numbers at the end of lines
s//\=i/ : a substitute command to replace the search matches with
the contents of i
let i=i+1 : increment i for the next match
All that's left to do is incorporate the commands to pad with zero's:
Vim: padding out lines with a character

Related

Yank and paste but with small change

I want to put a specified number after each pasted line automatically and this number will increment every past.
Sometimes I want to declare many variables. So I write one declaration and copy(yank) it and past to a second line. Then I can repeat this operation with dot operator ".". But it's very annoying to make these variables vary.
I want to achieve something like this:
variable (yy) variable1 (yy)
variable (p) variable2 (p)
variable (.) => variable3 (.)
variable (.) variable4 (.)
variable (.) variable5 (.)
Is it possible to perform such a operation in just the vim?
I don't know if someone can do it with ultisnips interpolation, I think it is possible but I came up with this solution
yy4p .......... copy 4 times
v} ............ select block
g<Ctrl-a> ..... increase the sequence
Until here you have the main solution that is increasing the numbers fast
fy ............ jump to the first 'y'
<Ctrl-v> ...... start selection block
3jl ........... extend selection
c.<Esc> ....... swich 'yy' with .
fy. ........... finishes
Using macro
qa ............ start recording marcro 'a'
yyp ........... copy line
ci(.<Esc> ..... change first ()
Ctrl-a ........ increase
ci(. .......... change second ()
Esc ........... stop recording macro 'a'
3#a ........... 4x macro 'a'
:%s/variable/\= printf("variable%d",line('.')-6)/
or if you have a vim with perl support (default in many linux distributions)
:perldo s/variable/$& . ++$n/e
My UnconditionalPaste plugin has gpp and gPp mappings for this (and many more paste variations). The first only increments the first decimal number, the second one all numbers.
So, starting with
variable1 = 'foo1'
yygpp will create:
variable1 = 'foo1'
variable2 = 'foo1'
whereas yygPp will do this:
variable1 = 'foo1'
variable2 = 'foo2'
These support a [count] and can be repeated with ., too.

How to replace finding words with the different in each occurrence in VI/VIM editor ?

For example, I have a text ,
10 3 4 2 10 , 4 ,10 ....
No I want to change each 10 with different words
I know %s/10/replace-words/gc but it only let me replace interactively like yes/no but I want to change each occurrence of 10 with different words like replace1, 3, 4 , 2 , replace2, 4, replace3 ....
Replaces each occurence of 10 with replace{index_of_match}:
:let #a=1 | %s/10/\='replace'.(#a+setreg('a',#a+1))/g
Replaces each occurence of 10 with a word from a predefined array:
:let b = ['foo', 'bar', 'vim'] | %s/10/\=(remove(b, 0))/g
Replaces each occurence of 10 with a word from a predefined array, and the index of the match:
:let #a=1 | let b = ['foo', 'bar', 'vim'] | %s/10/\=(b[#a-1]).(#a+setreg('a',#a+1))/g
But since you have to type in any word anyway, the benefit of the second and third function this is minimal. See the answer from SpoonMeiser for the "manual" solution.
Update: As wished, the explanation for the regex part in the second example:
%= on every line in the document
s/<search>/<replace>/g = s means do a search & replace, g means replace every occurence.
\= interprets the following as code.
remove(b, 0) removes the element at index 0 of the list b and returns it.
so for the first occurrence. the line will be %s/10/foo/g the second time, the list is now only ['bar', 'vim'] so the line will be %s/10/bar/g and so on
Note: This is a quick draft, and unlikely the best & cleanest way to achieve it, if somebody wants to improve it, feel free to add a comment
Is there a pattern to the words you want or would you want to type each word at each occurrence of the word you're replacing?
If I were replacing each instance of "10" with a different word, I'd probably do it somewhat manually:
/10
cw
<type word>ESC
ncw
<type word>ESC
ncw
<type word>ESC
Which doesn't seem too onerous, if each word is different and has to be typed separately anyway.

vim copy-paste buffers

Suppose I have the following text (I have numbered the lines for clarity) and the cursor is at the beginning of the 5th line:
1
2 var x = 1;
3 var y = 2;
4
5 if (true) {
6 print("Hey!");
7 }
Okay, now I try to cut the lines 5, 6, 7 (all that "if" thingy). For that purpose I do:
Vjjd. Now it appears I am at the beginning of the 4th line which is an empty string.
My question: is it possible at this moment to remove the 4th line without loosing previously copied lines 5, 6, 7 (that "if" thingy), so that I'll be able to paste them somewhere else, say, on the 1st line later?
You can always yank or delete into a register using "n, where n is just about any key. See a list of available registers in "help registers", some of which have special meaning. For example, you could do:
> "a3dd (to delete the last three lines into a register called a)
> dd (to delete the blank line)
> "ap (to paste the a register)
You can also use Vjj"ad, to match what you were doing in the original question.
Yes: You can use the blackhole buffer register: type "_dd
if your #4 line is empty line, it is easy, you don't have to play with register. just do:
kJ
it means:
k: move to #3
J: (shift-J) Join #3 and #4
or you prefer do it in INSERT mode.
i<BS>
or
I<c-u>
if that line is not empty:
using register to store the 3 lines or #4, like #Derek suggested
using blackhole register like #Jan suggest
or using numbered register.
say, now you just did 3dd (without named register), and cursor on a not-empty line (#4), you could directly do dd. the 3 lines are not gone. you can paste them again by:
"2p

A complicated case of conditional line splitting to be performed in Vim

Here is a sample of text that I’m working with:
Word1
Word2
...
Word4 / Word5 Word6
Word7
Word8 Word9 Word10 / Word11 Word12 Word13 Word14
Word15
Word16
...
I would like to transform it by splitting the lines containing
slash-separated chunks, so that the first chunk (preceding the slash)
gets the trailing words copied from the second chunk (following the
slash) to equalize the number of words in both lines resulting from
the chunks, if the former one has fewer words than the latter.
In other words, the desired transformation is to target the lines
consisting of two groups of words separated by a (space-surrounded)
slash character. The first group of words (preceding the slash) on
a target line has 1 to 3 words, but always fewer than the second
group.
Thus, the target lines have the following structure:
‹G1› / ‹G2› ‹G3›
where ‹G1› and
‹G2› ‹G3› (i.e.,
‹G2› concatenated with ‹G3›)
constitute the two aforementioned groups of words, with
‹G2› standing for as many of the leading words of the
after-slash group as there are in the before-slash one, and
‹G3› standing for the remaining words in the
after-slash group.
Such lines should be replaced with two lines, as follows:
‹G1› ‹G3›
‹G2› ‹G3›
For the above example, the desired result is as follows:
Word1
Word2
...
Word4 Word6
Word5 Word6
Word7
Word8 Word9 Word10 Word14
Word11 Word12 Word13 Word14
Word15
Word16
...
Could you please help me implement this transformation in Vim?
You can write a function to expand slash:
fun! ExpandSlash() range
for i in range(a:firstline, a:lastline)
let ws = split(getline(i))
let idx = index(ws, '/')
if idx==-1
continue
endif
let h= join(ws[ : idx-1])
let m= join(ws[idx+1 : 2*idx])
let t= join(ws[2*idx+1 : ])
call setline(i, h.' '.t.'/'.m.' '.t)
endfor
endfun
:%call ExpandSlash()
:%s#/#\r#
before
1 2 3 / 4 5 6 7 8
after
1 2 3 7 8
4 5 6 7 8
One can use the following command to perform the desired transformation:
:g~/~s~\s*/\s*~\r~|-|exe's/\ze\n\%(\s*\w\+\)\{'.len(split(getline('.'))).'}\(.*\)$/\1'
This :global command selects the lines matching the pattern /
(here, it is delimited by ~ characters) and executes the commands
that follow it for each of those lines.
Let us consider them one by one.
The slash character with optional surrounding whitespace that
separates the first and the second groups of words on the
current line (as defined in the question’s statement), is
replaced by the newline character:
:s~\s*/\s*~\r~
Here the tilde characters are used again to delimit the
pattern and the replacement strings, so that there is no'
need to escape the slash.
After the above substitution the cursor is located on the line
next to the one where the substituted slash was. To make writing
the following commands more convenient, the cursor is moved back
that line just above:
:-
The - address is the shortening for the .-1 range denoting
the line preceding the current one (see :help :range).
The third group of words, which is now at the end of the next
line, is to be appended to the current one. In order to do
that, the number of words in the first group is determined.
Since the current line contains the first group only, that
number can be calculated by separating the contents of that
line into whitespace-delimited groups with the help of the
split() function:
len(split(getline('.')))
The getline('.') call returns the current line as a string,
split() converts that string into a list of words, and
len() counts the number of items in that list.
Using the number of words, a substitution command is generated
and run with the :execute command:
:exe's/\ze\n\%(\s*\w\+\)\{'.len(split(getline('.'))).'}\(.*\)$/\1'
The substitutions have the following structure:
:s/\ze\n\%(\s*\w\+\)\{N}\(.*\)$/\1
where N is the number of words that were placed before
the slash.
The pattern matches the newline character of the current line
followed by exactly N words on the second line. A word
is matched as a sequence of whitespace preceding a series of
one or more word characters (see :help /\s and :help /\w).
The word pattern is enclosed between the \%( and \)
escaped parentheses (see :help /\%() to treat it as a single
atom for the \{N} specifier (see :help /\{) to match
exactly N occurrences of it. The remaining text to the
end of the next line is matched as a subgroup to be referenced
from the replacement expression.
Because of the \ze atom at the very beginning of the
pattern, its match has zero width (see :help /\ze). Thanks
to that, the substitution command replaces the empty string
just before the newline character with the text matched by the
subgroup, thus inserting the third group of words after the
first one.
For the given example the result is equivalent to replacing each / with the last word on the line and a line break \r. Here is a global substitute command to do it:
:%s#/ \ze.*\(\<\w\+$\)#\1\r#
Explanation:
/ \ze match the / end stop matching (nothing after the \ze will be substituted)
.* match any intermediate characters
\( start another match group
\<\w\+$ match the last word before the end of the line
\) stop the match group
However, you then say that the trailing group g3 may contain more than one word, which means the replace operation needs to be able to count the number of words before and after the /. I'm afraid I don't know how to do that, but I'm sure someone will leap to your rescue before long!

Move lines matched by :g to the top of the file

I have a large text file with several calls to a specific function method_name.
I've matched them using :g/method_name.
How would I move them to the top of the file (with the first match being on the top)?
I tried :g/method_name/normal ddggP but that reverses the order. Is there a better way to directly cut and paste all the matching lines, in order?
Example input file:
method_name 1
foo
method_name 2
bar
method_name 3
baz
Example output file:
method_name 1
method_name 2
method_name 3
foo
bar
baz
How about trying it the other way around: moving the un-matched lines to the bottom:
:v/method_name/normal ddGp
This seems to achieve what you want.
I think you can achieve the desired result by first creating a variable assigned
to 0:
:let i=0
And then executing this command:
:g/method_name/exec "m ".i | let i+= 1
It basically calls :m passing as address the value of i, and then increments
that value by one so it can be used in the next match. Seems to work.
Of course, you can delete the variable when you don't need it anymore:
:unlet i
If the file is really large, count of matching entries is small, and you don't want to move around the entire file with solution v/<pattern>/ m$, you may do this:
Pick any mark you don't care about, say 'k. Now the following key sequence does what you want:
ggmk:g/method_name/ m 'k-1
ggmk marks first line with 'k.
m 'k-1 moves matching line to 1 line before the 'k mark (and mark moves down with the line it is attached to).
This will only move a few matching lines, not the entire file.
Note: this somehow works even if the first line contains the pattern -- and I don't have an explanation for that.
For scripts:
normal ggmk
g/method_name/ m 'k-1

Resources