Vim Tips Wiki
Register
m (Do you know the "g/" and "g?" commands? moved to Search for visually selected text: Page moved by JohnBot to improve title)
(Add a simple, one-liner solution to the problem, to accommodate anyone looking for a hassle-free snippet.)
Tag: Visual edit
(31 intermediate revisions by 10 users not shown)
Line 1: Line 1:
  +
{{TipImported
 
{{review}}
 
{{Tip
 
 
|id=171
 
|id=171
  +
|previous=168
|title=Do you know the "g/" and "g?" commands?
 
  +
|next=172
|created=December 2, 2001 11:11
+
|created=2001
 
|complexity=basic
 
|complexity=basic
|author=Raymond Li (rayli--AT--rayli.net)
+
|author=Raymond Li
|version=5.7
+
|version=6.0
 
|rating=83/28
 
|rating=83/28
  +
|category1=Searching
|text=
 
  +
|category2=
Directly from the Vim Todo list:
 
 
}}
  +
With this tip, you can select some text, then press a key to search for the next occurrence of the text. Two alternative methods are presented.
   
  +
== '''Simple''' ==
  +
The simplest solution is:
  +
vnorem // y/<c-r>"<cr>
   
 
== Advanced ==
  +
The following is a more advanced implementation, with more robust functionality than the above keymap.
   
  +
'''Features'''
7 For Visual mode: Command to do a search for the string in the marked area.
 
  +
*Press <code>*</code> to search forwards for selected text, or <code>#</code> to search backwards.
  +
*As normal, press <code>n</code> for next search, or <code>N</code> 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]]:
Only when less than two lines. Use "g/" and "g?".
 
  +
<pre>
  +
" Search for selected text, forwards or backwards.
  +
vnoremap <silent> * :<C-U>
  +
\let old_reg=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
  +
\gvy/<C-R><C-R>=substitute(
  +
\escape(@", '/\.*$^~['), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  +
\gV:call setreg('"', old_reg, old_regtype)<CR>
  +
vnoremap <silent> # :<C-U>
  +
\let old_reg=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
  +
\gvy?<C-R><C-R>=substitute(
  +
\escape(@", '?\.*$^~['), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  +
\gV:call setreg('"', old_reg, old_regtype)<CR>
  +
</pre>
   
  +
Following is an alternative version with some extra features:
  +
*A global variable (<code>g:VeryLiteral</code>) controls whether selected whitespace matches any whitespace (by default, VeryLiteral is off, so any whitespace is found).
  +
*Type <code>\vl</code> 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) <code>~/.vim/plugin/vsearch.vim</code> (Unix) or <code>$HOME/vimfiles/plugin/vsearch.vim</code> (Windows) with contents:
  +
<pre>
  +
" Search for 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 old_reg = getreg('"')
  +
let old_regtype = getregtype('"')
 
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
  +
call setreg('"', old_reg, old_regtype)
 
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
  +
</pre>
   
  +
==Explanation==
  +
The first suggested mapping was:
  +
<pre>
  +
vnoremap <silent> * :<C-U>
  +
\let old_reg=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
  +
\gvy/<C-R><C-R>=substitute(
  +
\escape(@", '/\.*$^~['), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  +
\gV:call setreg('"', old_reg, old_regtype)<CR>
  +
</pre>
   
  +
When in visual mode, pressing <code>*</code> will then perform these commands:
  +
<pre>
  +
:<C-U>
  +
let old_reg=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
  +
gvy
  +
/<C-R><C-R>=
  +
substitute(
  +
escape(@", '/\.*$^~['),
  +
'\_s\+',
  +
'\\_s\\+',
  +
'g')<CR><CR>
  +
gV
  +
:call setreg('"', old_reg, old_regtype)<CR>
  +
</pre>
   
  +
<code>:<C-U></code> enters command mode and deletes (Ctrl-u) the <code>'<,'></code> range automatically inserted due to the visual selection. The unnamed register (<code>@"</code>) is saved and later restored.
In other words, a way to search for visually selected text !! :-)
 
   
  +
<code>gvy</code> reselects then yanks the visual selection (copy to <code>@"</code>).
   
  +
<code>/<C-R><C-R>=</code> starts a search, then substitutes the expression register (<code>@=</code>) literally {{help|c_CTRL-R_CTRL-R}}. The result of the following expression is inserted into the command line.
   
  +
<code>escape()</code> inserts a backslash before each <code>/\.*$^~[</code> character found in <code>@"</code>. The <code>/</code> must be escaped because we are using a <code>/</code> command. The other characters need to be escaped because they have a special meaning in a regular expression.
"==== vsearch.vim ====
 
   
  +
<code>substitute()</code> replaces every sequence of one or more whitespace characters (space, tab, newline) with an escaped regular expression that will search for any similar sequence.
   
  +
<code>gV</code> allows the mappings to work in <code>--SELECT--</code> mode as well as <code>--VISUAL--</code>. Without <code>gV</code>, searching for text in select mode would not move the cursor because the selection is automatically reselected after the mapping.
   
" Visual mode search
 
   
  +
==Paste matching text of last search==
  +
When using <code>^r/</code> in INSERT mode what one most of the time wants is to paste the matched text not the regex used to search the text. Example: after using * on a word, <code>^r/</code> will paste the word with <code>\<</code> prepended and <code>\></code> appended, not what we want. Similarly after a visual search we don't want the <code>\V</code> prepended. The following map takes care of these issues:
  +
<source lang="vim">
  +
function! Del_word_delims()
  +
let reg = getreg('/')
  +
" After * i^r/ will give me pattern instead of \<pattern\>
  +
let res = substitute(reg, '^\\<\(.*\)\\>$', '\1', '' )
  +
if res != reg
  +
return res
  +
endif
  +
" After * on a selection i^r/ will give me pattern instead of \Vpattern
  +
let res = substitute(reg, '^\\V' , '' , '' )
  +
let res = substitute(res, '\\\\' , '\\', 'g')
  +
let res = substitute(res, '\\n' , '\n', 'g')
  +
return res
  +
endfunction
  +
inoremap <silent> <c-r>/ <c-r>=Del_word_delims()<cr>
  +
cnoremap <c-r>/ <c-r>=Del_word_delims()<cr>
  +
</source>
   
  +
For more complicated patterns, it's better to act on the text matched with the last search, using the {{help|prefix=no|gn}} object.
   
  +
So, you could also accomplish insertion of a search match using <code>maygn`ap</code> in normal mode. I.e. <code>ma</code> to drop a mark to return to later, <code>y</code> to yank the <code>gn</code> object, then <code>`a</code> to jump back where you were (because the yank will leave you on the text copied), finally <code>p</code> to paste.
vmap g/ :call VsearchPatternSave()&lt;cr&gt;/&lt;c-r&gt;/&lt;cr&gt;
 
 
vmap g? :call VsearchPatternSave()&lt;cr&gt;?&lt;c-r&gt;/&lt;cr&gt;
 
 
 
 
function! VsearchPatternSave()
 
 
let l:temp = @@
 
 
normal gvy
 
 
let @/ = substitute(escape(@@, '/'), "\n", "\\\\n", "g")
 
 
let @@ = l:temp
 
 
unlet l:temp
 
 
endfunction
 
 
 
 
"==== END ====
 
 
 
 
 
 
Normally, this file should reside in the plugins directory and be automatically
 
 
sourced. If not, you must manually source this file using ':source vsearch.vim'.
 
 
 
 
In Visual mode, highlight the text for searching. Then you can use the
 
 
default visual key mappings
 
 
 
 
g/ - search forwards
 
 
g? - search backwards
 
 
 
 
Visual searches behave like normal searches. The 'n' and 'N' commands
 
 
work as they should, and the search history correctly records each search.
 
 
Multi-line searches behave as they should (this corrects the 'yank-only'
 
 
method mentioned in the Vim help files). Block visual searches do not work
 
 
yet. Hopefully, someone can figure out a way to do this easily.
 
 
 
 
I've only tested this on Win2000 and Redhat Linux 7.1. I'm not really clear
 
 
on how the carriage returns are dealt with on other systems.
 
 
 
 
Anyway, enjoy!
 
}}
 
 
== Comments ==
 
Here's YAW (Yet Another Way): put the following lines into your &lt;.vimrc&gt;:
 
   
  +
When starting from insert mode, you don't even need a mark: you can use the <code>gi</code> command to start again from where you left off. For example:
vnoremap &lt;silent&gt; g/ y/&lt;C-R&gt;=escape(--AT--", '\\/.*$^~[]')&lt;CR&gt;&lt;CR&gt;
 
vnoremap &lt;silent&gt; g? y?&lt;C-R&gt;=escape(--AT--", '\\/.*$^~[]')&lt;CR&gt;&lt;CR&gt;
 
   
  +
<pre>
Use n and N to repeat searches and gv to restore the previous visual highlighting.
 
  +
:inoremap <F3> <Esc>ygngi<C-R>0
  +
</pre>
   
  +
Here, <code>ygn</code> is as before, but <code>gi</code> is used to go back to insert mode in the same place you left off, then <code><C-R>0</code> inserts the copied text.
   
  +
==See also==
cec--AT--NdjOinnSi.gPsfAc.nMasa.gov
 
  +
* [[Script:2944|visualstar.vim]]
, August 26, 2003 7:59
 
----
 
I changed this to the following so it works with line breaks:
 
   
  +
==Comments==
vmap &lt;silent&gt; g/ y/&lt;C-R&gt;=substitute(escape(--AT--", '\\/.*$^~[]'),'\n','\\n','g')&lt;CR&gt;&lt;CR&gt;
 
  +
{{Todo}}
  +
Tips related to visual searching (need to merge):
  +
*[[VimTip1011|1011 Mappings and commands for visual mode]]
  +
*[[VimTip1151|1151 Search visually]]
   
  +
This mapping forms a substitute command with the selected text:
  +
<pre>
  +
vnoremap <C-r> "hy:%s/<C-r>h//gc<left><left><left>
  +
</pre>
  +
Found on [http://stackoverflow.com/questions/676600/vim-replace-selected-text Stack Overflow: Vim replace selected text].
   
  +
To auto escape certain characters (e.g. slash and backslash), one can use:
'''Anonymous'''
 
  +
<pre>
, September 26, 2006 8:50
 
  +
vnoremap <C-h> ""y:%s/<C-R>=escape(@", '/\')<CR>//g<Left><Left>
----
 
  +
</pre>
<!-- parsed by vimtips.py in 0.279957 seconds-->
 

Revision as of 20:02, 6 June 2014

Tip 171 Printable Monobook Previous Next

created 2001 · complexity basic · author Raymond Li · version 6.0


With this tip, you can select some text, then press a key to search for the next occurrence of the text. Two alternative methods are presented.

Simple

The simplest solution is:

vnorem // y/<c-r>"<cr>

Advanced

The following is a more advanced implementation, with more robust functionality than the above keymap.

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=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
  \gvy/<C-R><C-R>=substitute(
  \escape(@", '/\.*$^~['), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  \gV:call setreg('"', old_reg, old_regtype)<CR>
vnoremap <silent> # :<C-U>
  \let old_reg=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
  \gvy?<C-R><C-R>=substitute(
  \escape(@", '?\.*$^~['), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  \gV:call setreg('"', old_reg, old_regtype)<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 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 old_reg = getreg('"')
  let old_regtype = getregtype('"')
  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
  call setreg('"', old_reg, old_regtype)
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=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
  \gvy/<C-R><C-R>=substitute(
  \escape(@", '/\.*$^~['), '\_s\+', '\\_s\\+', 'g')<CR><CR>
  \gV:call setreg('"', old_reg, old_regtype)<CR>

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

:<C-U>
let old_reg=getreg('"')<Bar>let old_regtype=getregtype('"')<CR>
gvy
/<C-R><C-R>=
substitute(
  escape(@", '/\.*$^~['),
  '\_s\+',
  '\\_s\\+',
  'g')<CR><CR>
gV
:call setreg('"', old_reg, old_regtype)<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.


Paste matching text of last search

When using ^r/ in INSERT mode what one most of the time wants is to paste the matched text not the regex used to search the text. Example: after using * on a word, ^r/ will paste the word with \< prepended and \> appended, not what we want. Similarly after a visual search we don't want the \V prepended. The following map takes care of these issues:

function! Del_word_delims()
   let reg = getreg('/')
   " After *                i^r/ will give me pattern instead of \<pattern\>
   let res = substitute(reg, '^\\<\(.*\)\\>$', '\1', '' )
   if res != reg
      return res
   endif
   " After * on a selection i^r/ will give me pattern instead of \Vpattern
   let res = substitute(reg, '^\\V'          , ''  , '' )
   let res = substitute(res, '\\\\'          , '\\', 'g')
   let res = substitute(res, '\\n'           , '\n', 'g')
   return res
endfunction
inoremap <silent> <c-r>/ <c-r>=Del_word_delims()<cr>
cnoremap          <c-r>/ <c-r>=Del_word_delims()<cr>

For more complicated patterns, it's better to act on the text matched with the last search, using the gn object.

So, you could also accomplish insertion of a search match using maygn`ap in normal mode. I.e. ma to drop a mark to return to later, y to yank the gn object, then `a to jump back where you were (because the yank will leave you on the text copied), finally p to paste.

When starting from insert mode, you don't even need a mark: you can use the gi command to start again from where you left off. For example:

:inoremap <F3> <Esc>ygngi<C-R>0

Here, ygn is as before, but gi is used to go back to insert mode in the same place you left off, then <C-R>0 inserts the copied text.

See also

Comments

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

This mapping forms a substitute command with the selected text:

vnoremap <C-r> "hy:%s/<C-r>h//gc<left><left><left>

Found on Stack Overflow: Vim replace selected text.

To auto escape certain characters (e.g. slash and backslash), one can use:

vnoremap <C-h> ""y:%s/<C-R>=escape(@", '/\')<CR>//g<Left><Left>