Repeat last change multiple times without replacing original count - vim

The documentation for . says:
Repeat last change, with count replaced with [count].
Is it possible to repeat one change without replacing count and without resorting to macros?
Alternately, is it possible to repeat a change multiple times without the number being interpreted as the count value to the command?
Context:
I was working on this VimGolf challenge where the objective is to reverse the alphabet:
abcdefghijklmnopqrstuvwxyz becomes zyxwvutsrqponmlkjihgfedcba
My approach was to jump to z and use "Ax to append each letter to then of register a, and then paste register a, which would contain the reversed string. To repeat "Ax, recording a macro and using 25#q works, pressing . 26 times works, but 26. and 26"Ax do not work because 26 is applied to the count of x, which makes the command attempt to delete 26 characters to the right of the cursor.

what you need here is macro, not [count]. because . repeat the last cmd. and if you give it count, as you have seen, the count would be applied on the last cmd, so it would be 26x, not pressing x 26 times. usually there is no difference, but if your cursor is at the end of the line, 26x does the same as x.
What you need is a macro, if you don't want to qx...q, there are some tricks you may want to know: (without thinking about the count of keystrokes too much)
trick1
use the = register:
26#='"Ax'<cr>
this does what you want.
trick2
use the . register.
Assume at the start point, your cursor is on the line1 col1 so, press: A"Ax<esc>U26#.
the first A jump to EOL and entering INSERT, big U undo the inserted text of current line, and save into '.' (dot) register.
Now you can "replay" the reg 26 times.
regarding this challenge, the most straightforward solution (might not be the shortest) would be: set revins option, in short: se ri.
hope it helps.

Related

How to append each line from 11-20 to each line from 1-10 in VIM

I have a file in which first 10 lines are the columns of a table and the rest 10 lines are the values of each column.
How can I use norm in VIM to append the values after each column names like this:
column1
...
column10
value1
...value10
-->
column1: value1
...
column10: value10
It is a little similar with this(Vim - Copy Nth word of each line, from line 10-100, to end of line), but I don't know how to go to line 1:10 and append the copied lines.
Any idea will be appreciated!
Fairly naive and crude way to do this, but:
:1,10norm! 10j0d$10kA: ^[p
Explanation:
1,10norm!: for lines 1 to 10, do the following (the ! means any custom mapping you have will be ignored, thanks to D. Ben Knoble for reminding of this):
10j: move down 10 lines
0d$: delete the whole line (not including newline)
10k: move back up 10 lines
A:: append (at the end of the line) ': ' (note the trailing space)
^[: input escape character, going back to normal mode. This (^[) is a single character and is inputted by typing Ctrl-v then escape, not by typing ^[.
p: paste the line deleted in step 3
Another (copy-pastable) way, (ab)using the substitute command:
:1,10s/\v(.*)\ze(.*\n){10}(.*)/\1: \3/ | 11,20d
which does:
1,10s/: for lines 1 to 10, execute the following substitution:
\v: use very-magic regex mode (see :help \v)
(.*): capture the entire current line (eg column1)
\ze: signal the end of the match. This way everything read (and captured) afterwards will not be affected (but can still be read)
(.*\n){10}: skip 10 (including current) lines, ie skip selector to 10 lines below
(.*): capture the line (eg value1)
/: end the 'select' part of the substitute command
\1: \3: replace with captured groups (eg column1: value1)
|: command separator
11,20d: delete lines 11 to 20
Use blockwise-visual mode to perform the operations.
You can enter visual block mode with Ctrl-V and it allows you to select and operate on columns. It also allows you to perform the same action on a block, which you can use to add the : to the lines with the column names.
I'll use normal Vim syntax for keystrokes in my examples, <C-v> means Ctrl-V.
Start by deleting the values into the default register, using a visual block:
11G<C-v>9j$d
Then Add the : to the column lines, also using a visual block:
1G<C-v>9j$A: <Esc>
Then add some more spaces to the first line, to ensure there's room for all the column names to fit:
A <Esc>
Finally, put the visual block at the end of the first line:
$p
It will actually put it on all lines all the way to the end.
This is slightly different from what you specified, since the values are all aligned on the same column. If you want different spacing, you can perhaps use a :s operation to fix spacing.
10:s/: */: /<cr>
Depending on where you pasted (if some column names had more trailing spaces than the first one), you might have some trailing spaces after the pasted values to fix as well, but that should be easy to do using a similar procedure.
Visual block operations are really powerful, it's a great feature to learn and keep in your "toolbox" in Vim. They're really handy with this kind of problem where thinking in "columns" makes the most sense.

vim change newline into space but not every nth-line

I have a file.txt of 1000 lines and every first 10 lines need to be on one line.
The eleventh line starts on a new line and is added with lines 12-20.
How can I do this in Vim!
I'm not 100% sure I grok what you're after, but I'll give you a few hints and perhaps they'll help.
To join lines together you can use J (that's capital J or Shift-J). You can precede this with a number, such as 10, and join 10 lines together. For example, typing 10J will give you:
Now, what you really need is a macro. This sounds fancy but it's just a way or recording a set of commands. So for example you could take 10J and record it to a macro. Repeat that macro 10 times and then you do that to 100 lines.
I think that the solution to your problem would therefore be something like this:
qa10Jjjq99#a
This does this (see below for an explanation):
Explanation:
q starts recording a macro. Everything that you type afterwards, until you type q again will be recorded. The next character a records the macro to the character a. Thus, qa of qa10Jjjq99#arecords 10Jjj to a. Now, to use the macro that you have stored to a you use the # symbol followed by the letter of where you stored the macro (in this case a, because we typed qa). The 99 means repeat this 99 times, thus, 99#a means repeat what's stored in macro a 99 times.
All together qa10Jjjq99#a means: record a macro q and store it in a. Then, join 10 lines 10J and move down two lines jj before stopping the recording q. Then repeat the macro stored 99 times 99#a.

Vim command to increment number digit ONLY at cursor position in a longer number string?

I have a few instances where I want to do the following
In file:
1234
With the cursor at the 1st digit, I want to use Ctrl-A to increment the digit so that I get
2234
instead of
1235
Are there intrinsic vim commands to do this?
Otherwise, should I set up a quick script:
Surround digit with leading and trailing space
Ctrl-A to increment
Delete leading and trailing space
Like so, and then map to a key?
The increment function takes a leading number like most vim commands. 1000 ctrl+a would return 2234 like you wanted. If all your numbers are 4 digits numbers then this would work. Or you could use r2 which replaces the current character under the cursor with a 2, but this may be too specific.
If you need your script you can record a macro.
qaa[space][esc]h[ctrl+a]lx
broken down:
qa - start recording a q macro and save to register a
a[space][esc] - add a space after number
h - move back to number
ctrl+a - add one
lx move right and delete space.
You shouldn't need to add a leading space, because as you noticed the ctrl+a function acts on the number as a whole and will always add 1.
You could do this as s<C-r>=<C-r>"+1<Enter>.
You can then map that to something else, like nnoremap g<C-a> s<C-r>=<C-r>"+1<cr> (you'll need to use Ctrl-vCtrl-r to insert the <C-r>s in this normal map).
Step by step:
s - delete the character under the cursor and begin insertion
<C-r>= - begin an expression evaluation.
<C-r>" - put contents of unnamed register in
See :help i_CTRL-r for more information on these.
+1<Enter> - add 1 to the value and complete the command.

vim: delete the first 2 spaces for multiple lines

What's the easiest way to delete the first 2 spaces for each line using VIM? Basically it's repeating "2x" for each line.
Clarification: here the assumption is the first 2 characters are spaces. So the question is about doing indentation for multiple lines together.
Enter visual block mode with Ctrl-V (or Ctrl-Q if you use Ctrl-V for paste);
Select the area to delete with the arrows;
Then press d to delete the selected area.
Press Esc
Some more options. You can decided which is the "easiest way".
Remove the first 2 characters of every line:
:%normal 2x
Remove first 2 characters of every line, only if they're spaces:
:%s/^ /
Note that the last slash is optional, and is only here so that you can see the two spaces. Without the slash, it's only 7 characters, including the :.
Move indentation to left for every line:
:%normal <<
You could also use a search and replace (in the ex editor, accessed via the : character):
Remove first two characters no matter what:
%s/^.\{2}//
Remove first two white space characters (must be at the beginning and both must be whitespace... any line not matching that criteria will be skipped):
%s/^\s\{2}//
Assuming a shiftwidth=2, then using shift with a range of %
:%<
Two spaces, or two characters? (2x does the latter.)
:[range]s/^ //
deletes two blanks at the beginning of each line; use % (equivalent to 1,$) as [range] do to this for the entire file.
:[range]s/^..//
deletes the first two characters of each line, whatever they are. (Note that it deletes two characters, not necessarily two columns; a tab character counts as one character).
If what you're really doing is changing indentation, you can use the < command to decrease it, or the > command to increase it. Set shiftwidth to control how far it shifts, e.g.
:set shiftwidth=2
I'd try one of two approaches:
Do column editing on the block to delete using Ctrl+V (often mapped to Ctrl+Q).
Record a macro on the first row using q1 (or any other number/letter you want to denote the recording register), then replay that macro multiple times using #1 (to use my previous example. Even better, use a preceding number to tell it how many times to run - 10#1 to run that macro 10 times, for example. It does, however, depends on what you recorded - make sure to rewind the cursor 0 or drop one line j, if that's relevant.
I'd also add: learn how to configure indentation for vim. Then a simple gg=G will do the trick.

How do I repeat an edit on multiple lines in Vim?

I'm aware that in Vim I can often repeat a command by simply adding a number in front of it. For example, one can delete 5 lines by:
5dd
It's also often possible to specify a range of lines to apply a command to, for example
:10,20s:hello:goodbye:gc
How can I perform a 'vertical edit'? I'd like to, for example, insert a particular symbol, say a comma, at the beggining (skipping whitespace, i.e. what you'd get if you type a comma after Shift-I in command mode) of every line in a given range. How can this be achieved (without resorting to down-period-down-period-down-period)?
Ctrl-v enters visual mode blockwise. You can then move (hjkl-wise, as normal), and if you want to insert something on multiple lines, use Shift-i.
So for the text:
abc123abc
def456def
ghi789ghi
if you hit Ctrl-v with your cursor over the 1, hit j twice to go down two columns, then Shift-i,ESC , your text would look like this:
abc,123abc
def,456def
ghi,789ghi
(the multi-line insert has a little lag, and won't render until AFTER you hit ESC).
:10,20s/^/,/
Or use a macro, record with:
q a i , ESC j h q
use with:
# a
Explanation: q a starts recording a macro to register a, q ends recording. There are registers a to z available for this.
That's what the :norm(al) command is for:
:10,20 normal I,
If you are already using the '.' to repeat your last command a lot, then I found this to be the most convenient solution so far. It allows you to repeat your last command on each line of a visual block by using
" allow the . to execute once for each line of a visual selection
vnoremap . :normal .<CR>
I believe the easiest way to do this is
1) record a macro for one line, call it 'a'; in this case one types
q a I , ESC j q
2) select the block of lines that you want to apply the macro to
3) use the 'norm' function to execute macro 'a' over this block of lines, i.e.,
:'<,'>norm#a
I think the easiest is to record a macro, and then repeat the macro as many times as you want. For example to add a comma at the start of every line, you type:
q a I , ESC j q
to repeat that 5 times, you enter
5 # a
With your edit already saved in the . operator, do the following:
Select text you want to apply the operator to using visual mode
Then run the command :norm .
Apart from the macros, as already answered, for the specific case of inserting a comma in a range of lines (say from line 10 to 20), you might do something like:
:10,20s/\(.*\)/,\1
That is, you can create a numbered group match with \( and \), and use \1 in the replacement string to say "replace with the contents of the match".
I use block visual mode. This allows you to perform inserts/edits across multiple lines (aka 'vertical edits').

Resources