diff --git a/Cargo.lock b/Cargo.lock index dd57996c7ef6dd711c1e67725d1bdfd86d277729..f829bf138a17828d1887409b8f8ea9b48e35f3c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -834,7 +834,6 @@ dependencies = [ "fs", "futures 0.3.31", "fuzzy", - "globset", "gpui", "html_to_markdown", "http_client", diff --git a/crates/assistant_slash_commands/Cargo.toml b/crates/assistant_slash_commands/Cargo.toml index 85dd92501f93fb79ba1d3f70b3a06f1077356cfa..b2a70449f449f73c7d0017c5d2ba3707e271559a 100644 --- a/crates/assistant_slash_commands/Cargo.toml +++ b/crates/assistant_slash_commands/Cargo.toml @@ -22,7 +22,6 @@ feature_flags.workspace = true fs.workspace = true futures.workspace = true fuzzy.workspace = true -globset.workspace = true gpui.workspace = true html_to_markdown.workspace = true http_client.workspace = true diff --git a/crates/assistant_slash_commands/src/file_command.rs b/crates/assistant_slash_commands/src/file_command.rs index a17e198ed300f00f70d35149cbe0286af3a65a57..ae4e8363b40d520b9ea33e5cba5ffa68d783ab04 100644 --- a/crates/assistant_slash_commands/src/file_command.rs +++ b/crates/assistant_slash_commands/src/file_command.rs @@ -226,10 +226,10 @@ fn collect_files( let Ok(matchers) = glob_inputs .iter() .map(|glob_input| { - custom_path_matcher::PathMatcher::new(&[glob_input.to_owned()]) + util::paths::PathMatcher::new(&[glob_input.to_owned()], project.read(cx).path_style(cx)) .with_context(|| format!("invalid path {glob_input}")) }) - .collect::>>() + .collect::>>() else { return futures::stream::once(async { anyhow::bail!("invalid path"); @@ -250,6 +250,7 @@ fn collect_files( let worktree_id = snapshot.id(); let path_style = snapshot.path_style(); let mut directory_stack: Vec> = Vec::new(); + let mut folded_directory_path: Option> = None; let mut folded_directory_names: Arc = RelPath::empty().into(); let mut is_top_level_directory = true; @@ -277,6 +278,16 @@ fn collect_files( )))?; } + if let Some(folded_path) = &folded_directory_path { + if !entry.path.starts_with(folded_path) { + folded_directory_names = RelPath::empty().into(); + folded_directory_path = None; + if directory_stack.is_empty() { + is_top_level_directory = true; + } + } + } + let filename = entry.path.file_name().unwrap_or_default().to_string(); if entry.is_dir() { @@ -292,13 +303,17 @@ fn collect_files( folded_directory_names = folded_directory_names.join(RelPath::unix(&filename).unwrap()); } + folded_directory_path = Some(entry.path.clone()); continue; } } else { // Skip empty directories folded_directory_names = RelPath::empty().into(); + folded_directory_path = None; continue; } + + // Render the directory (either folded or normal) if folded_directory_names.is_empty() { let label = if is_top_level_directory { is_top_level_directory = false; @@ -334,6 +349,8 @@ fn collect_files( }, )))?; directory_stack.push(entry.path.clone()); + folded_directory_names = RelPath::empty().into(); + folded_directory_path = None; } events_tx.unbounded_send(Ok(SlashCommandEvent::Content( SlashCommandContent::Text { @@ -447,87 +464,6 @@ pub fn build_entry_output_section( } } -/// This contains a small fork of the util::paths::PathMatcher, that is stricter about the prefix -/// check. Only subpaths pass the prefix check, rather than any prefix. -mod custom_path_matcher { - use globset::{Glob, GlobSet, GlobSetBuilder}; - use std::fmt::Debug as _; - use util::{paths::SanitizedPath, rel_path::RelPath}; - - #[derive(Clone, Debug, Default)] - pub struct PathMatcher { - sources: Vec, - sources_with_trailing_slash: Vec, - glob: GlobSet, - } - - impl std::fmt::Display for PathMatcher { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.sources.fmt(f) - } - } - - impl PartialEq for PathMatcher { - fn eq(&self, other: &Self) -> bool { - self.sources.eq(&other.sources) - } - } - - impl Eq for PathMatcher {} - - impl PathMatcher { - pub fn new(globs: &[String]) -> Result { - let globs = globs - .iter() - .map(|glob| Glob::new(&SanitizedPath::new(glob).to_string())) - .collect::, _>>()?; - let sources = globs.iter().map(|glob| glob.glob().to_owned()).collect(); - let sources_with_trailing_slash = globs - .iter() - .map(|glob| glob.glob().to_string() + "/") - .collect(); - let mut glob_builder = GlobSetBuilder::new(); - for single_glob in globs { - glob_builder.add(single_glob); - } - let glob = glob_builder.build()?; - Ok(PathMatcher { - glob, - sources, - sources_with_trailing_slash, - }) - } - - pub fn is_match(&self, other: &RelPath) -> bool { - self.sources - .iter() - .zip(self.sources_with_trailing_slash.iter()) - .any(|(source, with_slash)| { - let as_bytes = other.as_unix_str().as_bytes(); - let with_slash = if source.ends_with('/') { - source.as_bytes() - } else { - with_slash.as_bytes() - }; - - as_bytes.starts_with(with_slash) || as_bytes.ends_with(source.as_bytes()) - }) - || self.glob.is_match(other.as_std_path()) - || self.check_with_end_separator(other) - } - - fn check_with_end_separator(&self, path: &RelPath) -> bool { - let path_str = path.as_unix_str(); - let separator = "/"; - if path_str.ends_with(separator) { - false - } else { - self.glob.is_match(path_str.to_string() + separator) - } - } - } -} - pub fn append_buffer_to_output( buffer: &BufferSnapshot, path: Option<&str>,