matchmaker-cli 0.0.18

Command-line interface for the matchmaker fuzzy finder
# Matchmaker [![Crates.io]https://img.shields.io/crates/v/matchmaker-cli]https://crates.io/crates/matchmaker-cli [![License: AGPL v3]https://img.shields.io/badge/License-AGPL%20v3-blue.svg]https://github.com/squirreljetpack/matchmaker/blob/main/matchmaker-cli/LICENSE

Matchmaker is fast, configurable and intuitive fuzzy searcher.

It takes inspiration from [fzf](https://github.com/junegunn/fzf) in features and design, but reimagines the user experience. Built from the ground up in Rust, it brings a fully robust, modern and elegant search experience to the console.

![screen1](https://github.com/Squirreljetpack/matchmaker/blob/main/matchmaker-lib/assets/screen1.png)

## Features

- Matching with [nucleo]https://github.com/helix-editor/nucleo.
- _Fully_ configurable via a type-checked [toml file]./matchmaker-cli/assets/config.toml. [^11]
- A minimal yet powerful [syntax]./matchmaker-cli/assets/docs/options.md for overriding the configuration on the command line.
- Interactive preview supports color, scrolling, wrapping, multiple layouts, and even maximizing.
- Most of the familiar actions from [fzf]https://github.com/junegunn/fzf, as well as several new ones[^12].
- Mouse (location aware) scrolling! Horizontal scrolling! Grapheme-aware input wrapping!
- Extremely intelligent[^18] text wrapping and width sizing.
- Split input lines into multiple columns, that you can individually filter on (`%col query`[^17]), hide, and highlight.[^14]
- Define `Execute/Preview/Print/Accept` actions with [templates]https://github.com/Squirreljetpack/matchmaker/blob/main/matchmaker-cli/assets/docs/template.md which _safely_ inject the current item(s) (yes, columns are supported here too).
- All the dynamic UI support you could hope for: preview offsets, styled status lines, responsive header tables, wrapped footers, active and inactive column colors, stacked columns, multiple preview layouts[^15]... even overlays! (in the library).
- Bind keys to multiple actions, bind actions to mouse triggers, bind actions to event triggers, bind keys to rebind keys, bind keys to modify the configuration, bind keys to run a shell script and use its output to more keys, bind keys to set the header, footer, status, input, bind keys to semantic triggers, bind semantic triggers to actions, bind keys to -- wait nope thats about it.
- Comprehensive logging in case you need to debug applications.
- oh yeah, and `mm --last-key` gives you the last key that was pressed in a previous run of the program.[^13]
- Available as a rust library to use in your own code!

On the way:

- Matching with [frizbee]https://github.com/saghen/frizbee, a faster, typo-resistant matching algorithm.

[^11]: The benefits of a structured, hierarchical, global baseline configuration are many, including but not limited to the fact that toml strings make it much easier to bind keys to complex shell scripts.

[^12]: Custom exit codes, select all (`CycleAll`), PageUp/Down, Show Help, Cycle columns (`NextColumn`), Multiple input commands (`ReloadNext`), etc. ...

[^13]: This is useful for when you want to write a shell script that dispatches different actions on the output of matchmaker based on the key that was pressed.

[^14]: Column names must be alphanumeric. If no column names are configured, the autogenerated column names are sequential: 1, 2, 3...

[^15]: I like this so much i had to mention it twice

[^17]: https://github.com/Squirreljetpack/matchmaker/blob/main/matchmaker-cli/assets/docs/query.md

[^18]: There isn't a phd for text wrapping and width sizing, but if there was, I'd for sure claim it had one.

## Installation

```sh
# requires cargo
cargo install matchmaker-cli
```

Or grab it from the releases page with extra features:

```sh
curl -fsSL https://raw.githubusercontent.com/Squirreljetpack/matchmaker/main/install.sh | sh
```

Pass it some items:

```sh
find . | mm
```

> [!NOTE]
> The [default]./matchmaker-cli/assets/config.toml input and preview commands rely on fd, bat and eza. For an optimal experience, install them or update your configuration.

## Configuration

To begin, you can dump the default configuration to a file:

```sh
matchmaker --dump-config
```

The default locations are in order:

- `~/.config/matchmaker/config.toml` (If the folder exists already).
- `{PLATFORM_SPECIFIC_CONFIG_DIRECTORY}/matchmaker` (Generally the same as above when on linux)

Matchmaker options are hierarchical, although most categories live at the top level:

```toml
[preview]
    show = true
    wrap = true
    header_lines = 3 # sticky the top 3 lines

# Full specification of (the default values of) a single layout. Multiple layouts can be specified.
[[preview.layout]]
    command    = ""
    side       = "right"
    percentage = 60
    min        = 30
    max        = 120
```

The structure of the config file is defined [here](./matchmaker-cli/src/config.rs)[^1], and the full specification lives [here](./matchmaker-lib/src/config.rs)[^2]. You can also view your current config using `mm --dump-config | cat`[^30].

Options can be overridden on the command line, where abbreviations are supported:

```sh
mm p.l "cmd=echo []|||p=50|||max=20" cmd "ls" o "[=]"

# 1. Start mm with the following overrides:
# 2. List the contents of the current directory by executing `ls`
# 3. Show the current item name in the preview pane
# 4. Set a preferred percentage of 50 for the preview pane, but a column width of 20
# 5. Output the result without single quotes
```

[^1]: Note that the flatten attribute on the render field means that the subfields of RenderConfig should be specified at the top level of the toml (i.e. your toml should specify `[results]` instead of `[render.results]`).

[^2]: and parts of it [here]./matchmaker-lib/src/config-types.rs.

[^30]: Beware that without piping, this overwrites your config location with the default config!

### Keybindings

Actions can be defined in your `config.toml` or on the command line.

The list of currently supported actions can be found [here](./matchmaker-lib/src/action.rs) and [here](./matchmaker-cli/src/action.rs) or from `mm --doc binds`.

To get the names of keys, type `mm --test-keys`.

In addition to keys, actions can also be bound to Events and Crossterm events (check your default config for details).

## CLI

See [here](./matchmaker-cli/assets/docs/options.md) for the command-line syntax.

Matchmaker aims to achieve feature-parity with fzf (though not necessarily by the same means). If there's any specific feature that you'd like to see, open an issue!

## Examples

Examples can be found [here](https://github.com/Squirreljetpack/matchmaker/tree/main/matchmaker-cli/assets) (toml files), and [here](https://github.com/Squirreljetpack/matchmaker/tree/main/matchmaker-lib/examples) (library use).

Currently, the first includes an example for interactively performing a full text search with [ripgrep](https://github.com/BurntSushi/ripgrep):

- Toggle between ripgrep and mm with `ctrl-r`
- The displayed preview autoscrolls to matched line: `?` to toggle.
- `Enter` opens the file in your editor.
- Previous queries in each mode are stashed and restored upon switching.
- `ctrl-.` to cycle between columns.

![ripgrep](https://github.com/Squirreljetpack/matchmaker/blob/main/matchmaker-lib/assets/ripgrep.png)

```shell
# Try it yourself
mkdir -p ~/.config/matchmaker/presets
curl -L https://raw.githubusercontent.com/Squirreljetpack/matchmaker/main/matchmaker-cli/assets/rg.toml -o ~/.config/matchmaker/presets/rg.toml
mm --config ~/.config/matchmaker/presets/rg.toml
```

### Migrating from fzf

Migrating from `fzf` to `mm` is conceptually straightforward because the two tools are almost fully feature-compatible. You can continue using familiar actions, like `execute`, and they will function the same way.

The main difference is **syntax**. For example, opening a selected file in your editor:

- In `fzf`:

```bash
fzf --bind "ctrl-o:execute($EDITOR {+})"
```

- In `mm`:

```text
mm b.ctrl-o="Execute($EDITOR [+])"
```

> [!NOTE]
> The braces are changed to `[]` because templating and column splitting is slightly more powerful in mm. Since `[]` templates cannot contain whitespace, this won't interfere with shell conditionals.

Here is a second demonstration, taken from [zoxide](https://github.com/ajeetdsouza/zoxide/blob/main/src/cmd/query.rs).

- In `fzf`:

```shell
fzf \
  --exact \
  --no-sort \
  --bind=ctrl-z:ignore,btab:up,tab:down \
  --cycle \
  --keep-right \
  --border=sharp \
  --height=45% \
  --info=inline \
  --layout=reverse \
  --tabstop=1 \
  --exit-0
```

- In `mm`:

```shell
mm \
  matcher.sort_threshold=0 \
  binds.Shift-BackTab=Up \
  binds.BackTab=Up \
  binds.Tab=Down \
  ui.border.type=Plain \
  tui.percentage=45 \
  results.reverse=true \
  results.wrap=false \
  results.autoscroll.end=true \
  results.autoscroll.context=0 \
  exit.abort_empty=true

# Notes:
# - in mm, results.scroll_wrap is by default true, and results.wrap is true in the default config.
# - in mm --multi (from fzf) is always true. It can be disabled by not binding the Select actions, as is done here.
# - matcher.sort is not available on the cargo version and requires the installer.
# - results.autoscroll.context=0 is a setting which does not appear in fzf but which is 4 by default in mm.

# short version, (with some omissions)
mm m.sort=0 b.Shift-BackTab=Up b.BackTab=Up b.Tab=Down \
ui.border.type=Plain tui.p=45 \
r.r= r.w=false r.a.e= e.abort_empty=
```

For quick reference, `mm --doc` provides fairly readable and comprehensive guides to various topics.

## Library

Matchmaker can also be used as a library.

```sh
cargo add matchmaker
```

### Example

Here is how to use `Matchmaker` to select from a list of strings.

```rust
use matchmaker::nucleo::{Indexed, Worker};
use matchmaker::{MatchError, Matchmaker, Result, Selector};

#[tokio::main]
async fn main() -> Result<()> {
    let items = vec!["item1", "item2", "item3"];

    let worker = Worker::new_single_column();
    worker.append(items);
    let selector = Selector::new(Indexed::identifier);
    let mm = Matchmaker::new(worker, selector);

    match mm.pick_default().await {
        Ok(v) => {
            println!("{}", v[0]);
        }
        Err(err) => match err {
            MatchError::Abort(1) => {
                eprintln!("cancelled");
            }
            _ => {
                eprintln!("Error: {err}");
            }
        },
    }

    Ok(())
}
```

For more information, check out the [examples](./matchmaker-lib/examples/) and [Architecture.md](./matchmaker-lib/ARCHITECTURE.md)

# See also

- [junegunn/fzf]https://github.com/junegunn/fzf
- [helix-editor/nucleo]https://github.com/helix-editor/nucleo
- [ratatui]https://github.com/ratatui/ratatui
- [Canop/crokey]https://github.com/Canop/crokey
- [skim-rs/skim]https://github.com/skim-rs/skim
- [autobib/nucleo-picker]https://github.com/autobib/nucleo-picker
- [alexpasmantier/television]https://github.com/alexpasmantier/television
- [helix-editor/helix]https://github.com/helix-editor/helix