30 releases
Uses new Rust 2024
| 0.11.1 | Jan 3, 2026 |
|---|---|
| 0.11.0-rc.3 | Dec 31, 2025 |
| 0.9.0 | Sep 2, 2025 |
| 0.8.1 | Feb 7, 2025 |
| 0.5.0 | Nov 7, 2024 |
#99 in Command-line interface
67 downloads per month
Used in 5 crates
250KB
4.5K
SLoC
nucleo-picker
A native Rust library which enables you to incorporate a highly performant and Unicode-aware fuzzy picker directly in your own terminal application.
This library provides a TUI for the nucleo crate with an interface similar to the fzf command-line tool.
- For implementation examples, jump to the fzf example or see the
examplesdirectory. - For documentation of interactive usage of the picker, see the
USAGE.mdfile. - For a list of recent changes, see the
CHANGELOG.mdfile.
Elevator pitch
Why use this library instead of a general-purpose fuzzy-finder such as fzf or a lower level library such as nucleo?
- Much tighter integration between your data source and your application.
Instead of reading from a SQLite database with
sqlite3and then parsing raw text, read directly into in-memory data structures withrusqliteand render the in-memory objects in the picker. - Skip the subprocess overhead and improve startup time.
Instead of starting up a subprocess to call
fzf, have the picker integrated directly into your binary. - Distinguish items from their matcher representation.
Instead of writing your data structure to a string, passing it to
fzf, and then parsing the resulting match string back into your data structure, directly obtain the original data structure when matching is complete. - Don't spend time debugging terminal rendering edge cases.
Out-of-the-box,
nucleo-pickerhandles terminal rendering subtleties such as multiline rendering, double-width Unicode, automatic overflow scrollthrough, and grapheme-aware query input so you don't have to. - Handle complex use cases using events.
nucleo-pickerexposes a fully-featured event system which can be used to drive the picker. This lets you customize keybindings, support interactive restarts, and much more by implementing theEventSourcetrait. Simplified versions of such features are available in fzf but essentially require manual configuration via an embedded DSL.
Features
- Highly optimized matching.
- Robust rendering:
- Full Unicode handling with Unicode text segmentation and Unicode width.
- Match highlighting with automatic scroll-through.
- Correctly render multi-line or overflowed items, with standard and reversed item order.
- Responsive interface with batched keyboard input.
- Ergonomic API:
- Fully concurrent lock- and wait-free streaming of input items.
- Generic
Pickerfor any typeTwhich isSend + Sync + 'static. - Customizable rendering of crate-local and foreign types with the
Rendertrait.
- Fully configurable event system:
- Easily customizable keybindings.
- Run the picker concurrently with your application using a fully-featured event system, with optional support for complex features such as interactive restarting.
- Optional and flexible error propagation generics so your application errors can interface cleanly with the picker.
Example
Implement a heavily simplified fzf clone in 25 lines of code.
Try it out with:
cargo build --release --example fzf_basic
cat myfile.txt | ./target/release/examples/fzf_basic
The code to create the binary:
use std::{
io::{self, IsTerminal},
process::exit,
thread::spawn,
};
use nucleo_picker::{render::StrRenderer, Picker};
fn main() -> io::Result<()> {
let mut picker = Picker::new(StrRenderer);
let injector = picker.injector();
spawn(move || {
let stdin = io::stdin();
if !stdin.is_terminal() {
for line in stdin.lines() {
// silently drop IO errors!
if let Ok(s) = line {
injector.push(s);
}
}
}
});
match picker.pick()? {
Some(it) => println!("{it}"),
None => exit(1),
}
Ok(())
}
More examples can be found in the examples directory.
Feature parity with fzf
There is an extended fzf example demonstrating the current configuration options using the same syntax as fzf command-line tool.
Try it out:
cargo build --example fzf --release
./target/release/examples/fzf --help
The supported features are tracked below.
The checked examples are implemented and the unchecked examples are features I would like to support in the future.
If there are particular fzf-specific features that you would like to see supported that are not on this list, please submit an issue.
-
--multi:Picker::pick_multi -
--multi=[MAX]:PickerOptions::max_selection_count -
--reverse,--layout=(default|reverse):PickerOptions::reversed -
--no-sort:PickerOptions::sort_results -
--tac:PickerOptions::reverse_items -
--(no-)ignore-case,--smart-case:PickerOptions::case_matching -
--literal:PickerOptions::normalization -
--query:PickerOptions::query -
--preview: https://github.com/autobib/nucleo-picker/issues/5 -
--gap,--highlight-line: https://github.com/autobib/nucleo-picker/issues/91 -
--cycle -
--track -
--ghost
Related crates
This crate mainly exists as a result of the author's annoyance with pretty much every fuzzy picker TUI in the rust ecosystem. As far as I am aware, the fully-exposed event system is unique to this crate. Beyond this, here is a brief comparison:
- skim's
Arc<dyn SkimItem>is inconvenient for a variety of reasons.skimalso has a large number of dependencies and is designed more as a binary than a library. - fuzzypicker is based on
skimand inheritsskim's problems. - nucleo-ui only has a blocking API and only supports matching on
String. It also seems to be un-maintained. - fuzzy-select only has a blocking API.
- dialoguer
FuzzySelectonly has a blocking API and only supports matching onString. The terminal handling also has a few strange bugs.
Disclaimer
There are a currently a few known problems which have not been addressed (see the issues page on GitHub for a list). Issues and contributions are welcome!
Dependencies
~6–12MB
~234K SLoC