Skip to content

Instantly share code, notes, and snippets.

@yongrenjie
Last active April 19, 2020 10:05
Show Gist options
  • Save yongrenjie/6c003ade95128048eaa5d387937ad839 to your computer and use it in GitHub Desktop.
Save yongrenjie/6c003ade95128048eaa5d387937ad839 to your computer and use it in GitHub Desktop.

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.

Basic usage of :execute

: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.

Basic usage of :normal

: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! versus :normal

: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.

Using special characters with :normal!

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:

  1. 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 as Esc.

  2. Use :execute {expr-string}. Because expr-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.
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment