regexvimneovim

Capture groups not working in substitute?


I want to use regex capture groups with :substitute in Neovim but they don't seem to be working.

For example, I have this text:

- myKey: myValue

I want it to be:

- myKey: something-myValue

I enter the following:

:s/(myKey:\s*)(.*)/$1something-$2/

I get:

E486: Pattern not found: (myKey:\s*)(.*)

enter image description here

The same pattern works without using capture groups:

enter image description here

How do I enable capture groups in :substitute in Neovim? Additionally, how do I reference numbered capture groups in Neovim? Some programs use $1, $2, etc., some use \1, \2, etc.


Solution

  • Different regular expressions dialects have different syntaxes. The one used by Vim (and Neovim) is described in whole under :help pattern.

    Parentheses are not considered as special characters out of the box, so your pattern:

    (myKey:\s*)(.*)
    

    will literally match something like this:

    (myKey:      )(dfsdfsidrtysdis)
    

    match

    but not something like that:

    - myKey: myValue
    

    which is pretty much the opposite of what you want.

    To give them special meaning, you must escape them with a backslash:

    \(myKey:\s*\)\(.*\)
    

    Or you can use \v (for "verymagic") at the beginning of your pattern to tell Vim to parse the pattern with a "verymagic" syntax:

    \v(myKey:\s*)(.*)
    

    See :help /magic, :help \(, and :help \v, all under the aforementioned :help pattern.

    Note that using \v systematically, even to the point of mapping / to /\v and so on, is quite tempting at first (and common) but it becomes counterproductive very quickly if you work with code so I would advise against that.

    The replacement part of a substitution is not itself a regular expression pattern so it is documented elsewhere, under :help sub-replace-special, in the larger :help change. There, some characters have a special meaning. To reuse a capture group, use \1, \2, etc.

    Your command should thus be either:

    :s/\(myKey:\s*\)\(.*\)/\1something-\2/
    

    or:

    :s/\v(myKey:\s*)(.*)/\1something-\2/
    

    Note that Vim's regular expressions dialect contains unique atoms that can make your life easier. In this case, :help \zs and :help s/\& would make the pattern and the replacement simpler and shorter:

    :s/myKey:\s*\zs.*/something-&/
    

    where:

    In this specific case, myKey can even be ommited:

    :s/:\s*\zs.*/something-&/
    

    The regular version, with all the escaping is fine but it is noisy, and thus error-prone. It is also the longest to type.

    The \v version may be a little bit better than the regular version because it is more readable and thus less error-prone, and slightly shorter. But it still has the capture group overhead cost.

    The \zs version is better than both because it is even clearer, and thus less error-prone, while also being considerably shorter. And there is no capture group overhead anymore.

    This overview of regular expressions in Vim is old but still relevant.