diff --git a/Cargo.lock b/Cargo.lock index 5fbb95e..05c2936 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1079,15 +1079,6 @@ dependencies = [ "slab", ] -[[package]] -name = "fuzzy-matcher" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" -dependencies = [ - "thread_local", -] - [[package]] name = "fxhash" version = "0.2.1" @@ -1722,11 +1713,11 @@ dependencies = [ "dirs", "dunce", "embed-resource", - "fuzzy-matcher", "global-hotkey", "image", "notify", "notify-debouncer-mini", + "nucleo-matcher", "percent-encoding", "rfd", "rust-embed", @@ -2104,6 +2095,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "nucleo-matcher" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf33f538733d1a5a3494b836ba913207f14d9d4a1d3cd67030c5061bdd2cac85" +dependencies = [ + "memchr", + "unicode-segmentation", +] + [[package]] name = "num-conv" version = "0.1.0" diff --git a/kal/Cargo.toml b/kal/Cargo.toml index 6b277b2..a6df0a7 100644 --- a/kal/Cargo.toml +++ b/kal/Cargo.toml @@ -20,7 +20,7 @@ serde_json = "1" toml = "0.8" rust-embed = "8.1" serialize-to-javascript = "0.1" -fuzzy-matcher = "0.3" +nucleo-matcher = "0.3" dirs = "5.0" percent-encoding = "2.3" dunce = "1" diff --git a/kal/src/fuzzy_matcher.rs b/kal/src/fuzzy_matcher.rs new file mode 100644 index 0000000..ebad26f --- /dev/null +++ b/kal/src/fuzzy_matcher.rs @@ -0,0 +1,16 @@ +#[derive(Default)] +pub struct Matcher { + inner: nucleo_matcher::Matcher, +} + +impl Matcher { + pub fn fuzzy_match(&mut self, haystack: &str, needle: &str) -> Option { + let mut haystack_buf = Vec::new(); + let mut needle_buf = Vec::new(); + + let haystack = nucleo_matcher::Utf32Str::new(haystack, &mut haystack_buf); + let needle = nucleo_matcher::Utf32Str::new(needle, &mut needle_buf); + + self.inner.fuzzy_match(haystack, needle) + } +} diff --git a/kal/src/main.rs b/kal/src/main.rs index ce54c3a..4287b4f 100644 --- a/kal/src/main.rs +++ b/kal/src/main.rs @@ -15,6 +15,7 @@ mod app; mod config; #[cfg(not(debug_assertions))] mod embedded_assets; +mod fuzzy_matcher; mod icon; mod ipc; mod main_window; diff --git a/kal/src/main_window.rs b/kal/src/main_window.rs index 675b0a0..21ad0eb 100644 --- a/kal/src/main_window.rs +++ b/kal/src/main_window.rs @@ -1,6 +1,5 @@ use std::sync::{mpsc, Arc}; -use fuzzy_matcher::skim::SkimMatcherV2; use global_hotkey::hotkey::HotKey; use serialize_to_javascript::{Options as JsSerializeOptions, Template as JsTemplate}; use smol::lock::RwLock; @@ -112,7 +111,7 @@ pub struct MainWindowState { main_thread_sender: mpsc::Sender, event_loop_proxy: EventLoopProxy, - fuzzy_matcher: SkimMatcherV2, + fuzzy_matcher: RwLock, config: RwLock, plugin_store: RwLock, @@ -140,7 +139,7 @@ impl MainWindowState { Self { main_thread_sender, event_loop_proxy, - fuzzy_matcher: SkimMatcherV2::default(), + fuzzy_matcher: RwLock::new(crate::fuzzy_matcher::Matcher::default()), config: RwLock::new(config), plugin_store: RwLock::new(plugin_store), results: RwLock::new(Vec::with_capacity(max_results)), @@ -232,9 +231,12 @@ impl MainWindowState { let mut results = Vec::new(); + // it is fine to block here since only one query can be processed at a time let mut plugins_store = self.plugin_store.write().await; + let mut fuzzy_matcher = self.fuzzy_matcher.write().await; + plugins_store - .query(query, &self.fuzzy_matcher, &mut results) + .query(query, &mut fuzzy_matcher, &mut results) .await?; // sort results in reverse so higher scores are first diff --git a/kal/src/plugin.rs b/kal/src/plugin.rs index 7eb6d3a..5ee4c6a 100644 --- a/kal/src/plugin.rs +++ b/kal/src/plugin.rs @@ -1,5 +1,3 @@ -use fuzzy_matcher::skim::SkimMatcherV2; - use crate::config::{Config, GenericPluginConfig}; use crate::icon::BuiltinIcon; use crate::result_item::ResultItem; @@ -49,7 +47,7 @@ pub trait Plugin: std::fmt::Debug + Send + Sync { async fn query( &mut self, query: &str, - matcher: &SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { Ok(PluginQueryOutput::None) } @@ -58,7 +56,7 @@ pub trait Plugin: std::fmt::Debug + Send + Sync { async fn query_direct( &mut self, query: &str, - matcher: &SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { self.query(query, matcher).await } diff --git a/kal/src/plugin_store.rs b/kal/src/plugin_store.rs index 046da42..895d5b0 100644 --- a/kal/src/plugin_store.rs +++ b/kal/src/plugin_store.rs @@ -1,8 +1,5 @@ use std::ops::{Deref, DerefMut}; -use fuzzy_matcher::skim::SkimMatcherV2; -use smol::prelude::*; - use crate::config::Config; use crate::plugin::Plugin; use crate::result_item::ResultItem; @@ -101,7 +98,7 @@ impl PluginStore { pub async fn query( &mut self, query: &str, - matcher: &SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, results: &mut Vec, ) -> anyhow::Result<()> { // check if a plugin is being invoked directly @@ -116,15 +113,13 @@ impl PluginStore { } else { let trimmed_query = query.trim(); - // otherwise get result from all queriable plugins - let mut results_iter = smol::stream::iter(self.queriable_plugins()).map(|p| async { - p.query(trimmed_query, matcher) + for plugin in self.queriable_plugins() { + let result = plugin + .query(trimmed_query, matcher) .await - .map_err(|e| p.error_item(e.to_string())) - }); + .map_err(|e| plugin.error_item(e.to_string())); - while let Some(r) = results_iter.next().await { - match r.await { + match result { Ok(r) => r.extend_into(results), Err(r) => results.push(r), } diff --git a/kal/src/plugins/app_launcher/mod.rs b/kal/src/plugins/app_launcher/mod.rs index 0b77329..361bcdc 100644 --- a/kal/src/plugins/app_launcher/mod.rs +++ b/kal/src/plugins/app_launcher/mod.rs @@ -1,7 +1,6 @@ use std::path::Path; use std::sync::{Arc, Mutex}; -use fuzzy_matcher::skim::SkimMatcherV2; use notify::RecommendedWatcher; use notify_debouncer_mini::Debouncer; use serde::{Deserialize, Serialize}; @@ -111,7 +110,7 @@ impl crate::plugin::Plugin for Plugin { async fn query( &mut self, query: &str, - matcher: &SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { if query.is_empty() { return Ok(PluginQueryOutput::None); @@ -152,7 +151,7 @@ impl App { } impl IntoResultItem for App { - fn fuzzy_match(&self, query: &str, matcher: &SkimMatcherV2) -> Option { + fn fuzzy_match(&self, query: &str, matcher: &mut crate::fuzzy_matcher::Matcher) -> Option { match self { App::Program(program) => program.fuzzy_match(query, matcher), #[cfg(windows)] diff --git a/kal/src/plugins/app_launcher/packaged_app.rs b/kal/src/plugins/app_launcher/packaged_app.rs index b7ce418..65c248d 100644 --- a/kal/src/plugins/app_launcher/packaged_app.rs +++ b/kal/src/plugins/app_launcher/packaged_app.rs @@ -1,8 +1,6 @@ use std::ffi::OsString; use std::path::PathBuf; -use fuzzy_matcher::skim::SkimMatcherV2; -use fuzzy_matcher::FuzzyMatcher; use windows::core::{w, HSTRING, PCWSTR}; use windows::ApplicationModel::{ Package, PackageCatalog, PackageInstallingEventArgs, PackageUninstallingEventArgs, @@ -53,7 +51,7 @@ pub struct PackagedApp { } impl PackagedApp { - fn item(&self, args: &str, score: i64) -> ResultItem { + fn item(&self, args: &str, score: u16) -> ResultItem { let icon = self .icon .as_ref() @@ -92,7 +90,7 @@ impl PackagedApp { } impl IntoResultItem for PackagedApp { - fn fuzzy_match(&self, query: &str, matcher: &SkimMatcherV2) -> Option { + fn fuzzy_match(&self, query: &str, matcher: &mut crate::fuzzy_matcher::Matcher) -> Option { let (query, args) = query.split_args().unwrap_or((query, "")); matcher diff --git a/kal/src/plugins/app_launcher/program.rs b/kal/src/plugins/app_launcher/program.rs index e98166e..52dcdb3 100644 --- a/kal/src/plugins/app_launcher/program.rs +++ b/kal/src/plugins/app_launcher/program.rs @@ -2,8 +2,6 @@ use std::ffi::{OsStr, OsString}; use std::path::{Path, PathBuf}; use std::time::Duration; -use fuzzy_matcher::skim::SkimMatcherV2; -use fuzzy_matcher::FuzzyMatcher; use smol::prelude::*; use super::App; @@ -42,7 +40,7 @@ impl Program { } } - fn item(&self, args: &str, score: i64) -> ResultItem { + fn item(&self, args: &str, score: u16) -> ResultItem { let path = self.path.clone(); let args_ = args.to_string(); let open = Action::primary(move |_| utils::execute_with_args(&path, &args_, false, false)); @@ -70,7 +68,7 @@ impl Program { } impl IntoResultItem for Program { - fn fuzzy_match(&self, query: &str, matcher: &SkimMatcherV2) -> Option { + fn fuzzy_match(&self, query: &str, matcher: &mut crate::fuzzy_matcher::Matcher) -> Option { let (query, args) = query.split_args().unwrap_or((query, "")); matcher diff --git a/kal/src/plugins/calculator/mod.rs b/kal/src/plugins/calculator/mod.rs index cd297fe..ff2ce95 100644 --- a/kal/src/plugins/calculator/mod.rs +++ b/kal/src/plugins/calculator/mod.rs @@ -51,7 +51,7 @@ impl crate::plugin::Plugin for Plugin { async fn query( &mut self, query: &str, - _matcher: &fuzzy_matcher::skim::SkimMatcherV2, + _matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { if !query.starts_with(|c: char| c.is_ascii_digit()) { return Ok(PluginQueryOutput::None); diff --git a/kal/src/plugins/directory_indexer/mod.rs b/kal/src/plugins/directory_indexer/mod.rs index deb3e0b..ccf622a 100644 --- a/kal/src/plugins/directory_indexer/mod.rs +++ b/kal/src/plugins/directory_indexer/mod.rs @@ -1,8 +1,8 @@ use std::ffi::OsString; use std::path::{Path, PathBuf}; -use fuzzy_matcher::skim::SkimMatcherV2; -use fuzzy_matcher::FuzzyMatcher; + + use serde::{Deserialize, Serialize}; use smol::stream::*; @@ -71,7 +71,7 @@ impl crate::plugin::Plugin for Plugin { async fn query( &mut self, query: &str, - matcher: &SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { Ok(self .entries @@ -104,7 +104,7 @@ impl DirEntry { } } - fn item(&self, score: i64) -> ResultItem { + fn item(&self, score: u16) -> ResultItem { let actions = if self.is_dir { vec![ { @@ -146,7 +146,7 @@ impl DirEntry { } impl IntoResultItem for DirEntry { - fn fuzzy_match(&self, query: &str, matcher: &SkimMatcherV2) -> Option { + fn fuzzy_match(&self, query: &str, matcher: &mut crate::fuzzy_matcher::Matcher) -> Option { matcher .fuzzy_match(&self.name.to_string_lossy(), query) .or_else(|| matcher.fuzzy_match(&self.path.to_string_lossy(), query)) diff --git a/kal/src/plugins/everything/mod.rs b/kal/src/plugins/everything/mod.rs index f9fcfa7..775c07b 100644 --- a/kal/src/plugins/everything/mod.rs +++ b/kal/src/plugins/everything/mod.rs @@ -1,7 +1,7 @@ use std::ffi::OsString; use std::path::PathBuf; -use fuzzy_matcher::skim::SkimMatcherV2; + use serde::{Deserialize, Serialize}; use crate::config::{Config, GenericPluginConfig}; @@ -59,7 +59,7 @@ impl crate::plugin::Plugin for Plugin { async fn query_direct( &mut self, query: &str, - matcher: &SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { if query.is_empty() { return Ok(PluginQueryOutput::None); @@ -115,7 +115,7 @@ impl EverythingEntry { } impl IntoResultItem for EverythingEntry { - fn fuzzy_match(&self, _query: &str, _matcher: &SkimMatcherV2) -> Option { + fn fuzzy_match(&self, _query: &str, _matcher: &mut crate::fuzzy_matcher::Matcher) -> Option { let actions = if self.is_dir { vec![ { diff --git a/kal/src/plugins/shell/mod.rs b/kal/src/plugins/shell/mod.rs index fac7092..1ad8839 100644 --- a/kal/src/plugins/shell/mod.rs +++ b/kal/src/plugins/shell/mod.rs @@ -56,7 +56,7 @@ impl crate::plugin::Plugin for Plugin { async fn query_direct( &mut self, query: &str, - _matcher: &fuzzy_matcher::skim::SkimMatcherV2, + _matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { Ok(self.shell.item(query.to_string(), self.no_exit).into()) } diff --git a/kal/src/plugins/system_commands/mod.rs b/kal/src/plugins/system_commands/mod.rs index bbd69d1..a73efd8 100644 --- a/kal/src/plugins/system_commands/mod.rs +++ b/kal/src/plugins/system_commands/mod.rs @@ -1,5 +1,3 @@ -use fuzzy_matcher::skim::SkimMatcherV2; -use fuzzy_matcher::FuzzyMatcher; use strum::AsRefStr; use crate::config::{Config, GenericPluginConfig}; @@ -23,7 +21,7 @@ impl Plugin { .collect_non_empty() } - fn all_for_query(&self, query: &str, matcher: &SkimMatcherV2) -> Option> { + fn all_for_query(&self, query: &str, matcher: &mut crate::fuzzy_matcher::Matcher) -> Option> { self.commands .iter() .filter_map(|workflow| workflow.fuzzy_match(query, matcher)) @@ -58,7 +56,7 @@ impl crate::plugin::Plugin for Plugin { async fn query( &mut self, query: &str, - matcher: &fuzzy_matcher::skim::SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { Ok(self.all_for_query(query, matcher).into()) } @@ -66,7 +64,7 @@ impl crate::plugin::Plugin for Plugin { async fn query_direct( &mut self, query: &str, - matcher: &fuzzy_matcher::skim::SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { if query.is_empty() { Ok(self.all().into()) @@ -203,7 +201,7 @@ impl SystemCommand { unimplemented!() } - fn item(&self, score: i64) -> ResultItem { + fn item(&self, score: u16) -> ResultItem { let system_command = *self; ResultItem { id: self.id().into(), @@ -218,7 +216,7 @@ impl SystemCommand { } impl IntoResultItem for SystemCommand { - fn fuzzy_match(&self, query: &str, matcher: &SkimMatcherV2) -> Option { + fn fuzzy_match(&self, query: &str, matcher: &mut crate::fuzzy_matcher::Matcher) -> Option { matcher .fuzzy_match(self.as_ref(), query) .map(|score| self.item(score)) diff --git a/kal/src/plugins/vscode_workspaces/mod.rs b/kal/src/plugins/vscode_workspaces/mod.rs index b7b7320..90f4251 100644 --- a/kal/src/plugins/vscode_workspaces/mod.rs +++ b/kal/src/plugins/vscode_workspaces/mod.rs @@ -1,7 +1,5 @@ use std::path::PathBuf; -use fuzzy_matcher::skim::SkimMatcherV2; -use fuzzy_matcher::FuzzyMatcher; use serde::Deserialize; use sqlite::OpenFlags; @@ -81,7 +79,7 @@ impl crate::plugin::Plugin for Plugin { async fn query_direct( &mut self, query: &str, - matcher: &SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { Ok(self .workspaces @@ -139,7 +137,7 @@ impl Workspace { } impl Workspace { - fn item(&self, score: i64) -> ResultItem { + fn item(&self, score: u16) -> ResultItem { let uri = self.uri.clone(); ResultItem { @@ -162,7 +160,7 @@ impl Workspace { } impl IntoResultItem for Workspace { - fn fuzzy_match(&self, query: &str, matcher: &SkimMatcherV2) -> Option { + fn fuzzy_match(&self, query: &str, matcher: &mut crate::fuzzy_matcher::Matcher) -> Option { matcher .fuzzy_match(&self.name, query) .map(|score| self.item(score)) diff --git a/kal/src/plugins/workflows/mod.rs b/kal/src/plugins/workflows/mod.rs index a5bf36d..044b333 100644 --- a/kal/src/plugins/workflows/mod.rs +++ b/kal/src/plugins/workflows/mod.rs @@ -1,7 +1,5 @@ use std::path::PathBuf; -use fuzzy_matcher::skim::SkimMatcherV2; -use fuzzy_matcher::FuzzyMatcher; use serde::{Deserialize, Serialize}; use url::Url; @@ -42,7 +40,7 @@ impl Plugin { .collect_non_empty() } - fn all_for_query(&self, query: &str, matcher: &SkimMatcherV2) -> Option> { + fn all_for_query(&self, query: &str, matcher: &mut crate::fuzzy_matcher::Matcher) -> Option> { self.workflows .iter() .filter_map(|workflow| workflow.fuzzy_match(query, matcher)) @@ -88,7 +86,7 @@ impl crate::plugin::Plugin for Plugin { async fn query( &mut self, query: &str, - matcher: &fuzzy_matcher::skim::SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { Ok(self.all_for_query(query, matcher).into()) } @@ -96,7 +94,7 @@ impl crate::plugin::Plugin for Plugin { async fn query_direct( &mut self, query: &str, - matcher: &fuzzy_matcher::skim::SkimMatcherV2, + matcher: &mut crate::fuzzy_matcher::Matcher, ) -> anyhow::Result { if query.is_empty() { Ok(self.all().into()) @@ -185,7 +183,7 @@ impl Workflow { Ok(()) } - fn item(&self, score: i64) -> ResultItem { + fn item(&self, score: u16) -> ResultItem { let workflow = self.clone(); let open = Action::primary(move |_| workflow.execute(false)); @@ -205,7 +203,7 @@ impl Workflow { } impl IntoResultItem for Workflow { - fn fuzzy_match(&self, query: &str, matcher: &SkimMatcherV2) -> Option { + fn fuzzy_match(&self, query: &str, matcher: &mut crate::fuzzy_matcher::Matcher) -> Option { matcher .fuzzy_match(&self.name, query) .or_else(|| { diff --git a/kal/src/result_item.rs b/kal/src/result_item.rs index f86dd26..22bf54b 100644 --- a/kal/src/result_item.rs +++ b/kal/src/result_item.rs @@ -1,4 +1,3 @@ -use fuzzy_matcher::skim::SkimMatcherV2; use serde::Serialize; use crate::icon::{BuiltinIcon, Icon}; @@ -11,11 +10,15 @@ pub struct ResultItem { pub secondary_text: String, pub tooltip: Option, pub actions: Vec, - pub score: i64, + pub score: u16, } pub trait IntoResultItem { - fn fuzzy_match(&self, query: &str, matcher: &SkimMatcherV2) -> Option; + fn fuzzy_match( + &self, + query: &str, + matcher: &mut crate::fuzzy_matcher::Matcher, + ) -> Option; } type ActionFn = dyn Fn(&ResultItem) -> anyhow::Result<()> + Send + Sync;