To letter, then delete until letter, and repeat - vim

From "practical vim", I gather than a good practice is to try to move, act, and then repeat.
Say I have this string:
foo_bar fooo_bar foo abar foo_bar
I would like to move forwards until I find an f, and then delete until I find a b, and repeat.
I would have thought that the following would work:
ff
dtb
;
.
;
.
ff would be the command to move, and then dtb the one to act.
However, when I press ;, it goes forwards until just before the next b, while I would like to repeat my "move" command, i.e. ff.
Is there a way to do this, such that the "act" command doesn't change the behaviour of the "move" one?

; repeats the latest fFtT which, in your case, is tb, not ff. There is no way to make ; repeat something else.
Here are alternative methods…
With :help /:
ff
d/b<CR> " delete until next b
;
.
;
.
As mentioned by #mattb, the trick is to use a different motion than any of fFtT for the operation so that your ff is always the latest motion repeatable with ; or ,.
With :help recording:
qq " start recording in register q
ff
dtb
q " stop recording
#q " play it back
#q " play it back
The trick is to encapsulate the initial motion and the operation into a single macro that can be repeated over and over without involving ; at all.

You could record a macro into some register and replay it. Using the a
register:
qaffdtbq
this breaks down into:
qa to begin recording the macro into the a register
ffdtb are the commands you wanted to run
q to stop the recording
and then, with the cursor on the first character, run the macro 4 times with
4#a to change this:
foo_bar fooo_bar foo abar foo_bar
to this:
foo_bar bar bar bar
or just run the macro once with #a and then hit ## to repeat it

Maybe it's not what you need, but you could do the same thing using the :s command.
:s/f[^ft]*t//g

You could also search for the f with / and then jump to the next one with n:
Input:
foo_bar fooo_bar foo abar foo_bar
Actions:
/f<CR>
dtb
n
.
n
.
Result:
foo_bar bar bar bar

Related

is there something similar to f command in vim for multiple chars?

Beginning to use vim as a macro, is there an f command but instead of catching single char i want to catch multiple chars? While I know I can use the / I didn't see that I could repeat it with the ; to take me to the next target only with f
You can get to the next target with n and to the previous one with N when using /.
Put your cursor on the word and pres:
gd - goto first one
# - goto previous
* - goto next
and then n or N to repeat.
If your issue is that / is interactive (meaning: you have to type something) and you want to use that in a fixed value macro, then you can still do it.
For example if you do:
:map g /something^M
then you hit g, it will look for the string something.
Keep in mind that ^M is the combination of keys Ctrl+V followed by Ctrl+M
As a side note, the same goes when you want to enter some text and go back to normal mode:
:map g 0i--^[j
If you hit g it will insert -- at the beginning of the line and move down one line.
Here ^[ is Ctrl+V followed by Esc

vim replace a string when a change occurred in another place

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:
.

Bash: How to generate lines with sequential numbers?

I need to create a list which will hold fqdn's of about 30 servers.
Until now, whenever I needed to create such a list, I would:
Open vim
Manually insert the first line
Yanking the first line and then pasting the line as many times as the amount of servers in the list.
Then, I would manually edit the host names.
For example:
scraper01.nj.company.com
scraper02.nj.company.com
.
.
.
I wanted to know if there's a way to do it (given the fact that the names are the same apart for the chronological number) automatically, maybe by using sed but I don't know how to do it.
Can you please assist me?
Start with:
scraper01.nj.company.com
Press qq to start recording a macro in register q:
qq
Yank the current line:
yy
Paste it below:
p
Increment the number:
<C-a>
Stop recording:
q
Play it back 28 times:
28#q
All together:
qqyyp<C-a>q28#q
Inside vim you could do the following:
:put =map(range(1,30), 'printf(''scraper%02d.nj.company.com'', v:val)')
In a bash, just use printf with a ranged brace expansion:
printf "%s\n" "scraper"{01..30}".nj.company.com"
Prints:
scraper01.nj.company.com
scraper02.nj.company.com
[...]
scraper30.nj.company.com
Or another solution, with a for loop:
for (( i=0; i<=30; i++ )) ; do printf "scraper%02d.nj.company.com\n" $i; done
The solution posted by #chaos works fine, but since you tagged this with vim, here's another Vim way.
First, there is this very useful map for copying lines and blocks:
nnoremap <silent> <M-c> #='"zyy"zp'<CR>
With it, you can write scraper01.nj.company.com, then go to visual mode and press 9Meta-c to add 9 more copies of it (of course, you can replace 9 by any number).
Then install the VisIncr plugin. With it, you can now press Ctrl-v to mark the column of 01, then run :II. This will change the numbers to 01 ... 10. Save, and you're done.
Both the <M-c> map and the VisIncr plugin do more than shown above. They can be quite useful in many other situations.

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

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.

Resources