Vim Tips Wiki
Register
(Include Bill McCarthy version)
(Replace tip with new version by Bill McCarthy)
Line 6: Line 6:
 
|complexity=basic
 
|complexity=basic
 
|author=Raymond Li
 
|author=Raymond Li
|version=5.7
+
|version=6.0
 
|rating=83/28
 
|rating=83/28
 
|category1=Searching
 
|category1=Searching
 
|category2=
 
|category2=
 
}}
 
}}
  +
With this tip, you can visually select some text then press <tt>*</tt> to search forwards for the next occurrence of the text, or press <tt>#</tt> to search backwards.
With the following, you can use <tt>*</tt> (or <tt>#</tt>) to search forwards (or backwards) for the current visual selection from either characterwise visual mode or linewise visual mode (but not from blockwise visual mode). These visual searches behave like any other searches; the 'n' and 'N' commands work as they should, and the search history correctly records each search. This solution works for all characters, and even for searches that span multiple lines (that is, if you select "a" at the end of one line and "b" at the beginning of the next, we'll only find other lines that end in "a" and have "b" as the first character on the next line).
 
  +
 
'''Features'''
  +
*Press <tt>*</tt> to search forwards for selected text, or <tt>#</tt> to search backwards.
  +
*As normal, press <tt>n</tt> for next search, or <tt>N</tt> for previous.
  +
*Handles multiline selection and search.
 
*By default, whitespace in the selection matches ''any'' whitespace when searching (searching for "hello world" will also find "hello" at the end of a line, with "world" at the start of the next line).
 
*Type <tt>\vl</tt> to toggle VeryLiteral to turn whitespace matching off/on (assuming the default backslash leader key).
  +
*Each search is placed in the search history allowing you to easily repeat previous searches.
  +
*No registers are changed.
   
  +
Create file (for example) ~/.vim/plugin/vsearch.vim (Unix) or $HOME/vimfiles/plugin/vsearch.vim (Windows) with contents:
 
<pre>
 
<pre>
 
" Search for visually selected text.
" vsearch.vim
 
  +
" http://vim.wikia.com/wiki/VimTip171
" Visual mode search
 
 
let s:save_cpo = &cpo | set cpoptions&
function! s:VSetSearch()
 
 
if !exists('g:VeryLiteral')
 
let g:VeryLiteral = 0
 
endif
  +
 
function! s:VSetSearch(cmd)
 
let temp = @@
 
let temp = @@
norm! gvy
+
normal! gvy
let @/ = '\V' . substitute(escape(@@, '\'), '\n', '\\n', 'g')
+
if @@ =~? '^[0-9a-z,_]*$' || @@ =~? '^[0-9a-z ,_]*$' && g:VeryLiteral
 
let @/ = @@
  +
else
 
let pat = escape(@@, a:cmd.'\')
  +
if g:VeryLiteral
  +
let pat = substitute(pat, '\n', '\\n', 'g')
  +
else
  +
let pat = substitute(pat, '^\_s\+', '\\s\\+', '')
  +
let pat = substitute(pat, '\_s\+$', '\\s\\*', '')
 
let pat = substitute(pat, '\_s\+', '\\_s\\+', 'g')
 
endif
  +
let @/ = '\V'.pat
 
endif
 
let @@ = temp
 
let @@ = temp
 
endfunction
 
endfunction
   
vnoremap * :<C-u>call <SID>VSetSearch()<CR>//<CR>
+
vnoremap <silent> * :<C-U>call <SID>VSetSearch('/')<CR>/<C-R>/<CR>
vnoremap # :<C-u>call <SID>VSetSearch()<CR>??<CR>
+
vnoremap <silent> # :<C-U>call <SID>VSetSearch('?')<CR>?<C-R>/<CR>
</pre>
 
 
This code first defines a function, only accessible inside the script it's defined in ({{help|id=script-variable|label=s:}}), which first backs up the unnamed register ({{help|expr-register}}) so it we can restore it, then yanks the visual selection into the unnamed register ({{help|:norm}} {{help|gv}} {{help|y}}). Then, it takes that string and escapes all <tt>\</tt> to <tt>\\</tt>, and replaces every newline with a pattern that matches newlines ({{help|escape()}} {{help|substitute()}}), and finally stores the resulting string to the search pattern register ({{help|registers}}), prepended with <tt>\V</tt> to turn off the special meanings of all characters but <tt>\</tt> (which we already escaped all instance of) ({{help|/\V}}). Finally, the function restores the unnamed register to the value it had when we started. The visual map for <tt>*</tt> presses <tt>:</tt> in visual mode, then <C-u> in command line mode to remove the '<,'> that : inserts in visual mode ({{help|c_CTRL-u}}). Then, it calls the function to set up the search pattern register ({{help|:call}} {{help|id=<SID>|label=<SID>}}), and then searches forwards for the next instance of the pattern ({{help|/<CR>}}). The visual map for <tt>#</tt> does the same, but searches backwards instead ({{help|?<CR>}}).
 
 
You can either put this code into a file in your <tt>plugins</tt> directory (make sure the filename ends in '.vim') or include it directly in your [[vimrc]]. To make the <tt>*</tt> key on the numeric keypad also trigger this mapping, you can add this line:
 
<pre>
 
 
vmap <kMultiply> *
 
vmap <kMultiply> *
  +
 
nmap <silent> <Plug>VLToggle :let g:VeryLiteral = !g:VeryLiteral
 
\\| echo "VeryLiteral " . (g:VeryLiteral ? "On" : "Off")<CR>
 
if !hasmapto("<Plug>VLToggle")
 
nmap <unique> <leader>vl <Plug>VLToggle
  +
endif
 
let &cpoptions = s:save_cpo | unlet s:save_cpo
 
</pre>
 
</pre>
   
Line 38: Line 66:
 
Tips related to visual searching (need to merge):
 
Tips related to visual searching (need to merge):
 
*171 Search for visually selected text ''(this tip)''
 
*171 Search for visually selected text ''(this tip)''
<!-- *[[VimTip340|340 Visual select and search]] -->
 
<!-- *[[VimTip777|777 More words searching]] -->
 
 
*[[VimTip780|780 Generalized VISUAL CONTENT onto COMMAND-LINE]]
 
*[[VimTip780|780 Generalized VISUAL CONTENT onto COMMAND-LINE]]
 
*[[VimTip1011|1011 Mappings and commands for visual mode]]
 
*[[VimTip1011|1011 Mappings and commands for visual mode]]
<!-- *[[VimTip1038|1038 Search for selected text]] -->
 
 
*[[VimTip1151|1151 Search visually]]
 
*[[VimTip1151|1151 Search visually]]
  +
*Have the following been used? Should they be deleted?
<!-- *[[VimTip1387|1387 Standard editing shortcuts]] (contains info that should be here) -->
 
 
**[[VimTip340|340 Visual select and search]]
 
**[[VimTip777|777 More words searching]]
 
**[[VimTip1038|1038 Search for selected text]]
 
**[[VimTip1387|1387 Standard editing shortcuts]] (contains info that should be here)
   
 
----
 
----
'''See current comments after following box (the archived conversation in the box can be deleted soon).'''
+
'''See current comments after the following box (the archived conversation in the box can be deleted soon).'''
   
 
<div style="background:#FFEEDD; margin-top:0.5em; padding:0 10px 0 10px; border:1px solid #888888;">
 
<div style="background:#FFEEDD; margin-top:0.5em; padding:0 10px 0 10px; border:1px solid #888888;">
Line 145: Line 174:
 
</div>
 
</div>
   
  +
'''The following box contains the version of the tip from 13 July by godlygeek.'''
I like the following version by Bill McCarthy from vim_use mailing list. Please check it and add any comments at the end. --[[User:JohnBeckett|JohnBeckett]] 05:33, 18 August 2008 (UTC)
 
   
  +
<div style="background:#FFEEDD; margin-top:0.5em; padding:0 10px 0 10px; border:1px solid #888888;">
Features:
 
  +
<big>'''''Please don't change text in this box.'''''</big>
*Visually select text then press <tt>*</tt> (or <tt>#</tt>) to search forwards (or backwards).
 
*Default: Whitespace in selection matches ''any'' whitespace when searching (searching for "hello world" will find "hello" at end of a line with "world" at start of next line).
 
*Type <tt>\dv</tt> to toggle VeryLiteral to turn whitespace matching off/on (does "dv" have some meaning??).
 
   
 
With the following, you can use <tt>*</tt> (or <tt>#</tt>) to search forwards (or backwards) for the current visual selection from either characterwise visual mode or linewise visual mode (but not from blockwise visual mode). These visual searches behave like any other searches; the 'n' and 'N' commands work as they should, and the search history correctly records each search. This solution works for all characters, and even for searches that span multiple lines (that is, if you select "a" at the end of one line and "b" at the beginning of the next, we'll only find other lines that end in "a" and have "b" as the first character on the next line).
<pre>
 
" Search for visually selected text
 
let s:save_cpo = &cpo | set cpoptions&
 
if !exists('g:VeryLiteral')
 
let g:VeryLiteral = 0
 
endif
 
   
 
<pre>
function! s:VSetSearch(cmd)
 
 
" vsearch.vim
 
" Visual mode search
 
function! s:VSetSearch()
 
let temp = @@
 
let temp = @@
normal! gvy
+
norm! gvy
if @@ =~? '^[0-9a-z,_]*$' || @@ =~? '^[0-9a-z ,_]*$' && g:VeryLiteral
+
let @/ = '\V' . substitute(escape(@@, '\'), '\n', '\\n', 'g')
let @/ = @@
 
else | if g:VeryLiteral | let s1 = '\n' | let s2 = '\\n'
 
else | let s1 = '\_s\+' | let s2 = '\\_s\\+' | endif
 
let @/='\V'.substitute(escape(@@,a:cmd.'\'),s1,s2,'g')
 
endif
 
 
let @@ = temp
 
let @@ = temp
 
endfunction
 
endfunction
   
vnoremap <silent> * :<C-U>call <SID>VSetSearch('/')<CR>/<C-R>/<CR>
+
vnoremap * :<C-u>call <SID>VSetSearch()<CR>//<CR>
vnoremap <silent> # :<C-U>call <SID>VSetSearch('?')<CR>?<C-R>/<CR>
+
vnoremap # :<C-u>call <SID>VSetSearch()<CR>??<CR>
 
</pre>
vmap <kMultiply> *
 
   
 
This code first defines a function, only accessible inside the script it's defined in ({{help|id=script-variable|label=s:}}), which first backs up the unnamed register ({{help|expr-register}}) so it we can restore it, then yanks the visual selection into the unnamed register ({{help|:norm}} {{help|gv}} {{help|y}}). Then, it takes that string and escapes all <tt>\</tt> to <tt>\\</tt>, and replaces every newline with a pattern that matches newlines ({{help|escape()}} {{help|substitute()}}), and finally stores the resulting string to the search pattern register ({{help|registers}}), prepended with <tt>\V</tt> to turn off the special meanings of all characters but <tt>\</tt> (which we already escaped all instance of) ({{help|/\V}}). Finally, the function restores the unnamed register to the value it had when we started. The visual map for <tt>*</tt> presses <tt>:</tt> in visual mode, then <C-u> in command line mode to remove the '<,'> that : inserts in visual mode ({{help|c_CTRL-u}}). Then, it calls the function to set up the search pattern register ({{help|:call}} {{help|id=<SID>|label=<SID>}}), and then searches forwards for the next instance of the pattern ({{help|/<CR>}}). The visual map for <tt>#</tt> does the same, but searches backwards instead ({{help|?<CR>}}).
nmap <silent> <Plug>VLToggle :let g:VeryLiteral = !g:VeryLiteral
 
  +
\\| echo "VeryLiteral " . (g:VeryLiteral ? "On" : "Off")<CR>
 
 
You can either put this code into a file in your <tt>plugins</tt> directory (make sure the filename ends in '.vim') or include it directly in your [[vimrc]]. To make the <tt>*</tt> key on the numeric keypad also trigger this mapping, you can add this line:
if !hasmapto("<Plug>VLToggle")
 
 
<pre>
nmap <unique> <leader>dv <Plug>VLToggle
 
 
vmap <kMultiply> *
endif
 
let &cpoptions = s:save_cpo | unlet s:save_cpo
 
 
</pre>
 
</pre>
  +
</div>
  +
  +
I have replaced the body of the tip with a version by Bill McCarthy from the vim_use mailing list (2008-08-19 15:43). I have edited the code in an attempt to clarify its function. I hope I haven't changed its meaning (except I changed the mapping to toggle VeryLiteral from '\dv' to '\vl'). The new code is quite a bit more complex, but I think that is justified by its functionality.
  +
  +
I need to add some explanations to the tip, but first let's decide what version to use, and finalise it.
  +
 
Please check the current tip and add any comments below. --[[User:JohnBeckett|JohnBeckett]] 11:03, 19 August 2008 (UTC)
  +
  +
----

Revision as of 11:03, 19 August 2008

Tip 171 Printable Monobook Previous Next

created December 2, 2001 · complexity basic · author Raymond Li · version 6.0


With this tip, you can visually select some text then press * to search forwards for the next occurrence of the text, or press # to search backwards.

Features

  • Press * to search forwards for selected text, or # to search backwards.
  • As normal, press n for next search, or N for previous.
  • Handles multiline selection and search.
  • By default, whitespace in the selection matches any whitespace when searching (searching for "hello world" will also find "hello" at the end of a line, with "world" at the start of the next line).
  • Type \vl to toggle VeryLiteral to turn whitespace matching off/on (assuming the default backslash leader key).
  • Each search is placed in the search history allowing you to easily repeat previous searches.
  • No registers are changed.

Create file (for example) ~/.vim/plugin/vsearch.vim (Unix) or $HOME/vimfiles/plugin/vsearch.vim (Windows) with contents:

" Search for visually selected text.
" http://vim.wikia.com/wiki/VimTip171
let s:save_cpo = &cpo | set cpoptions&
if !exists('g:VeryLiteral')
  let g:VeryLiteral = 0
endif

function! s:VSetSearch(cmd)
  let temp = @@
  normal! gvy
  if @@ =~? '^[0-9a-z,_]*$' || @@ =~? '^[0-9a-z ,_]*$' && g:VeryLiteral
    let @/ = @@
  else
    let pat = escape(@@, a:cmd.'\')
    if g:VeryLiteral
      let pat = substitute(pat, '\n', '\\n', 'g')
    else
      let pat = substitute(pat, '^\_s\+', '\\s\\+', '')
      let pat = substitute(pat, '\_s\+$', '\\s\\*', '')
      let pat = substitute(pat, '\_s\+', '\\_s\\+', 'g')
    endif
    let @/ = '\V'.pat
  endif
  let @@ = temp
endfunction

vnoremap <silent> * :<C-U>call <SID>VSetSearch('/')<CR>/<C-R>/<CR>
vnoremap <silent> # :<C-U>call <SID>VSetSearch('?')<CR>?<C-R>/<CR>
vmap <kMultiply> *

nmap <silent> <Plug>VLToggle :let g:VeryLiteral = !g:VeryLiteral
  \\| echo "VeryLiteral " . (g:VeryLiteral ? "On" : "Off")<CR>
if !hasmapto("<Plug>VLToggle")
  nmap <unique> <leader>vl <Plug>VLToggle
endif
let &cpoptions = s:save_cpo | unlet s:save_cpo

Comments

 TO DO 
Tips related to visual searching (need to merge):


See current comments after the following box (the archived conversation in the box can be deleted soon).

Please don't change text in this box.
Add any additional comments below the box (if wanted, copy anything relevant from here to your comment).

One thing I don't like about the tip is that using it to search for something fails to put that something in the search history. Therefore, I can't press / followed by some up arrows to find what I searched for a few minutes ago.

Some approaches do not use \V but prefer to escape all the magic characters (I think '\.*$^~[]'). I like the \V (seems more robust), but I wanted to avoid having the '\V' in search history for simple searches.

I think the following fixes these points. I'm going to try this for a while and will, if ok, use this to replace the tip. Please add any comments below.

function! s:VSetSearch()
  let temp = @@
  normal! gvy
  if @@ =~# '^[0-9A-Za-z ,_]*$'
    let @/ = @@  " keep simple cases simple in search history
  else
    " Escape both slash and backslash because we're using @/ as a
    " temp variable that will be inserted into a '/' command.
    let @/ = '\V' . substitute(escape(@@, '/\'), '\n', '\\n', 'g')
  endif
  let @@ = temp
endfunction
vnoremap * :<C-u>call <SID>VSetSearch()<CR>/<C-r>/<CR>
vnoremap # :<C-u>call <SID>VSetSearch()<CR>?<C-r>/<CR>
vmap <kMultiply> *

Definitely not ok. That breaks pretty easily; try doing # after selecting "a?b" and you'll see that only "a" was searched for.

This works a little better:

function! s:VSetSearch()
  let temp = @@
  norm! gvy
  if @@ =~# '^[0-9A-Za-z ,_]*$'
    let @/ = @@
  else
    let @/ = '\V' . substitute(escape(@@, '\'), '\n', '\\n', 'g')
  endif
  let @@ = temp
  call histadd('/', @/)
endfunction

vnoremap * :<C-u>call <SID>VSetSearch()<CR>//<CR>
vnoremap # :<C-u>call <SID>VSetSearch()<CR>??<CR>

but it still has some quirks with handling / and ?... Consider, for example, selecting "a?b", pressing * (searches for "a?b"), pressing ?<Up><CR> (uses search "\Va\?b"; so matches "a?b"), then pressing /<Up><CR> (searches for "\Va\?b"; and \? behaves differently in a / search than a ? search - it now matches either "ab" or "b").

Changing the histadd() call to the following seems to work, but it yields an uglier pattern...

call histadd('/', substitute(@/, '[?/]', '\="\\%d".char2nr(submatch(0))', 'g'))

With that, we when searching on "a?b" or "a/b" we insert into the search history /\Va\%d63b/ and /\Va\%d47b/, respectively, which matches correctly but isn't terribly readable.

Any thoughts, John?


Thanks for pointing out the problem. Sorry, but somehow I missed seeing your edit, so my reply is delayed.

When I tried your new solution, I find that selecting 'something' and pressing * puts two items in the search history: 'something' (or '\Vsomething') and '//'. I don't like the resulting confusion, for example, when using /<Up> to locate the item-before-last that I searched for.

The char2nr code is sensational, but as you say, the search history is too unreadable for my preference.

There are some quirks I wouldn't worry about, for example, Vim itself has a quirk:

/a?b<CR>    (search forwards for 'a?b' -- good)
?<Up><CR>   (search backwards for 'a\?b' -- good)
/<Up><CR>   (search forwards for 'a\?b' -- bad, finds 'b' or 'ab')

So, here is what I an currently trying:

function! s:VSetSearch(cmd)
  let temp = @@
  normal! gvy
  if @@ =~# '^[0-9A-Za-z ,_]*$'
    let @/ = @@  " keep simple cases simple in search history
  else
    " Escape both cmd and backslash because we're using @/ as a
    " temp variable that will be inserted into a '/' or '?' command.
    let @/ = '\V' . substitute(escape(@@, a:cmd.'\'), '\n', '\\n', 'g')
  endif
  let @@ = temp
endfunction
vnoremap * :<C-u>call <SID>VSetSearch('/')<CR>/<C-r>/<CR>
vnoremap # :<C-u>call <SID>VSetSearch('?')<CR>?<C-r>/<CR>
vmap <kMultiply> *

Please tell me what you think. --JohnBeckett 09:11, 16 August 2008 (UTC)

The following box contains the version of the tip from 13 July by godlygeek.

Please don't change text in this box.

With the following, you can use * (or #) to search forwards (or backwards) for the current visual selection from either characterwise visual mode or linewise visual mode (but not from blockwise visual mode). These visual searches behave like any other searches; the 'n' and 'N' commands work as they should, and the search history correctly records each search. This solution works for all characters, and even for searches that span multiple lines (that is, if you select "a" at the end of one line and "b" at the beginning of the next, we'll only find other lines that end in "a" and have "b" as the first character on the next line).

" vsearch.vim
" Visual mode search
function! s:VSetSearch()
  let temp = @@
  norm! gvy
  let @/ = '\V' . substitute(escape(@@, '\'), '\n', '\\n', 'g')
  let @@ = temp
endfunction

vnoremap * :<C-u>call <SID>VSetSearch()<CR>//<CR>
vnoremap # :<C-u>call <SID>VSetSearch()<CR>??<CR>

This code first defines a function, only accessible inside the script it's defined in (:help s:), which first backs up the unnamed register (:help expr-register) so it we can restore it, then yanks the visual selection into the unnamed register (:help :norm :help gv :help y). Then, it takes that string and escapes all \ to \\, and replaces every newline with a pattern that matches newlines (:help escape() :help substitute()), and finally stores the resulting string to the search pattern register (:help registers), prepended with \V to turn off the special meanings of all characters but \ (which we already escaped all instance of) (:help /\V). Finally, the function restores the unnamed register to the value it had when we started. The visual map for * presses : in visual mode, then <C-u> in command line mode to remove the '<,'> that : inserts in visual mode (:help c_CTRL-u). Then, it calls the function to set up the search pattern register (:help :call :help <SID>), and then searches forwards for the next instance of the pattern (:help /<CR>). The visual map for # does the same, but searches backwards instead (:help ?<CR>).

You can either put this code into a file in your plugins directory (make sure the filename ends in '.vim') or include it directly in your vimrc. To make the * key on the numeric keypad also trigger this mapping, you can add this line:

vmap <kMultiply> *

I have replaced the body of the tip with a version by Bill McCarthy from the vim_use mailing list (2008-08-19 15:43). I have edited the code in an attempt to clarify its function. I hope I haven't changed its meaning (except I changed the mapping to toggle VeryLiteral from '\dv' to '\vl'). The new code is quite a bit more complex, but I think that is justified by its functionality.

I need to add some explanations to the tip, but first let's decide what version to use, and finalise it.

Please check the current tip and add any comments below. --JohnBeckett 11:03, 19 August 2008 (UTC)