From 82d86110d2c0f090cfcc6a9dbf3deec2fa6a4a79 Mon Sep 17 00:00:00 2001 From: dino Date: Tue, 4 Nov 2025 23:22:29 +0000 Subject: [PATCH] chore(vim): add initial projections structs --- crates/vim/src/projections.rs | 73 +++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/crates/vim/src/projections.rs b/crates/vim/src/projections.rs index 9e242e66b40390b703ef297431441f15c5756913..26aa7ca0b7894df1594524c6265292f705092e09 100644 --- a/crates/vim/src/projections.rs +++ b/crates/vim/src/projections.rs @@ -24,8 +24,52 @@ use gpui::Window; use gpui::actions; use project::ProjectItem; use project::ProjectPath; +use regex::Regex; use util::rel_path::RelPath; +#[derive(Debug)] +struct Projection { + source: Regex, + target: String, +} + +impl Projection { + fn new(source: &str, target: &str) -> Self { + // Replace the `*` character in the source string, if such a character + // is present, with a capture group, so we can then replace that value + // when determining the target. + // TODO!: Support for multiple `*` characters? + // TODO!: Validation that the number of `{}` in the target matches the + // number of `*` on the source. + // TODO!: Avoid `unwrap` here by updating `new` to return + // `Result`/`Option`. + let source = Regex::new(&source.replace("*", "(.*)")).unwrap(); + let target = String::from(target); + + Self { source, target } + } + + /// Determines whether the provided path matches this projection's source. + /// TODO!: We'll likely want to update this to use `ProjectPath` instead of + /// `&str`. + fn matches(&self, path: &str) -> bool { + self.source.is_match(path) + } + + /// Returns the alternate path for the provided path. + /// TODO!: Update to work with more than one capture group? + fn alternate(&self, path: &str) -> String { + // Determine the captures for the path. + if let Some(capture) = self.source.captures_iter(path).next() { + let (_, [name]) = capture.extract(); + self.target.replace("{}", name) + } else { + // TODO!: Can't find capture. Is this a regex without capture group? + String::new() + } + } +} + actions!( vim, [ @@ -86,3 +130,32 @@ impl Vim { }); } } + +#[cfg(test)] +mod tests { + use super::Projection; + use gpui::TestAppContext; + + #[gpui::test] + async fn test_matches(_cx: &mut TestAppContext) { + let source = "lib/app/*.ex"; + let target = "test/app/{}_test.exs"; + let projection = Projection::new(source, target); + + let path = "lib/app/module.ex"; + assert_eq!(projection.matches(path), true); + + let path = "test/app/module_test.exs"; + assert_eq!(projection.matches(path), false); + } + + #[gpui::test] + async fn test_alternate(_cx: &mut TestAppContext) { + let source = "lib/app/*.ex"; + let target = "test/app/{}_test.exs"; + let projection = Projection::new(source, target); + + let path = "lib/app/module.ex"; + assert_eq!(projection.alternate(path), "test/app/module_test.exs"); + } +}