Vim: How to handle newlines when storing multiple commands in registers?
I have a file where I store snippets of vim commands. When I need a snippet, I yank it and then execute it with @"
. The snippets are stored as a script, one line per command, like this:
:s/foo/bar/g
:echo "hello"
:s/1/2/g
Edit: I removed normal mode commands from the example, as they were not part of the problem.
Now this procedure doesn't work anymore: when executing the snippet, it just stops at the first line as if waiting for a newline.
Is there an option somewhere affecting how @
is executed? I'm pretty sure it was working some time ago...
Substituting the newline with a ^M character works but makes the file more difficult to handle.
Additional information:
Here's another symptom: when I yank a snippet, if I execute it with @"
it stops at the first line as I just explained. But if I execute it with :@
it works. But the help file doesn't seem to imply any difference in how开发者_开发问答 the two commands treat the register's content...
I don't think the problem is ^M
vs. ^J
. Vim macros will treat either one as a valid end-of-line character for recorded macros. I think the problem is extra newlines.
In your example, there's at least one spurious newline after 2j
, and unless you're particularly careful when copying the snippet, there's probably another one after 10k
as well. These extra newlines are like pressing <Enter>
in Normal mode -- they move the cursor down one line.
Here's what I think you want the snippet to look like:
:s/foo/bar/g
2j:s/1/2/g
10k
(Even that's a little misleading -- you'd still have to be careful not to copy the newline after the 10k
.)
Why do these extra newlines make such a big difference? Well, for one thing, they cause you to be at least one line away from where you expect to be, which throws off anything you want to do on a particular line (like execute the :s//
command).
More importantly, however -- and this is what I think is happening in your example -- is that Vim stops macro playback if the macro attempts to use <Enter>
on the last line of a buffer. (I'm guessing Vim considers it an error, and any error causes a macro to stop running.)
Here's an example. Suppose you've got this snippet stored in register x:
4j
:echo "Done"
(Notice the newline after 4j
.)
Furthermore, suppose you have the following five lines (and only these five lines) in a buffer:
line 1
line 2
line 3
line 4
line 5
If you now press @x
on line 1
, the :echo "Done"
never executes. Vim moves the cursor down 4 lines to line 5
, then attempts to move down one more line because of the extra newline, but it can't. The macro stops executing at that point, before the :echo
command gets a chance to run.
However, it works if you change the x register to this:
4j:echo "Done"
So to return to your original example, I'll bet what's happening is that the extra newline after 2j
is attempting to move your cursor somewhere it can't go, and that causes the macro to stop. The bottom line of the screen contains the last command executed (:s/foo/bar/g
), which makes it look like Vim is waiting for you to press Return.
Finally, I'd strongly recommend using another method to store and execute Vim command sequences. The technique you're using is tolerable for simple cases, but it's fragile and doesn't scale well. Vim has a full scripting language that includes functions and custom commands, and it can be used to do all the things you're doing now, but in a much more robust fashion. Vim scripting is a big topic, but I'd start here:
:help script
Be sure to read about the :normal
command, which lets you execute Normal-mode commands (like 2j
and 10k
) within scripts.
Good luck!
I finally found the culprit. Somehow I had a command mapping on <C-J>
in my .vimrc file. When read with the default cpoptions
, this turned into a mapping on <NL>
.
How I found out: I noticed that when starting vim with -u ~/.vimrc
, it would indeed execute yanked snippets. I generated a session file with and without that commandline option and compared them. This way I found out that a different set of cpoptions
where used to read the same .vimrc file, so that in one case the mapping was indeed on <C-J>
, in the other it was converted into a mapping on <NL>
!
If someone has a similar problem, I suggest to look carefully at the currently set command mappings, with :cmap
.
精彩评论