There's a Vimgolf exercise to convert this:
- One number per line -
-----------------------
2,3,5,7,
11,13,17,
19,23,29,
To this using Vim:
2
3
5
7
11
13
17
19
23
29
One possible solution is:
dj3JAwr<CR><Esc>ux09#.ZZ
My question is - what does the "09#." part of the command do? I understand 0 is the beginning of a sentence, 9 is the end, and "." means repeat command.
But what command is being repeated? And do "09#." mean something different when strung together in sequence?
09#. is:
0: go to first character of the line.
9#.: execute the content of register . 9 times.
The register . is defined as:
quote_. quote. E29
". Contains the last inserted text (the same as what is inserted
with the insert mode commands CTRL-A and CTRL-#). (...)
You can still check its content by executing:
:register .
which gives wr^# So vim here is changing the newline into a null character and do not give what you expect.
So dj3JAwr<CR><Esc>ux09#.ZZ can never be a solution for the problem especially as J leave also a space when joining lines.
Related
when I need to insert some same text in several lines, normally I would switch to VISUAL-BLOCK and insert the desired text. (CTRL+V, motions, SHIFT+I) but as in the default behavior of vim, It only give the first line of visual feedback before returning to NORMAL mode (as in the second image). Is there any option or way I could achieve the result as stated in the title of this question?
I don't know why you feel you need to stay in visual-block mode; you could accomplish this in normal mode in multiple ways.
Label all the lines you need as 0 NULL, and then use visual-block mode to highlight the column of zeros; then, use g<C-a> to increment each line.
You could also use a :global command like :g/\v^\d+/normal A<Tab>NULL.
There's probably also a solution with a macro, but you see where I'm going with this. It's an easily-scriptable task.
Insert the first null with A + literal tab + NULL like this:
ID MEMBER_TYP
--------------
1 NULL
2
3
If you are using relative number:
:4,+14 normal .
More about Relative Numbers: If you are at the line 4 and the last relative number shows 13 you can type:
14: ..................... shortcut to :.,.+13
After typing 14: just type norm . and hit Enter
Starting from next line until the end:
:+1,$ norm .
A third way:
:.,$s/\v\d+/& NULL
NOTE: Again the tab must be typed literally
:.,$ ..................... from this line to the end
\v ....................... very magic
\d+ ...................... match digits
& ........................ matched pattern
Maybe the best way:
ID MEMBER_TYP
--------------
1
2
3
At the first number, 1
Ctrl-v ........... start visual block
} ................ extends selection till the end
A ................ starts insert at the end of line
<TAB> ............ literal tab
Here is the initial text.
test1
test2
Only two lines in the text.
I want to insert strings sequence into from 5th line into 16th line.
I have tried it with below codes.
for i in range(1,12)
echo ".item".i.","
endfor
1.the initial text.
2.to enter into command mode and input the codes
Two problems to be solved.
1.echo command output the first string .item1 before endfor.
for i in range(1,12)
echo ".item".i.","
2.How create the strings sequence into specified line:from 5th till 16th in edited text with vimscript?
The desired result is as below.
Almost done!
What i get is as below with the command :pu! =map(range(1,12), 'printf(''item%1d'', v:val)').
Both of them can't work.
:5pu! =map(range(1,12), 'printf(''item%1d'', v:val)')
:5,16pu! =map(range(1,12), 'printf(''item%1d'', v:val)')
The last issue for my desired format is when the cursor is on the 3th line ,how to create the desired output?
In order to insert the missing lines, without inserting unrequired empty lines (-> append() + repeat([''], nb) + possible negative nb)
:let lin = 5 - 1
:call append('$', repeat([''], lin-line('$')))
Then, in order to insert what you're looking for (no need for printf() if you don't want to format the numbers)
:call append(lin, map(range(1,12), '"item".v:val'))
PS: I'd rather avoid :put when I can as it's kind of difficult to use with complex expressions.
Assuming you are in a Unix based operating system, you have a seq command.
So you can do:
$ seq -f 'Item %.0f' 20
Item 1
Item 2
...
Item 20
Inside vim you can try the reading from external command approach:
:r! seq -f 'Item \%.0f' 20
I'm dealing with a fixed-width file format and I need to increase all of the numbers in some columns. I have a simple macro that adds a value to a number, moves to the next line and repeats (like 2aj) However, these numbers start from 1 and usually end above 10000, so the column widths get messed up, e.g. (underscores as spaces, this example only covering the jump from 9 to 10)
FOO_7_BAR
FOO_8_BAR
FOO_9_BAR
becomes
FOO_9BAR
FOO_10_BAR
FOO_11_BAR
(note the new column of text that will break my program)
when I need
FOO_9_BAR
FOO10_BAR
FOO11_BAR
I have been manual going through and deleting a space from the first 9 columns, then 90, then 900, but I am looking for a more robust way to handle this without dealing with the first 10, 100, 1000, etc. with different macros or any manual input.
Thanks.
I come up with this way, I think the animation explains itself:
The final result is:
FOO 3 BAR
FOO 4 BAR
FOO 5 BAR
FOO 6 BAR
FOO 7 BAR
FOO 8 BAR
FOO 9 BAR
FOO10 BAR
FOO11 BAR
This requires a bit of manual hackery, but it's still better than manually deleting spaces.
You could also probably write a function that does this automagically, via Vimscript, though!
First, find the length of the shortest line. You can do this via ex. :echo col("$") on the shortest line.
Then, run the following command:
:g/.\{NUM\}/exec "norm! /[0-9]\<cr>X"
Replace NUM in the above command with the original number you got in the previous step.
Here's an explanation of how it works:
:g/.\{NUM\}/ Find lines that are too long
exec "norm! A common idiom: build a string to execute in normal mode
/[0-9]<cr> Find the first number on the line
X Delete the space before it (equivalent to "hx")
Then simply repeatedly run the same command (you can do this by pressing :UpReturn) until all the lines are the same length—it will result in an error once this is the case (since it won't find any matching lines).
Here's a short animation of the entire process:
This can be done with :s and a sub-replace expression.
:%s/\v([^0-9]*)(\d+)/\=strpart(submatch(1), 0, 5 - len(submatch(2))).submatch(2)
The idea is we capture the portion before the digits and the digits themselves. The replacement execute an vim expression via \= which put the two capture groups back together. However slice the first capture group via strpart() to a fixed width (5 in this example) minus the length of our second capture group, len(submatch(2)).
For more help see:
:h sub-replace-expression
:h strpart()
:h len()
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).
I can use
:5,12s/foo/bar/g
to search for foo and replace it by bar between lines 5 and 12. How can I do that only in line 5 and 12 (and not in the lines in between)?
Vim has special regular expression atoms that match in certain lines, columns, etc.; you can use them (possibly in addition to the range) to limit the matches:
:5,12s/\(\%5l\|\%12l\)foo/bar/g
See :help /\%l
You can do the substitution on line 5 and repeat it with minimal effort on line 12:
:5s/foo/bar
:12&
As pointed out by Ingo, :& forgets your flags. Since you are using /g, the correct command would be :&&:
:5s/foo/bar/g
:12&&
See :help :& and friends.
You could always add a c to the end. This will ask for confirmation for each and every match.
:5,12s/foo/bar/gc
Interesting question. Seems like there's only range selection and no multiple line selection:
http://vim.wikia.com/wiki/Ranges
However, if you have something special on line 5 and 12, you could use the :g operator. If your file looks like this (numbers only for reference):
1 line one
2 line one
3 line one
4 line one
5 enil one
6 line one
7 line one
8 line one
9 line one
10 line one
11 line one
12 enil one
And you want to replace one by eno on the lines where there's enil instead of line:
:g/enil/s/one/eno/
You could use ed - a line oriented text editor with similar commands to vi and vim. It probably predates vi and vim.
In a script (using a here document which processes input till the EndCommand marker) it would look like:
ed file <<EndCommands
5
s/foo/bar/g
7
s/foo/bar/g
wq
EndCommands
Obviously, the ed commands can be used on the command line also.