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

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.

Related

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 match using regular expression in matchstr function of vim script?

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

An easy way to center text between first and last non-white word in vim?

Is there an easy way using a macro or ~10 line function (no plugin!) to center some text between the first and last word (=sequence of non-blank characters) on a line? E.g. to turn
>>> No user serviceable parts below. <<<
into
>>> No user serviceable parts below. <<<
by balancing the spaces +/-1? You can assume no tabs and the result should not contain tabs, but note that the first word may not start in column 1. (EDIT: ... in fact, both delimiter words as well as the start and end of the text to center may be on arbitrary columns.)
source this function:
fun! CenterInSpaces()
let l = getline('.')
let lre = '\v^\s*\S+\zs\s*\ze'
let rre = '\v\zs\s*\ze\S+\s*$'
let sp = matchstr(l,lre)
let sp = sp.matchstr(l,rre)
let ln = len(sp)
let l = substitute(l,lre,sp[:ln/2-1],'')
let l = substitute(l,rre,sp[ln/2:],'')
call setline('.',l)
endf
note
this function might NOT work in all cases. I just wrote it quick for usual case. this is not a plugin after all
the codes lines could be reduced by combining function calls. but i think it is clear in this way, so I just leave it like this.
if it worked for you, you could create a map
it works like this: (last two lines I typed #: to repeat cmd call)
You can use the :s command with the \= aka sub-replace-expression.
:s#\v^\s*\S+\zs(\s+)(.{-})(\s+)\ze\S+\s*$#\=substitute(submatch(1).submatch(3),'\v^(\s*)(\1\s=)$','\1'.escape(submatch(2),'~&\').'\2','')#
Overview
Capture the text (including white-space) between the >>> and <<< marks. Divide up the white-space on both sides of the text in half and substitute in the non-white-space text in between. This white-space balancing act is done via the regex engine's backtracking because math is hard. Lets go shopping!
Notes:
using \v or very magic mode to reduce escaping as this command is long enough
already
use # as an alternative separator instead of the usual / for :s/pat/sub/ in hopes to make it slightly more readable
Matching Pattern
:s#\v^\s*\S+\zs(\s+)(.{-})(\s+)\ze\S+\s*$#...
:s with no range supplied only do the substitution on the current line.
^\s*\S+ match the starting white-space followed by non-white-space. >>> in this case.
(\s+)(.{-})(\s+) match white-space followed by the "text" followed by white-space
3 capture groups: 1) leading white-space, 2) the "text", and 3) trailing white-space. These will be later referenced by submatch(1), submatch(2), and submatch(3) respectively
.{-} is vim-speak for non-greedy matching or .*? in perl-speak
without the non-greedy matching the second capture group would include too much white-space at its end
\S+\s*$ match the non-white-space (i.e. <<<) and any trailing white-space
Use \zs and ze to designate the start and end of the match to be replaced
Replacement
\=substitute(submatch(1).submatch(3),'\v^(\s*)(\1\s=)$','\1'.escape(submatch(2),'~&\').'\2','')
\= tells vim that replacement will be a vim expression. Also allows the use of submatch() functions
substitute({str}, {pat}, {sub}, {flags}) Our expression will be a nested substitution
substitute(submatch(1).submatch(3), ...) do a substitute over the concatenation of leading and trailing white-spacing captured in submatch(1) and submatch(3)
The {pat} is ^(\s*)(\1\s=)$. Match some white-space followed by white-space of the same length as the first or 1 character longer. Capture both halves.
escape(submatch(2),'~&\') escape submatch(2) for any special characters. e.g. ~,&,\1, ...
The {sub} is '\1'.escape(submatch(2),'~&\').'\2'. Replace with the the escaped submatch(2) (i.e. the "text" we want to center) in between the halves of white-space, \1 and \2 from the {pat}
No {flag}'s are needed so ''
Usage
If you use this often I would suggest creating a command and putting it in ~/.vimrc.
command! -range -bar -nargs=0 CenterBetween <line1>,<line2>s#\v^\s*\S+\zs(\s+)(.{-})(\s+)\ze\S+\s*$#\=substitute(submatch(1).submatch(3),'\v^(\s*)(\1\s=)$','\1'.submatch(2).'\2','')#`
Otherwise use this once and then repeat the last substitution via & on each needed line.
For more help see
:h :s/
:h :s/\=
:h sub-replace-\=
:h submatch(
:h substitute(
:h escape(
:h /\v
:h /\S
:h /\{-
:h /\zs
:h &
EDIT by Kent
Don't be jealous, your answer has it too. ^_^
I didn't change the command, just cp/paste to my vim. only add |noh at the end to disable highlighting.
If execute this command, it looks like:
I don't know of any good way. I usually do it in a semi-automatic way, by using :center on a line of text that only contains the parts that are to be centered and then move the result into the line containing the surrounding parts.
If nobody else has a better answer, perhaps boxes can help if you need to do this kind of thing a lot.

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.

Why is \r a newline for Vim?

From question How to replace a character for a newline in Vim?. You have to use \r when replacing text for a newline, like this
:%s/%/\r/g
But when replacing end of lines and newlines for a character, you can do it like:
:%s/\n/%/g
What section of the manual documents these behaviors, and what's the reasoning behind them?
From http://vim.wikia.com/wiki/Search_and_replace :
When Searching
...
\n is newline, \r is CR (carriage return = Ctrl-M = ^M)
When Replacing
...
\r is newline, \n is a null byte (0x00).
From vim docs on patterns:
\r matches <CR>
\n matches an end-of-line -
When matching in a string instead of
buffer text a literal newline
character is matched.
Another aspect to this is that \0, which is traditionally NULL, is taken in
s//\0/ to mean "the whole matched pattern". (Which, by the way, is redundant with, and longer than, &).
So you can't use \0 to mean NULL, so you use \n
So you can't use \n to mean \n, so you use \r.
So you can't use \r to mean \r, but I don't know who would want to add that char on purpose.
—☈
:help NL-used-for-Nul
Technical detail:
<Nul> characters in the file are stored as <NL> in memory. In the display
they are shown as "^#". The translation is done when reading and writing
files. To match a <Nul> with a search pattern you can just enter CTRL-# or
"CTRL-V 000". This is probably just what you expect. Internally the
character is replaced with a <NL> in the search pattern. What is unusual is
that typing CTRL-V CTRL-J also inserts a <NL>, thus also searches for a <Nul>
in the file. {Vi cannot handle <Nul> characters in the file at all}
First of all, open :h :s to see the section "4.2 Substitute" of documentation on "Change". Here's what the command accepts:
:[range]s[ubstitute]/{pattern}/{string}/[flags] [count]
Notice the description about pattern and string
For the {pattern} see |pattern|.
{string} can be a literal string, or something
special; see |sub-replace-special|.
So now you know that the search pattern and replacement patterns follow different rules.
If you follow the link to |pattern|, it takes you to the section that explains the whole regexp patterns used in Vim.
Meanwhile, |sub-replace-special| takes you to the subsection of "4.2 Substitute", which contains the patterns for substitution, among which is \r for line break/split.
(The shortcut to this part of manual is :h :s%)

Resources