" Copyright 2015-present Greg Hurrell. All rights reserved. " Licensed under the terms of the BSD 2-clause license. "" " @plugin ferret Ferret plug-in for Vim " " # Intro " " > "ferret (verb)
" > (ferret something out) search tenaciously for and find something: she had " > the ability to ferret out the facts." " " *ferret-features* " Ferret improves Vim's multi-file search in four ways: " " ## 1. Powerful multi-file search " " Ferret provides an |:Ack| command for searching across multiple files using " The Silver Searcher (https://github.com/ggreer/the_silver_searcher), Ack " (http://beyondgrep.com/), or Grep (http://www.gnu.org/software/grep/). Support " for passing options through to the underlying search command exists, along " with the ability to use full regular expression syntax without doing special " escaping. " " Shortcut mappings are provided to start an |:Ack| search (a) or to " search for the word currently under the cursor (s). " " Results are normally displayed in the |quickfix| window, but Ferret also " provides a |:Lack| command that behaves like |:Ack| but uses the " |location-list| instead, and a l mapping as a shortcut to |:Lack|. " " Finally, Ferret offers integration with dispatch.vim " (https://github.com/tpope/vim-dispatch), which enables asynchronous searching " despite the fact that Vim itself is single-threaded. " " ## 2. Streamlined multi-file replace " " The companion to |:Ack| is |:Acks| (mnemonic: "Ack substitute", accessible via " shortcut r), which allows you to run a multi-file replace across all " the files placed in the |quickfix| window by a previous invocation of |:Ack|. " " ## 3. Quickfix listing enhancements " " The |quickfix| listing itself is enhanced with settings to improve its " usability, and natural mappings that allow quick removal of items from the " list (for example, you can reduce clutter in the listing by removing lines " that you don't intend to make changes to). " " Additionally, Vim's |:cn|, |:cp|, |:cnf| and |:cpf| commands are tweaked to " make it easier to immediately identify matches by centering them within the " viewport. " " ## 4. Easy operations on files in the quickfix listing " " Finally, Ferret provides a |:Qargs| command that puts the files currently in " the |quickfix| listing into the |:args| list, where they can be operated on in " bulk via the |:argdo| command. This is what's used under the covers by |:Acks| " to do its work. " " " # Installation " " To install Ferret, use your plug-in management system of choice. " " If you don't have a "plug-in management system of choice", I recommend " Pathogen (https://github.com/tpope/vim-pathogen) due to its simplicity and " robustness. Assuming that you have Pathogen installed and configured, and that " you want to install Ferret into `~/.vim/bundle`, you can do so with: " " ``` " git clone https://github.com/wincent/ferret.git ~/.vim/bundle/ferret " ``` " " Alternatively, if you use a Git submodule for each Vim plug-in, you could do " the following after `cd`-ing into the top-level of your Git superproject: " " ``` " git submodule add https://github.com/wincent/ferret.git ~/vim/bundle/ferret " git submodule init " ``` " " To generate help tags under Pathogen, you can do so from inside Vim with: " " ``` " :call pathogen#helptags() " ``` " " @mappings " " ## Circumstances where mappings do not get set up " " Note that Ferret will not try to set up the mappings if any of the " following are true: " " - A mapping for already exists. " - An alternative mapping for the same functionality has already been set up " from a |.vimrc|. " - The mapping has been suppressed by setting |g:FerretMap| to 1 in your " |.vimrc|. " " ## Mappings specific to the quickfix window " " Additionally, Ferret will set up special mappings in |quickfix| listings, " unless prevented from doing so by |g:FerretQFMap|: " " - `d` (|visual-mode|): delete visual selection " - `dd` (|Normal-mode|): delete current line " - `d`{motion} (|Normal-mode|): delete range indicated by {motion} " " " @footer " " # Custom autocommands " " *FerretWillWrite* *FerretDidWrite* " For maximum compatibility with other plug-ins, Ferret runs the following " "User" autocommands before and after running the file writing operations " during |:Acks|: " " - FerretWillWrite " - FerretDidWrite " " For example, to call a pair of custom functions in response to these events, " you might do: " " ``` " autocmd! User FerretWillWrite " autocmd User FerretWillWrite call CustomWillWrite() " autocmd! User FerretDidWrite " autocmd User FerretDidWrite call CustomDidWrite() " ``` " " " # Overrides " " Ferret overrides the 'grepformat' and 'grepprg' settings, preferentially " setting `ag`, `ack` or `grep` as the 'grepprg' (in that order) and configuring " a suitable 'grepformat'. " " Additionally, Ferret includes an |ftplugin| for the |quickfix| listing that " adjusts a number of settings to improve the usability of search results. " " @indent " *ferret-nolist* " 'nolist' " " Turned off to reduce visual clutter in the search results, and because " 'list' is most useful in files that are being actively edited, which is not " the case for |quickfix| results. " " *ferret-norelativenumber* " 'norelativenumber' " " Turned off, because it is more useful to have a sense of absolute progress " through the results list than to have the ability to jump to nearby results " (especially seeing as the most common operations are moving to the next or " previous file, which are both handled nicely by |:cnf| and |:cpf| " respectively). " " *ferret-nowrap* " 'nowrap' " " Turned off to avoid ugly wrapping that makes the results list hard to read, " and because in search results, the most relevant information is the " filename, which is on the left and is usually visible even without wrapping. " " *ferret-number* " 'number' " " Turned on to give a sense of absolute progress through the results. " " *ferret-scrolloff* " 'scrolloff' " " Set to 0 because the |quickfix| listing is usually small by default, so " trying to keep the current line away from the edge of the viewpoint is " futile; by definition it is usually near the edge. " " *ferret-nocursorline* " 'nocursorline' " " Turned off to reduce visual clutter. " " @dedent " " To prevent any of these |quickfix|-specific overrides from being set up, you " can set |g:FerretQFOptions| to 0 in your |.vimrc|: " " ``` " let g:FerretQFOptions=0 " ``` " " " # Troubleshooting " " *ferret-quotes* " ## Ferret fails to find patterns containing spaces " " As described in the documentation for |:Ack|, the search pattern is passed " through as-is to the underlying search command, and no escaping is required " other than preceding spaces by a single backslash. " " So, to find "foo bar", you would search like: " " ``` " :Ack foo\ bar " ``` " " Unescaped spaces in the search are treated as argument separators, so a " command like the following means pass the `-w` option through, search for " pattern "foo", and limit search to the "bar" directory: " " ``` " :Ack -w foo bar " ``` " " Note that including quotes will not do what you intend. " " ``` " " Search for '"foo' in the 'bar"' directory: " :Ack "foo bar" " " " Search for "'foo' in the "bar'" directory: " :Ack 'foo bar' " ``` " " This approach to escaping is taken in order to make it straightfoward to use " powerful Perl-compatible regular expression syntax in an unambiguous way " without having to worry about shell escaping rules: " " ``` " :Ack \blog\((['"]).*?\1\) -i --ignore-dir=src/vendor src dist build " ``` " " " # FAQ " " ## Why do Ferret commands start with "Ack", "Lack" and so on? " " Ferret was originally the thinnest of wrappers (7 lines of code in my " |.vimrc|) around `ack`. The earliest traces of it can be seen in the initial " commit to my dotfiles repo in May, 2009 (https://wt.pe/h). " " So, even though Ferret has a new name now and actually prefers `ag` over `ack` " when available, I prefer to keep the command names intact and benefit from " years of accumulated muscle-memory. " " " " # Related " " Just as Ferret aims to improve the multi-file search and replace experience, " Loupe does the same for within-file searching: " " https://github.com/wincent/loupe " " " # Website " " The official Ferret source code repo is at: " " http://git.wincent.com/ferret.git " " A mirror exists at: " " https://github.com/wincent/ferret " " Official releases are listed at: " " http://www.vim.org/scripts/script.php?script_id=5220 " " " # License " " Copyright 2015-present Greg Hurrell. All rights reserved. " " Redistribution and use in source and binary forms, with or without " modification, are permitted provided that the following conditions are met: " " 1. Redistributions of source code must retain the above copyright notice, " this list of conditions and the following disclaimer. " " 2. Redistributions in binary form must reproduce the above copyright notice, " this list of conditions and the following disclaimer in the documentation " and/or other materials provided with the distribution. " " THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" " AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE " IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE " ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE " LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR " CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF " SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS " INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN " CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) " ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE " POSSIBILITY OF SUCH DAMAGE. " " " # Development " " ## Contributing patches " " Patches can be sent via mail to greg@hurrell.net, or as GitHub pull requests " at: https://github.com/wincent/ferret/pulls " " ## Cutting a new release " " At the moment the release process is manual: " " - Perform final sanity checks and manual testing " - Update the |ferret-history| section of the documentation " - Verify clean work tree: " " ``` " git status " ``` " " - Tag the release: " " ``` " git tag -s -m "$VERSION release" $VERSION " ``` " " - Publish the code: " " ``` " git push origin master --follow-tags " git push github master --follow-tags " ``` " " - Produce the release archive: " " ``` " git archive -o ferret-$VERSION.zip HEAD -- . " ``` " " - Upload to http://www.vim.org/scripts/script.php?script_id=5220 " " " # Authors " " Ferret is written and maintained by Greg Hurrell . " " The idea for vim-dispatch integration was taken from Miles Sterrett's ack.vim " plug-in (https://github.com/mileszs/ack.vim). " " Other contributors that have submitted patches include (in alphabetical " order): " " - Daniel Silva " - Joe Lencioni " - Nelo-Thara Wallus " - Vaibhav Sagar " " " # History " " ## 1.2a (16 May 2016) " " - Add optional support for running searches asynchronously using Vim's |+job| " feature (enabled by default in sufficiently recent versions of Vim); see " |g:FerretJob|, |:FerretCancelAsync| and |:FerretPullAsync|. " " ## 1.1.1 (7 March 2016) " " - Fix another edge case when searching for patterns containing "#", only " manifesting under dispatch.vim. " " ## 1.1 (7 March 2016) " " - Fix edge case when searching for strings of the form "". " - Fix edge case when searching for patterns containing "#" and "%". " - Provide completion for `ag` and `ack` options when using |:Ack| and |:Lack|. " - Fix display of error messages under dispatch.vim. " " ## 1.0 (28 December 2015) " " - Fix broken |:Qargs| command (patch from Daniel Silva). " - Add |g:FerretQFHandler| and |g:FerretLLHandler| options (patch from Daniel " Silva). " - Make || mappings accessible even |g:FerretMap| is set to 0. " - Fix failure to report filename when using `ack` and explicitly scoping " search to a single file (patch from Daniel Silva). " - When using `ag`, report multiple matches per line instead of just the first " (patch from Daniel Silva). " - Improve content and display of error messages. " " ## 0.3 (24 July 2015) " " - Added highlighting of search pattern and related |g:FerretHlsearch| option " (patch from Nelo-Thara Wallus). " - Add better error reporting for failed or incorrect searches. " " ## 0.2 (16 July 2015) " " - Added |FerretDidWrite| and |FerretWillWrite| autocommands (patch from Joe " Lencioni). " - Add |(FerretAcks)| mapping (patch from Nelo-Thara Wallus). " " ## 0.1 (8 July 2015) " " - Initial release, extracted from my dotfiles " (https://github.com/wincent/wincent). "" " @option g:FerretLoaded any " " To prevent Ferret from being loaded, set |g:FerretLoaded| to any value in your " |.vimrc|. For example: " " ``` " let g:FerretLoaded=1 " ``` if exists('g:FerretLoaded') || &compatible || v:version < 700 finish endif let g:FerretLoaded = 1 " Temporarily set 'cpoptions' to Vim default as per `:h use-cpo-save`. let s:cpoptions = &cpoptions set cpoptions&vim if executable('ag') " The Silver Searcher: faster than ack. let s:ackprg = 'ag --vimgrep' elseif executable('ack') " Ack: better than grep. let s:ackprg = 'ack --column --with-filename' elseif executable('grep') " Grep: it's just grep. let s:ackprg = &grepprg " default is: grep -n $* /dev/null endif if !empty(s:ackprg) let &grepprg=s:ackprg set grepformat=%f:%l:%c:%m endif if has('autocmd') augroup Ferret autocmd! autocmd QuickFixCmdPost [^l]* nested cwindow autocmd QuickFixCmdPost l* nested lwindow augroup END endif "" " @command :Ack {pattern} {options} " " Searches for {pattern} in all the files under the current directory (see " |:pwd|), unless otherwise overridden via {options}, and displays the results " in the |quickfix| listing. " " `ag` (The Silver Searcher) will be used preferentially if present on the " system, because it is faster, falling back to `ack` and then `grep` as needed. " " If dispatch.vim is installed the search process will run asynchronously via " the |:Make| command, otherwise it will be run synchronously via |:cexpr|. " Asynchronous searches are preferred because they do not block, despite the " fact that Vim itself is single threaded. The |g:FerretDispatch| option can be " used to prevent the use of dispatch.vim. " " The {pattern} is passed through as-is to the underlying search program, and no " escaping is required other than preceding spaces by a single backslash. For " example, to search for "\bfoo[0-9]{2} bar\b" (ie. using `ag`'s Perl-style " regular expression syntax), you could do: " " ``` " :Ack \bfoo[0-9]{2}\ bar\b " ``` " " Likewise, {options} are passed through. In this example, we pass the `-w` " option (to search on word boundaries), and scope the search to the "foo" and " "bar" subdirectories: > " " ``` " :Ack -w something foo bar " ``` " " As a convenience a is set-up (|(FerretAck)|) as a shortcut to " enter |Cmdline-mode| with `:Ack` inserted on the |Cmdline|. Likewise s " (|(FerretAckWord)|) is a shortcut for running |:Ack| with the word " currently under the cursor. command! -nargs=+ -complete=customlist,ferret#private#ackcomplete Ack call ferret#private#ack() "" " @command :Lack {pattern} {options} " " Just like |:Ack|, but instead of using the |quickfix| listing, which is global " across an entire Vim instance, it uses the |location-list|, which is a " per-window construct. " " Note that |:Lack| always runs synchronously via |:cexpr|, because dispatch.vim " doesn't currently support the |location-list|. command! -nargs=+ -complete=customlist,ferret#private#lackcomplete Lack call ferret#private#lack() "" " @command :Acks /{pattern}/{replacement}/ " " Takes all of the files currently in the |quickfix| listing and performs a " substitution of all instances of {pattern} (a standard Vim search |pattern|) " by {replacement}. " " A typical sequence consists of an |:Ack| invocation to populate the |quickfix| " listing and then |:Acks| (mnemonic: "Ack substitute") to perform replacements. " For example, to replace "foo" with "bar" across all files in the current " directory: " " ``` " :Ack foo " :Acks /foo/bar/ " ``` command! -nargs=1 Acks call ferret#private#acks() command! FerretCancelAsync call ferret#private#async#cancel() command! FerretPullAsync call ferret#private#async#pull() nnoremap (FerretAck) :Ack nnoremap (FerretLack) :Lack nnoremap (FerretAckWord) :Ack nnoremap (FerretAcks) \ :Acks =(exists('g:ferret_lastsearch') ? '/' . g:ferret_lastsearch . '//' : ' ') "" " @option g:FerretMap boolean 1 " " Controls whether to set up the Ferret mappings, such as |(FerretAck)| " (see |ferret-mappings| for a full list). To prevent any mapping from being " configured, set to 0: " " ``` " let g:FerretMap=0 " ``` let s:map=get(g:, 'FerretMap', 1) if s:map if !hasmapto('(FerretAck)') && maparg('a', 'n') ==# '' "" " @mapping (FerretAck) " " Ferret maps a to |(FerretAck)|, which triggers the |:Ack| " command. To use an alternative mapping instead, create a different one in " your |.vimrc| instead using |:nmap|: " " ``` " " Instead of a, use x. " nmap x (FerretAck) " ``` nmap a (FerretAck) endif if !hasmapto('FerretLack') && maparg('l', 'n') ==# '' "" " @mapping (FerretLack) " " Ferret maps l to |(FerretLack)|, which triggers the |:Lack| " command. To use an alternative mapping instead, create a different one in " your |.vimrc| instead using |:nmap|: " " ``` " " Instead of l, use y. " nmap y (FerretLack) " ``` nmap l (FerretLack) endif if !hasmapto('(FerretAckWord)') && maparg('s', 'n') ==# '' "" " @mapping (FerretAckWord) " " Ferret maps s (mnemonix: "selection) to |(FerretAckWord)|, " which uses |:Ack| to search for the word currently under the cursor. To " use an alternative mapping instead, create a different one in your " |.vimrc| instead using |:nmap|: " " ``` " " Instead of s, use z. " nmap z (FerretAckWord) " ``` nmap s (FerretAckWord) endif if !hasmapto('(FerretAcks)') && maparg('r', 'n') ==# '' "" " @mapping (FerretAcks) " " Ferret maps r (mnemonic: "replace") to |(FerretAcks)|, which " triggers the |:Acks| command and fills the prompt with the last search " term from Ferret. to use an alternative mapping instead, create a " different one in your |.vimrc| instead using |:nmap|: " " ``` " " Instead of r, use u. " nmap u (FerretAcks) " ``` nmap r (FerretAcks) endif endif "" " @command :Qargs " " This is a utility function that is used by the |:Acks| command but is also " generally useful enough to warrant being exposed publicly. " " It takes the files currently in the |quickfix| listing and sets them as " |:args| so that they can be operated on en masse via the |:argdo| command. command! -bar Qargs execute 'args' ferret#private#qargs() "" " @option g:FerretQFCommands boolean 1 " " Controls whether to set up custom versions of the |quickfix| commands, |:cn|, " |:cnf|, |:cp| an |:cpf|. These overrides vertically center the match within " the viewport on each jump. To prevent the custom versions from being " configured, set to 0: " " ``` " let g:FerretQFCommands=0 " ``` let s:commands=get(g:, 'FerretQFCommands', 1) if s:commands " Keep quickfix result centered, if possible, when jumping from result to result. cabbrev cn ((getcmdtype() == ':' && getcmdpos() == 3) ? 'cn normal zz' : 'cn') cabbrev cnf ((getcmdtype() == ':' && getcmdpos() == 4) ? 'cnf normal zz' : 'cnf') cabbrev cp ((getcmdtype() == ':' && getcmdpos() == 3) ? 'cp normal zz' : 'cp') cabbrev cpf ((getcmdtype() == ':' && getcmdpos() == 4) ? 'cpf normal zz' : 'cpf') endif " Restore 'cpoptions' to its former value. let &cpoptions = s:cpoptions unlet s:cpoptions