Replacing in Vim every fourth occurrence - vim

I have a file like this:
Question 1
b) answer b
c) answer c
a) answer a
d) answer d
Question 2
a) answer a
d) answer d
b) answer b
c) answer c
Alls answers are unsorted. I need to set all the first answers (no matter what letter, just that they are the first answer) to v) and the rest to x), so the output will be:
Question 1
v) answer b
x) answer c
x) answer a
x) answer d
Question 2
v) answer a
x) answer d
x) answer b
x) answer c
Is it possible with one-line command?

If you really wanted a one line sure. Anything can be made into a one liner if you use enough | to chain commands together.
You can do the iteration your self and run a different substitution command depending on how many of the pattern you have seen.
:let i = 0 | g/^[abcd])/if i % 4 == 0 | s//v)/ | else | s//x)/ | endif | let i = i + 1

If all the questions have the same number of lines, you could do this.
Starting with the cursor on the first column of the first line (in normal mode), you could record a macro (here, into a) that will do this for one block:
qajrvj^V}0rx'>)q
In more detail:
qa record a macro in register a
jrv move down one line and replace the first character with v
j^V} (type ^V as Ctrl-V) visually block select the first characters of the rest of the lines of the block
0 make sure we are on column 0 (this is important if we run into the end of the file)
rx replace these characters with x
'> go to the end of the last visual selection
) go to the next sentence (the beginning of the next question)
q stop recording the macro
This assumes all questions are on one line (this would be easy to change), that we are at the first column of the question line when the macro is run (also a pretty small change to generalize this) and that all answers are one line (the approach would need to be changed a bit, but generalizing this wouldn't be too much harder).
Now you can run this again with #a (and after that, ## repeats the last run macro). If you want to turn this into a recursive macro (which will keep running until one of the actions, probably a motion, fails), you can use this to append the recursive call to the end of the macro:
qA#aq
Using a capital register name means append to that register, rather than overwrite it. If you do this, and all the questions follow the restrictions, you can apply this replacement to an entire file by running the macro only once (manually).
You could also turn this into a function using the :normal command, if you wanted to keep it around for later use. If you do this, I would suggest spreading it out over several :normal commands and commenting it similar to how I did above.

You could use something like this:
:%s/\d\+\_s\zs\l\ze)/v/ | %s/\D\_s\zs\l\ze)/x/
But this is only for your special case.
Little bit of explanation:
%s substitute in all lines
\d\+ one or more numbers
\_s white space or end-of-line
\zs and \ze start and end of the match
\l lowercase character: [a-z]
\D non-digit: [^0-9]
| can be used to separate commands, so you can give multiple commands in one line.

Related

Selecting multiple text spots with visual mode?

I'm doing some markdown editing in vim on a file. I'm trying to convert it to markdown with code highlighting, etc.
- Arithmetic operators:`+,−,*, /`
- Constants: `e`, `pi`
- Functions: (abs x), (max x y... ), (ceiling x) (expt x y), (exp x),
(cos x), ...
I want to select only the things that are in parantheses (including the parantheses) in the following using visual mode (so they would be disjoint by the commas):
(abs x), (max x y... ), (ceiling x) (expt x y), (exp x),
(cos x), ...
And then do S` to surround the each piece of text with backticks. How can I do this without selcting each one, then doing S` repeatedly?
How can I do this without selcting each one, then doing S` repeatedly?
This is actually what works best in Vim. With a bit of help from macros:
Interactive version:
/(.\{-})<CR>
qqysa)`nnq
#q
##
##
... till you do them all and wrap around to where you started.
Non-interactive "just do it" version:
:set nows<CR>
gg
/(.\{-})<CR>
qqqqqysa)`nn#qq#q
You'll probably want to go back to :set ns afterwards.
Of course, if you know that there are no nested parentheses, then the simplest answer is using :s, like the other answerer suggested.
EDIT with the explanation of the macro:
qqqqq...#qq#q is a loop. Here's how it works:
qq followed by q clears the q register. This will be important later.
qq starts the macro recording.
ysa) surrounds around the parentheses with `.
nn goes to the next match. We have to do it twice, because surround jumps to before the paren, and n will match the same parentheses again.
#q invokes the q macro. It is empty, so this does nothing... now. However, read further...
q stops the macro recording, and stores it to the q register.
Now that q is not empty any more, we can execute it with #q. However, during the execution q will still not be empty, so when we get to the point in the macro that did nothing during the recording, the macro will relaunch, giving us a primitive, but functioning, recursion loop.
The loop stops when something in it breaks: for example, not being able to go to the next match. Usually, you'd just change all the matches so no more matches remain; however, the edit this macro does does not make the match fail, so we have to rely on :set nows to make sure we don't continue infinitely adding backticks to all parentheses.
After a bit of thought, you can actually rewrite the pattern so that :set nows (and additional n) is not needed:
/`\#<!(.\{-})<CR>
qqqqqysa)`n#qq#q
This matches a pair of parentheses not preceded by a backtick, so that after all matches have been dealt with there is no match for n, naturally breaking the loop.
If anyone thinks this is complex... note that most editors plain can't do it (since this takes into account proper parenthesis nesting, whereas I haven't yet seen an editor with search-replace robust enough to be able to pull it off).
Using a global command (assuming `S`` comes from surround.vim):
:global/(/normal f(ysab`
(This affects the whole file, and may only do one change at a time. Repeat with #:)
With a macro:
qqf(ysab`q
Repeat with #q and then ##
Or with substitute:
:substitute/([^)]\+)/`&`/g

vim - Prefix number to 3 letter combination bdw

I am learning Vim and I have come across this situation.
Hello, World!
-
I want to delete Hello, World. If I entered bdw (go to beginning of word, delete word) three times then it would delete it. However, I want to type 3bdwto save typing, but it goes three words back and deletes that word, leaving me with , World!. Is there any way to prefix a number to a three letter command or is there another command I should be looking at?
Move the cursor under !, then press
dFH (delete back until first H)
F=Shift+f
Is there any way to prefix a number to a three letter command or is there another command I should be looking at?
The problem is that it's not a 3-letter command, it's two seperate commands.
3b 3dw will do what you want, but it does require the extra 3.
EDIT: Years later... I notice a small optimization to this case w3db. The only small issue is that you need that leading w to correct the cursor position.
Hello, World!
Go to the beginning of line (by pressing 0) and type the following:
dt!
Explanation:
d delete
t until the character before ! in the line.
Move the cursor under the ! ($ or l)
Then write d0 (which means delete til beginning of line)
You can also do 0 (go to beginning of line) then dt! (delete until next !)

VIM - explain 'normal' and 'global' commands

Can somebody clarify me how global (:h :g) and norm (:h norm) commands are working in VIM? I have file:
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
G
H
I have issued :g/[0-9]/norm 4gg dd hoping that it will work in following maner:
[0-9] = match only lines with numbers
4gg = jump to 4th line
dd = delete current (4th) line
So I was expecting this:
1
2
3
5
6
7
8
9
A
B
C
D
E
F
G
H
But instead of it I get:
1
2
3
4
A
B
C
D
E
F
G
H
Also it does not matter if I use norm or norm!, what is the difference, can you please explain me how this is working or point me to some good references, I have read :h :g and :h :norm but it does not help. Thank you
PS: I can use :4d but I am interested in :g and :norm explanation, the problem was mentioned just as simple example.
:g/pattern/do something
will do do something on each line that matches the pattern. so you got that output.
try:
:g/\d/echo getline('.')
:g/\d/echo line('.')
1st line prints the matched line
2nd prints the matched line number.
so you will see for each matched line, vim does something (echo, in this case)
and for the reason, why 4 is till there, because you have a space between 4gg and dd.
Your line with number has only one char, so space will move cursor to next line. that's why 4 won't be deleted.
remove the space!
:normal
:{range}normal {command}
executes normal mode {command} on every line in {range}. Without {range}, it operates on the current line.
Example:
:5,18normal 0df=
:global
:{range}g/{pattern}/{command}
executes {command} only on lines in {range} that match {pattern}. Without {range}, it operates on the whole buffer.
Examples:
:5,18g/^foo/normal 0df=
:5,18g/^foo/s/^.\{-}=/
It means:
For every line that contains any digit between 0 and 9, go to fourth line and delete it. So in first iteration it deletes the number 4, in the next one the number 5 and so on. When cursor begins with lines that don't contain number, it doesn't match and doesn't execute the normal instruction, so it doesn't delete anything.
To complete your questions (note that it is bad style to ask several questions (unless tightly related), and you should have been able to resolve most of that through the excellent :help):
The difference between :normal and :normal! is that the former considers mappings, whereas the latter always works on the default Vim commands. Therefore, the former is okay for ad-hoc commands (that make use of your mappings), but the latter is recommended for plugins (to be independent of any mappings).

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

Vim jump back over spaces inside of curled braces

Lets say, I have wrote this piece of LaTeX in vim:
A_{\vec B}_
^
Cursor
The cursor is at the marked position. Now I want to add a \vec in front of the A.
I tried <Esc>Bi\vec <Esc>WW but I want B and W to jump over spaces which are inside of curled braces.
Any ideas how to do that?
Edit: Maybe I should've wrote, that I wanted to map this to a shortcut. A and B are just Examples for Strings which do not include spaces.
SOLUTION: I think I have found, what I was searching. With ? you can do a backward search with a regular expression.
You can't make B or W treat a braced string as a Word, but you can use % to jump from one end of a braced string to the other.
There are several ways to achieve a similar job. W and B only stop at whitespaces, as far as I know, so you can jump twice between words with 2B, or set insert mode at the beginning with I, or search for a letter, with f, F, t or T. Also 0 and $ to go to the beginning or end directly. There are many possibilities. I hope one of them can be useful instead.
FAi\vecESC$
Does it assuming that B is really at the end of the line

Resources