Crate nucleo_picker

Crate nucleo_picker 

Source
Expand description

§A generic fuzzy item picker

This is a generic picker implementation based on the nucleo::Nucleo matching engine. The crate allows you to incorporate an interactive fuzzy picker TUI (similar in spirit to the very popular fzf) into your own applications.

In short, initialize a Picker using PickerOptions and describe how the items should be represented by implementing Render, or use a built-in renderer.

For more complex use-cases and integration with an existing application, see the event module.

§Usage examples

For more usage examples, visit the examples folder on GitHub.

§fzf example

Run this example with cat myfile.txt | cargo run --release --example fzf.

//! # Simple `fzf` clone
//!
//! Read lines from `stdin` in a streaming fashion and populate the picker, imitating the basic
//! functionality of [fzf](https://github.com/junegunn/fzf).
use std::{
    io::{self, IsTerminal},
    process::exit,
    thread::spawn,
};

use argh::FromArgs;
use nucleo_picker::{CaseMatching, PickerOptions, render::StrRenderer};

/// A basic fzf clone with support for a few options.
#[derive(FromArgs)]
struct Args {
    /// reverse the order of input items
    #[argh(switch)]
    tac: bool,

    /// layout: 'default' or 'reverse'
    #[argh(option, default = "String::from(\"default\")")]
    layout: String,

    /// disable sorting of results
    #[argh(switch)]
    no_sort: bool,

    /// case-insensitive matching
    #[argh(switch, short = 'i')]
    ignore_case: bool,

    /// initial query string
    #[argh(option, short = 'q', default = "String::new()")]
    query: String,
}

fn main() -> io::Result<()> {
    let args: Args = argh::from_env();

    // Configure picker options based on command-line flags
    let mut options = PickerOptions::new()
        .reverse_items(args.tac)
        .sort_results(!args.no_sort)
        .query(args.query);

    if args.layout == "reverse" {
        options = options.reversed(true);
    } else if args.layout != "default" {
        eprintln!(
            "Invalid layout option: {}. Valid choices are 'default' or 'reverse'",
            args.layout
        );
        exit(1);
    }

    if args.ignore_case {
        options = options.case_matching(CaseMatching::Ignore);
    }

    let mut picker = options.picker(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(())
}

§find example

Run this example with cargo run --release --example find ~.

//! # Non-blocking `find`-style picker
//!
//! Iterate over directories to populate the picker, but do not block so that
//! matching can be done while the picker is populated.
use std::{borrow::Cow, env::args, io, path::PathBuf, thread::spawn};

use ignore::{DirEntry, WalkBuilder, WalkState};
use nucleo_picker::{PickerOptions, Render};

pub struct DirEntryRender;

impl Render<DirEntry> for DirEntryRender {
    type Str<'a> = Cow<'a, str>;

    /// Render a `DirEntry` using its internal path buffer.
    fn render<'a>(&self, value: &'a DirEntry) -> Self::Str<'a> {
        value.path().to_string_lossy()
    }
}

fn main() -> io::Result<()> {
    let mut picker = PickerOptions::default()
        // Enable bonuses for paths.
        .match_paths()
        // Use our custom renderer for a `DirEntry`
        .picker(DirEntryRender);

    // "argument parsing"
    let root: PathBuf = match args().nth(1) {
        Some(path) => path.into(),
        None => ".".into(),
    };

    // populate from a separate thread to avoid locking the picker interface
    let injector = picker.injector();
    spawn(move || {
        WalkBuilder::new(root).build_parallel().run(|| {
            let injector = injector.clone();
            Box::new(move |walk_res| {
                if let Ok(dir) = walk_res {
                    injector.push(dir);
                }
                WalkState::Continue
            })
        });
    });

    match picker.pick()? {
        // the matched `entry` is `&DirEntry`
        Some(entry) => println!("Path of selected file: '{}'", entry.path().display()),
        None => println!("No file selected!"),
    }

    Ok(())
}

Re-exports§

pub use nucleo;

Modules§

error
Errors during interactive picker usage
event
Extended event handling
render
Renderers for use in a Picker

Structs§

Injector
A handle which allows adding new items to a Picker.
Picker
A fuzzy matching interactive item picker.
PickerOptions
Specify configuration options for a Picker.

Enums§

CaseMatching
How to treat a case mismatch between two characters.
Normalization
How to handle Unicode Latin normalization.

Traits§

Render
A trait which describes how to render objects for matching and display.