Vim Tips Wiki
Register
(Add explanation + remove comments (discussion leading to fixing of tip))
(Need 'set cpo&vim' for '\' line continuations)
Line 43: Line 43:
 
" Search for visually selected text.
 
" Search for visually selected text.
 
" http://vim.wikia.com/wiki/VimTip171
 
" http://vim.wikia.com/wiki/VimTip171
let s:save_cpo = &cpo | set cpoptions&
+
let s:save_cpo = &cpo | set cpo&vim
 
if !exists('g:VeryLiteral')
 
if !exists('g:VeryLiteral')
 
let g:VeryLiteral = 0
 
let g:VeryLiteral = 0
Line 77: Line 77:
 
nmap <unique> <leader>vl <Plug>VLToggle
 
nmap <unique> <leader>vl <Plug>VLToggle
 
endif
 
endif
let &cpoptions = s:save_cpo | unlet s:save_cpo
+
let &cpo = s:save_cpo | unlet s:save_cpo
 
</pre>
 
</pre>
   

Revision as of 00:48, 26 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 a key to search for the next occurrence of the text. Two alternative methods are presented.

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.
  • 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).
  • Each search is placed in the search history allowing you to easily repeat previous searches.
  • No registers are changed.

Place the following mappings in your vimrc:

" Search for selected text, forwards or backwards.
vnoremap <silent> * :<C-U>let old_reg=@"<CR>
  \gvy/<C-R><C-R>=substitute(
  \escape(@", '/\.*$^~[]'), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  \gV:let @"=old_reg<CR>
vnoremap <silent> # :<C-U>let old_reg=@"<CR>
  \gvy?<C-R><C-R>=substitute(
  \escape(@", '?\.*$^~[]'), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  \gV:let @"=old_reg<CR>

Following is an alternative version with some extra features:

  • A global variable (g:VeryLiteral) controls whether selected whitespace matches any whitespace (by default, VeryLiteral is off, so any whitespace is found).
  • Type \vl to toggle VeryLiteral to turn whitespace matching off/on (assuming the default backslash leader key).
  • When VeryLiteral is off, any selected leading or trailing whitespace will not match newlines, which is more convenient, and avoids false search hits.

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 cpo&vim
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
  normal! gV
  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 &cpo = s:save_cpo | unlet s:save_cpo

Explanation

The first suggested mapping was:

vnoremap <silent> * :<C-U>let old_reg=@"<CR>
  \gvy/<C-R><C-R>=substitute(
  \escape(@", '/\.*$^~[]'), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  \gV:let @"=old_reg<CR>

When in visual mode, pressing * will then perform these commands:

:<C-U>let old_reg=@"<CR>
gvy
/<C-R><C-R>=
substitute(
  escape(@", '/\.*$^~[]'),
  '\_s\+',
  '\\_s\\+',
  'g')<CR><CR>
gV
:let @"=old_reg<CR>

:<C-U> enters command mode and deletes (Ctrl-u) the '<,'> range automatically inserted due to the visual selection. The unnamed register (@") is saved and later restored.

gvy reselects then yanks the visual selection (copy to @").

/<C-R><C-R>= starts a search, then substitutes the expression register (@=) literally :help c_CTRL-R_CTRL-R. The result of the following expression is inserted into the command line.

escape() inserts a backslash before each /\.*$^~[] character found in @". The / must be escaped because we are using a / command. The other characters need to be escaped because they have a special meaning in a regular expression.

substitute() replaces every sequence of one or more whitespace characters (space, tab, newline) with an escaped regular expression that will search for any similar sequence.

gV allows the mappings to work in --SELECT-- mode as well as --VISUAL--. Without gV, searching for text in select mode would not move the cursor because the selection is automatically reselected after the mapping.

Comments

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