├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── autoload └── agriculture.vim ├── plugin └── agriculture.vim └── test ├── smart_quote_input.vader └── trim_and_escape_register_a.vader /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: jesseleite 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | tags 2 | *.swp 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jesse Leite 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Vim Agriculture 🚜 2 | 3 | A vim plugin to improve the project search experience when using tools like [ag](https://github.com/ggreer/the_silver_searcher) and [rg](https://github.com/BurntSushi/ripgrep). 4 | 5 | - [Rationale](#rationale) 6 | - [Installation](#installation) 7 | - [Usage](#usage) 8 | - [Mappings](#mappings) 9 | - [Customization](#customization) 10 | - [How It Works](#how-it-works) 11 | 12 | ## Rationale 13 | 14 | I was inspired by [fzf.vim](https://github.com/junegunn/fzf.vim)'s ability to quickly `:Ag` search multiple words without quotes, narrow down multiple results in realtime with [extended search syntax](https://github.com/junegunn/fzf#search-syntax), then populate quickfix for a large refactor 👌 15 | 16 | ``` 17 | :Ag function index 18 | ``` 19 | 20 | But I found myself missing the ability to pass command line options like I could with [ack.vim](https://github.com/mileszs/ack.vim)'s `:Ack` 😢 21 | 22 | ``` 23 | :Ack -Q -i 'function index' vendor 24 | ``` 25 | 26 | Furthermore, fzf.vim's `:Ag` treats quotes as a literal part of the search query, which is inconsistent with `ag` on the command line. 27 | 28 | Thus, the intention of this plugin is to bring the best of both worlds to your favourite search wrapper. Perform multi-word searches with or without quotes, pass command line options, and do it all from one command. 29 | 30 | [Read more about my project searching workflow with fzf.vim and agriculture](https://jesseleite.com/posts/4/project-search-your-feelings) ❤️ 31 | 32 | ## Installation 33 | 34 | Install using [vim-plug](https://github.com/junegunn/vim-plug) or similar: 35 | 36 | ``` 37 | Plug 'jesseleite/vim-agriculture' 38 | ``` 39 | 40 | ## Usage 41 | 42 | If you are already using [fzf.vim](https://github.com/junegunn/fzf.vim), you can use the provided `:AgRaw` / `:RgRaw` commands. 43 | 44 | ``` 45 | :AgRaw func.*index 46 | :AgRaw 'func.*index' 47 | :AgRaw -Q 'function index()' app/Http/Controllers 48 | ``` 49 | 50 | Likewise for `:RgRaw`, just substitute `AgRaw` in `RgRaw` in the above examples. 51 | 52 | If you are using another search wrapper, you'll need to wrap your input with `agriculture#smart_quote_input()`. 53 | 54 | ## Mappings 55 | 56 | If you are using one of the provided commands, you can hook into the provided `` mappings in your `.vimrc`: 57 | 58 | ``` 59 | nmap / AgRawSearch 60 | vmap / AgRawVisualSelection 61 | nmap * AgRawWordUnderCursor 62 | ``` 63 | 64 | Likewise for `:RgRaw`, just substitute `AgRaw` in `RgRaw` in the above examples. 65 | 66 | ## Customization 67 | 68 | ### Command Line Options 69 | 70 | If you are using one of the provided commands, you can also set default command line options in your `.vimrc`: 71 | 72 | ``` 73 | let g:agriculture#ag_options = '--case-sensitive' 74 | ``` 75 | 76 | Again likewise for `:RgRaw` with `g:agriculture#rg_options`. 77 | 78 | ### Disable Smart Quoting 79 | 80 | If you are using one of the provided commands, and want to disable smart quoting for CLI consistency: 81 | 82 | ``` 83 | let g:agriculture#disable_smart_quoting = 1 84 | ``` 85 | 86 | ### Preview Window 87 | 88 | Preview windows are now rendered by default in many [fzf.vim](https://github.com/junegunn/fzf.vim) commands. If you wish to customize or disable this behaviour, [see fzf.vim's documentation](https://github.com/junegunn/fzf.vim#preview-window) on preview windows. 89 | 90 | ## How It Works 91 | 92 | Your input will be automatically quoted _unless_ the following conditions are met: 93 | - Quotes in your query, signifying you might want to handle your own quoting/escaping, ie. 94 | ``` 95 | :Ag -Q "Already quoted this pattern." 96 | :Ag Why you "scruffy looking nerf herder"! 97 | :Ag Who's scruffy looking? 98 | ``` 99 | - A space followed by a dash in your query, signifying you might be passing an option, ie. 100 | ``` 101 | :Ag -Q function index 102 | :Ag Which way to the beach? -> that way! 103 | ``` 104 | - An escaped pattern followed by an unescaped space, signifying you might be passing a path, ie. 105 | ``` 106 | :Ag an\ escaped\ pattern vendor/folder 107 | ``` 108 | 109 | TL;DR: If you use quotes, dashes, or need to pass a path, it's recommended you quote/escape your own pattern and vim-agriculture will stay out of your way 👍 110 | 111 | ## Who am I? 112 | 113 | Just a hack 🔨 114 | 115 | - [jesseleite.com](https://jesseleite.com) 116 | - [@jesseleite85](https://twitter.com/jesseleite85) 117 | -------------------------------------------------------------------------------- /autoload/agriculture.vim: -------------------------------------------------------------------------------- 1 | function! agriculture#smart_quote_input(input) 2 | if get(g:, 'agriculture#disable_smart_quoting', 0) > 0 3 | return a:input 4 | endif 5 | let hasQuotes = match(a:input, '"') > -1 || match(a:input, "'") > -1 6 | let hasOptions = match(' ' . a:input, '\s-[-a-zA-Z]') > -1 7 | let hasEscapedSpacesPlusPath = match(a:input, '\\ .*\ ') > 0 8 | return hasQuotes || hasOptions || hasEscapedSpacesPlusPath ? a:input : '-- "' . a:input . '"' 9 | endfunction 10 | 11 | function! agriculture#trim_and_escape_register_a() 12 | let query = getreg('a') 13 | let trimmedQuery = s:trim(query) 14 | let escapedQuery = shellescape(trimmedQuery) 15 | call setreg('a', escapedQuery) 16 | endfunction 17 | 18 | function! agriculture#fzf_ag_raw(command_suffix, ...) 19 | if !executable('ag') 20 | return s:warn('ag is not found') 21 | endif 22 | let userOptions = get(g:, 'agriculture#ag_options', '') 23 | let command = 'ag --nogroup --column --color ' . s:trim(userOptions . ' ' . a:command_suffix) 24 | let bang = a:000[0] 25 | return call('fzf#vim#grep', extend([command, 1], [s:preview(bang), bang])) 26 | endfunction 27 | 28 | function! agriculture#fzf_rg_raw(command_suffix, ...) 29 | if !executable('rg') 30 | return s:warn('rg is not found') 31 | endif 32 | let userOptions = get(g:, 'agriculture#rg_options', '') 33 | let command = 'rg --column --line-number --no-heading --color=always ' . s:trim(userOptions . ' ' . a:command_suffix) 34 | let bang = a:000[0] 35 | return call('fzf#vim#grep', extend([command, 1], [s:preview(bang), bang])) 36 | endfunction 37 | 38 | function! s:preview(bang, ...) 39 | let preview_window = get(g:, 'fzf_preview_window', a:bang && &columns >= 80 || &columns >= 120 ? 'right': '') 40 | if empty(preview_window) 41 | return {} 42 | endif 43 | " For backward-compatiblity 44 | if type(preview_window) == type('') 45 | let preview_window = [preview_window] 46 | endif 47 | return call('fzf#vim#with_preview', extend(copy(a:000), preview_window)) 48 | endfunction 49 | 50 | function! s:trim(str) 51 | if exists('*trim') 52 | return trim(a:str) 53 | else 54 | return matchstr(a:str, '^\s*\zs.\{-}\ze\s*$') 55 | endif 56 | endfunction 57 | 58 | function! s:warn(message) 59 | echohl WarningMsg 60 | echom a:message 61 | echohl None 62 | return 0 63 | endfunction 64 | -------------------------------------------------------------------------------- /plugin/agriculture.vim: -------------------------------------------------------------------------------- 1 | " Commands 2 | command! -bang -nargs=+ -complete=dir AgRaw call agriculture#fzf_ag_raw(agriculture#smart_quote_input(), 0) 3 | command! -bang -nargs=+ -complete=dir RgRaw call agriculture#fzf_rg_raw(agriculture#smart_quote_input(), 0) 4 | 5 | " Mappings 6 | nnoremap AgRawSearch :AgRaw 7 | nnoremap RgRawSearch :RgRaw 8 | 9 | " Mappings to search visual selection 10 | vnoremap AgRawVisualSelection "ay:call agriculture#trim_and_escape_register_a():AgRaw -Q -- a 11 | vnoremap RgRawVisualSelection "ay:call agriculture#trim_and_escape_register_a():RgRaw -F -- a 12 | 13 | " Mappings to search word under cursor 14 | nnoremap AgRawWordUnderCursor "ayiw:call agriculture#trim_and_escape_register_a():AgRaw -Q -- a 15 | nnoremap RgRawWordUnderCursor "ayiw:call agriculture#trim_and_escape_register_a():RgRaw -F -- a 16 | -------------------------------------------------------------------------------- /test/smart_quote_input.vader: -------------------------------------------------------------------------------- 1 | Before (Enable smart quoting): 2 | let g:agriculture#disable_smart_quoting = 0 3 | 4 | Execute (Smart quotes without detected options or path): 5 | AssertEqual '-- "func.* index"', agriculture#smart_quote_input('func.* index') 6 | AssertEqual '-- "func.*\ index"', agriculture#smart_quote_input('func.*\ index') 7 | AssertEqual '-- "->middleware"', agriculture#smart_quote_input('->middleware') 8 | 9 | Execute (Hands off with quotes): 10 | AssertEqual '"func.* index"', agriculture#smart_quote_input('"func.* index"') 11 | AssertEqual "'func.* index'", agriculture#smart_quote_input("'func.* index'") 12 | AssertEqual 'Why you "scruffy looking nerf herder"!', agriculture#smart_quote_input('Why you "scruffy looking nerf herder"!') 13 | AssertEqual "Who's scruffy looking?", agriculture#smart_quote_input("Who's scruffy looking?") 14 | 15 | Execute (Hands off with options): 16 | AssertEqual '-Q function', agriculture#smart_quote_input('-Q function') 17 | AssertEqual 'function -Q', agriculture#smart_quote_input('function -Q') 18 | AssertEqual '-u function -Q', agriculture#smart_quote_input('-u function -Q') 19 | 20 | Execute (Hands off with space escaped query and path): 21 | AssertEqual 'func.*\ index vendor/package', agriculture#smart_quote_input('func.*\ index vendor/package') 22 | 23 | Before (Disable smart quoting): 24 | let g:agriculture#disable_smart_quoting = 1 25 | 26 | Execute (Hands off if explicitly disabled): 27 | AssertEqual 'func.* index', agriculture#smart_quote_input('func.* index') 28 | -------------------------------------------------------------------------------- /test/trim_and_escape_register_a.vader: -------------------------------------------------------------------------------- 1 | Given (A fancy star wars line that needs escaping): 2 | # Who's scruffy \looking%? 3 | 4 | Do (Yank text to register a): 5 | V"ay 6 | 7 | Execute (Escape text in register a): 8 | call agriculture#trim_and_escape_register_a() 9 | 10 | Do (Paste escaped text from register a): 11 | V"ap 12 | 13 | Expect (Pasted text was properly escaped): 14 | \# Who\'s scruffy \\looking\%? 15 | --------------------------------------------------------------------------------