2

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?

Victoria Stuart
  • 4,610
  • 2
  • 44
  • 37
  • 1
    Although it does not address the issue raised in my question (above), this answer (https://stackoverflow.com/a/12469028/1904943) provides a solution: `:g/^/norm yyp`. As I am trying to duplicate each line in a file, with a blank line after each duplicated line, this works: `:g/^/norm yypo`. Explanation: `g`: look for lines that match the pattern (here beginning of lines: `^`); `norm` executes the command `yypo` (yank-paste-append blank line). You can also apply that command over selected lines: `:'<,'>:g/^/norm yypo`. – Victoria Stuart Jul 22 '19 at 23:01
  • 1
    You might want to check out the [Vi and Vim Stack Exchange](https://vi.stackexchange.com/) for questions on Vim! – filbranden Jul 23 '19 at 04:53

1 Answers1

5

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.

bk2204
  • 64,793
  • 6
  • 84
  • 100