Just a bunch of things I'm learning about :execute
and :normal
in Vim.
Some of this is picked up from https://learnvimscriptthehardway.stevelosh.com/chapters/16.html but mostly it's learnt by reading Vim's help and experimentation.
:execute {expr-string}
essentially does some parsing on {expr-string}
(see :h expr-string
), then runs it as an Ex command, i.e. it does what would happen if you were to type in :{parsed-expr-string}
at the Vim command line.
For example, :execute "write"
does the same as :write
, i.e. saves the file. Note that the string "write"
has to be in quotes, otherwise Vim thinks it's a variable. Of course, this does mean that you can set a variable to be equal to "write
, viz.:
:let mystring = "write"
:execute mystring
Multiple {expr-strings}
will be concatenated with a space between them, so for example you can do:
:let fname = "foo.bar"
:execute "write" fname
which saves the file with the name foo.bar
. If you don't want the space that joins them, then put a period between the {expr-strings}
:
:let silly = "te"
:execute "wri" . silly
saves your file, because "wri"
and "te"
are concatenated directly without a space between them.
:normal {commands}
executes {commands} as if they were typed at the keyboard in Normal mode. For example, :normal gg
brings you to the top of the document; and :normal ddp
moves the current line one line down.
The above examples are trivial when actually editing files (you would just type the commands), but they are useful in functions or scripts. Some more complicated examples can also be useful when editing, e.g. because they can be repeated and edited from Command-line mode.
:normal!
ignores mappings. For example, if you run :map dd x
(i.e. when you press dd
it actually deletes one character), then run :normal ddp
, Vim will interchange the current character with the next, as if you had pressed xp
.
However, running :normal! ddp
switches the lines as one would expect. Having the exclamation mark is therefore useful to make sure that scripts run in the same way on different setups.
If we want to search forwards to the next occurrence of foo
, the usual keystrokes (in Normal mode) would be /foo<CR>
where <CR>
is literally a keypress of the carriage return, or Enter, key.
However, typing :normal! /foo<CR>
does not work in the desired way! It turns out that :normal!
interprets <CR>
as sequential key-presses of left-bracket, C, R, and right-bracket. There are a couple of ways around this:
-
Press
<C-V><CR>
or<C-V><C-M>
to enter a special (non-printable) character which appears as a greyed-out^M
in the command line::normal! /foo^M
works correctly.<C-M>
is the same thing as<Enter>
, much like how<C-[>
is the same asEsc
. -
Use
:execute {expr-string}
. Becauseexpr-string
is parsed before actually being executed, there are certain escape sequences which allow non-printable characters to be typed with printable characters. In this case, the non-printable^M
can be written using either\r
or\<CR>
(the backslash in the second is necessary; see:h expr-string
). Of course, you could just use the non-printable character directly if you so wished, instead of letting:execute
parse something else into it. So any of the following will work::execute "normal /foo\r" :execute "normal /foo\<CR>" :execute "normal /foo^M" <-- typed using <C-V><C-M> or <C-V><CR>, as before.