diff --git a/autoload/ag.vim b/autoload/ag.vim index a9567575..37be3dfc 100644 --- a/autoload/ag.vim +++ b/autoload/ag.vim @@ -2,95 +2,79 @@ " " Variables required to manage async let s:job_number = 0 -let s:locListCommand = 0 +let s:toLocList = 0 let s:args = '' let s:cwd = getcwd() -let s:data = [] +let s:data = [''] let s:resetData = 1 "----------------------------------------------------------------------------- " Public API "----------------------------------------------------------------------------- -function! ag#Ag(cmd, args) abort - let l:ag_executable = get(split(g:ag_prg, ' '), 0) +function! ag#run(cmd, args, cwd) abort + if empty(a:args) + let l:args = [expand('')] + els + " If no pattern is provided, search for the word under the cursor + let l:args = a:args + end - " Ensure that `ag` is installed - if !executable(l:ag_executable) - echoe "Ag command '" . l:ag_executable . "' was not found. Is the silver searcher installed and on your $PATH?" - return - endif + let s:ag_current_format = g:ag_format + let s:ag_current_prg = g:ag_prg - " If no pattern is provided, search for the word under the cursor - if empty(a:args) - let l:grepargs = expand('') + " Set the script variables that will later be used by the async callback + if a:cmd =~# '^l' + let s:toLocList = 1 else - let l:grepargs = a:args . join(a:000, ' ') - end + let s:toLocList = 0 + endif - if empty(l:grepargs) - echo "Usage: ':Ag {pattern}'. See ':help :Ag' for more information." - return + if a:cmd =~# 'find' + let s:ag_current_format = g:fd_format + let s:ag_current_prg = g:fd_prg endif - " Format, used to manage column jump - if a:args =~# '-g' - let s:ag_format_backup = g:ag_format - let g:ag_format = '%f' - elseif exists('s:ag_format_backup') - let g:ag_format = s:ag_format_backup + if a:cmd =~# 'findfull' + let s:ag_current_prg = s:ag_current_prg + ['--full-path'] endif - " Set the script variables that will later be used by the async callback - let s:args = l:grepargs - let l:cmd = a:cmd . ' ' . escape(l:grepargs, '|') - if l:cmd =~# '^l' - let s:locListCommand = 1 - else - let s:locListCommand = 0 + if a:cmd =~ 'glob' + let s:ag_current_prg = s:ag_current_prg + ['--glob=' .. l:args[0]] + let l:args = l:args[1:] endif + if a:cmd =~# '!$' + let s:ag_current_prg = s:ag_current_prg + ['--hidden'] + endif + + " Store the backups - let l:grepprg_bak = &grepprg - let l:grepprg_bak = &l:grepprg - let l:grepformat_bak = &grepformat let l:t_ti_bak = &t_ti let l:t_te_bak = &t_te " Try to change all the system variables and run ag in the right folder try - let &l:grepprg = g:ag_prg - let &grepformat = g:ag_format set t_ti= " These 2 commands make ag.vim not bleed in terminal set t_te= - if g:ag_working_path_mode ==? 'r' " Try to find the project root for current buffer - let l:cwd_back = getcwd() - let s:cwd = s:guessProjectRoot() - try - exe 'lcd '.s:cwd - catch - finally - call s:executeCmd(l:grepargs, l:cmd) - exe 'lcd '.l:cwd_back - endtry - else " Someone chose an undefined value or 'c' so we revert to searching in the cwd - call s:executeCmd(l:grepargs, l:cmd) - endif + call s:execAg(s:ag_current_prg, l:args, { 'cwd': a:cwd }) finally - let &l:grepprg = l:grepprg_bak - let &grepformat = l:grepformat_bak let &t_ti = l:t_ti_bak let &t_te = l:t_te_bak endtry +endfunction - " No neovim, when we finally get here we already have the output so run handleOutput - if !has('nvim') - call s:handleOutput() - return +function! ag#Ag(cmd, ...) abort + let l:args = a:000 + if g:ag_working_path_mode ==? 'r' " Try to find the project root for current buffer + let l:cwd = s:guessProjectRoot() + else " Someone chose an undefined value or 'c' so we revert to searching in the cwd + let l:cwd = getcwd() endif -endfunction + call ag#run(a:cmd, l:args, l:cwd) +endf -function! ag#AgBuffer(cmd, args) abort +function! ag#AgBuffer(...) abort let l:bufs = filter(range(1, bufnr('$')), 'buflisted(v:val)') let l:files = [] for buf in l:bufs @@ -99,29 +83,14 @@ function! ag#AgBuffer(cmd, args) abort call add(l:files, l:file) endif endfor - call ag#Ag(a:cmd, a:args . ' ' . join(l:files, ' ')) + + let l:args = a:000 + ["--"] + l:files + call call(function('ag#Ag'), l:args) endfunction -function! ag#AgFromSearch(cmd, args) abort - let l:search = getreg('/') - " translate vim regular expression to perl regular expression. - let l:search = substitute(l:search,'\(\\<\|\\>\)','\\b','g') - call ag#Ag(a:cmd, '"' . l:search .'" '. a:args) -endfunction - -function! ag#AgHelp(cmd,args) abort - let l:args = a:args.' '.s:GetDocLocations() - call ag#Ag(a:cmd,l:args) -endfunction - -function! ag#AgFile(cmd, args) abort - let l:args = ' -g ' . a:args - call ag#Ag(a:cmd, l:args) -endfunction - -function! ag#AgAdd(cmd, args) abort +function! ag#AgAdd(...) abort let s:resetData = 0 - call ag#Ag(a:cmd, a:args) + call call(function('ag#Ag'), a:000) endfunction "----------------------------------------------------------------------------- @@ -129,14 +98,14 @@ endfunction "----------------------------------------------------------------------------- function! s:handleOutput() abort - if s:locListCommand + if s:toLocList let l:match_count = len(getloclist(winnr())) else let l:match_count = len(getqflist()) endif if l:match_count - if s:locListCommand + if s:toLocList exe g:ag_lhandler let l:apply_mappings = g:ag_apply_lmappings let l:matches_window_prefix = 'l' " we're using the location list @@ -155,11 +124,11 @@ function! s:handleOutput() abort redraw! " Regular vim needs some1 to tell it to redraw if l:apply_mappings - nnoremap h :exe 'wincmd ' (&splitbelow ? 'J' : 'K')pJp - nnoremap H :exe 'wincmd ' (&splitbelow ? 'J' : 'K')pJ - nnoremap o + nnoremap f :exe 'wincmd ' (&splitbelow ? 'J' : 'K')pJp + " nnoremap H :exe 'wincmd ' (&splitbelow ? 'J' : 'K')pJ + " nnoremap o nnoremap t T - nnoremap T TgT + " nnoremap T TgT nnoremap v :exe 'wincmd ' (&splitright ? 'L' : 'H')pJp let l:closecmd = l:matches_window_prefix . 'close' @@ -180,7 +149,7 @@ function! s:handleOutput() abort endif endfunction -function! s:handleAsyncOutput(job_id, data, event) abort +function! s:handleAsyncOutput(job_id, data, event) abort dict " Don't care about older async calls that have been killed or replaced if s:job_number !=# a:job_id return @@ -188,7 +157,8 @@ function! s:handleAsyncOutput(job_id, data, event) abort " Store all the input we get from the shell if a:event ==# 'stdout' - let s:data = s:data+a:data + let s:data[-1] .= a:data[0] + call extend(s:data, a:data[1:]) " When the program has finished running we parse the data elseif a:event ==# 'exit' @@ -196,25 +166,29 @@ function! s:handleAsyncOutput(job_id, data, event) abort let l:expandeddata = [] " Expand the path of the result so we can jump to it for l:result in s:data - if( l:result !~? '^/home/' ) " Only expand when the path is not a full path already - let l:result = s:cwd.'/'.l:result + " At the end we usually have some bogous/empty lines, so skip them + if( l:result =~ '^\s*$') + continue + endif + if( l:result !~? '^/' ) " Only expand when the path is not a full path already + let l:result = self.cwd.'/'.l:result endif let l:result = substitute(l:result , '//', '/' ,'g') " Get rid of excess slashes in filename if present call add(l:expandeddata, l:result) endfor if len(l:expandeddata) " Only if we actually find something + let l:errorformat_bak = &errorformat + let &errorformat = s:ag_current_format - " The last element is always bogus for some reason - let l:expandeddata = l:expandeddata[0:-2] - - if s:locListCommand + if s:toLocList " Add to location list lgete l:expandeddata else " Add to quickfix list cgete l:expandeddata endif + let &errorformat = l:errorformat_bak call s:handleOutput() else echom 'No matches for "'.s:args.'"' @@ -222,13 +196,7 @@ function! s:handleAsyncOutput(job_id, data, event) abort endif endfunction -function! s:executeCmd(grepargs, cmd) abort - if !has('nvim') - silent! execute a:cmd - return - endif - - " Stop older running ag jobs if any +function! s:execAg(prg, args, opts) abort try call jobstop(s:job_number) catch @@ -236,48 +204,32 @@ function! s:executeCmd(grepargs, cmd) abort " Clear all of the old captures if s:resetData - let s:data = [] + let s:data = [''] endif let s:resetData = 1 - " All types of exiting the job should be directed to handleAsyncOutput - let s:callbacks = { - \ 'on_stdout': function('s:handleAsyncOutput'), - \ 'on_stderr': function('s:handleAsyncOutput'), - \ 'on_exit': function('s:handleAsyncOutput') - \ } - - let l:splitargs = split(a:grepargs) - let l:grepargs = '' - "Make sure we shellescape arguments separately and expand the ~ in a string - for l:splitarg in l:splitargs - if l:splitarg !~? '^-' - let l:grepargs = l:grepargs.' '.shellescape(substitute(l:splitarg,'^\~',$HOME, '')) - else - let l:grepargs = l:grepargs.' '.l:splitarg - endif - endfor + let l:opts = { + \ 'on_stdout': function('s:handleAsyncOutput'), + \ 'on_stderr': function('s:handleAsyncOutput'), + \ 'on_exit': function('s:handleAsyncOutput') + \ } - " Construct the command string send to job shell - - " cd [directory]; ag --vimgrep [extra flags] '[value]' '[optional directory]' - let l:agcmd = 'cd '.s:cwd.'; '.g:ag_prg . ' ' . l:grepargs - - echom 'Ag search started' - let s:job_number = jobstart(['sh', '-c', l:agcmd], extend({'shell': 'shell 1'}, s:callbacks)) -endfunction + let l:args = copy(a:args) + let l:idx = index(l:args, "--") + if l:idx >= 0 + call remove(l:args, l:idx) + else + call add(l:args, "./") + endif + let l:cmd = a:prg + l:args + let s:args = join(l:args, " ") -function! s:GetDocLocations() abort - let dp = '' - for p in split(&runtimepath,',') - let p = p.'doc/' - if isdirectory(p) - let dp = p.'*.txt '.dp - endif - endfor - return dp + echom 'Ag search started (' . join(l:cmd, " ") . ')' + let s:job_number = jobstart(l:cmd, extend(l:opts, a:opts)) endfunction + " Called from within a list window, preserves its height after shuffling vsplit. " The parameter indicates whether list was opened as copen or lopen. function! s:PreviewVertical(opencmd) abort diff --git a/lua/agvim/init.lua b/lua/agvim/init.lua new file mode 100644 index 00000000..59c171bc --- /dev/null +++ b/lua/agvim/init.lua @@ -0,0 +1,45 @@ +-- > package.loaded[ 'agvim' ] = nil +-- > x = require("agvim") +-- > x.get_file("abc|") +-- abc| +-- lua agvim = require("agvim") +-- call v:lua.agvim.as_args() + +local function get_fname(entry) + local idx = string.find(entry, "|") + if idx == nil then + return nil + end + return string.sub(entry, 1, idx-1) +end + +local function extract_files(lines) + local seen = {} + local files = {} + local n = 1 + for _, v in ipairs(lines) do + local fn = get_fname(v) + if fn and not seen[fn] then + seen[fn] = true + files[n] = fn + n = n+1 + end + end + return files +end + +local function as_args() + local bufnr = vim.api.nvim_get_current_buf() + local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false) + local files = extract_files(lines) + + for i,v in ipairs(files) do + files[i] = vim.api.nvim_call_function("fnameescape",{v}) + end + vim.api.nvim_command("close") + vim.api.nvim_command("args " .. table.concat(files, " ")) +end + +return { + as_args = as_args +} diff --git a/plugin/ag.vim b/plugin/ag.vim index 1139c574..d6075bd2 100644 --- a/plugin/ag.vim +++ b/plugin/ag.vim @@ -4,26 +4,29 @@ if exists('g:autoloaded_ag') finish endif -if !executable('ag') - echoe "Ag command was not found. Is the silver searcher installed and on your $PATH?" +if !executable('rg') + echoe "rg command was not found. Is ripgrep installed?" finish endif " Location of the ag utility if !exists('g:ag_prg') - " --vimgrep (consistent output we can parse) is available from version 0.25.0+ - if split(system('ag --version'), '[ \n\r\t]')[2] =~? '\d\+.\(2[5-9]\|[3-9][0-9]\)\(.\d\+\)\?' - let g:ag_prg = 'ag --vimgrep --silent' - else - " --noheading seems odd here, but see https://github.com/ggreer/the_silver_searcher/issues/361 - let g:ag_prg = 'ag --column --nogroup --noheading' - endif + let g:ag_prg = ['rg','--follow', '--smart-case', '--vimgrep'] +endif + +if !exists('g:fd_prg') + let g:fd_prg = ['fd','--follow'] +endif + +if !exists('g:fd_format') + let g:fd_format = '%f' endif if !exists('g:ag_format') let g:ag_format = '%f:%l:%c:%m' endif + if !exists('g:ag_apply_qmappings') let g:ag_apply_qmappings = 1 endif @@ -52,15 +55,15 @@ if !exists('g:ag_working_path_mode') let g:ag_working_path_mode = 'c' endif -command! -bang -nargs=* -complete=file Ag call ag#Ag('grep',) -command! -bang -nargs=* -complete=file AgBuffer call ag#AgBuffer('grep',) -command! -bang -nargs=* -complete=file AgAdd call ag#AgAdd('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 LAgBuffer call ag#AgBuffer('lgrep',) -command! -bang -nargs=* -complete=file LAgAdd call ag#AgAdd('lgrepadd', ) -command! -bang -nargs=* -complete=file AgFile call ag#AgFile('grep', ) -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 Ag call ag#Ag('grep',) +command! -bang -nargs=* -complete=file Af call ag#Ag('grepglob',) +command! -bang -nargs=* -complete=file AgBuffer call ag#AgBuffer('grep',) +command! -bang -nargs=* -complete=file AgAdd call ag#AgAdd('grepadd', ) +command! -bang -nargs=* -complete=file LAg call ag#Ag('lgrep', ) +command! -bang -nargs=* -complete=file LAgBuffer call ag#AgBuffer('lgrep',) +command! -bang -nargs=* -complete=file LAgAdd call ag#AgAdd('lgrepadd', ) +command! -bang -nargs=* -complete=file Fd call ag#Ag('find', ) +command! -bang -nargs=* -complete=file Fdabs call ag#Ag('findfull', ) +command FdAsArgs lua require("agvim").as_args() let g:autoloaded_ag = 1