VIM: How to avoid substitution within substitution? - vim

I created a function to search a custom number of empty lines (with or without spaces) and to replace them with a new (custom) number of empty lines.
fun! s:AddRemoveNumbEmptyLines()
if !exists("emptylinesbefore")
let emptylinesbefore = "How many empty lines do you search? \n (p.e. 2,3 4, ,3)"
endif
let b = inputdialog(emptylinesbefore)
if !exists("emptylinesafter")
let emptylinesafter = "How many empty lines must it be?"
endif
let c = inputdialog(emptylinesafter)
let m = repeat('\r', c)
exe 's/\(^\s*$\n\)\{'.b.'}/'.m.'/gc'
endfun
Let say b = 2, (2 and more) AND m = 3
If vim finds 4 empty lines it does a substitution to 3 empty lines.
(thats ok).
But when I refuse the substitution request (I use the "c" (confirm) flag) it finds at the same place 3 empty lines and asks again if it has to be replaced with 3 empty lines. When I refuse again, it finds at the same place, 2 empty lines and asks again if I want to do a substitution.
How can I avoid these multiple substitution requests (at the same place)?
Hope I made myself clear :)

resolved it! I just had to check for a non space \S in the line before and the line after.
My new exe = 's/^.*\S\+.*\n\zs\(^\s*$\n\)\{'.b.'}\ze\s*\S\+/'.m.'/gc

Related

How to replace a `\norm{ some string }` by `\| some string\|` quickly? [duplicate]

This question already has answers here:
Find and replace strings in vim on multiple lines
(11 answers)
Vim - Capture strings on Search and use on Replace
(4 answers)
Closed 3 years ago.
I am editing a markdown file with vim. This file exist many string \norm{ some string }. I want to replace them by \| some string \|. Does there exist any quick way? Thanks very much.
The answer in Find and replace strings in vim on multiple lines can not answer my question. It just talk about general replacing for one line and multi line. Here I want to replace a surrounding and keep the string in the surrounding.
What you're looking for are so-called capture groups and backreferences. Provided there are no nested forms (curly braces inside do not mean a problem in themselves) and forms spanning multiple lines, a quick solution could be: %s/\\norm {\(.*\)}/\\|\1\\|/g.
The \1 in the substitution part refers to the group captured by \(.*\), i.e. the original content inside the outermost pair of curly braces. See e.g. http://www.vimregex.com/#backreferences for more.
You could also use a macro to accomplish what you want.
Place your cursor on the first line that have the pattern you want to substitute. Then start recording the macro:
qq0ldwr|$xi\|ESCjq
Meaning:
qq = start recording a macro (q) in register q
0 = move to the beginning of the line
l = move one char to the right
dw = delete the word
r| = substitute what is under the cursor with a "|"
$ = move to the end of line
x = delete last char of the line
i = insert mode
\| = insert chars "\|"
ESC = exit insert mode
j = move to next line
q = stop recording
Execute the macro with:
#q
Execute the macro once again:
##
Keep doing it for as many lines as needed, or use:
<number>##
ex. 100##
To execute the macro number times.

VI switch and replace with vi.

This is a config file, and a swith failed, so the networking
guy wanted to set up a new path to the swith, and I needed to change the ports - FAST.
So what i used was %s/:64*$/:57000/g and that did not change them.
I ended up doing them by hand. How could I quickly have changed the :
in vi?
Addr.corp-unix-b1 = corp6503xap:64001
Addr.corp-unix-b2 = corp6503xap:64002
Addr.corp-unix-b3 = corp6503xap:64003
Addr.corp-unix-b4 = corp6503xap:64004
Addr.corp-unix-b4-dia = corp6503xap:64005
Addr.corp-unix-b5 = corp6503xap:64006
Addr.corp-unix-b6 = corp6504xap:64007
Addr.corp-unix-b6-aapl = corp6504xap:64008
Addr.corp-unix-b7 = corp6504xap:64009
Addr.corp-unix-b8 = corp6504xap:64010
Addr.corp-unix-b8-spy = corp6504xap:64011
Addr.corp-unix-b8-fas = corp6504xap:64012
Addr.corp-unix-b8-2 = corp6504xap:64013
Addr.corp-unix-8a-gld = corp6504xap:64014
Addr.corp-unix-b9 = corp6504xap:64015
Addr.corp-unix-b10 = corp6504xap:64016
Addr.togen-xpho-b6-aapl = corp9189pap:3333
Your expression is missing a ., so you were trying to match 6 followed by a variable number of 4's. If you have the ., then you are matching the expression 64 followed by a variable number of any character to the end of line.
%s/:64.*$/:57000/g
In regular expressions, * indicates 0 or more of the preceding character, which in your case is 4.
. matches any character, so your substitution would be written :%s/:64.*$/:57000/g
Just to provide an alternate solution in the spirit of SE:
You could use :g in combination with :normal
:g/:64/norm $bC57000
This finds lines with :64, goes to the end of the line, moves the cursor to the beginning of the word and changes the remaining of the line to 57000.

How to quickly edit determinate part of code inside different similar lines

I have this problem I'm adjusting a code I've made I have a structure like this:
Apple1 = Fruit("ss","ss",[0.1,0.4],'w')
PineApple = Fruit("ss","ss",[0.315,0.4],'w')
Banana = Fruit("ss","ss",[0.315,0.280],'w')
...
...
Instead of "ss"I would like to type further information like "Golden Delicious". For the moment I'm simply deleting "ss"clicking over it and then replacing it with the information I want to insert. I'm sure there is a faster way to do it, I've tried something with VIM macros but I can't figure out how to "Raw input" my data.
I've try simply to substitute it with Spyder, but is slow because I have to click substitute every time, with VIM for what I've try is the same.
Then I wonder how insert something else after 'w'...
This is an example of an final output only to understand better the question :
Apple1 = Fruit("Golden Delicous","red",[0.1,0.4],'w')
PineApple = Fruit("Ananas comosus","green",[0.315,0.4],'w')
Banana = Fruit(" Cavendish banana","yellow",[0.315,0.280],'w')
...
...
I reformulate the question: which is the faster way to change "ss", for the moment I'm clicking over "ss" delate "ss" and write e.g "Golden Delicous" but is very slow. What I would like is that for every single ss the editor ask me to insert something to replace the single ss.
e.g. first ssin the fist line: I want to replace it typing something else e.g. "Golden Delicous" second ssin the first line I want to replace it typing somethingelse e.g. red. First ssin the second line I want to replace it with s.e. e.g. Ananas comosussecond ssin the second line I want to replace with s.e. e.g. green and so on.
I'm sure there is an answer for this somewhere but I can't find it!
Please if you down vote explain me why so I can improve it!
As far as I understand, the data that you want to substitute for "ss" does not have regular structure, so you will need to enter it by hand.
In Vim you would do it like this:
Place the cursor over the first "ss", then press * and then N.
Press ce, enter the new data (e.g. "Golden Delicious"), then leave Insert mode by pressing Escape.
Press n to jump to the next instance of "ss".
Repeat steps 2 and 3 ad libitum.
Look up :h * and :h n for more information.
I would do it like that:
:%s/ss/\=input('Replacement: ')/gc
This queries you for each occurrence. With the /c flag, the display is even updated during the loop (at the cost of having to additionally answer y for each occurrence); without the flag, you would need to keep track of where you are yourself.
You can use a function that searches the whole file substituting all "ss" strings with values from arrays populated with the replacement data:
function! ChangeSS()
let ss1 = ['Golden Delicous', 'Ananas comosus', 'Cavendish banana']
let ss2 = ['red', 'green', 'yellow']
call cursor(1, 1)
let l = "ss2"
while search('"ss"', 'W') > 0
if l == "ss1"
let l = "ss2"
else
let l = "ss1"
endif
execute 'normal ci"' . remove({l}, 0)
endwhile
endfunction
It uses a reference variable (l) that exchanges which array you want to extract data from. ss1 is for first appearance of "ss" in the line and ss2 for the second one.
Run it like:
:call ChangeSS()
That (in my test) yields:
Apple1 = Fruit("Golden Delicous","red",[0.1,0.4],'w')
PineApple = Fruit("Ananas comosus","green",[0.315,0.4],'w')
Banana = Fruit("Cavendish banana","yellow",[0.315,0.280],'w')

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!

vim - non rectangular visual block

Is there a way to select the second column in the following code,
which turns out to be non rectangular.
I tried "CTRLv 3jE" , but that doesn't work.
int var_one = 1;
int var_two = 2;
int var_three = 3;
int var_very_long = 4;
You could use one of the Align plugins to align your column, select and copy it and afterwards undo the alignment (or leave it aligned)
Based on the comments, I think the way to go is writing a custom function that
passes the task to awk. It could be done with some regex also, splitting each
line on spaces, but awk should be easier. Here is my first try:
function! ExtractColToRegister(reg, ...) range
let input = join(getline(a:firstline, a:lastline), "\n")
if a:1 | let column = a:1
else | let column = 1 | endif
exec "let #". a:reg . " = system(\"awk '{ print $" .
\ column . " }'\", input)"
endfunction
You should have no trouble understanding it if you're already writing Vim
scripts :-) however let me know if some part of it is unclear, and if there is
something to improve as well.
Basically what the function does is saving a specific column to a register. If
you visually select the example code given in the question, and then:
:'<,'>call ExtractColToRegister("a", 2)
Register a will now contain:
var_one
var_two
var_three
var_very_long
And you can easily "ap somewhere else. Notice the column defaults to 1 if the
argument was omitted.
Creating a custom command "Column to Register" makes the process even nicier to
use outside of Vim scripts:
:command! -range -nargs=+ CTR <line1>,<line2>call ExtractColToRegister(<f-args>)
use the CopyMatches function from http://vim.wikia.com/wiki/Copy_the_search_results_into_clipboard
then select the lines and do something like
:'<,'>CopyMatches .*=

Resources