How to match using regular expression in matchstr function of vim script? - vim

I'm trying to understand how VIM uses 'pattern' argument to 'matchstr' function.
I tried creating a pattern that matches either 'a' or 'b' but I'm unable to do this.
Here is what I tried:
:echo matchstr("ab", "a|b")
:echo matchstr("ab", "a\|b")
:echo matchstr("ab", "(a|b)")
:echo matchstr("ab", "(a|b)")
:echo matchstr("ab", "(a\|b)")
Note: 'set magic?' shows 'magic'

Vim uses a regex dialect, in which by default you need to escape special letters, if you need their regex feature. E.g. for OR you need to write \| and not like in perl regexes | This applies e.g. to the multi atom + and the OR atom |. (This can be changed by the regex atom \v which provides a more perl like regex dialect, see :h /\v)
Now you are using double quotes in your expression. When using double quotes, Vim will parse special characters and therefore remove one backslash, before the regex engine even sees them. Therefore, you need to either double your backslashes or use single quotes. This is explained at :h expr-quote

Related

What does the + sign mean in vim patterns when used to quote a pattern?

The vim help example gives this line for a syntax statement:
:syntax region String start=+"+ skip=+\\"+ end=+"+
Is the + sign here an alternative to quoting? I couldn't find a reference to this in the help pages on patterns.
An introduction to patterns, see :h pattern.txt. As in the example of chapter 10 (:h :match), :match MyGroup /TODO/, instead of // any character can be used to mark the start and end of the pattern.
Like your question (:h syntax.txt), this is explained in Chapter 8 (:h :syn-pattern) :
In the syntax commands, a pattern must be surrounded by two identical characters. This is like it works for the ":s" command. The most common to use is the double quote. But if the pattern contains a double quote, you can use another character that is not used in the pattern. Examples: :syntax region String start=+"+ end=+"+ skip=+\\"+
Just like the :s command, see :h :s and :h pattern-delimiter, the benefit and convenience is the handling of escape characters. Using the :s command as an example is more convenient for you to verify quickly.
If you want to replace a with b, you can use :s/a/b/ or :s+a+b+. But if you want to replace / with //, using delimiter / require :s/\//\/\//, we can change the delimiter / to + (:s+\/+\/\/+). Actually, here we no longer need to escape /, so in the end there is only a concise :s+/+//+.
Back to your question, if the pattern contains a lot of double quotes, we can use another character that is not used in the pattern as delimiter (e.g. +), otherwise each double quote in the pattern needs to be escaped.
That sample command in your question isn't provided without context. It is provided as an illustration for the paragraph right above it:
In the syntax commands, a pattern must be surrounded by two identical
characters. This is like it works for the ":s" command. The most common to
use is the double quote. But if the pattern contains a double quote, you can
use another character that is not used in the pattern.

vim: How to search for a hard-coded string (not regex)?

In order to search for a string in Vim, I click "/" and then type the word that I have to search. Vim looks at this string as regular expression. I want to know how to search a string, as it it, and not treat it as a regex.
Search commands always search for patterns (also known as regular expressions). You can make patterns more or less magic but cannot turn metacharacters completely off. If you have a fixed string you have to escape the characters that vim understands as metacharacters.
With the very nomagic mode of Vim's regular expressions (:help /\V), only the backslash is a special character that needs escaping.
So, prepend \V to your literal search, and (either manually or via escape(pattern, '\')) duplicate any backslashes. The following turns a "regular" search in to a literal one; you could define a mapping for that:
:let #/ = '\V' . escape(#/, '\')

How to search for a string containing a regex in Vim?

Is it possible to search a string that is a regex, without escaping all the fancy characters?
For example, I want to find this string in my source file: ^[\d\| *]$, without escaping \, $, etc.
I would like to just copy and paste the regex and get the result.
What you want is a grep that searches for matching strings, rather than attempting to match a regular expression. With GNU grep, you can invoke the command with the
-F or --fixed-strings flags, or just invoke the command as fgrep instead. The following are all equivalent:
grep -F '^[\d\| *]$'
grep --fixed-strings '^[\d\| *]$'
fgrep '^[\d\| *]$'
Fixed-string searches are exactly what you need when you want to match code that represents a regular expression, or when you want a faster grep that doesn't need the advanced matching capability of a regular expression engine.
There is an easy way to avoid special treatment of the characters
in Vim regular expressions. The \V specifier allows to switch
interpretation of the rest of the pattern to the “very nomagic” mode,
which means that every character but the backslash is understood
literally.
Therefore, one can set the last search register accordingly:
:let #/ = '\V' . escape('^[\d\| *]$', '\')
and use n and N for searching immediately.
You can use the command line tool fgrep (“fast grep”),
Supposing you have yanked the regexp to search for to the default register (#"), you can do this:
/\V<C-R><C-R>=escape(#", '/\')<CR><CR>
The \V starts a "very nomagic" search, where only atoms starting with a backslash have special, non-literal meaning. The escape() renders all those contained backslashes ineffective (and escapes / which would otherwise end the search pattern), so that this is a purely literal search. The text is inserted via Ctrl+R= into the search command line.

vim regexps from the perl's point of view: which special characters to escape with backslash

Imagine, we have to construct a regexp in vi/vim. Which special characters we have to escape with backslash?
By special characters I mean the following chars: {}|()-[]+*.^$?
Seems like we have to escape: {|()+?
And leave as is: }^$*.[]-
Thanks.
p.s. AFAIK, we have no '?' character in vi/vim but '=' instead which should be also escaped by backslash.
I think the Vim documentation on magic characters will give you a definitive list.
In vim:
:help magic
I think the simplest way, that will work regardless of vim's magic setting, is
let re = '\V' . escape(fixed_string, '\')
\V disables magic entirely from that point onward in the RE. This means that anything not preceded by a single backslash (well, technically by an odd number of backslashes) is a normal character. Since we escape any backslashes in the fixed string, there can be no magic characters contained in it.
Remember that with \V in effect, you have to backslash-prefix any RE meta-characters. So if you're looking for a line that entirely consists of the fixed string, you would
let full_line_re = '\V\^' . escape(fixed_string, '\') . '\$'
Also keep in mind that vim's ignorecase and smartcase settings will affect RE behaviour. The \C (case-sensitive) and \c (case-insensitive) switches work the same way as \V: they will override those settings for the part of any regexp which follows them.

Exact string match in vim? (Like 'regex-off' mode in less.)

In vim, I often want to search on a string with finicky characters which need escaping. Is there a way I can turn off the meaning of all special characters, kind of like regex-off mode in less, or fgrep?
I am dealing with particularly hairy strings; here's an example:
((N/N)/(N/N))/N
Not having to escape any characters to do a search in vim would be a major timesaver.
\V in Vim helps with some metacharacters, but critically not / or \.
Thanks all! In the end, I added this to my .vimrc:
command! -nargs=1 S let #/ = escape('<args>', '\')
nmap <Leader>S :execute(":S " . input('Regex-off: /'))<CR>
Depending on the exact string you're searching, the \V prefix will probably do the trick.
See :help \V:
after: \v \m \M \V matches ~
'magic' 'nomagic'
$ $ $ \$ matches end-of-line
. . \. \. matches any character
* * \* \* any number of the previous atom
() \(\) \(\) \(\) grouping into an atom
| \| \| \| separating alternatives
\a \a \a \a alphabetic character
\\ \\ \\ \\ literal backslash
\. \. . . literal dot
\{ { { { literal '{'
a a a a literal 'a'
So if I have a string hello.*$world, I can use the command /\V.*$ to find just .*$ -- the only part of the string that should need escaping is another backslash, but you can still do grouping, etc., by escaping the special symbol.
Another command you can use to "avoid" forward slashes is:
:g #\V((N/N)/(N/N))/N#
The :g command is a global search, noting that:
:[range]g[lobal]/{pattern}/[cmd]
Execute the Ex command [cmd] (default ":p") on the
lines within [range] where {pattern} matches.
Instead of the '/' which surrounds the {pattern}, you can use any other
single byte character, but not an alphanumeric character, '\', '"' or '|'.
This is useful if you want to include a '/' in the search pattern or
replacement string.
So where I used a #, you could use a ?, #, or whatever other character meeting the above condition. The catch with that :g command is that it expects a command at the end, so if you do not have a trailing space after the final character, it won't perform the search as you would expect. And again, even though you're using \V, you'll still have to escape backslashes.
If that still doesn't cut it for you, this Nabble post has a suggestion that takes a literal string with embedded backslashes and other special Vim characters, and claims to search for it without a problem; but it requires creating a Vim function, which may or may not be okay in your environment.
Looking at your specific example, probably the easiest way to do this (since \V won't help here) is to use ? instead of /: then you won't have to escape the /s:
?((N/N)/(N/N))/N
This will search backwards instead of forwards, but you can always do 'N' instead of 'n' to search forwards after the first go. Or you can press / followed by the up cursor key to automatically escape the forward slashes.
However, there's nothing you can easily do about escaping backslashes. I guess you could do:
:let #/ = escape(pattern, '\')
and then use n to search, but it's probably not much easier. The best I can come up with is:
:command! -nargs=1 S let #/ = escape('<args>', '\')
Then do:
:S (N)/(N+1)\(N)
n
Maybe something like this:
nmap <Leader>s :execute '/\V' . escape(input('/'), '\\/')<CR>
It'll give you a / prompt and behave otherwise like the built-in search, but it won't do search-as-you-type or other such goodies.

Resources