neek-core 1.0.0

Core fuzzy matching and scoring engine
Documentation
  • Coverage
  • 100%
    28 out of 28 items documented3 out of 7 items with examples
  • Size
  • Source code size: 61.21 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 407.52 kB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 3s Average build duration of successful builds.
  • all releases: 3s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • NotAShelf

neek

This is a high-performance, interactive fuzzy finder for the terminal that started as a faithful reimplementation of jhawthorn/fzy with SIMD-accelerated matching via memchr and parallel candidate scoring via Rayon. The workspace itself features two crates. Those are neek-core, the no_std core library that requires only alloc, and the interactive TUI with raw terminal mode, and ANSI highlighting of matched positions,.

Features

  • Non-interactive filter mode - drop‑in replacement for grep‑style pipelines: read candidates from stdin, output the top matches to stdout.
  • Gotoh‑style affine‑gap scoring - prefers consecutive letters, word boundaries (/, -, _, ``), CamelCase transitions, and dots. Produces more intuitive rankings than length‑based sorters.
  • SIMD matching - has_match uses memchr::memchr2 (SSE4.2 / AVX2 on x86‑64) for per‑needle‑character scanning, 4–8 times faster than the original C strpbrk loop.
  • #![no_std] core library - the neek-core crate only requires alloc, making it usable in embedded or kernel contexts.
  • Parallel search - rayon::par_iter distributes has_match + score across all CPU cores.

Installation

Nix is the recommended way of downloading (and developing!) neek. You can install it using Nix flakes using nix profile add if on non-nixos or add neek as a flake input if you are on NixOS.

{
  # Add neek to your inputs like so:
  inputs.neek.url = "github:NotAShelf/neek";

  outputs = { /* ... */ };
}

Then you can get the package from your flake input, and add it to your packages to make neek available in your system.

{inputs, pkgs, ...}: let
  neekPkg = inputs.neek.packages.${pkgs.stdenv.hostPlatform}.neek;
in {
  environment.systemPackages = [neekPkg];
}

If you want to test out the program before running it, nix shell will give you a pretty good sandbox to play with it.

# Enter a development shell with 'neek' from the repository:
$ nix shell github:notashelf/neek

# Then use it normally:
$ find . -type f | neek

Without Nix

You can also install neek on any of your systems without using Nix. The easiest method is to build and install from source with Cargo:

cargo install neek --locked

Additionally, you may get neek from source via cargo install using cargo install --git https://github.com/notashelf/neek --locked or you may check out to the repository, and use Cargo to build it. You'll need Rust 1.85.0 or above. Most distributions should package this version already. You may, of course, prefer to package the built releases if you'd like.

Usage

neek [OPTIONS]

For example, to pipe candidates on stdin:

# Use neek with find interactively
$ find . -type f | neek

Or to filter non‑interactively:

$ find . -type f | neek -e 'readme'

Interactive keybindings

Key Action
Enter / Ctrl-M Accept selection
Esc / Ctrl-C / Ctrl-D / Ctrl-G Exit
Ctrl-P / Ctrl-K / Previous match
Ctrl-N / Ctrl-J / Next match
Tab / Ctrl-I Autocomplete search to selection
Backspace / Ctrl-H / Del Delete character before cursor
Ctrl-W Delete word before cursor
Ctrl-U Delete to beginning of line
/ Move cursor (jumps word boundaries)
Ctrl-A / Home Beginning of line
Ctrl-E / End End of line
Page Up / Page Down Scroll one page

Scoring algorithm

We use a variant of Gotoh's algorithm (Smith–Waterman–Gotoh with affine gap penalties). Only insertions into the needle (gaps in the haystack) are permitted. There is no delegation and no substitutions.

Two DP matrices are maintained:

  • D[i][j] -> best score ending with a match at haystack[j] for needle[0..=i]
  • M[i][j] -> best possible score for needle[0..=i] against haystack[0..=j]

D tracks consecutive‑match bonuses; M propagates the maximum score along the row with per‑row gap penalties.

The score() function reserves only two rows of each matrix (O(m) space) using in‑place pointer aliasing, and avoids zero‑initialisation with MaybeUninit. The score_positions() variant stores the full DP table for backtracking when match positions are needed for highlighting. Further details can be found in the algorithm document. You're also welcome, invited even, to correct my math :)

License

This project is made available under Mozilla Public License (MPL) version 2.0. See LICENSE for more details on the exact conditions. An online copy is provided here.