vim: search files under the current working directory with denite & defx without changing the actual working directory

Takuya Matsuyama - May 6 '20 - - Dev Community

I'm using defx as filer, and denite and ag to search files on Neovim.
My current configuration searches files under the current working directory.
defx supports auto-cd option which changes the working directory automatically while navigating with it.
It allows you to quickly search files under the directory where you are viewing on defx, which is handy because you don't need to filter files in other directories which you are not currently interested in.
It basically works great, but my problem is that tools that work in the project root directory don't work when the current directory got changed.
In my case, eslint and flow can't be invoked via coc.nvim, causing error like this:

[coc.nvim] Definition provider not found for current document
Enter fullscreen mode Exit fullscreen mode

So, I want to search files under the directory I am currently working without changing the actual working directory.

defx

Fig. using defx

Use defx#get_candidate() to get the currently selected file path

First off, I need to know the path where defx is displaying.
I found that defx#get_candidate() returns the information about currently selected item like so:

echo defx#get_candidate()
"{'word': 'native-base-theme/', 'is_directory': v:true, 'action__path': '/foo/bar.txt', 'level': 0, 'is_opened_tree': v:false}
Enter fullscreen mode Exit fullscreen mode

Seems like you can use action__path to know where you are as following:

let l:path = expand('%:p:h')
if has_key(defx#get_candidate(), 'action__path')
  let l:path = fnamemodify(defx#get_candidate()['action__path'], ':p:h')
endif 
Enter fullscreen mode Exit fullscreen mode

If defx is not opened, it uses the parent path of the currently editing file instead, so you can quickly search sibling files without opening up defx.

vim config

So, I've update my vim config like so:

" use floating
let s:denite_win_width_percent = 0.8
let s:denite_win_height_percent = 0.7
let s:denite_default_options = {
    \ 'split': 'floating',
    \ 'winwidth': float2nr(&columns * s:denite_win_width_percent),
    \ 'wincol': float2nr((&columns - (&columns * s:denite_win_width_percent)) / 2),
    \ 'winheight': float2nr(&lines * s:denite_win_height_percent),
    \ 'winrow': float2nr((&lines - (&lines * s:denite_win_height_percent)) / 2),
    \ 'highlight_filter_background': 'DeniteFilter',
    \ 'prompt': 'λ ',
    \ 'start_filter': v:true,
    \ }
let s:denite_option_array = []
for [key, value] in items(s:denite_default_options)
  call add(s:denite_option_array, '-'.key.'='.value)
endfor
call denite#custom#option('default', s:denite_default_options)

" Ag command on grep source
call denite#custom#var('grep', 'command', ['ag'])
call denite#custom#var('grep', 'default_opts', ['-i', '--vimgrep'])
call denite#custom#var('grep', 'recursive_opts', [])
call denite#custom#var('grep', 'pattern_opt', [])
call denite#custom#var('grep', 'separator', ['--'])
call denite#custom#var('grep', 'final_opts', [])

" grep
command! -nargs=? Dgrep call s:Dgrep(<f-args>)
function s:Dgrep(...)
  if a:0 > 0
    execute(':Denite -buffer-name=grep-buffer-denite grep -path='.a:1)
  else
    let l:path = expand('%:p:h')
    if has_key(defx#get_candidate(), 'action__path')
      let l:path = fnamemodify(defx#get_candidate()['action__path'], ':p:h')
    endif 
    execute(':Denite -buffer-name=grep-buffer-denite -no-empty '.join(s:denite_option_array, ' ').' grep -path='.l:path)
  endif
endfunction

" show Denite grep results
command! Dresume execute(':Denite -resume -buffer-name=grep-buffer-denite '.join(s:denite_option_array, ' ').'')

nnoremap <silent> ;r :<C-u>Dgrep<CR>
Enter fullscreen mode Exit fullscreen mode

Works like a charm!

End result

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .