What is this just the middle vimgolf challenge doing? - vim

So in working http://www.vimgolf.com/challenges/54862fbb3f90ac0002904cf5 one solution is:
)3:wq!<CR>
My vim cheat sheet says:
) is "end sentence". Not sure how the "3" fits in.
But "wq!" I know as write/quit.
What is this set of keystrokes doing?

Command
)3:wq!<CR>
Input
Leave only the
numbered lines.
LINE 1
LINE 2
LINE 3
That's all.
Thank you
very much.
Breakdown
) goes one sentence forward. This positions the cursor at LINE 1
3 starts a range of 3 lines for the next command.
:wq! writes the range to the file.
You should notice that when typing :, the range get's set to .,.+2
I'm not sure where to place the : as it switches from normal mode to command mode so following breakdown is equally valid
) goes one sentence forward. This positions the cursor at LINE 1
3: starts a range of 3 lines and enters command line mode
wq! writes the range to the file.
You should notice that when typing :, the range get's set to .,.+2

) puts the cursor after the current sentence, on LINE 1.
3:command is expanded by Vim to :.,.+2command, which means "execute command on the current line and the two next lines".
.,.+2wq! writes only the given lines to file, effectively removing any other line from the buffer/file.
See :help range and :help :write.

Related

Vim issue with yank-paste macro applied over selected lines

I've tried this in Vim 8.1 and Neovim (nvr 2.1.10) with and without my ~/.vimrc (the latter to check if vimrc entries were causative -- no effect).
Given this example
apple
banana
carrot
dates
I can record a macro (#a)
yy
p
to yank and paste (i.e. duplicate) a line. When I apply (#a) that macro to individual lines, and repeat that macro (##) on individual lines it duplicates that line.
However, when I visually select those lines and try to apply the macro, trying any of these
:'<,>'norm #a ## :'<,>'norm #a on single line works
:'<,>'normal #a
:'<,>'norm! #a
:'<,>':norm #a
:'<,>':normal #a
:'<,>':norm! #a
:1,4norm! #a ## https://stackoverflow.com/a/390194/1904943
etc. the macro duplicates (once per selected line) the first line in the selected text:
apple
apple
apple
apple
apple
banana
carrot
dates
What's the problem, here?
The reason you're seeing this issue is because of the way ranges work on ex commands.
When you specify a range, that range is evaluated to line numbers before any operations occur. In this case, '< is evaluated as 1, and '> is evaluated as 4, because these are linewise operations. When you run the macro the first time on line 1, a new line is created with "apple". When the macro runs on line 2, that line contains "apple", so that word is duplicated, and so on. Whatever contents are on that line at the time the command is executed are used. You can see the same behavior if you use % (for all lines) or 2,4 to select "banana" and below.
However, POSIX specifies different behavior for the :g command:
The global and v commands are logically two-pass operations. First, mark the lines within the specified lines for which the line excluding the terminating matches (global) or does not match (v or global!) the specified pattern. Second, execute the ex commands given by commands, with the current line ( '.' ) set to each marked line.
That's why :g/^/norm yyp works here: because the lines to modify are marked before execution, and only the marked lines have the command executed. Lines that are inserted after the fact aren't considered. :g does accept ranges, as you noted, so you can limit your operation to a set of lines you'd like to handle.

How to select from line to line in vi?

I frequently want to select multiple lines in vi. e.g. from line 1 to line 10.
So, what I usually do when I want to jump from line to line is I type :110 to jump to line 110, e.g.
And, when I want to select from line to line, I usually press v to get into visual mode, and then I just scroll down using k or l.
So, intuitively it makes sense to me to just press v, and then type :<line number>. but that doesn't work.
How to select from line X to line Y in vi?
Let's assume you want to highlight from line 10 to line 20. You can use:
10GV20G
Breakdown:
10 enters 10 into the buffer
G goes to the line number in the buffer
V enters visual line mode
20 enters 20 into the buffer
G goes to the line number in the buffer
Note that G means Shift+g (capital G).
Source and a : command are here.
Selection by itself isn't meaningful; you usually want to invoke a command on the selection. Many commands that work on the visual selection have a corresponding Ex command. With that, going through visual mode is unnecessary if you already know the exact ranges. The great benefit of visual mode is that you can interactively and iteratively adapt the selected area if there's no single motion or text object.
The benefit :help :range is that you can succinctly specify the lines. For example, lines 110 to 120 can be written as :110,120, but also as :110;+10.
simple just press Shift v line number gg
example: your current line to line 41
Just press Shift v 41 gg
note: you can move to selected line by press line number gg
If you set both number and relative number it becomes easy to see the target end line.
:set number relativenumber
So, let's say you are at the line 10 and the target line shows 11, you start your selection with capital V, then press 11j
V11j
If your block has blank lines before and after, just type vip (visuall inner paragraph)

vim - when triggering a macro, normal mode isn't switched from insert mode (xterm)

For testing purpose I created simple macro which wraps current line into single quotes and goes to next line.
Here is output from the register the macro is saved in: I'^[A'^[j
And here is testing text:
Line number 1
Line number 2
if I trigger the macro on the line number 1, cursor position should be changed to the line number 2 and the text should be changed to:
'Line number 1'
Line number 2{CURSOR_POSITION}
Instead of the expected result, vim stays in insert mode at the end of line 1 and result is following:
'Line number 1'ê{CURSOR_POSITION}
Line number 2
...where {CURSOR_POSITION} is current cursor position
Why vim place ê character at the end of first line and doesn't go to the next line?
I got same result when I ran vim with --noplugin option.
I use xterm-256color
Vim 7.4
This is kind of a bug (discussion here). I know it is stupid but this should work :-)
I'^[A'^[1j
It is because ^[j can be interpreted as a Ctrl+V Alt+J (link here).

Separate lines with blank lines in Vim?

I am dealing with a block of comments like:
//this is comment 1
//this is comment 2
//this is comment 3
//this is comment 4
I would like to make it look like:
//this is comment 1
//this is comment 2
//this is comment 3
//this is comment 4
Is there a Vim shortcut to make this transformation on selected lines while staying in command mode?
You can use the :substitute command. With the cursor anywhere on the
first of the lines:
:,+3s/$/\r
This inserts an additional newline at the end of each line.
You can also use the :global command. With the cursor anywhere on
the first of the lines, run:
:,+3g//norm o
For each of the next four lines, this executes the o Normal-mode
command, adding a new blank line.
In both of the commands, the ,+3 prefix is a range for the
command, see :help range. Briefly, the comma separates the addresses
of the starting and ending lines of the range, where the current line
is used if we omit the former of the two addresses. The +3 address
refers to the line that is three lines below from the current line.
Rather than specifying a range, e.g., ,+3, for either of these
commands, you can use the V Normal-mode command to make a Visual
block across all the lines that you want. Then typing : to begin the
command will auto-fill the range specifying the visual block, and you
can then enter either of the two commands starting with s or g:
:'<,'>s/$/\r
You can use a macro:
qao<esc>jq
then use 3#a to apply the macro 3 times over the last lines.
where:
qa "Start recording a macro named a
o "Insert new line under current line
<esc> "Exit insert mode
j " Move down next line
q " end macro
Select your visual selection with V
Then run a regex replace to replace one line break with two
:s/\n/\r\r/g
One can use the command
:g/^/pu_
on the whole buffer (by default) or on a selected range of lines.
Select the lines you want with V
Then type : and s/\ze/\r

Macro for making numbered lists in vim?

Often times it seems I have a list of items, and I need to add numbers in front of them. For example:
Item one
Item two
Item three
Which should be:
1. Item one
2. Item two
3. Item three
In vim, I can press I in edit mode, insert "1.", hit escape. Then I go to the next line, press ., and then ^A to increment the number. This seems hugely inefficient... how would I make a macro so that I can go to the next line, and insert a number at the beginning which is one greater than the line before?
You can easily record a macro to do it.
First insert 1. at the start of the first line (there are a couple of spaces after the 1. but you can't see them).
Go to the start of the second line and go into record mode with qa.
Press the following key sequence:
i # insert mode
<ctrl-Y><ctrl-Y><ctrl-Y> # copy the first few characters from the line above
<ESC> # back to normal mode
| # go back to the start of the line
<ctrl-A> # increment the number
j # down to the next line
q # stop recording
Now you can play back the recording with #a (the first time; for subsequent times, you can do ## to repeat the last-executed macro) and it will add a new incremented number to the start of each line.
Select your lines in visual mode with: V, then type:
:'<,'>s/^\s*\zs/\=(line('.') - line("'<")+1).'. '
Which is easy to put in a command:
command! -nargs=0 -range=% Number <line1>,<line2>s/^\s*\zs/\=(line('.') - <line1>+1).'. '
Here's an easy way, without recording a macro:
Make a blockwise, visual selection on the first character of each list item:
^<C-V>2j
Insert a 0. at the beginning of these lines:
I0. <Esc>
Re-select the visual selection (which is now all of the 0s) with gv and increment them as a sequence g<C-A>:
gvg<C-A>
The entire sequence: ^<C-V>2jI0. <Esc>gvg<C-A>.
A recording of the process in action.
There are also some plugins for doing this type of work if you have to do it on occasion:
http://vim.sourceforge.net/scripts/script.php?script_id=670
You can use the 'record' feature.
It is an easy way to record macros in Vim.
See :help record
In normal mode 'qa' to start recording what you type in the 'a' register
Type the necessary command to insert a number at the beginning of line, copy it to next line and use CTRL-A to increase its value.
'q' to end the recording
then '#a' to replay the macro stored in register 'a'
('##' repeat the last macro).
And you can do things like '20#a' to do it twenty times in a row.
It is pretty handy to repeat text modification.
Depending of the cases, it is easier or harder to use than a regexp.
Maybe it's not a macro solution, but at least it's easy.
add numbers to all lines
It's possible to use :%!nl -ba or :%!cat -n commands which will add line numbers to all the lines.
On Windows, you've to have Cygwin/MSYS/SUA installed.
add numbers to selected lines
To add numbers only for selected lines, please select them in visual mode (v and cursors), then when finished - execute the command: :%!nl (ignore blank lines) or :%!cat -n (blank lines included).
formatting
To remove extra spaces, select them in visual block (Ctrl+v) and remove them (x).
To add some characters (., :, )) after the numbers, select them in visual block (Ctrl+v), then append the character (A, type the character, then finish with Esc).
Insert a number at the start of the block of text eg.
1. Item One
Enter the vim normal mode command as follows:
qb^yW+P^<Ctrl-A>q
This means:
qb # start recording macro 'b'
^ # move to start of text on the line
yW # 'yank' or copy a word including the ending whitespace.
+ # move one line down to the start of the next line
P # place text ahead of the cursor
^ # move to start of text
<Ctrl-A> # increment text
q # Finish recording macro
What this allows you to do is replay the macro across the last line of numbered list as many times as needed.
It is some time later and I think it is time to upgrade this answer, at least for neovim users.
Here I wrote a lua function you can bind to Enter and it will work on any imaginable type of list, such as
1. foo
1.99-> bar
and after pressing enter, this line will be added:
1.100->
all using this function
vim.api.nvim_set_keymap('i','<Enter>','v:lua.enter_or_list()', {expr = true})
function _G.enter_or_list()
local line = vim.api.nvim_buf_get_lines(0, vim.fn.line('.') - 1, -1, false)[1]:match('^%s*[^%a%s]+')
if not line then
return '\r'
else
local start, finish = line:find('[^%a%s]*%d')
local main = line:sub(start,finish)
local suffix = line:sub(finish+1)
return table.concat({
'\r',
main,
vim.api.nvim_replace_termcodes('<Esc><C-a>a', true, true, true),
suffix,
' '
})
end
end
for vim users, I have a little simpler, but a little less capable keybinding:
imap <silent> <S-Enter> <CR><Esc>kk<End>Ev<Home>yjpk<End>e<C-a><End>a<Space>
I hope this will be useful to other people as well, as it is very convenient.
Cheers

Resources