vim replace a string when a change occurred in another place - vim

Say, I have a file:
Program foo
<program text>
End Program foo
Is it possible that if I change the word foo in the first line to bar (which may not be the first line of the file), the last line's foo will also be changed to bar automatically?

You could use a global command and a find and replace to mark the start en stop of the area that should be replace like this
:g/^Program foo$/.,/foo$/s//bar
Breakdown
:g Starts the global command
/^Program foo$ Search for Program foo where the line starts with Program and ends with foo
/.,/foo$/ for each mach, expand the rang until the next foo at the end of the line
s//bar substitute your last search results with bar

I usually do such things as follows:
move the cursor over the name to be changed
press * or # (this searches for this word forwards or backwards, respectively, and only for the word as a whole, so in your example only foo occurrences were found, not foobar or myfoo)
change the name: caw<new name><ESC>
repeat until done: press n to jump to the next possible occurrence, press . to repeat the name changing operation if required
You could also use a single search-and-replace command like :%s/\<foo\>/new_name/gc and then press y or n for every foo occurrence, but personally I prefer the above method because it saves me from typing foo and from remembering to put \< and \> around it (#/* do this for me).
If you are sure that you want to replace all occurrences of foo to bar, you can omit the confirm flag from the search-and-replace command, which will then be the shortest way to do what you want that I can think of.

You can try this plugin out, i think it would exactly what you want:
https://github.com/terryma/vim-multiple-cursors

Assuming your cursor is on the program name this can be done pretty easily.
Change program name:
ciW
foobar<Esc>
Move to end of program:
/End<CR>
$
Repeat change:
.

Related

How to select the similar text in gvim and modify them?

How I can select all the text start with foo_list starting from line 4 (see. below code) and rename them with list_values or any other preferred name ? Please note, I don't want to change in the first line.
Thanks in advance !
foo_list = [5, 2, 3, 1, 4]
def reverse_list_1():
foo_list=[0,10,20,40]
for i in reversed(foo_list):
print i,
foo_list=[0,10,20,40]
print foo_list[::-1]
for i in reversed(foo_list):
print i,
length = len(foo_list)
for i in range(length):
print foo_list[length-i-1],
Preferable solution: key map in the .vimrc or .gvimrc file, don't want to use any plugin.
That's a job for :substitute. You can specify the range with explicit line numbers (here: 4 to end of buffer $, or maybe next empty line /^$/):
:4,$substitute/\<foo_list\>/list_values/g
You can also first move to the first line and use the .,$ range.
Since that's still a lot of typing, you can pull in the current word (assuming you first position the cursor on the foo_list occurrence in line 4) into the command line via <C-R><C-W>.
Or, for a plugin solution, my ChangeGlobally plugin provides a mapping that avoids the use of :s.
:%s/foo_list/list_values/gc
This command says to replace the word foo_list with list_values in the whole document, asking for confirmation each time. Then for the first occurrence of foo_list on line 1, press n (to indicate NO) , and press y (to indicate YES) for all further occurrences to replace them. This solution works when you have to replace a few words. You can read the command as follows:
In the whole document (%), substitute (s) the word foo_list with list_values and do this globally (g), asking for confirmation (c) each time. For more options in the substitute command type :help :s in vim.
Solution 2 :
When there are thousands of words to replace, you surely don't want to type a y/n confirmation each time (which is enabled by the c flag in the end in the above command).
Take your cursor to line 4 and run
:.,$s/foo_list/list_values/g
Read the above command as from here (.) to the end of file (,$)
replace (s) the word foo_list with list_values
globally (g).
For small changes like this I like to use the gn motion. The gn motion visually selects the current search pattern. This makes for a powerful search/replace method when combining the gn motion with the change, c, operator and the repeat command, ..
Basic steps:
Make foo_list your search pattern. e.g. /foo_list or via *
Use c and gn to change the first foo_list. e.g. cgnbar_list<esc>
Now repeat that change on the next search result via .
Use n to advance to the next search results. (Hit n twice to skip an occurrence)
Keep using n and . until done
There is a nice Vimcasts episode on this topic: Operating on search matches using gn
For more information see:
:h gn
:substitute is the 'correct' way, but if you're only making a few changes, and you are not very experienced with ex commands, sometimes it takes longer to think through the command than to bounce through the list of changes you want to make using motions.
If you start with your cursor on the first instance of foo_list, hit '*' to jump to the next occurrence in the file. Hit 'ce' to delete to the end of the word and enter insert mode. Type in your new variable name and return to normal mode. Now you can jump through the rest of the file using 'n' to jump to the next occurrence (or 'N' to go back), and '.' to repeat your last edit action.

A short command to clear the current line?

Sometimes I want to clear a line in vim rather than delete it.
Before:
foo
bar
lineToClear
baz
After
foo
bar
baz
Of the vim commands I know, the closest I can get to this is D (upper case d), but usually this requires me to type 0 first to go to the beginning of the line.
I know, I'm lazy.
Does there exist a command that just clears the entire line, not just the characters after the cursor?
Maybe some sort of Containment-esque type of direct brain interface?
You can use S. It clears the line, then puts you into insert mode. If you don't want to do insert mode, 0D will be the quickest command set.
As glts mentioned, you can create a custom mapping by running one of the following commands. The first argument (S/D) can be changed to whatever you'd like.
:nnoremap S S<Esc>
or
:nnoremap D 0D
Reference
There is a slightly better way than 0D. It is still two keys, but does not require shift or going all the way up to the top row to press 0:
Simply just:
dd

Vim - Delete til last occurrence of character in line

I'm trying to figure out how to dt or df the last occurrence of a character in a string.
For example, let's say I have the following line:
foo not.relevant.text.bar
If I f df. I expectedly get foo relevant.text.bar but I would like to get foo bar. Using f 3df. is not an option as I don't know how many of that character will be in the string. Additionally, I may want to get foo .bar (f 3dt.), or if the line ends with a dot, I may want to get foo .. I want to always find the last one regardless of how many there are.
Is this possible without a regex? I suppose I could use a regex but I was hoping there was a simple vim command that I'm missing. I find myself trying to do something like this often.
one way without using regex, without counting "dot" (could be other letters)... see if others have better way..
foo[I]not.relevant.text.bar ([I] is cursor)
you could try:
lmm$T.d`m
or in this format, may look better?
lmm$T.d`m
this will do the job. you could create a mapping if you use that often.
EDIT
I add a GIF animation to show it works. :)
note
I typed #= in normal mode after moving my cursor to the right starting point (by f(space)), to display the keys I pressed in command line.
You can use my JumpToLastOccurrence plugin. It provides ,f / ,F / ,t / ,T commands that do just that.
I would use f df...
It is not necessarily shorter to type, but I find it easier to use "repeat last command" than counting in advance the number of word/sentence I want to delete.
Then you can adjust the number of . you type to adjust the length of the string you want to delete.
For your example: ET.dB
foo not.relevant.text.bar
And it works, as long as the cursor is anywhere within the text following "foo".
Strip Path from Path+Filename: ET/dB
I use it for stripping a pathname of all but the trailing filename.
Strip the path from /some/long/path/filename.ext leaving only the filename.
Just as long as:
The cursor is anywhere within the bold word
There are no spaces in that word
E Go to the end (since there are no spaces - also works if not the last thing on the line)
T/ Find the last / (stop just after it, so it will be deleted, as well)
dB Delete to the beginning of the word
In visual mode:
$F.d^
The $ goes to the end of the current line, F searches backward for a period and d^ deletes till the beginning of the line.

delete from end of lines using block select in vim

I'm getting an unusual behavior when I try to delete from end of lines using block selection in vim.
So let's say I have a text as such:
delete this char:x
and this:x
also this:x
and then this:x
lastly this:x
If I want to append y to every line I can:
start block selection with C-v
select all the lines with 4j
go to ends of lines with $
start appending with A
type the desired text y
in order to get:
delete this char:xy
and this:xy
also this:xy
and then this:xy
lastly this:xy
but if I try to delete x in the last step instead of appending I would expect to get:
delete this char:
and this:
also this:
and then this:
lastly this:
although I end up with:
delete this char:
and this:x:
also this:x:
and then this:x:
lastly this:x:
As far as I understand it appends the last char in the first line to all other lines (in this case :) rather than deleting the missing ones (in this case x).
I can do this with macros or substitutes but I don't quite understand the rationale behind such behavior. Is there a way I can do this with block selection?
Have you tried :{range}normal? This should work:
:'<,'>normal $x
(The '<,'> bit is filled in for you when you type :.)
$ C-v 4j x
go to end of line with $
toggle visual block C-v
go down (in your case 4x) 4j
delete that stuff with x
Edit: (reacting on your comment for arbitrary indentation)
That can be done with simple macro. Macros are not so hard as you can think:
start recording a macro, we will name it 'a', so qa
go to the end of line $
delete one character x
go down by one line with j
end our macro q
Now apply our macro: 20#a - will do the same you did while you was recording the macro, 20x.
If I have a small number of lines I typically do Abackspaceesc. Then repeatedly do j. until done. Not the fastest way but easy to remember.
For a large amount of lines I typically visually select the lines via V then do a substitution or a normal command on the range.
:'<,'>s/.$//
:'<,'>norm $x
Note: you do not have to type '<,'>. It will be inserted automatically when you start a command when some text is visually selected.
The substitution command is pretty simple, match the last character (.$) and then replace it with nothing.
The normal command is just how you would delete the last character in normal mode via $x for a single line except it will apply it to each line in the range.
For more help see:
:h range
:h :s
:h :norm
as you said yourself, to achieve your goal, there are other ways, in fact better ways to go. :s or q(macro) or :g/.../norm $x. :s/.$//g is pretty straightforward.
Ctrl-V is not suitable for this job. As for its name: Visual BLOCK. You want to remove the last x, and they (only x) are not in a block.
However if you really want to stick with ctrl-v, you have to do some extra work, to make those 'x' in a block. If you have Align plugin installed, you could :
Select (V) all lines you want to do the trick,
<leader>t:
then your text looks like:
delete this char : x
and this : x
also this : x
and then this : x
lastly this : x
Ctrl-V to remove x, you should know how to do it.
then
:%s/ *:/:/g
to remove padded spaces before ':'
However I don't think it is a good way to go.

Vim: substitution in a range that is less than a line

Let's say I have the following line of code:
something:somethingElse:anotherThing:woahYetAnotherThing
And I want to replace each : with a ; except the first one, such that the line looks like this:
something:somethingElse;anotherThing;woahYetAnotherThing
Is there a way to do this with the :[range]s/[search]/[replace]/[options] command without using the c option to confirm each replace operation?
As far as I can tell, the smallest range that s acts on is a single line. If this is true, then what is the fastest way to do the above task?
I'm fairly new to vim myself; I think you're right about range being lines-only (not 100% certain), but for this specific example you might try replacing all of the instances with a global flag, and then putting back the first one by omitting the global -- something like :s/:/;/g|s/;/:/.
Note: if the line contains a ; before the first : then this will not work.
Here you go...
:%s/\(:.*\):/\1;/|&|&|&|&
This is a simple regex substitute that takes care of one single not-the-first :.
The & command repeats the last substitute.
The | syntax separates multiple commands on one line. So, each substitute is repeated as many times as there are |& things.
Here is how you could use a single keystroke to do what you want (by mapping capital Q):
map Q :s/:/;/g\|:s/;/:<Enter>j
Every time you press Q the current line will be modified and the cursor will move to the next line.
In other words, you could just keep hitting Q multiple times to edit each successive line.
Explanation:
This will operate globally on the current line:
:s/:/;/g
This will switch the first semi-colon back to a colon:
:s/;/:
The answer by #AlliedEnvy combines these into one statement.
My map command assigns #AlliedEnvy's answer to the capital Q character.
Another approach (what I would probably do if I only had to do this once):
f:;r;;.
Then you can repeatedly press ;. until you reach the end of the line.
(Your choice to replace a semi-colon makes this somewhat comfusing)
Explanation:
f: - go to the first colon
; - go to the next colon (repeat in-line search)
r; - replace the current character with a semi-colon
; - repeat the last in-line search (again)
. - repeat the last command (replace current character with a semi-colon)
Long story short:
fx - moves to the next occurrence of x on the current line
; repeats the last inline search
While the other answers work well for this particular case, here's a more general solution:
Create a visual selection starting from the second element to the end of the line. Then, limit the substitution to the visual area by including \%V:
:'<,'>s/\%V:/;/g
Alternatively, you can use the vis.vim plugin
:'<,'>B s/:/;/g

Resources