Selecta is a fuzzy selector. You can use it for fuzzy selection in the style of Command-T, ctrlp, etc. You can also use it to fuzzy select anything else: command names, help topics, identifiers; anything you have a list of.
It was originally written to select things from vim, but it has no dependency on vim at all and is used for many other purposes. Its interface is dead simple:
For example, you can say:
cat $(ls *.txt | selecta)
which will prompt the user to fuzzy-select one of the text files in the current directory, then print the contents of whichever one they choose. It looks like this:
Selecta supports these keys:
Selecta is unusual in that it's a filter (it reads from/to stdin/stdout), but
it's also an interactive program (it accepts user keystrokes and draws a UI).
It directly opens /dev/tty
to do the latter.
With that exception aside, Selecta is a normal, well-behaved Unix tool. If a selection is made, its path will be written to stdout with a single trailing newline. If no selection is made (meaning the user killed Selecta with ^C), it will write nothing to stdout and exit with status code 1.
Because it's just a filter program, Selecta doesn't know about any other tools. Specifically, it does not:
The ranking algorithm is:
Some concrete examples: - "ct" will match "cat" and "Crate", but not "tack". - "le" will match "lend" and "ladder". "lend" will appear higher in the results because the matching substring is shorter ("le" vs. "ladde").
Selecta requires Ruby 1.9.3 or better.
To install on your Mac using Homebrew, say:
shell
brew install selecta
For other systems, copy the selecta
script to your path. ~/bin is a great
place for it. If you don't currently have a ~/bin, just do mkdir ~/bin
and
add export PATH="$HOME/bin:$PATH"
to your .zshrc, .bashrc, etc.
Selecta is not installable as a Ruby gem! Gems are only good for application-specific tools. You want Selecta available at all times. If it were a gem, it would sometimes disappear when switching Rubies, gemsets, etc.
There's no vim plugin. It may not end up needing one; we'll see. For now, you
can just stick the code below in your .vimrc to invoke Selecta with <leader>f
(this will be \f
unless you've changed your leader). Note that Selecta has to
run in a terminal by its nature, so this Vim example will not work in graphical
Vims like MacVim.
```vimscript " Run a given vim command on the results of fuzzy selecting from a given shell " command. See usage below. function! SelectaCommand(choicecommand, selectaargs, vimcommand) try let selection = system(a:choicecommand . " | selecta " . a:selectaargs) catch /Vim:Interrupt/ " Swallow the ^C so that the redraw below happens; otherwise there will be " leftovers from selecta on the screen redraw! return endtry redraw! exec a:vimcommand . " " . selection endfunction
" Find all files in all non-dot directories starting in the working directory.
" Fuzzy select one of those. Open the selected file with :e.
nnoremap
$ git branch | cut -c 3- | selecta | xargs git checkout
When you say git branch
, you get something like this:
* master
mytopic
release
The cut
removes those first two columns. Then we just select a branch and use
it as an argument to git checkout
.
When you put your cursor anywhere in the word "User" and press <c-g>
, this
mapping will open Selecta with the search box pre-populated with "User". It's
a quick and dirty way to find files related to an identifier.
vimscript
function! SelectaIdentifier()
" Yank the word under the cursor into the z register
normal "zyiw
" Fuzzy match files in the current directory, starting with the word under
" the cursor
call SelectaCommand("find * -type f", "-s " . @z, ":e")
endfunction
nnoremap <c-g> :call SelectaIdentifier()<cr>
You can use Selecta to write a simple shell function that will allow you to quickly switch to a particular project's directory. For example, supposing you have projects under both ~/rails-projects and ~/django-projects:
shell
proj() {
cd $(find ~/rails-projects ~/django-projects -maxdepth 1 -type d | selecta)
}
This only works with zsh (patches to make it work with Bash would be
appreciated). Put it in your ~/.zshrc. Then, whenever you press ^S, zsh will
run find * -type f | selecta
and append the resulting selected path to the
current command line.
Caveats: 1) You're running find
in the current working directory. If you do
it in a large directory, like ~, then it's going to take a while. 2) This also
disables flow control to free up the ^S keystroke. If you normally use ^S and
^Q, you may want to map to a different key and remove the unsetopt
flowcontrol
.
```shell
unsetopt flowcontrol
function insert-selecta-path-in-command-line() { local selectedpath # Print a newline or we'll clobber the old prompt. echo # Find the path; abort if the user doesn't select anything. selectedpath=$(find * -type f | selecta) || return # Append the selection to the current command buffer. eval 'LBUFFER="$LBUFFER$selected_path"' # Redraw the prompt since Selecta has drawn several new lines of text. zle reset-prompt }
zle -N insert-selecta-path-in-command-line
bindkey "^S" "insert-selecta-path-in-command-line" ```
Won't this be slow?
Nope: startup and termination together burn about 23 ms of CPU on my machine (a mid-2011 MacBook Air). File finding may be slow, but, speaking of that...
What about caching, selecting only certain file types, etc.?
Those are different problems. This is just a simple fuzzy finding user interface. It could be combined with caching, etc. I don't use caching myself, prefering to keep repos small. Selecta was partially motivated by frustration with existing fuzzy file finders, which occasionally showed incorrect results with caching turned on but were painfully slow with caching disabled.