From b17f2089a2fb7e2f142eb71bf78b007f397629b0 Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Tue, 17 Dec 2024 13:02:46 -0500 Subject: [PATCH] assistant2: Sketch in directory context picker (#22148) This PR sketches in the structure for the directory context picker. Waiting on implementing the actual behavior until we fix the issues with the file context picker. Release Notes: - N/A --- crates/assistant2/src/context.rs | 11 ++ crates/assistant2/src/context_picker.rs | 23 ++++ .../directory_context_picker.rs | 117 ++++++++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 crates/assistant2/src/context_picker/directory_context_picker.rs diff --git a/crates/assistant2/src/context.rs b/crates/assistant2/src/context.rs index 7e335f413..1aff6c982 100644 --- a/crates/assistant2/src/context.rs +++ b/crates/assistant2/src/context.rs @@ -24,6 +24,7 @@ pub struct Context { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ContextKind { File, + Directory, FetchedUrl, Thread, } @@ -33,6 +34,7 @@ pub fn attach_context_to_message( context: impl IntoIterator, ) { let mut file_context = String::new(); + let mut directory_context = String::new(); let mut fetch_context = String::new(); let mut thread_context = String::new(); @@ -42,6 +44,10 @@ pub fn attach_context_to_message( file_context.push_str(&context.text); file_context.push('\n'); } + ContextKind::Directory => { + directory_context.push_str(&context.text); + directory_context.push('\n'); + } ContextKind::FetchedUrl => { fetch_context.push_str(&context.name); fetch_context.push('\n'); @@ -63,6 +69,11 @@ pub fn attach_context_to_message( context_text.push_str(&file_context); } + if !directory_context.is_empty() { + context_text.push_str("The following directories are available:\n"); + context_text.push_str(&directory_context); + } + if !fetch_context.is_empty() { context_text.push_str("The following fetched results are available\n"); context_text.push_str(&fetch_context); diff --git a/crates/assistant2/src/context_picker.rs b/crates/assistant2/src/context_picker.rs index f6a49d028..27835deb0 100644 --- a/crates/assistant2/src/context_picker.rs +++ b/crates/assistant2/src/context_picker.rs @@ -1,3 +1,4 @@ +mod directory_context_picker; mod fetch_context_picker; mod file_context_picker; mod thread_context_picker; @@ -14,6 +15,7 @@ use util::ResultExt; use workspace::Workspace; use crate::context::ContextKind; +use crate::context_picker::directory_context_picker::DirectoryContextPicker; use crate::context_picker::fetch_context_picker::FetchContextPicker; use crate::context_picker::file_context_picker::FileContextPicker; use crate::context_picker::thread_context_picker::ThreadContextPicker; @@ -24,6 +26,7 @@ use crate::thread_store::ThreadStore; enum ContextPickerMode { Default, File(View), + Directory(View), Fetch(View), Thread(View), } @@ -46,6 +49,11 @@ impl ContextPicker { kind: ContextKind::File, icon: IconName::File, }, + ContextPickerEntry { + name: "Folder".into(), + kind: ContextKind::Directory, + icon: IconName::Folder, + }, ContextPickerEntry { name: "Fetch".into(), kind: ContextKind::FetchedUrl, @@ -92,6 +100,7 @@ impl FocusableView for ContextPicker { match &self.mode { ContextPickerMode::Default => self.picker.focus_handle(cx), ContextPickerMode::File(file_picker) => file_picker.focus_handle(cx), + ContextPickerMode::Directory(directory_picker) => directory_picker.focus_handle(cx), ContextPickerMode::Fetch(fetch_picker) => fetch_picker.focus_handle(cx), ContextPickerMode::Thread(thread_picker) => thread_picker.focus_handle(cx), } @@ -106,6 +115,9 @@ impl Render for ContextPicker { .map(|parent| match &self.mode { ContextPickerMode::Default => parent.child(self.picker.clone()), ContextPickerMode::File(file_picker) => parent.child(file_picker.clone()), + ContextPickerMode::Directory(directory_picker) => { + parent.child(directory_picker.clone()) + } ContextPickerMode::Fetch(fetch_picker) => parent.child(fetch_picker.clone()), ContextPickerMode::Thread(thread_picker) => parent.child(thread_picker.clone()), }) @@ -167,6 +179,16 @@ impl PickerDelegate for ContextPickerDelegate { ) })); } + ContextKind::Directory => { + this.mode = ContextPickerMode::Directory(cx.new_view(|cx| { + DirectoryContextPicker::new( + self.context_picker.clone(), + self.workspace.clone(), + self.context_store.clone(), + cx, + ) + })); + } ContextKind::FetchedUrl => { this.mode = ContextPickerMode::Fetch(cx.new_view(|cx| { FetchContextPicker::new( @@ -202,6 +224,7 @@ impl PickerDelegate for ContextPickerDelegate { .update(cx, |this, cx| match this.mode { ContextPickerMode::Default => cx.emit(DismissEvent), ContextPickerMode::File(_) + | ContextPickerMode::Directory(_) | ContextPickerMode::Fetch(_) | ContextPickerMode::Thread(_) => {} }) diff --git a/crates/assistant2/src/context_picker/directory_context_picker.rs b/crates/assistant2/src/context_picker/directory_context_picker.rs new file mode 100644 index 000000000..104515356 --- /dev/null +++ b/crates/assistant2/src/context_picker/directory_context_picker.rs @@ -0,0 +1,117 @@ +// TODO: Remove this once we've implemented the functionality. +#![allow(unused)] + +use std::sync::Arc; + +use fuzzy::PathMatch; +use gpui::{AppContext, DismissEvent, FocusHandle, FocusableView, Task, View, WeakModel, WeakView}; +use picker::{Picker, PickerDelegate}; +use project::{PathMatchCandidateSet, WorktreeId}; +use ui::{prelude::*, ListItem}; +use util::ResultExt as _; +use workspace::Workspace; + +use crate::context_picker::ContextPicker; +use crate::context_store::ContextStore; + +pub struct DirectoryContextPicker { + picker: View>, +} + +impl DirectoryContextPicker { + pub fn new( + context_picker: WeakView, + workspace: WeakView, + context_store: WeakModel, + cx: &mut ViewContext, + ) -> Self { + let delegate = + DirectoryContextPickerDelegate::new(context_picker, workspace, context_store); + let picker = cx.new_view(|cx| Picker::uniform_list(delegate, cx)); + + Self { picker } + } +} + +impl FocusableView for DirectoryContextPicker { + fn focus_handle(&self, cx: &AppContext) -> FocusHandle { + self.picker.focus_handle(cx) + } +} + +impl Render for DirectoryContextPicker { + fn render(&mut self, _cx: &mut ViewContext) -> impl IntoElement { + self.picker.clone() + } +} + +pub struct DirectoryContextPickerDelegate { + context_picker: WeakView, + workspace: WeakView, + context_store: WeakModel, + matches: Vec, + selected_index: usize, +} + +impl DirectoryContextPickerDelegate { + pub fn new( + context_picker: WeakView, + workspace: WeakView, + context_store: WeakModel, + ) -> Self { + Self { + context_picker, + workspace, + context_store, + matches: Vec::new(), + selected_index: 0, + } + } +} + +impl PickerDelegate for DirectoryContextPickerDelegate { + type ListItem = ListItem; + + fn match_count(&self) -> usize { + self.matches.len() + } + + fn selected_index(&self) -> usize { + self.selected_index + } + + fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext>) { + self.selected_index = ix; + } + + fn placeholder_text(&self, _cx: &mut WindowContext) -> Arc { + "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 confirm(&mut self, _secondary: bool, _cx: &mut ViewContext>) { + // TODO: Implement this once we fix the issues with the file context picker. + } + + fn dismissed(&mut self, cx: &mut ViewContext>) { + self.context_picker + .update(cx, |this, cx| { + this.reset_mode(); + cx.emit(DismissEvent); + }) + .ok(); + } + + fn render_match( + &self, + _ix: usize, + _selected: bool, + _cx: &mut ViewContext>, + ) -> Option { + None + } +}