Search code examples
vimfuzzy-search

vim sbuffer with fuzzy search


I usually have plenty of files opened in different windows and tabs within vim (or neovim). To jump to the window where the file is open I use :sbuffer. For that, I input the begging of the file name and autocomplete with . I would like to find the name of the file in the way fuzzy search does.

How I see this going, I press , I input a part of the file name, until I see the file found by fuzzy search, then I press . If the file is already opened in a window in any tab, vim moves there rather than opening it in the actual window.

Do you know how to achieve this with any fuzzy search plugin? I'm not worried about the behaviour if the file is not open as usually I take manual care of splits when opening files.

Thanks!


Solution

  • Fzf is probably one of your best options here. You'll need both fzf.vim and fzf.

    Also, there's a readme for fzf vim in fzf main repo (yeah...).

    I can't recall where I found this, but here's a config I copied from Fzf manual somewhere:

    let g:fzf_command_prefix = 'Fzf'
    let g:fzf_buffers_jump = 1      " [Buffers] to existing split
    
    function! s:build_location_list(lines) abort
        call setloclist(0, map(copy(a:lines), '{ "filename": v:val }'))
        lopen
    endfunction
    
    function! s:build_quickfix_list(lines) abort
        call setqflist(map(copy(a:lines), '{ "filename": v:val }'))
        copen
    endfunction
    
    " An action can be a reference to a function that processes selected lines
    let g:fzf_action = {
                \ 'ctrl-l': function('s:build_quickfix_list'),
                \ 'ctrl-r': function('s:build_location_list'),
                \ 'ctrl-t': 'tab split',
                \ 'ctrl-x': 'split',
                \ 'ctrl-v': 'vsplit'}
    " \ 'ctrl-o': '<S-tab>',
    " \ 'ctrl-i': 'insert_match',
    
    " function! s:insert_match(lines) abort
    "   <c-r>=echo('a:lines')<cr>
    " endfunction
    
    nnoremap <leader>ff :FzfFiles $HOME<cr>
    nnoremap <leader><c-f> :FzfFiles .<cr>
    nnoremap <leader>F :FzfFiles /<cr>
    nnoremap <leader>fb :FzfBuffers<cr>
    nnoremap <leader>b :FzfBuffers<cr>
    nnoremap <leader>fw :FzfWindows<cr>
    nnoremap <leader>ft :FzfTags<cr>
    nnoremap <leader>f<c-t> :FzfBTags<cr>
    nnoremap <leader>fc :FzfCommit<cr>
    nnoremap <leader>f<c-c> :FzfBCommit<cr>
    nnoremap <leader>fg :FzfGFiles?<cr>
    nnoremap <leader>f<c-g> :FzfGFiles<cr>
    nnoremap <leader>fl :FzfLines<cr>
    nnoremap <leader>f<c-l> :FzfBLines<cr>
    nnoremap <leader>f; :FzfHistory:<cr>
    nnoremap <leader>f/ :FzfHistory/<cr>
    nnoremap <leader>fh :FzfHistory<cr>
    nnoremap <leader>fm :FzfHelptags<cr>
    nnoremap <leader>fs <esc>:FzfSnippets<cr>
    nnoremap <leader>fr <esc>:Rg<cr>
    inoremap <c-x><c-s> <c-o>:FzfSnippets<cr>
    
    " Enable per-command history.
    " CTRL-N and CTRL-P will be automatically bound to next-history and
    " previous-history instead of down and up. If you don't like the change,
    " explicitly bind the keys to down and up in your $FZF_DEFAULT_OPTS.
    let g:fzf_history_dir = '~/.local/share/fzf-history'
    
    let g:fzf_tags_command = 'ctags -R'
    " Border color
    let g:fzf_layout = {'up':'~90%', 'window': { 'width': 0.8, 'height': 0.8,'yoffset':0.5,'xoffset': 0.5, 'highlight': 'Todo', 'border': 'rounded' } }
    
    let $FZF_DEFAULT_OPTS = '--layout=reverse --info=inline --bind "ctrl-o:toggle+up,ctrl-space:toggle-preview"'
    let $FZF_DEFAULT_COMMAND="rg --files --hidden --glob '!.git/**'"
    "-g '!{node_modules,.git}'
    
    " Customize fzf colors to match your color scheme
    let g:fzf_colors =
    \ { 'fg':      ['fg', 'Normal'],
        \ 'bg':      ['bg', 'Normal'],
        \ 'gutter':  ['bg', 'Normal'],
        \ 'hl':      ['fg', 'Comment'],
        \ 'fg+':     ['fg', 'CursorLine', 'CursorColumn', 'Normal'],
        \ 'bg+':     ['bg', 'Visual', 'CursorColumn'],
        \ 'hl+':     ['fg', 'Statement'],
        \ 'info':    ['fg', 'PreProc'],
        \ 'border':  ['fg', 'vertsplit'],
        \ 'prompt':  ['fg', 'Conditional'],
        \ 'pointer': ['fg', 'Exception'],
        \ 'marker':  ['fg', 'Keyword'],
        \ 'spinner': ['fg', 'Label'],
        \ 'header':  ['fg', 'Comment'] }
        " \ 'border':  ['fg', 'Conditional'],
    
    "Get Files
    command! -bang -nargs=? -complete=dir Files
            \ call fzf#vim#files(<q-args>, fzf#vim#with_preview({'options': ['--layout=reverse', '--info=inline']}), <bang>0)
    
    " Get text in files with Rg
    command! -bang -nargs=* Rg
        \ call fzf#vim#grep(
        \   "rg --column --line-number --no-heading --color=always --smart-case --glob '!.git/**' ".shellescape(<q-args>), 1,
        \   fzf#vim#with_preview(), <bang>0)
    
    " Ripgrep advanced
    function! RipgrepFzf(query, fullscreen) abort
        let command_fmt = 'rg --column --line-number --no-heading --color=always --smart-case %s || true'
        let initial_command = printf(command_fmt, shellescape(a:query))
        let reload_command = printf(command_fmt, '{q}')
        let spec = {'options': ['--phony', '--query', a:query, '--bind', 'change:reload:'.reload_command]}
        call fzf#vim#grep(initial_command, 1, fzf#vim#with_preview(spec), a:fullscreen)
    endfunction
    
    command! -nargs=* -bang RG call RipgrepFzf(<q-args>, <bang>0)
    
    " Git grep
    command! -bang -nargs=* GGrep
        \ call fzf#vim#grep(
        \   'git grep --line-number '.shellescape(<q-args>), 0,
        \   fzf#vim#with_preview({'dir': systemlist('git rev-parse --show-toplevel')[0]}), <bang>0)
    

    I tweaked it only a tad, but with something you'll probably enjoy because it's fzf paradigm to set switchbuf=usetab (or useopen). It lets Vim use a window where the buffer is already opened rather than the current.

    let g:fzf_buffers_jump = 1      " [Buffers] to existing split
    

    I hope it helps!