diff --git a/crates/assistant2/src/context_picker/directory_context_picker.rs b/crates/assistant2/src/context_picker/directory_context_picker.rs index b5d967c90a..691a69908d 100644 --- a/crates/assistant2/src/context_picker/directory_context_picker.rs +++ b/crates/assistant2/src/context_picker/directory_context_picker.rs @@ -1,6 +1,8 @@ -// TODO: Remove this once we've implemented the functionality. +// TODO: Remove this when we finish the implementation. #![allow(unused)] +use std::path::Path; +use std::sync::atomic::AtomicBool; use std::sync::Arc; use fuzzy::PathMatch; @@ -11,6 +13,7 @@ use ui::{prelude::*, ListItem}; use util::ResultExt as _; use workspace::Workspace; +use crate::context::ContextKind; use crate::context_picker::{ConfirmBehavior, ContextPicker}; use crate::context_store::ContextStore; @@ -75,6 +78,65 @@ impl DirectoryContextPickerDelegate { selected_index: 0, } } + + fn search( + &mut self, + query: String, + cancellation_flag: Arc, + workspace: &View, + cx: &mut ViewContext>, + ) -> Task> { + if query.is_empty() { + let workspace = workspace.read(cx); + let project = workspace.project().read(cx); + let directory_matches = project.worktrees(cx).flat_map(|worktree| { + let worktree = worktree.read(cx); + let path_prefix: Arc = worktree.root_name().into(); + worktree.directories(false, 0).map(move |entry| PathMatch { + score: 0., + positions: Vec::new(), + worktree_id: worktree.id().to_usize(), + path: entry.path.clone(), + path_prefix: path_prefix.clone(), + distance_to_relative_ancestor: 0, + is_dir: true, + }) + }); + + Task::ready(directory_matches.collect()) + } else { + let worktrees = workspace.read(cx).visible_worktrees(cx).collect::>(); + let candidate_sets = worktrees + .into_iter() + .map(|worktree| { + let worktree = worktree.read(cx); + + PathMatchCandidateSet { + snapshot: worktree.snapshot(), + include_ignored: worktree + .root_entry() + .map_or(false, |entry| entry.is_ignored), + include_root_name: true, + candidates: project::Candidates::Directories, + } + }) + .collect::>(); + + let executor = cx.background_executor().clone(); + cx.foreground_executor().spawn(async move { + fuzzy::match_path_sets( + candidate_sets.as_slice(), + query.as_str(), + None, + false, + 100, + &cancellation_flag, + executor, + ) + .await + }) + } + } } impl PickerDelegate for DirectoryContextPickerDelegate { @@ -88,7 +150,7 @@ impl PickerDelegate for DirectoryContextPickerDelegate { self.selected_index } - fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext>) { + fn set_selected_index(&mut self, ix: usize, _cx: &mut ViewContext>) { self.selected_index = ix; } @@ -96,17 +158,65 @@ impl PickerDelegate for DirectoryContextPickerDelegate { "Search folders…".into() } - fn update_matches(&mut self, _query: String, _cx: &mut ViewContext>) -> Task<()> { - // TODO: Implement this once we fix the issues with the file context picker. - Task::ready(()) + fn update_matches(&mut self, query: String, cx: &mut ViewContext>) -> Task<()> { + let Some(workspace) = self.workspace.upgrade() else { + return Task::ready(()); + }; + + let search_task = self.search(query, Arc::::default(), &workspace, cx); + + cx.spawn(|this, mut cx| async move { + let mut paths = search_task.await; + let empty_path = Path::new(""); + paths.retain(|path_match| path_match.path.as_ref() != empty_path); + + this.update(&mut cx, |this, _cx| { + this.delegate.matches = paths; + }) + .log_err(); + }) } fn confirm(&mut self, _secondary: bool, cx: &mut ViewContext>) { - // TODO: Implement this once we fix the issues with the file context picker. - match self.confirm_behavior { - ConfirmBehavior::KeepOpen => {} - ConfirmBehavior::Close => self.dismissed(cx), - } + let mat = &self.matches[self.selected_index]; + + let workspace = self.workspace.clone(); + let Some(project) = workspace + .upgrade() + .map(|workspace| workspace.read(cx).project().clone()) + else { + return; + }; + let path = mat.path.clone(); + let worktree_id = WorktreeId::from_usize(mat.worktree_id); + let confirm_behavior = self.confirm_behavior; + cx.spawn(|this, mut cx| async move { + this.update(&mut cx, |this, cx| { + let mut text = String::new(); + + // TODO: Add the files from the selected directory. + + this.delegate + .context_store + .update(cx, |context_store, cx| { + context_store.insert_context( + ContextKind::Directory, + path.to_string_lossy().to_string(), + text, + ); + })?; + + match confirm_behavior { + ConfirmBehavior::KeepOpen => {} + ConfirmBehavior::Close => this.delegate.dismissed(cx), + } + + anyhow::Ok(()) + })??; + + anyhow::Ok(()) + }) + .detach_and_log_err(cx) } fn dismissed(&mut self, cx: &mut ViewContext>) { @@ -120,10 +230,18 @@ impl PickerDelegate for DirectoryContextPickerDelegate { fn render_match( &self, - _ix: usize, - _selected: bool, + ix: usize, + selected: bool, _cx: &mut ViewContext>, ) -> Option { - None + let path_match = &self.matches[ix]; + let directory_name = path_match.path.to_string_lossy().to_string(); + + Some( + ListItem::new(ix) + .inset(true) + .toggle_state(selected) + .child(h_flex().gap_2().child(Label::new(directory_name))), + ) } }