diff --git a/Cargo.lock b/Cargo.lock index 85bc4307..ee7b3349 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1440,6 +1440,7 @@ dependencies = [ "indexmap", "line-index", "log", + "nucleo-matcher", "petgraph", "schemars", "semver", @@ -1552,6 +1553,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" @@ -2519,6 +2530,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + [[package]] name = "unicode-width" version = "0.1.13" diff --git a/Cargo.toml b/Cargo.toml index e638d0e0..54a808b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -91,6 +91,7 @@ fs4 = { version = "0.8.3", features = ["sync"] } ariadne = { version = "0.4.1", features = ["auto-color"] } clap_complete = { version = "4.5.4" } schemars = "0.8" +nucleo-matcher = "0.3.1" [profile.release] debug = false diff --git a/crates/moon/src/cli/test.rs b/crates/moon/src/cli/test.rs index c715668d..77521727 100644 --- a/crates/moon/src/cli/test.rs +++ b/crates/moon/src/cli/test.rs @@ -32,7 +32,9 @@ use moonutil::dirs::PackageDirs; use moonutil::module::ModuleDB; use moonutil::mooncakes::sync::AutoSyncFlags; use moonutil::mooncakes::RegistryConfig; +use moonutil::package::Package; use n2::trace; +use std::collections::HashSet; use std::path::Path; use std::path::PathBuf; use std::sync::Arc; @@ -210,7 +212,51 @@ fn run_test_internal( &moonbuild_opt, )?; - let package_filter = moonbuild_opt.get_package_filter(); + let (package_filter, moonbuild_opt) = if let Some(filter_package) = moonbuild_opt + .test_opt + .as_ref() + .and_then(|opt| opt.filter_package.as_ref()) + { + let all_packages: HashSet = module + .get_all_packages() + .iter() + .map(|pkg| pkg.0.to_string()) + .collect(); + + let mut final_set = HashSet::new(); + for pkg in filter_package { + let needle = pkg.display().to_string(); + if all_packages.contains(&needle) { + // exact matching + final_set.insert(needle); + } else { + let xs = moonutil::fuzzy_match::fuzzy_match(&needle, &all_packages); + if let Some(xs) = xs { + final_set.extend(xs); + } + } + } + + let moonbuild_opt = MoonbuildOpt { + test_opt: Some(TestOpt { + filter_package: Some( + final_set + .clone() + .into_iter() + .map(|it| PathBuf::from(it)) + .collect(), + ), + ..moonbuild_opt.test_opt.unwrap() + }), + ..moonbuild_opt + }; + + let package_filter = Some(move |pkg: &Package| final_set.contains(&pkg.full_name())); + (package_filter, moonbuild_opt) + } else { + (None, moonbuild_opt) + }; + for (_, pkg) in module.get_filtered_packages_mut(package_filter) { if pkg.is_third_party || pkg.is_main { continue; diff --git a/crates/moonutil/Cargo.toml b/crates/moonutil/Cargo.toml index ac956855..5233d033 100644 --- a/crates/moonutil/Cargo.toml +++ b/crates/moonutil/Cargo.toml @@ -45,6 +45,7 @@ log.workspace = true thiserror.workspace = true schemars.workspace = true line-index.workspace = true +nucleo-matcher.workspace = true [dev-dependencies] expect-test.workspace = true diff --git a/crates/moonutil/src/fuzzy_match.rs b/crates/moonutil/src/fuzzy_match.rs new file mode 100644 index 00000000..3d252ff9 --- /dev/null +++ b/crates/moonutil/src/fuzzy_match.rs @@ -0,0 +1,56 @@ +pub fn fuzzy_match>( + needle: T, + haystack: impl IntoIterator, +) -> Option> { + let mut matcher = nucleo_matcher::Matcher::new(nucleo_matcher::Config::DEFAULT.match_paths()); + let matches = nucleo_matcher::pattern::Pattern::parse( + needle.as_ref(), + nucleo_matcher::pattern::CaseMatching::Ignore, + nucleo_matcher::pattern::Normalization::Smart, + ) + .match_list(haystack, &mut matcher); + if matches.is_empty() { + None + } else { + Some( + matches + .into_iter() + .map(|m| m.0.as_ref().to_string()) + .collect(), + ) + } +} + +#[test] +fn test_fuzzy() { + let haystack = [ + "moonbitlang/core/builtin", + "moonbitlang/core/int", + "moonbitlang/core/list", + "moonbitlang/core/list/internal", + "moonbitlang/core/hashmap", + ]; + let result = fuzzy_match("mci", haystack); + expect_test::expect![[r#" + Some( + [ + "moonbitlang/core/int", + "moonbitlang/core/list/internal", + "moonbitlang/core/list", + "moonbitlang/core/builtin", + ], + ) + "#]] + .assert_debug_eq(&result); + + let result = fuzzy_match("moonbitlang/core/list", haystack); + expect_test::expect![[r#" + Some( + [ + "moonbitlang/core/list", + "moonbitlang/core/list/internal", + ], + ) + "#]] + .assert_debug_eq(&result); +} diff --git a/crates/moonutil/src/lib.rs b/crates/moonutil/src/lib.rs index 1923b5e0..e9dc494d 100644 --- a/crates/moonutil/src/lib.rs +++ b/crates/moonutil/src/lib.rs @@ -23,6 +23,7 @@ pub mod common; pub mod cond_expr; pub mod dependency; pub mod dirs; +pub mod fuzzy_match; pub mod git; pub mod graph; pub mod module;