sponsor Vim development Vim logo Vim Book Ad

intermediate Tip #780: Generalized VISUAL CONTENT onto COMMAND-LINE

 tip karma   Rating 68/20, Viewed by 1017 

Read and edit this tip on the Vim tip wiki. The wiki may have a more recent version of this tip.

created:   September 3, 2004 19:41      complexity:   intermediate
author:   Maxwell Pincer      as of Vim:   6.0

Key idea:  Yank the visual area for use on the command-line.

Key use:  searches, complex substitutions, etc.

Refinement:  Make sure all magic characters are escaped.
(Thanks to vimtip #777 for this.)
Focus on the content of the visual -- independent
of how it was formatted.

Usage:  visual on the area, and hit the TAB key.

DISCUSSION:  imagine searching for this line in your text:
"The price of foobars is $1.89 [see page 7]."

Typing it out literally will not work:

     :/The price of foobars is $1.89 [see page 7]./

The problem is the magic characters.  Escaping them will make it work:

     :/The price of foobars is \$1\.89 \[see page 7\]\./

We really don't have to type it out, just use yank and the " register.
And let vim automatically figure out how to properly escape...

Now suppose elsewhere in the text we have this:
"The price of foobars
is $1.89 [see page 7]."

Will our previous command work?  No.   It's MULTI-LINE.
Here's what would work:
     :/The\_s\+price\_s\+of\_s\+foobars\_s\+is\_s\+\$1\.89\_s\+\[see\_s\+page\_s\+7\]\./

That would work even on this:
"The price
of foobars is     $1.89
[see page 7]."

That's independence from formatting!
The secret is the substitution function using whitespace \_s\+
and the 'one or more' operator.

The CODE which magically puts the discussion
into action (three versions):

"               Yank the " register onto the command-line.
"X- vmap <C-I>  y:/<C-R>"/
"                       ^ plain version without escaping out of magic characters.
"X- vmap <C-I>  y:/<C-R>=escape(@", '\/.*$^~[]')<CR>/
"                        ^ now more fancy.
vmap     <C-I>  y:/<C-R>=substitute(escape(@", '\/.*$^~[]'), "[ \t\n]\\+", "\\\\_s\\\\+", "g")<CR>/
"                                    visual ^ HIGHLIGHTED text placed on the COMMAND LINE.
"                      Input <CR> to perform a SEARCH-,
"                      otherwise EDIT the COMMAND-LINE containing the yanked text.
"                 Function ESCAPE to prevent magic characters.
"                 4-tuple function SUBSTITUTE will generalize white spaces & EOL,
"                                  ^ so that string will be independent of formatting!
"  USAGE:  visual on desired area, and simply hit TAB.
"          To search, follow up by carriage return.
"          Else, you may modify the command line to suit your purpose.

 rate this tip  Life Changing Helpful Unfulfilling 

<< OEM to ANSI conversion | Changing the hotkey for "&Diff with Vim" >>

Additional Notes

Anonymous, September 4, 2004 8:08
Finally!   I have been trying to figure this out
for 2 years.   Thanks very much for adding to
visual mode toolset.   Those functions are gems
in combination with vmap = visual mapping.

For a review, see

:help visual.txt
:help vmap

Now I can search for meaningful context without
worrying about how the text was shaped.
Even the period appearing in generic sentences
was a minor annoyance -- now solved with 'escape'.
mosh @ albany, September 7, 2004 17:06
Awesome, I had been asking for this since vim57 something like:

:set whitemagic=1

and all white spaces are treated identically as [\s\n]\+ inside regexp!

I thought of modifying the regexp engine to do it for everything
including search and replace.

- Mohsin.
Breadman, September 9, 2004 15:27
Combining this with another tip or two by Michael Naumann and Jürgen Krämer, I now have:

" Visually select text, then search for it, forwards or backwards
vmap <silent> * :<C-U>let old_reg=@"<cr>
      \gvy/<C-R><C-R>=substitute(
      \escape(@", '\\/.*$^~[]'), "[ \t\n]\\+", '\\_s\\+', 'g')<CR><CR>
      \:let @"=old_reg<cr>
vmap <silent> # :<C-U>let old_reg=@"<cr>
      \gvy?<C-R><C-R>=substitute(
      \escape(@", '\\?.*$^~[]'), "[ \t\n]\\+", '\\_s\\+', 'g')<CR><CR>
      \:let @"=old_reg<cr>
salmanhalim@hotmail.com, February 25, 2006 22:37
I use the following:

vmap <silent> * "yy:let @/='\(' . substitute( escape( @y, '$*^[]~\/.' ), '\_s\+', '\\_s\\+', 'g' ) . '\)'<cr>:set hls<cr>

The only thing this one does that seems to be an improvement on the example above (rather than simply being different) is the substitution for whitespace:  '\_s\+' instead of '[ \t\n]\+'.

The differences between this and the example above:

- This doesn't move the cursor; it just highlights matches, allowing n to move forward and N to move back.  (I like that it doesn't move the cursor.  I usually just want to SEE the matches, not actually move to them.)

- It clobbers @y mercilessly.  I don't use it elsewhere and haven't yet had any reason to try to save/restore it -- of course, it would be trivial to do so.

- If you don't use 'hlsearch', simply remove the last bit that sets it (simply setting @/ doesn't turn on the highlight back if :nohls was called before; this does...)
If you have questions or remarks about this site, visit the vimonline development pages. Please use this site responsibly.
Questions about Vim should go to the maillist. Help Bram help Uganda.
   
Sponsored by Web Concept Group Inc. SourceForge.net Logo