1

I refer to those 4 terms using terminology picked up by various sources:

  • Steve Losh, in his Learn Vimscript the Hard Way's chapter 16 refers to things like <cr> (these 4 characters typed in a Vim script) as "special characters";
  • as regards things like \<esc> (these 6 characters typed in a Vim script), he calls them string escape sequences in chapter 30; in the title of my question I'm using the term escaped special characters because \<esc> looks a lot like (is?) an escaped <esc>, and because...
  • Steve Losh also refers to \r (the 2 characters typed in a Vim script) as an escape sequence (also this in chapter 6);
  • Vim documentation, in :help i_CTRL-V, refers to stuff like ^[, 1 indivisible entity (visually represented as 2 characters) that can be obtained via Ctrl-VEscape, as a terminal code.

I'd like to consolidate my understanding of when each of those 4 ways of inserting special characters should be used, because I think I'm a bit confused on the matter.

Below is what I've understood so far.


Chapter 6 has this (valid) line:

:onoremap ih :<c-u>execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<cr> 

My understanding of :*map commands is that their {rhs} argument is truly the sequence of keystrokes that are "automatically hit" by Vim (right after the {lhs} has been typed) from whatever mode(s) the specific :*map is defined on.

Therefore, if I want the mapping to do the same action as when I press, say, Ctrl-U (resp. Enter), I have to type <c-u> (resp. <cr>) in the {rhs}.

I can further note that if I had made a typo, e.g. typing <ccr> instead of <cr> in the above operator mode mapping, then pressing cih in normal mode would result in Vim populating the command line with

:execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<ccr>

without pressing enter, which means that Vim has not tried to interpret <ccr> as a special character (otherwise it would have failed in doing so), and simply inserted the 5 characters in sequence.

However, I can't help but notice that my claim that

{rhs} argument is truly the sequence of keystrokes

is not totally correct, because if the typo I made was to write <|cr> instead of <cr> the effect would not be that the command line is populated with

:execute "normal! ?^==\\+$\r:nohlsearch\rkvg_"<|cr>

but an error. Only if the typo was <\|cr>, would the line be populated that way

So it looks like the {rhs} of a :*map is not exactly the keystrokes that Vim does, because of two aspects

  • some sequences (such as <cr> or <c-u>) are special, in the sense that when Vim sees them, it interprets them as a single keystroke of the key they name (Enter or Ctrl-U respectively);
  • some characters, such as | or \, which would normally have a special behavior, need to be escaped via \.

One thing which I'm pretty sure about, is that writing special characters like <esc>, <cr>, or <c-u> in Vim script has the same effect as if pressing those keys after Ctrl-V, i.e. pressing Ctrl-VEscape, Ctrl-VEnter, and Ctrl-VCtrl-U respectively, thus obtaining the single-sequences ^[, ^M, and ^U.

Going back to the onoremap command above, my understanding of :execute is that it takes a string and executes it, so it's clear to me that I can't put <cr> or Enter/^M in it, because that would mean that Vim presses Enter right after the $, i.e. it would try to enter the following ex command, which is obviously erroneous (because it lacks a closing ", beside being incomplete):

:execute "normal! ?^==\\+$

Instead, I have to use sequences like \r (for <cr>/Enter) and \e (for <esc>/Escape). And since \ is the escape character, I need to escape it too, to get a literal \, just like in \\+. (This probably also tells me that | has a special meaning in ex command line, because I needed to escape it, if I wanted it to appear literally in the command line. Probably the fact is that | is to separate commands, as explained at :help :bar.)

Then, in chapter 30, Steve Losh presents this:

:execute "normal! mqA;\<esc>`q"

where I can deduce that <esc> is meant to mean that Escape is pressed during the execution of normal!, but it needs to be escaped for :execute to understand that it is a special character within the double quoted string. Therefore, I reason, I could write \e instad of \<esc>:

:execute "normal! mqA;\e`q"

and indeed that works too.

However, while this

:onoremap ih :<c-u>execute "normal! mqA;\e`q"

is valid, this

:onoremap ih :<c-u>execute "normal! mqA;\<esc>`q"

isn't.

1 Answer 1

0

All substitutions are applied in series, one after another, from beginning to end of current command argument. They don't respect "nested command rules" and such.

So you've got "map" that substitutes all angle brackets until end of line (or "bar" if you have any); then "execute" that tries to substitute "backslashes" while no angle brackets left anymore; then one "normal" more, etc.

So this is where quantity transforms into quality. Not a topic for beginners, really. You'll need some experience to be able to apply all three transformations inside your brain and not to break it in the process. Otherwise, it is all just "a simple string substitution". Well, three of them.

2
  • Without the last paragraph I think this is a fine answer.
    – D. Ben Knoble
    Apr 10, 2022 at 16:42
  • @D.BenKnoble I don't like giving fine andswers. This is too dull.
    – Matt
    Apr 10, 2022 at 17:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.