" 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