Skip to content

Commit

Permalink
switch all fuzzy matches to nucleo
Browse files Browse the repository at this point in the history
Centralize the thread local state management into the selector module
to make it a bit more convenient to consume from the various other
locations.

closes: #5532
  • Loading branch information
wez committed Jul 14, 2024
1 parent 6b5edad commit b20c619
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 65 deletions.
20 changes: 0 additions & 20 deletions Cargo.lock

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

3 changes: 2 additions & 1 deletion docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ As features stabilize some brief notes about them will accumulate here.
[custom_block_glyphs](config/lua/config/custom_block_glyphs.md) for more
details. Thanks to @stribor14! #5051 #5169
* Switched to the [nucleo](https://github.com/helix-editor/nucleo) fuzzy
matcher for `CharSelect`. #5532
matcher which produces matches that more closely match the popular `fzf`
program. #5532

#### New
* [wezterm.serde](config/lua/wezterm.serde/index.md) module for serialization
Expand Down
1 change: 0 additions & 1 deletion wezterm-gui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ filedescriptor = { version="0.8", path = "../filedescriptor" }
finl_unicode = "1.2"
frecency = { path = "../frecency" }
futures = "0.3"
fuzzy-matcher = "0.3"
nucleo-matcher = "0.3"
hdrhistogram = "7.1"
http_req = "0.11"
Expand Down
12 changes: 6 additions & 6 deletions wezterm-gui/src/overlay/launcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
//! menus.
use crate::commands::derive_command_from_key_assignment;
use crate::inputmap::InputMap;
use crate::overlay::selector::{matcher_pattern, matcher_score};
use crate::termwindow::TermWindowNotif;
use config::configuration;
use config::keyassignment::{KeyAssignment, SpawnCommand, SpawnTabDomain};
use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher;
use mux::domain::{DomainId, DomainState};
use mux::pane::PaneId;
use mux::termwiztermtab::TermWizTerminal;
use mux::window::WindowId;
use mux::Mux;
use rayon::prelude::*;
use std::collections::BTreeMap;
use termwiz::cell::{AttributeChange, CellAttributes};
use termwiz::color::ColorAttribute;
Expand Down Expand Up @@ -183,19 +183,19 @@ impl LauncherState {

self.filtered_entries.clear();

let matcher = SkimMatcherV2::default();
let pattern = matcher_pattern(&self.filter_term);

struct MatchResult {
row_idx: usize,
score: i64,
score: u32,
}

let mut scores: Vec<MatchResult> = self
.entries
.iter()
.par_iter()
.enumerate()
.filter_map(|(row_idx, entry)| {
let score = matcher.fuzzy_match(&entry.label, &self.filter_term)?;
let score = matcher_score(&pattern, &entry.label)?;
Some(MatchResult { row_idx, score })
})
.collect();
Expand Down
35 changes: 28 additions & 7 deletions wezterm-gui/src/overlay/selector.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use super::quickselect;
use crate::scripting::guiwin::GuiWin;
use config::keyassignment::{InputSelector, InputSelectorEntry, KeyAssignment};
use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher;
use mux::termwiztermtab::TermWizTerminal;
use mux_lua::MuxPane;
use nucleo_matcher::pattern::Pattern;
use nucleo_matcher::{Matcher, Utf32Str};
use rayon::prelude::*;
use std::cell::RefCell;
use std::rc::Rc;
use termwiz::cell::{AttributeChange, CellAttributes};
use termwiz::color::ColorAttribute;
Expand All @@ -15,6 +17,25 @@ use termwiz_funcs::truncate_right;

const ROW_OVERHEAD: usize = 3;

thread_local! {
pub static MATCHER: RefCell<Matcher> = RefCell::new(Matcher::new(nucleo_matcher::Config::DEFAULT));
}

pub fn matcher_score(pattern: &Pattern, s: &str) -> Option<u32> {
MATCHER.with_borrow_mut(|matcher| {
let mut buf = vec![];
pattern.score(Utf32Str::new(s, &mut buf), matcher)
})
}

pub fn matcher_pattern(s: &str) -> Pattern {
nucleo_matcher::pattern::Pattern::parse(
s,
nucleo_matcher::pattern::CaseMatching::Ignore,
nucleo_matcher::pattern::Normalization::Smart,
)
}

struct SelectorState {
active_idx: usize,
max_items: usize,
Expand All @@ -40,20 +61,20 @@ impl SelectorState {

self.filtered_entries.clear();

let matcher = SkimMatcherV2::default();

struct MatchResult {
row_idx: usize,
score: i64,
score: u32,
}

let pattern = matcher_pattern(&self.filter_term);

let mut scores: Vec<MatchResult> = self
.args
.choices
.iter()
.par_iter()
.enumerate()
.filter_map(|(row_idx, entry)| {
let score = matcher.fuzzy_match(&entry.label, &self.filter_term)?;
let score = matcher_score(&pattern, &entry.label)?;
Some(MatchResult { row_idx, score })
})
.collect();
Expand Down
27 changes: 6 additions & 21 deletions wezterm-gui/src/termwindow/charselect.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::overlay::selector::{matcher_pattern, matcher_score};
use crate::termwindow::box_model::*;
use crate::termwindow::modal::Modal;
use crate::termwindow::render::corners::{
Expand All @@ -13,7 +14,6 @@ use config::keyassignment::{
use config::Dimension;
use emojis::{Emoji, Group};
use frecency::Frecency;
use nucleo_matcher::{Matcher, Utf32Str};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
Expand Down Expand Up @@ -271,11 +271,7 @@ fn compute_matches(selection: &str, aliases: &[Alias], group: CharSelectGroup) -
.map(|(idx, _a)| idx)
.collect()
} else {
let pattern = nucleo_matcher::pattern::Pattern::parse(
selection,
nucleo_matcher::pattern::CaseMatching::Ignore,
nucleo_matcher::pattern::Normalization::Smart,
);
let pattern = matcher_pattern(selection);

let numeric_selection = if selection.chars().all(|c| c.is_ascii_hexdigit()) {
// Make this uppercase so that eg: `e1` matches `U+E1` rather
Expand All @@ -293,18 +289,10 @@ fn compute_matches(selection: &str, aliases: &[Alias], group: CharSelectGroup) -
.par_iter()
.enumerate()
.filter_map(|(row_idx, entry)| {
thread_local! {
static MATCHER: RefCell<Matcher> = RefCell::new(Matcher::new(nucleo_matcher::Config::DEFAULT));
}

let glyph = entry.glyph();

let alias_result = MATCHER.with_borrow_mut(|matcher| {
let mut buf = vec![];
pattern
.score(Utf32Str::new(&entry.name, &mut buf), matcher)
.map(|score| MatchResult::new(row_idx, score , selection, aliases))
});
let alias_result = matcher_score(&pattern, &entry.name)
.map(|score| MatchResult::new(row_idx, score, selection, aliases));

match &numeric_selection {
Some(sel) => {
Expand All @@ -318,11 +306,8 @@ fn compute_matches(selection: &str, aliases: &[Alias], group: CharSelectGroup) -
},
))
} else {
let number_result = MATCHER.with_borrow_mut(|matcher| {
let mut buf = vec![];
pattern
.score(Utf32Str::new(&codepoints, &mut buf), matcher)
.map(|score| MatchResult::new(row_idx, score , selection, aliases))});
let number_result = matcher_score(&pattern, &codepoints)
.map(|score| MatchResult::new(row_idx, score, selection, aliases));

match (alias_result, number_result) {
(
Expand Down
17 changes: 8 additions & 9 deletions wezterm-gui/src/termwindow/palette.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::commands::{CommandDef, ExpandedCommand};
use crate::overlay::selector::{matcher_pattern, matcher_score};
use crate::termwindow::box_model::*;
use crate::termwindow::modal::Modal;
use crate::termwindow::render::corners::{
Expand All @@ -10,10 +11,9 @@ use crate::utilsprites::RenderMetrics;
use config::keyassignment::KeyAssignment;
use config::Dimension;
use frecency::Frecency;
use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher;
use luahelper::{from_lua_value_dynamic, impl_lua_conversion_dynamic};
use mux_lua::MuxPane;
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::cell::{Ref, RefCell};
Expand Down Expand Up @@ -172,18 +172,18 @@ fn build_commands(
#[derive(Debug)]
struct MatchResult {
row_idx: usize,
score: i64,
score: u32,
}

impl MatchResult {
fn new(row_idx: usize, score: i64, selection: &str, commands: &[ExpandedCommand]) -> Self {
fn new(row_idx: usize, score: u32, selection: &str, commands: &[ExpandedCommand]) -> Self {
Self {
row_idx,
score: if commands[row_idx].brief == selection {
// Pump up the score for an exact match, otherwise
// the order may be undesirable if there are a lot
// of candidates with the same score
i64::max_value()
u32::max_value()
} else {
score
},
Expand All @@ -195,17 +195,16 @@ fn compute_matches(selection: &str, commands: &[ExpandedCommand]) -> Vec<usize>
if selection.is_empty() {
commands.iter().enumerate().map(|(idx, _)| idx).collect()
} else {
let matcher = SkimMatcherV2::default();
let pattern = matcher_pattern(selection);

let start = std::time::Instant::now();
let mut scores: Vec<MatchResult> = commands
.iter()
.par_iter()
.enumerate()
.filter_map(|(row_idx, entry)| {
let group = entry.menubar.join(" ");
let text = format!("{group}: {}. {} {:?}", entry.brief, entry.doc, entry.action);
matcher
.fuzzy_match(&text, selection)
matcher_score(&pattern, &text)
.map(|score| MatchResult::new(row_idx, score, selection, commands))
})
.collect();
Expand Down

0 comments on commit b20c619

Please sign in to comment.