diff --git a/autoload/ag.vim b/autoload/ag.vim index 630736cb..40390987 100644 --- a/autoload/ag.vim +++ b/autoload/ag.vim @@ -25,13 +25,95 @@ if !exists("g:ag_mapping_message") let g:ag_mapping_message=1 endif -function! ag#Ag(cmd, args) - " If no pattern is provided, search for the word under the cursor - if empty(a:args) - let l:grepargs = expand("") +if !exists("g:ag_scm_dirs") + let g:ag_scm_dirs = [ '.git', '.svn', '.hg' ] +endif + +let s:ag_results_mapping = { + \ 'open_and_close' : 'e', + \ 'open' : 'o,', + \ 'preview_open' : 'go', + \ 'new_tab' : 't', + \ 'new_tab_silent' : 'T', + \ 'horizontal_split' : 'h', + \ 'horizontal_split_silent' : 'H', + \ 'vertical_split' : 'v', + \ 'vertical_split_silent' : 'gv', + \ 'quit' : 'q' + \ } + +if exists("g:ag_results_mapping_replacements") + call extend(s:ag_results_mapping, g:ag_results_mapping_replacements, 'force') +endif + +function! ag#FindSCMDir() + let filedir = expand('%:p:h') + for candidate in g:ag_scm_dirs + let dir = finddir(candidate, filedir . ';') + if dir == candidate + return '.' + elseif dir != "" + let dir = substitute(dir, '/' . candidate, '', '') + return dir + endif + endfor + return "~" +endfunction + +function! ag#ApplyMapping(dictkey, mapping) + for key in split(s:ag_results_mapping[a:dictkey], ',') + exe "nnoremap " . key . " " . a:mapping + endfor +endfunction + +function! ag#AgForExtension(cmd, opts, regex, ...) + let exts = [] + " map() is just too much of a pain in the ass + for e in a:000 + call add(exts, substitute(e, '^\.\=\(.*\)', '\\.\1$', '')) + endfor + if empty(exts) + echoerr "No extensions provided." else - let l:grepargs = a:args . join(a:000, ' ') - end + let extRegex = join(exts, '|') + let l:opts = a:opts + call ag#Ag(a:cmd, a:regex, extend(l:opts, {'specific_file_exts': extRegex})) + endif +endfunction + +function! ag#AgFrontend(cmd, args) + call ag#Ag(a:cmd, a:args, {}) +endfunction + +function! ag#Ag(cmd, args, opts) + let l:ag_args = "" + + let l:opts = a:opts + + " Handle the types of files to search + if has_key(l:opts, 'current_file_ext') + let l:ag_args = l:ag_args . " -G'\\." . expand('%:e') . "$'" + elseif has_key(l:opts, 'specific_file_exts') + let l:ag_args = l:ag_args . " -G'" . l:opts['specific_file_exts'] . "'" + endif + + " If no pattern is provided, search for the word under the cursor + let l:pat = expand('') + if !empty(a:args) + let l:pat = a:args + let l:pat = substitute(l:pat, '\%(\\<\|\\>\)', '\\b', 'g') + let l:pat = substitute(l:pat, '\\', '\\\\', 'g') + endif + let l:ag_args = l:ag_args . ' ' . l:pat + + " If they want to search from the 'scm' directory + if has_key(l:opts, 'scmdir') + let l:ag_args = l:ag_args . ' ' . ag#FindSCMDir() + elseif has_key(l:opts, 'current_file_dir') + let l:ag_args = l:ag_args . ' ' . expand('%:p:h') + elseif has_key(l:opts, 'specific_dirs') + let l:ag_args = l:ag_args . ' ' . l:opts['specific_dirs'] + endif " Format, used to manage column jump if a:cmd =~# '-g$' @@ -45,7 +127,8 @@ function! ag#Ag(cmd, args) try let &grepprg=g:agprg let &grepformat=g:agformat - silent execute a:cmd . " " . escape(l:grepargs, '|') + let toExecute = a:cmd . " " . escape(l:ag_args, "|") + silent execute toExecute finally let &grepprg=grepprg_bak let &grepformat=grepformat_bak @@ -69,7 +152,7 @@ function! ag#Ag(cmd, args) " If highlighting is on, highlight the search keyword. if exists("g:aghighlight") - let @/=a:args + let @/ = l:pat set hlsearch end @@ -77,18 +160,18 @@ function! ag#Ag(cmd, args) if l:match_count if l:apply_mappings - nnoremap h K - nnoremap H Kb - nnoremap o - nnoremap t T - nnoremap T TgT - nnoremap v HbJt - - exe 'nnoremap e :' . l:matches_window_prefix .'close' - exe 'nnoremap go :' . l:matches_window_prefix . 'open' - exe 'nnoremap q :' . l:matches_window_prefix . 'close' - - exe 'nnoremap gv :let b:height=winheight(0)H:' . l:matches_window_prefix . 'openJ:exe printf(":normal %d\c-w>_", b:height)' + call ag#ApplyMapping('horizontal_split', 'K') + call ag#ApplyMapping('horizontal_split_silent', 'Kb') + call ag#ApplyMapping('open', '') + call ag#ApplyMapping('new_tab', 'T') + call ag#ApplyMapping('new_tab_silent', 'TgT') + call ag#ApplyMapping('vertical_split', 'HbJt') + + call ag#ApplyMapping('open_and_close', ':' . l:matches_window_prefix . 'close') + call ag#ApplyMapping('preview_open', ':' . l:matches_window_prefix . 'open') + call ag#ApplyMapping('quit', ':' . l:matches_window_prefix . 'close') + + call ag#ApplyMapping('vertical_split_silent', ':let b:height=winheight(0)H:' . l:matches_window_prefix . 'openJ:exe printf(":normal %d\c-w>_", b:height)') " Interpretation: " :let b:height=winheight(0) Get the height of the quickfix/location list window " Open the current item in a new split @@ -98,19 +181,25 @@ function! ag#Ag(cmd, args) " :exe printf(":normal %d\c-w>_", b:height) Restore the quickfix/location list window's height from before we opened the match if g:ag_mapping_message && l:apply_mappings - echom "ag.vim keys: q=quit /e/t/h/v=enter/edit/tab/split/vsplit go/T/H/gv=preview versions of same" + echom "ag.vim keys: " . s:ag_results_mapping['quit'] . "=quit " . + \ s:ag_results_mapping['open'] . '/' . + \ s:ag_results_mapping['open_and_close'] . '/' . + \ s:ag_results_mapping['new_tab'] . '/' . + \ s:ag_results_mapping['horizontal_split'] . '/' . + \ s:ag_results_mapping['vertical_split'] . "=enter/edit/tab/split/vsplit " . + \ s:ag_results_mapping['preview_open'] . '/' . + \ s:ag_results_mapping['horizontal_split_silent'] . '/' . + \ s:ag_results_mapping['vertical_split_silent'] . "=preview versions of same" endif endif else - echom 'No matches for "'.a:args.'"' + echom 'No matches for "' . l:pat . '"' endif endfunction -function! ag#AgFromSearch(cmd, args) - let search = getreg('/') - " translate vim regular expression to perl regular expression. - let search = substitute(search,'\(\\<\|\\>\)','\\b','g') - call ag#Ag(a:cmd, '"' . search .'" '. a:args) +function! ag#AgFromSearch(cmd, opts) + let search = getreg('/') + call ag#Ag(a:cmd, search, a:opts) endfunction function! ag#GetDocLocations() @@ -124,7 +213,6 @@ function! ag#GetDocLocations() return dp endfunction -function! ag#AgHelp(cmd,args) - let args = a:args.' '.ag#GetDocLocations() - call ag#Ag(a:cmd,args) +function! ag#AgHelp(cmd, args) + call ag#Ag(a:cmd, a:args, {'specific_dirs': ag#GetDocLocations()}) endfunction diff --git a/doc/ag.txt b/doc/ag.txt index 37b107b7..368f2aaf 100644 --- a/doc/ag.txt +++ b/doc/ag.txt @@ -49,6 +49,29 @@ shows the results in a split window. Just like |:AgHelp| but instead of the |quickfix| list, matches are placed in the current |location-list|. +:AgForCurrentFileDir [options] {pattern} *:AgForCurrentFileDir* + + Uses the 'current_file_dir' option to ensure that we limit the search to + the directory of the current buffer's file + +:AgForProjectRoot [options] {pattern} *:AgForProjectRoot* + + The notion of a "project" is the directory subtree that starts at the root + of a source control managed directory. This is currently defined with + 'g:ag_scm_dirs' variable. + +:AgForExtension [options] {pattern} *:AgForExtension* + + This command exists to let you specify a number of specific file extensions. + It implies the "scmdir" option so if you don't want that you'll have to + create a new command on your own (or override this one). Call it like + this: > + + AgForExtension 'search for me' .js .java .txt +< + This will search for 'search for me' in {js}, {java} and {txt} files. The + leading "." on all of the extensions is optional. + Files containing the search term will be listed in the split window, along with the line number of the occurrence, once for each occurrence. on a line in this window will open the file, and place the cursor on the matching @@ -113,6 +136,45 @@ the mappings are not applied (see |g:ag_apply_qmappings| and |g:ag_apply_lmappings| for more info. Default 1. Example: > let g:ag_mapping_message=0 < + *g:ag_scm_dirs* +This is a list containing the directories that should be treated as source +control management directories. It's what allows this plugin to limit the +searches to subtrees of entire source directories. + +============================================================================== +THE AG FUNCTION *ag-function* + +The ag#Ag() function has some special configuration options that you might be +interested in. The permutations are annoying so pre-canning all of those +permutations hasn't been done. The function looks like > + + ag#Ag(command, arguments, options) +< +The first two should always look something like: > + + ag#Ag('grep', , ...) +< +But the "options" argument is a dictionary. You can put the following into +the dictionary: +> + 'current_file_ext': 1 +< + Limits the search to the current buffer's file's extension (e.g. ".vim") +> + 'current_file_dir': 1 +< + Limits the search to the current buffer's file's directory. +> + 'scmdir': 1 +< + Limits the search to the "project" root subtree. This will search up the + directory tree for the first of the elements in the g:ag_scm_dirs list and + use that containing directoy as the directory argument to ag. +> + 'specific_dirs': 'directory1 directory2 and so forth' +< + Limits the search to the specific directories given in this string + ============================================================================== MAPPINGS *ag-mappings* @@ -139,4 +201,30 @@ gv open in vertical split silently. q close the quickfix window. + *g:ag_results_mapping_replacements* +You can customize all of the mappings using the +g:ag_results_mapping_replacements dictionary. The default values for the +dictionary are: > + + { + 'open_and_close': 'e' + 'open': 'o,' + 'preview_open': 'go' + 'new_tab': 't' + 'new_tab_silent': 'T' + 'horizontal_split': 'h' + 'horizontal_split_silent': 'H' + 'vertical_split': 'v' + 'vertical_split_silent': 'gv' + 'quit': 'q' + } +< +Personally, I prefer that closes the quickfix window, so I've customized +these as: > + + let g:ag_results_mapping_replacements = { + \ 'open_and_close': '', + \ 'open': 'o', + \ } +< vim:tw=78:fo=tcq2:ft=help:norl: diff --git a/plugin/ag.vim b/plugin/ag.vim index eb5bc224..817b7a5f 100644 --- a/plugin/ag.vim +++ b/plugin/ag.vim @@ -1,9 +1,12 @@ " NOTE: You must, of course, install ag / the_silver_searcher -command! -bang -nargs=* -complete=file Ag call ag#Ag('grep',) -command! -bang -nargs=* -complete=file AgAdd call ag#Ag('grepadd', ) +command! -bang -nargs=* -complete=file Ag call ag#Ag('grep',, {}) +command! -bang -nargs=* -complete=file AgAdd call ag#Ag('grepadd', , {}) command! -bang -nargs=* -complete=file AgFromSearch call ag#AgFromSearch('grep', ) -command! -bang -nargs=* -complete=file LAg call ag#Ag('lgrep', ) -command! -bang -nargs=* -complete=file LAgAdd call ag#Ag('lgrepadd', ) -command! -bang -nargs=* -complete=file AgFile call ag#Ag('grep -g', ) +command! -bang -nargs=* -complete=file LAg call ag#Ag('lgrep', , {}) +command! -bang -nargs=* -complete=file LAgAdd call ag#Ag('lgrepadd', , {}) +command! -bang -nargs=* -complete=file AgFile call ag#Ag('grep -g', , {}) command! -bang -nargs=* -complete=help AgHelp call ag#AgHelp('grep',) command! -bang -nargs=* -complete=help LAgHelp call ag#AgHelp('lgrep',) +command! -bang -nargs=* -complete=file AgForCurrentFileDir call ag#Ag('grep', , {'current_file_dir': 1}) +command! -bang -nargs=* -complete=file AgForProjectRoot call ag#Ag('grep', , {'current_file_ext': 1, 'scmdir': 1}) +command! -bang -nargs=+ -complete=file AgForExtension call ag#AgForExtension('grep', {'scmdir': 1}, )