/vim-select

Vim fuzzy file/buffer/mru selector plugin

Primary LanguageVim ScriptMIT LicenseMIT

vim-select: fuzzy select file/buffer/MRU/etc

Note
Plugin works with recent (nightly) vim having matchfuzzypos() function and prompt-buffer feature implemented.

For project files, buffers and MRU it lets you narrow down the list with fuzzy matching and select with <CR> or <Tab> if only 1 candidate is in the list.

For files it will let you go to parent directory with <BS> and to selected directory with <CR>.

Project file search uses either fd or ripgrep (with fallback to find on non-windows) tool so be sure you have one of them installed on your system. By default it uses vim current working directory as a project root.

99876845 bac99400 2c0a 11eb 87cf e881690b20c7
Note
Extensions are available: https://github.com/habamax/vim-select-more

Installation

If you use package/plugin manager, consult their documentation.

For manual installation clone this repository to your ~/.vim/pack/plugins/start/ path, where plugins subdirectory is arbitrary.

Note
for windows users replace ~/.vim to ~/vimfiles.

Mappings

There are no default global mappings — create your own.

Safe (will not create mappings if plugin was not loaded)
" add it to ~/.vim/after/plugin/select.vim
if exists("g:loaded_select")
    nmap <leader>fe <Plug>(SelectFile)
    nmap <leader>ff <Plug>(SelectProjectFile)
    nmap <leader>fp <Plug>(SelectProject)
    nmap <leader>b <Plug>(SelectBuffer)
    nmap <leader>m <Plug>(SelectMRU)
endif

or

Regular
" add it to your vimrc
nmap <leader>fe <Plug>(SelectFile)
nmap <leader>ff <Plug>(SelectProjectFile)
nmap <leader>fp <Plug>(SelectProject)
nmap <leader>b <Plug>(SelectBuffer)
nmap <leader>m <Plug>(SelectMRU)

Select window has default mappings:

  • <CR> to open/execute current item.

  • <S-CR> or <C-s> to open current item in split.

  • <C-v> to open current item in vertical split.

  • <C-t> to open current item in a tab.

  • <C-j> to create a new file out of prompt value. Only available for :Select file.

  • <ESC> to close the select window.

  • <TAB> select next item in the list, for a single item it will open/execute it.

  • <BS> open parent directory. Only for Select file.

  • <C-n> or <Down> select next item.

  • <C-p> or <Up> select previous item.

  • <PageUp> scroll up select window.

  • <PageDown> scroll down select window.

Commands

  • :Select file from current file directory.

  • :Select file from current file directory. Or from directory passed as an argument.

  • :Select projectfile from current working directory(and sub-directories). Or from directory passed as an argument.

    Note
    External tool fd or ripgrep is required. On non-windows find would be used if none is installed.
  • :Select project from the list of projects and run :Select projectfile on it. Each project is a current working directory where :Select projectfile was run and a file was selected. The list is persisted in ~/.vim/.selectprojects or ~/.selectprojects file.

  • :Select buffer from buffer list.

  • :Select mru from v:oldfiles.

Extending

First of all create an "extension point":

let g:select_info = get(g:, "select_info", {})

And then…​

Basic example

Let’s select something from the list and echo it in vim:

let g:select_info.test = {}
let g:select_info.test.data = {-> ['hello', 'from', 'vim-select', 'plugin']}
let g:select_info.test.sink = "echomsg '%s'"

Then with the command :Select test you can select a value from the list and see it was echoed as a vim message.

%s would be substituted with the selected value in the sink string parameter.

You can also provide a dict with action there, like:

func! ShowMessage(msg) abort
    echom a:msg
endfunc

let g:select_info.test = {}
let g:select_info.test.data = {-> ['hello', 'from', 'vim-select', 'plugin']}
let g:select_info.test.sink = {"action": {v -> ShowMessage(v)}}

Show highlight group

let g:select_info.highlight = {}
let g:select_info.highlight.data = {-> getcompletion('', 'highlight')}
let g:select_info.highlight.sink = {"action": {v -> feedkeys(':hi '..v.."\<CR>", "nt")}}

Then use :Select highlight to select and show syntax highlight group parameters.

Loading sessions

Imagine you have all your sessions saved in ~/.vimdata/sessions folder. I do have them there and usually create session with a helper command:

command! -nargs=1 S :mksession! ~/.vimdata/sessions/<args>

Then just a simple :S my_another_project to persist a session.

Now to narrow down and source/apply a session you can setup select plugin with:

let g:select_info.session = {}
let g:select_info.session.data = {-> map(glob("~/.vimdata/sessions/*", 1, 1), {_, v -> fnamemodify(v, ":t")})}
let g:select_info.session.sink = "%%bd | source ~/.vimdata/sessions/%s"
nnoremap <leader>fs :Select session<CR>

Play mp3. Yes, mp3s.

Funny thing, vim can play mp3s, so just for fun we can select a music file and play it:

let g:select_info.sound = {}
let g:select_info.sound.data = {"job": "rg --files --glob *.mp3"}
let g:select_info.sound.sink = {"transform": {p, v -> p..v}, "action": {v -> sound_playfile(v)}}

Having this you can :Select sound ~/Music, select and play mp3 file.

A new key "transform" is to apply additional logic for a value to be passed for an action. It receives a current working directory path and a selected value. In this example the value is transformed to be a full path to a mp3 file.

Filetype specific example

There is b:select_info you can use in the same way as g:select_info.

For example I would like to be able to select and run Godot scene and it should only be availble in gdscript files.

Just add to ~/.vim/after/ftplugin/gdscript.vim:

let b:select_info = {"godotscene": {}}
let b:select_info.godotscene.data = {"job": "rg --files --glob *.tscn"}
let b:select_info.godotscene.sink = {"transform": {_, v -> fnameescape(v)}, "action": "GodotRun %s"}
nnoremap <buffer> <leader><leader>f :Select godotscene<CR>