Skip to content
Merged
Show file tree
Hide file tree
Changes from 78 commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
ba10498
feat: initial work on skim v3
LoricAndre Feb 21, 2026
7486189
wip: SW
LoricAndre Feb 22, 2026
33ed2b4
chore: refactor SkimV3 to make it more maintainable
LoricAndre Feb 22, 2026
4b0e3e7
chore: remove SIMD batch scores
LoricAndre Feb 22, 2026
f4eac16
fix: fix Skim V3 tests
LoricAndre Feb 22, 2026
3785ed4
feat: small optimizations
LoricAndre Feb 23, 2026
6197b53
feat: bigger optimizations
LoricAndre Feb 23, 2026
4d9043f
chore: generate completions & manpage
Feb 23, 2026
d9e6fa9
chore: remove unused wide dependency
LoricAndre Feb 23, 2026
41b797c
Merge branch 'master' into skim-v3
LoricAndre Feb 23, 2026
b72df58
chore: update deps
LoricAndre Feb 23, 2026
6edc4a0
Merge branch 'skim-v3' of github.com:skim-rs/skim into skim-v3
LoricAndre Feb 23, 2026
7600031
chore: generate completions & manpage
Feb 23, 2026
363e8ed
fix: make sure all subsequences pass in non-typos mode
LoricAndre Feb 23, 2026
e935622
chore: trade some performance against more precision with typos
LoricAndre Feb 23, 2026
abdef81
feat: gain the performance back using unchecked accesses
LoricAndre Feb 23, 2026
b55c61c
chore: remove failing tests
LoricAndre Feb 23, 2026
80c12f2
feat: use banding across whole upper triangle
LoricAndre Feb 24, 2026
79cc41a
chore: remove useless DEAD_COL checks
LoricAndre Feb 24, 2026
38ba9e8
feat: make sure we match everything `frizbee` does while enforcing fi…
LoricAndre Feb 24, 2026
af75390
feat: minor optimizations
LoricAndre Feb 24, 2026
026401e
feat: more minor optimizations
LoricAndre Feb 24, 2026
d54e18f
chore: tweak parameters to find a good balance between performance an…
LoricAndre Feb 24, 2026
3869905
chore: accept snap
LoricAndre Feb 24, 2026
4bfeb5b
Merge branch 'master' into skim-v3
LoricAndre Feb 24, 2026
0775be4
chore: penalize consecutive typos
LoricAndre Feb 25, 2026
61079ba
chore: revert consecutive typos penalization as it seems useless in p…
LoricAndre Feb 25, 2026
7373188
wip: optimizations
LoricAndre Feb 25, 2026
3e5051d
feat: multiple optimizations
LoricAndre Feb 26, 2026
3acacaa
perf(skim_v3): use 2-row rolling buffer for score-only DP path
LoricAndre Feb 26, 2026
073195b
perf(skim_v3): add early termination when DP rows are all-zero
LoricAndre Feb 26, 2026
3917406
perf(skim_v3): add range_dp for fuzzy_match_range, avoiding full inde…
LoricAndre Feb 26, 2026
292d0e6
perf(skim_v3): remove redundant is_subsequence scan in exact mode
LoricAndre Feb 26, 2026
ffa9a21
perf(skim_v3): avoid clone in traceback by using mem::take on thread-…
LoricAndre Feb 26, 2026
f38ca3a
perf(skim_v3): tighten typo-mode upper band bound in typo_vband_row
LoricAndre Feb 26, 2026
4efe981
perf(skim_v3): use memchr SIMD for first-char search in prefilter and…
LoricAndre Feb 26, 2026
90ffc46
revert(skim_v3): restore m upper bound in typo_vband_row
LoricAndre Feb 26, 2026
069710a
perf(skim_v3): add ASCII fast path to char::eq_ignore_case
LoricAndre Feb 26, 2026
0806683
perf(skim_v3): replace RefCell with UnsafeCell (TLCell) in thread-locals
LoricAndre Feb 26, 2026
31e4633
fix(skim_v3): fix precompute_bonuses reserve logic
LoricAndre Feb 26, 2026
58c470a
guard: return None for pat.len() > MAX_PAT_LEN in exact mode
LoricAndre Feb 26, 2026
032529d
perf: re-encode Dir::None=0 so CELL_ZERO is all-zero bytes
LoricAndre Feb 26, 2026
d79947f
perf: 128-bit ASCII bitset for cheap_typo_prefilter tail scan
LoricAndre Feb 26, 2026
2972155
perf: early exit in count_tail_present_ordered when match is impossible
LoricAndre Feb 26, 2026
928afaa
cleanup: remove unused constants SEPARATOR_MASK_LO/HI and FIRST_CHAR_…
LoricAndre Feb 26, 2026
8805fa1
refactor: replace unsafe transmute in Cell::dir() and compute_cell wi…
LoricAndre Feb 26, 2026
175f26a
perf: Atom::is_sep() trait method avoids u8→char→u32 in separator check
LoricAndre Feb 26, 2026
ff5192f
refactor: precompute_bonuses rewritten as safe iterator chain
LoricAndre Feb 26, 2026
0fb7f05
refactor: extract match_slices_range; simplify run_range
LoricAndre Feb 26, 2026
9c8571e
mem: SWMatrix::resize shrinks when buffer is 4× over-allocated
LoricAndre Feb 26, 2026
0619e76
Revert "mem: SWMatrix::resize shrinks when buffer is 4× over-allocated"
LoricAndre Feb 26, 2026
2a632de
Revert "refactor: replace unsafe transmute in Cell::dir() and compute…
LoricAndre Feb 26, 2026
772bdb2
Revert "perf: Atom::is_sep() trait method avoids u8→char→u32 in separ…
LoricAndre Feb 26, 2026
39ee336
Revert "perf: early exit in count_tail_present_ordered when match is …
LoricAndre Feb 26, 2026
c882c5b
Revert "perf: 128-bit ASCII bitset for cheap_typo_prefilter tail scan"
LoricAndre Feb 26, 2026
e69645c
Revert "refactor: extract match_slices_range; simplify run_range"
LoricAndre Feb 26, 2026
6a9edb1
Revert "perf(skim_v3): replace RefCell with UnsafeCell (TLCell) in th…
LoricAndre Feb 26, 2026
a9aff98
Revert "perf(skim_v3): add ASCII fast path to char::eq_ignore_case"
LoricAndre Feb 26, 2026
1b3b2be
Revert "revert(skim_v3): restore m upper bound in typo_vband_row"
LoricAndre Feb 26, 2026
d335bd3
Revert "perf(skim_v3): tighten typo-mode upper band bound in typo_vba…
LoricAndre Feb 26, 2026
582b5ad
Revert "perf(skim_v3): avoid clone in traceback by using mem::take on…
LoricAndre Feb 26, 2026
dff6456
Revert "perf(skim_v3): add early termination when DP rows are all-zero"
LoricAndre Feb 26, 2026
2b10b02
Revert "perf(skim_v3): use 2-row rolling buffer for score-only DP path"
LoricAndre Feb 26, 2026
ff7b77c
fix: reverse only order of frizbee indices
LoricAndre Feb 26, 2026
880448a
chore: rename & refactor into multiple files
LoricAndre Feb 26, 2026
9892c2b
Merge branch 'master' into skim-v3
LoricAndre Feb 26, 2026
63e34cb
chore: optimizations to the main flow
LoricAndre Feb 26, 2026
e1a3888
fix: correct banding in non-typo path
LoricAndre Feb 26, 2026
d2b3faa
chore: generate completions & manpage
Feb 26, 2026
2a8d747
docs: add algorithms section to the README [skip ci]
LoricAndre Feb 26, 2026
3f882ac
fix(ari): correctly bound vband low
LoricAndre Feb 26, 2026
6dae55c
chore(ari): specific pre-separator bonuses
LoricAndre Feb 27, 2026
a2ffb98
fix(ari): boost consec a bit more to beat start/sep
LoricAndre Feb 27, 2026
654b912
chore: generate completions & manpage
Feb 27, 2026
c9c54e0
feat: run matcher over chunks
LoricAndre Feb 27, 2026
f8875ba
chore: adjust penalties to keep typos under subsequences
LoricAndre Feb 28, 2026
58f9424
chore: accept snapshot
LoricAndre Feb 28, 2026
9046330
fix: replace greedy ordered prefilter with looser unordered
LoricAndre Feb 28, 2026
ea8cd28
chore: finish up rename
LoricAndre Feb 28, 2026
11ba019
chore: review
LoricAndre Mar 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,9 @@ codecov.json
*.profraw

benches/fixtures/*.txt

# Profiling
profile.json.gz
perf.data.old
perf.data
flamegraph.svg
25 changes: 13 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ strip = true
[profile.dist]
inherits = "release"

[profile.release-debug]
inherits = "release"
debug = true
strip = false


[lib]
name = "skim"
Expand Down Expand Up @@ -57,6 +62,7 @@ thiserror = "=2.0.18"
tempfile = "=3.25.0"
crossterm = { version = ">=0.0.0", features = ["event-stream", "use-dev-tty", "libc"] }
thread_local = "=1.1.9"
memchr = "=2.8.0"
clap_complete_nushell = "=4.5.10"
interprocess = { version = "=2.4.0", features = ["tokio"] }
serde = { version = "=1.0.228", features = ["derive"] }
Expand Down Expand Up @@ -95,3 +101,7 @@ harness = false
[[bench]]
name = "partial"
harness = false

[[bench]]
name = "matcher_micro"
harness = false
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Skim provides a single executable called `sk`. Think of it as a smarter alternat
* [Interactive mode](#interactive-mode)
+ [How does it work?](#how-does-it-work)
* [Executing external programs](#executing-external-programs)
* [Algorithms](#algorithms)
* [Preview Window](#preview-window)
+ [How does it work?](#how-does-it-work-1)
* [Fields support](#fields-support)
Expand Down Expand Up @@ -84,7 +85,7 @@ The skim project contains several components:
| macOS | MacPorts | `sudo port install skim` |
| Alpine | apk | `apk add skim` |
| Arch | pacman | `pacman -S skim` |
| Fedora | COPR | see below |
| Fedora | COPR | see below |
| Gentoo | Portage | `emerge --ask app-misc/skim` |
| Guix | guix | `guix install skim` |
| Void | XBPS | `xbps-install -S skim` |
Expand Down Expand Up @@ -452,6 +453,14 @@ You can configure key bindings to start external processes without leaving Skim
sk --bind 'f1:execute(less -f {}),ctrl-y:execute-silent(echo {} | pbcopy)+abort'
```

## Algorithms

Skim offers multiple algorithms, check the help or manpage for an exhaustive list. Among them are:
- `skim_v2`, the default algorithm, loosely based on `fzf`'s algorithm
- `frizbee`([crate](https://crates.io/frizbee), the typo-resistant algorithm used in the [blink.cmp](https://github.com/saghen/blink.cmp) neovim plugin
- `fzy`, based on [fzy](https://github.com/jhawthorn/fzy/)'s algorithm expanded for basic typo-resistance
- `arinae`, skim's newest algorithm, designed in-house with typo-resistance in mind, expanding on all the above to make typo-resistant matching feel more natural while keeping the per-item performance up to the best standards

## Preview Window

This is a great feature of fzf that skim borrows. For example, we use 'ag' to
Expand Down
42 changes: 42 additions & 0 deletions benches/filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,27 @@ fn criterion_benchmark_10m(c: &mut Criterion) {
Skim::run_with(opts, None)
});
});
c.bench_function("filter_10M_skim_v3", |b| {
b.iter(|| {
let opts = SkimOptionsBuilder::default()
.cmd("cat benches/fixtures/10M.txt")
.filter("test")
.algorithm(FuzzyAlgorithm::Arinae)
.build()?;
Skim::run_with(opts, None)
});
});
c.bench_function("filter_10M_skim_v3_typos", |b| {
b.iter(|| {
let opts = SkimOptionsBuilder::default()
.cmd("cat benches/fixtures/10M.txt")
.filter("test")
.typos(Typos::Smart)
.algorithm(FuzzyAlgorithm::Arinae)
.build()?;
Skim::run_with(opts, None)
});
});
}

fn criterion_benchmark_1m(c: &mut Criterion) {
Expand Down Expand Up @@ -150,6 +171,27 @@ fn criterion_benchmark_1m(c: &mut Criterion) {
Skim::run_with(opts, None)
});
});
c.bench_function("filter_1M_skim_v3", |b| {
b.iter(|| {
let opts = SkimOptionsBuilder::default()
.cmd("cat benches/fixtures/1M.txt")
.filter("test")
.algorithm(FuzzyAlgorithm::Arinae)
.build()?;
Skim::run_with(opts, None)
});
});
c.bench_function("filter_1M_skim_v3_typos", |b| {
b.iter(|| {
let opts = SkimOptionsBuilder::default()
.cmd("cat benches/fixtures/1M.txt")
.filter("test")
.typos(Typos::Smart)
.algorithm(FuzzyAlgorithm::Arinae)
.build()?;
Skim::run_with(opts, None)
});
});

c.bench_function("filter_1M_andor", |b| {
b.iter(|| {
Expand Down
133 changes: 133 additions & 0 deletions benches/matcher_micro.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//! Microbenchmark that isolates the fuzzy matcher DP from all other overhead
//! (I/O, threading, sorting).

use std::fs;

use criterion::{Criterion, criterion_group, criterion_main};

use skim::CaseMatching;
use skim::fuzzy_matcher::FuzzyMatcher;
use skim::fuzzy_matcher::arinae::ArinaeMatcher;
use skim::fuzzy_matcher::frizbee::FrizbeeMatcher;
use skim::prelude::SkimMatcherV2;

fn load_lines() -> Vec<String> {
let data = fs::read_to_string("benches/fixtures/1M.txt").expect("1M.txt missing");
data.lines().map(|l| l.to_string()).collect()
}

fn bench_matcher(c: &mut Criterion) {
let lines = load_lines();

c.bench_function("micro_skim_v2", |b| {
let m = SkimMatcherV2::default().smart_case();
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_frizbee", |b| {
let m = FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(0));
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_typos_frizbee", |b| {
let m = FrizbeeMatcher::default().case(CaseMatching::Smart).max_typos(Some(1));
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_arinae", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, false);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_arinae_range", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, false);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_match_range(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_arinae_score", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, false);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_match(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_typos_arinae", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, true);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_indices(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_typos_arinae_range", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, true);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_match_range(line, "test").is_some() {
count += 1;
}
}
count
});
});
c.bench_function("micro_typos_arinae_score", |b| {
let m = ArinaeMatcher::new(CaseMatching::Smart, true);
b.iter(|| {
let mut count = 0u64;
for line in &lines {
if m.fuzzy_match(line, "test").is_some() {
count += 1;
}
}
count
});
});
}

criterion_group!(benches, bench_matcher);
criterion_main!(benches);
Loading