Swap all occurrences of two strings [duplicate] - vim

This question already has answers here:
What is the easiest way to swap occurrences of two strings in Vim?
(5 answers)
Closed 8 years ago.
Say, for example, I have a document with all pairs of parenthesis flipped:
Lorem )ipsum( dolor )sit( amet
And I want to correct it to be:
Lorem (ipsum) dolor (sit) amet
Of course I can't make the swap with two replacements; first replace all ( with ) and then all ) with (, because then I will end up with:
Lorem (ipsum( dolor (sit( amet
Please provide a way to do it in vim for any pair of strings.
Update:
Thanks ernix for a good answer. For those wondering what the tr function does:
http://vimdoc.sourceforge.net/htmldoc/eval.html#tr()

using vimscript:
:%call setline(".", tr(getline("."), "()", ")("))
or simply call tr:
:%!tr ')(' '()'
update
If you consider about white spaces, then Kent's answer is the best :)
%s/\(\s*\((\|)\)\s*\)/\=substitute(submatch(0),submatch(0),submatch(2)=='('?') ':' (', 'g')/g

this line will do it:
:s/[()]/\=substitute(submatch(0),submatch(0),submatch(0)==')'?'(':')','g')/g
it looks lengthy because the function name submatch(, you can write a little function or assign the match part to a var.
with nice idea from ernix:
s/[()]/\=tr(submatch(0),')(','()')/g

One trivial method:
%s/(/1UnIqUe1/g
%s/)/(/g
%s/1UnIqUe1/)/g

Related

Vim: same change to all line-endings of selected lines?

In https://stackoverflow.com/a/11790464/5354137, there is a very detailed description how to, e.g. perform the same insert to the beginnings of all selected lines by placing the cursor over the first character of the first line, then after <C-v> moving down, then Isomething<Esc><down> inserting something everywhere.
But how does one do the same thing in the case of lines of unequal length, such as:
'Lorem ipsum
'dolor sit amet,
'consectetur adipiscing elit,
'sed do...
to their respective line endings? E.g. if I wanted to insert ', after m, ,, , and ., how would I do that without resorting to :substitute? (Or to a plugin, just using original vim-fu...)
Substitute (original answer)
The easiest way to do it is with the :substitute command. Select the lines in visual mode and then do:
:'<,'>s/$/',/<CR>
The $ stands for the end of the line. Note that vim will already write the :'<,'> part, so you only have to type from there.
Using a macro
With the question having been updated and :substitute off the table, I'd suggest recording a macro. Place the cursor on the line with "Lorm ipsum" and do:
qaA',<Esc>jq
then call the macro three times for the remaining lines. Either by hitting # a lot:
#a####
or with the more elegant
3#a
Using a recursive macro
As commented by #AndR, it's possible to record a macro that calls itself which will run until it reaches the end of the buffer:
qaqqaA',<Esc>j#aq
Note that the macro already calls itself with #a.
Afterwards, call #a and single quotes will be appended to every line until the end of the buffer.
Without visual selection
Both #AndR's answer using :normal and my :substitute answer above rely on a visual selection of the lines to modify. It is possible without that by typing in a range or line numbers. For "Lorem ipsum..." on lines 1 through 4, type either of the two commands:
:1,4s/$/',/
:1,4norm A',
This has the benefit that it works without a visual selection and from anywhere in the buffer.
Based on your reference link and reproduction steps, this is your problem halfway:
But how does one do the same thing in the case of lines of unequal length ...
Follow the link and use the first method Using only visual-block mode, let's start with step 5:
Select any column, hit <C-v> to enter visual-block mode and expand your selection toward the bottom:
'Lo[r]em ipsum
'do[l]or sit amet,
'co[n]sectetur adipiscing elit,
'se[d] do...
Hit $ or <End> (see :help $), will get the selection like this:
'Lo[rem ipsum]
'do[lor sit amet,]
'co[nsectetur adipiscing elit,]
'se[d do...]
Hit A',<Esc> to achieve your goal:
'Lorem ipsum',
'dolor sit amet,',
'consectetur adipiscing elit,',
'sed do...',
Note and supplement:
If you use the latest Vim9 and your virtualedit option contains block, when you hit $ or <End>, you may see this selection (Vim8 won't):
'Lo[rem ipsum ]
'do[lor sit amet, ]
'co[nsectetur adipiscing elit, ]
'se[d do... ]
This could be a issue with the VIM9 or an intentional design? I don't know, but don't care, just press A. You'll see your cursor at the end of the first line, which is fine (| is the cursor position):
'Lorem ipsum|
'dolor sit amet,
'consectetur adipiscing elit,
'sed do...
Then you enter the text you need (e.g. ',) and <Esc> ends editing, it works fine:
'Lorem ipsum',
'dolor sit amet,',
'consectetur adipiscing elit,',
'sed do...',
As a supplement, oppositely, if you want to insert text in the same column where has no text when there are irregular endings : You can add block to your virtualedit option (see :h 'virtualedit'), using other left-right-motions (e.g. l or |) instead of $ to get this selection:
'Lo[rem ipsum ]
'do[lor sit amet, ]
'co[nsectetur adipiscing elit, ]
'se[d do... ]
When you press A, the cursor will be at (| is the cursor position):
'Lorem ipsum |
'dolor sit amet,
'consectetur adipiscing elit,
'sed do...
Then enter the text you want (e.g. ',), hit <Esc> to obtain:
'Lorem ipsum ',
'dolor sit amet, ',
'consectetur adipiscing elit, ',
'sed do... ',
Here the goal can be achieved by appending ' at the end of each line. In one line you'd normally do A'. In order to do this this in each line of the visual selection, run
:<,>norm A'
Assuming that you have selected the needed lines visually (with v or V).
See :h :normal for details.

How to add some lines to the current Vim buffer by using a VimL script?

I am confused about doing a really simple task. I need to add lines to the current buffer with a VimL script.
As it is known, there are two functions for this: append() and setline(). Both of them work approximately similarly — the first argument is the number of the line to perform insertion at (after or before, respectively), the second one defines the line(s) to add.
But the problem is that I cannot determine the number properly without inventing weird tricks. The only standard method I have found is to ask the line('$') function. So, to add a line to the end of the current buffer it can be append(line('$'), ['example']) or setline(line('$')+1, 'example'). But this approach gives unpredictable results depending on the content of the current buffer.
Let’s consider append(line('$'), ['example']) first. If the buffer is not empty, I get what I expect:
first existing line
example
But if the buffer is empty, I will get the first line blank:
example
Now, let’s take a look at setline(line('$')+1, 'example'). It works conversely. If the buffer is empty, everything is fine:
example
But if the buffer is not empty, trying to add “example” after the last line, I get a mess of text lines:
first existing line
example
second existing line
Well, I could test the last line whether it is blank or not. But what if the last line is empty due to the content?
So, here are my questions. Should my code analyze the content of the current buffer to decide which of the functions it must choose, considering whether the existing last line is empty because this is how the function line('$') works or it empty due to the desired content? Or should I use another approach that I have missed?
Perhaps, I should utilize something like:
write! buffer.tmp
exe '! printf '.join(g:my_lines_to_add, "\n")."\n >>buffer.tmp"
edit! buffer.tmp
rm -f buffer.tmp
Is there a trivial standard way to solve this trivial task? My text may sound brute, but I love Vim and count myself as its experienced user. Thank you in advance for your help.
You have a few conflicting questions in here.
As you've already noticed, you use append() (or, to borrow a phrase from this answer, "appendline()") or setline(). Both of these take a line number and a string or an array, which is then handled appropriately by the function.
the first argument is the number of the line to perform insertion at (after or before, respectively).
Strictly speaking, no. Go to any file, and go to the first line. Run :echo line('.'). It'll return 1. This is the part I hate most about Vim/Vimscript: line() and getline() are 1-indexed. But if you use the array returned by getline(), you get a 0-indexed array. Consequently:
append() adds a number of lines after the first argument. It doesn't affect the line at idx. setline() changes the line at idx and len(text) out. This means that the functions perform their operation, respectively, after or at, not before.
Should my code analyze the content of the current buffer to decide which of the functions it must choose, considering whether the existing last line is empty because this is how the function line('$') works or it empty due to the desired content?
I can't answer this with a definite yes or no, because it's a matter of what you want to do. There's different approaches based on what end result you're looking for, and how much scripting you're fine with.
You may some times need to do comparisons, but you often don't need to make it that complicated. In your particular case, as I'll demonstrate in a bit, you can get away with checking the line count, and then making sure the only line isn't empty. It's a bit messy to write, but it does the trick. You can also do various comparisons.
Consider append(line('$') - 1, ["example"]): in an empty buffer, this gives
example
... and note the blank line at the end of the file. Line 1 is still preserved, because append doesn't overwrite lines. Sadly, the blank line is considered a line, so there's no particularly easy way to do it with built-in functions, because there aren't any that do thisAFAIK.
So, let's look at your options:
Single-line setline()
The shortest single-line option is:
call setline(1, add(line('$') == 1 && col('$') == 1 ? [] : getline(0, '$'), 'example'))
There's a lot going on here, so let's break it down:
setline( We use setline(), because it can override existing lines.
1, Start at line 1.
add( add() appends to an array.
Then we have a condition using ternary.
line('$') == 1 If the last line is line 1...
&& col('$') == 1 and the last column is 1...
? [] then we use an empty array.
: getline(0, '$'), Otherwise, we get the contents of the buffer
'example') and add 'example' to the array
)
Note that you need to use extend() if you want to add several items, NOT insert! See :h insert() and :h extend().
Single-line append()
If you're fine with a blank line at the end of the file for empty files, you can also use append:
call append(line('$') == 1 && col('$') == 1 ? 0 : line('$'), ["example"])
Again, append() CANNOT override existing lines, so you're stuck with a blank line somewhere. Which leads us to...
If approach
This can be reduced into a single statement, which I'll include for copy-pasta purposes, but this is much better used if you're writing a script, and not just using it as a command:
if line('$') == 1 && col('$') == 1 | call setline(1, 'example') | else | call append(line('$'), "example") | endif
Or, in its expanded, script-friendly form:
if line('$') == 1 && col('$') == 1
call setline(1, 'example')
else
call append(line('$'), "example")
endif
... which is indirectly what you asked about.
In some ways, this is an easier to read variant of the first option, at least for script use. If you have a script, you can actually get away with just append.
A note on hacks
By using the append approach, you can move the condition that determines the line into a variable, and use that in combination with normal! "Gdd" (Goto last line, ddelete line) to get rid of the blank line. You can use an equivalent approach if you detect an empty file.
TL;DR:
But the problem is that I cannot determine the number properly without inventing weird tricks.
It's not particularly weird with this particular use-case, at least when you know exactly what it means. It's definitely a hack none the less, but not much to do about that in this particular case.
Should my code analyze the content of the current buffer to decide which of the functions it must choose,
in this case, line('$') == 1 && col('$') == 1 is enough. (AFAIK, we don't have a better approach, but this does avoid string operations).
considering whether the existing last line is empty because this is how the function line('$') works
line('$') is a victim here. Blank lines are still considered lines, and that holds for every other function as well. Consequently, append() doesn't want to override it. Whether the line is blank or not, line('$') == 1 if there's only one line in the buffer. Annoyingly enough, line('.') == 1, but line('^') == 0. I can't explain that one, but this happens in single-line buffers regardless of the contents of said line.
or it empty due to the desired content?
Now mildly out of context thanks to creative quoting, but the blank line that appears from a few of these combinations is an unavoidable fact based on how the functions work. You need a varying index to deal with these cases, but it really depends on what you want to do in the first place. There may be a better and/or shorter alternative, but that requires me to make assumptions about your code.
Or should I use another approach that I have missed?
There are a few alternatives in this answer already, that can be expanded if necessary. There's a lot of ways to Rome with this particular question, so there probably is. I've probably missed a few approaches as well though, for what little that's worth.
You are making things sound harder than they are.
You can check if the current buffer is empty with:
wordcount().bytes == 0
and you can forget about append() as setline() does the job perfectly as long as you give it the right line:
if wordcount().bytes == 0
" buffer is empty so our target is the last line itself
call setline('$', ['foo', 'bar'])
else
" buffer is not empty so our target is the line after the last line:
call setline(line('$') + 1, ['foo', 'bar'])
endif
From there, you can fine tune the way you handle the "last line is blank or empty" scenario by adding more checks to the second branch of the conditional…
if a given line is empty:
getline(<line spec>) =~ '^$'
if a given line is non-empty and made only of whitespace:
getline(<line spec>) !~ '^$' && getline(<line spec>) !~ '\S'
etc.
Looking for a fancy one-liner?
let foo =<< END
Lorem ipsum dolor sit amet, consectetur adipiscing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit
esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident,
sunt in culpa qui officia deserunt mollit anim id est laborum.
END
:call execute(["$append"] + foo)
Thanks for all, especially to Olivia and romainl!
Files may or may not have zero lines, but a Vim buffer always has at least one line, even it is empty actually. So, it is impossible to determine the line number the new content is adding after without understanding the existing content of the buffer.
We can use two main approaches:
not to analyze the content and not to worry about a redundant blank line;
to analyze the content to avoid an unwanted blank line;
In the first case, we must always use append(line('$'), ...), not as I said append(line('$')-1, ...) in my answer to Olivia. I was wrong because if the content of the buffer had an empty line at the end (for example, to separate the paragraphs), using line('$')-1 would lead to a loss of the text structure. But using append(line('$'), ... leads to the first line is always blank.
The second approach is more interesting as it implies content analysis. It is not so hard as it can be thought. romainl suggests using the wordcount() function, Olivia advises testing the line and column numbers.
I did not profile the code. But, I am sure, on huge texts, Olivia’s suggestion is much more effective because knowing the number of lines and counting the bytes in the text are jobs of significantly different complexity.
So, the solution is turning out the following. (The code has not been tested yet, this is a concept).
function Append(lines)
" Add the given lines to the end of the current buffer.
"
" Arguments:
" ‛lines’
" a list of text lines to add to the buffer.
"
let index = (line('$') == 1) && (col('$') == 1) ? 1 : line('$') + 1
call setline(index, a:lines)
endfunction
Thanks for all one more time! The question is closed.

How to paste same text in Multiple Position of Multiple Line on Vim

I'm switching my TextEditor from Sublime Text to Vim with iTerm2 recently.
I'm looking for a plugin to paste text in Multiple Position of Multiple Line on Vim.
(Similar to the multicursor on Sublime Text which I can select cursor and use CMD+Click to select in other position then paste.)
I've seen vim-multiple-cursors plugin but it will only allow me to select the same word of next occurrence.
e.g - to place cursor at different location on different line as below.,
Line 1: Lorem ipsum dolor sit amet, [CURSOR HERE] consectetuer adipiscing elit. Aenean
Line 2: commodo ligula [CURSOR HERE] eget dolor. Aenean massa. Cum sociis natoque
Line 3: penatibus et magnis dis parturient montes,[CURSOR HERE] nascetur ridiculus mus.
Line 4: Donec [CURSOR HERE] quam felis, ultricies nec, pellentesque eu,pretium quis, sem.
Your help would be greatly appreciated and Many Thanks in advance.
VIM Version
VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Apr 21 2014 14:54:22)
MacOS X (unix) version
Warning
I recommend against trying to port your previous editing habits unchanged to Vim; after all, Vim's mode-based editing and command-based syntax are its distingishing features and make editing highly efficient.
Vim way
The "Vim way" would be more like using a regular expression to specify the places, moving to the first match, pasting, and then repeating with n and .
Emulation
You can sort-of emulate your intended workflow by manually inserting a marker char (e.g. §) at each location, and then replacing it via:
:%s/§/\=##/g
This uses :help sub-replace-expr to replace with the default register's contents.
Vim has a different buffer that stores what you have copied. To paste you just have to use "P" and the content is pasted.
Or I would recommend you to use "." [dot] - in Vim it is used for last action. So, once you have written/copied and pasted it in one line, you can just use the "dot" everywhere you want the same text to be pasted (i.e. last action to be taken).
The canonical Vim way to do that, the "dot formula" as coined by Drew Neil, is the reverse of the Sublime Text way.
In Sublime, you mark each match of a pattern, before doing the change :
mark Cmd+D
mark Cmd+D
mark Cmd+D
mark Cmd+D
change foo
In Vim, you "mark" a pattern, do the change once and repeat it on further instances of that pattern:
mark *N
change ciwfoo<Esc>
repeat n.
repeat n.
repeat n.
But, since you have 7.4, you can use the "new" gn normal mode command/motion:
nnoremap <key> *``cgn
and shorten the process above to:
change <key>foo<Esc>
repeat .
repeat .
repeat .
repeat .
Which is not that bad, I think.

Learning Vim: Best way to remove space between words

I'm an experienced developer on the Windows platform and I'm trying to teach myself how to use Vim. I am pretty good with regular expressions and understand the principles of how to use Vim. However, I have a specific problem, and although I have a solution, it feels as though there ought to be a better one.
I have a file which contains a line similar to the following:
CODE<tab><tab>Lorem ipsum dolor sit amet
There could be a variable number of <tab> or <space> characters between CODE and Lorem. Assuming the cursor is over the 'C' of CODE in normal mode, what I want to be able to do is find a key combination that will produce the following output, and leave the cursor between the 'E' of CODE and he 'L' of Lorem in insert mode.
CODELorem ipsum dolor sit amet
My curent solution is to use the following key sequence:
w d ? \ s \ + <return>
This works, but it feels illogical to go past the thing I want to delete before I can delete it. I feel like I should move to the end of CODE and delete forwards. I realise this could simply be a Vim idiom that I'm not aware of. I could also be totally missing a key piece of Vim knowledge.
What's the best way to achieve my goal?
eldw will do it.
e - move forward to the end of the word
l - move 1 character to the right
dw - delete all the blanks
If your cursor is in the middle of some whitespace, diw will delete whitespace left and right of the cursor. (If it is somewhere in the middle of a word, it will delete the word.)
Some alternatives:
:s/\s\+//
e xx
elxx
eldtL
f<tab>xx
eldw

vim copy and replace text

Lets say that i have this text:
$test = 'lorem';
$test2= 'ipsum';
and I want to copy lorem and paste into ipsum.
I tried to do yi' on lorem and then went on ipsum and did ci' but that replaced my pastebin with ipsum. and my previous copy was lost.
yi' on lorem, move to i of ipsum, vep?
Easy:
"kyi' on lorem, move to i of ipsum, ve"kp
This yanks lorem into register k, pastes it over ipsum from register k, and keeps it in register k ready to paste again anywhere else you might want to put it. ipsum still ends up in the default register, but that's no longer a problem and could be useful too.
If you've already got something in register k, you can use a different register instead (just use a different key).
I usually go to the sed command.
:%s/ipsum/lorem/g
% means do this for every line
s means sed, or search and replace
g at the end means replace every ipsum with lorem; if you omit this, it only replaces the first.
Go to the l of lorem, ye (yank to end of word).
Go to the i of ipsum, "_de (delete to end of word, putting the deleted text in the black hole register.
P (paste register before cursor).
Altogether: yej"_deP
Why don't you yank into a named buffer, using "ayi', then delete and paste with d'i"aP?
I'm not sure what do you want exactly.
You have this on two lines:
$test = 'lorem';
$test2= 'ipsum';
If you go to l and yw, it yanks lorem,
then go to i of ipsum, and cw, and p and it will replace ipsum with lorem.
You will still have both lorem and ipsum in the registers.
You can get the contents of those registers with :reg
If you want to paste something from them then "*p, or ":p, or "0p (you'll see it when you type :reg)
vi'y on lorem
vi'p on ipsum
gvy to copy back lorem to register for possible macro with vi'p
(qa - gvy - j - vi'p - q - #a - ## - ## ...)
Words here to stop Markdown treating the code that follows as plain text.
/lorem
"adw
/ipsum
"aP"bdw
``
"bp
The first text searches for 'lorem'; the next deletes the word into the buffer named 'a', leaving a pair of empty quotes behind in the text. The next search finds 'ipsum'; the "aP pulls the buffer named 'a' in before the word ipsum; the "bdw deletes the word into the buffer named 'b', leaving 'lorem' behind. The double back-tick goes back to the place the last search came from - the empty quotes; and "bp pulls the named buffer after the first quote.
You can also omit the "a and "b, but this way, the values are in the named buffers "a and "b and can be copied again, and again, and again until you don't need the values any more.
After you do ci' on impsum your lorem is in register "0. So, you can do ci'^R0 (^R means Ctrl+r) and paste your lorem in place of ipsum.
See :help quote_number for more info on numbered registers.
You want to use y to copy a selection and p to paste.
Here is a good list to keep handy.

Resources