From 2c2ca9e3700ce4e463cb200a72d13727a07bf1df Mon Sep 17 00:00:00 2001 From: Marshall Bowers Date: Thu, 2 Jan 2025 14:42:59 -0500 Subject: [PATCH] assistant2: Wire up the directory context picker (#22582) This PR wires up the functionality of the directory context picker. Release Notes: - N/A --------- Co-authored-by: Agus --- Cargo.lock | 1 - crates/assistant2/Cargo.toml | 1 - crates/assistant2/src/context_picker.rs | 16 ++--- .../directory_context_picker.rs | 72 +++++++++++++++++-- .../src/context_picker/file_context_picker.rs | 5 +- 5 files changed, 75 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7d2d28a01a..c75e66ebb1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -494,7 +494,6 @@ dependencies = [ "project", "proto", "rand 0.8.5", - "release_channel", "rope", "schemars", "serde", diff --git a/crates/assistant2/Cargo.toml b/crates/assistant2/Cargo.toml index 59d2b567f3..c67674b437 100644 --- a/crates/assistant2/Cargo.toml +++ b/crates/assistant2/Cargo.toml @@ -50,7 +50,6 @@ parking_lot.workspace = true picker.workspace = true project.workspace = true proto.workspace = true -release_channel.workspace = true rope.workspace = true schemars.workspace = true serde.workspace = true diff --git a/crates/assistant2/src/context_picker.rs b/crates/assistant2/src/context_picker.rs index ec3421d63f..dda25d0643 100644 --- a/crates/assistant2/src/context_picker.rs +++ b/crates/assistant2/src/context_picker.rs @@ -10,7 +10,6 @@ use gpui::{ WeakModel, WeakView, }; use picker::{Picker, PickerDelegate}; -use release_channel::ReleaseChannel; use ui::{prelude::*, ListItem, ListItemSpacing}; use util::ResultExt; use workspace::Workspace; @@ -56,16 +55,11 @@ impl ContextPicker { kind: ContextPickerEntryKind::File, icon: IconName::File, }); - let release_channel = ReleaseChannel::global(cx); - // The directory context picker isn't fully implemented yet, so limit it - // to development builds. - if release_channel == ReleaseChannel::Dev { - entries.push(ContextPickerEntry { - name: "Folder".into(), - kind: ContextPickerEntryKind::Directory, - icon: IconName::Folder, - }); - } + entries.push(ContextPickerEntry { + name: "Folder".into(), + kind: ContextPickerEntryKind::Directory, + icon: IconName::Folder, + }); entries.push(ContextPickerEntry { name: "Fetch".into(), kind: ContextPickerEntryKind::FetchedUrl, diff --git a/crates/assistant2/src/context_picker/directory_context_picker.rs b/crates/assistant2/src/context_picker/directory_context_picker.rs index 9684e70db2..ecd8b6b70e 100644 --- a/crates/assistant2/src/context_picker/directory_context_picker.rs +++ b/crates/assistant2/src/context_picker/directory_context_picker.rs @@ -1,19 +1,18 @@ -// TODO: Remove this when we finish the implementation. -#![allow(unused)] - use std::path::Path; use std::sync::atomic::AtomicBool; use std::sync::Arc; +use anyhow::anyhow; use fuzzy::PathMatch; use gpui::{AppContext, DismissEvent, FocusHandle, FocusableView, Task, View, WeakModel, WeakView}; use picker::{Picker, PickerDelegate}; -use project::{PathMatchCandidateSet, WorktreeId}; +use project::{PathMatchCandidateSet, ProjectPath, Worktree, WorktreeId}; use ui::{prelude::*, ListItem}; use util::ResultExt as _; use workspace::Workspace; use crate::context::ContextKind; +use crate::context_picker::file_context_picker::codeblock_fence_for_path; use crate::context_picker::{ConfirmBehavior, ContextPicker}; use crate::context_store::ContextStore; @@ -193,14 +192,61 @@ impl PickerDelegate for DirectoryContextPickerDelegate { let worktree_id = WorktreeId::from_usize(mat.worktree_id); let confirm_behavior = self.confirm_behavior; cx.spawn(|this, mut cx| async move { + let worktree = project.update(&mut cx, |project, cx| { + project + .worktree_for_id(worktree_id, cx) + .ok_or_else(|| anyhow!("no worktree found for {worktree_id:?}")) + })??; + + let files = worktree.update(&mut cx, |worktree, _cx| { + collect_files_in_path(worktree, &path) + })?; + + let open_buffer_tasks = project.update(&mut cx, |project, cx| { + files + .into_iter() + .map(|file_path| { + project.open_buffer( + ProjectPath { + worktree_id, + path: file_path.clone(), + }, + cx, + ) + }) + .collect::>() + })?; + + let open_all_buffers_tasks = cx.background_executor().spawn(async move { + let mut buffers = Vec::with_capacity(open_buffer_tasks.len()); + + for open_buffer_task in open_buffer_tasks { + let buffer = open_buffer_task.await?; + + buffers.push(buffer); + } + + anyhow::Ok(buffers) + }); + + let buffers = open_all_buffers_tasks.await?; + this.update(&mut cx, |this, cx| { let mut text = String::new(); - // TODO: Add the files from the selected directory. + for buffer in buffers { + text.push_str(&codeblock_fence_for_path(Some(&path), None)); + text.push_str(&buffer.read(cx).text()); + if !text.ends_with('\n') { + text.push('\n'); + } + + text.push_str("```\n"); + } this.delegate .context_store - .update(cx, |context_store, cx| { + .update(cx, |context_store, _cx| { context_store.insert_context( ContextKind::Directory, path.to_string_lossy().to_string(), @@ -247,3 +293,17 @@ impl PickerDelegate for DirectoryContextPickerDelegate { ) } } + +fn collect_files_in_path(worktree: &Worktree, path: &Path) -> Vec> { + let mut files = Vec::new(); + + for entry in worktree.child_entries(path) { + if entry.is_dir() { + files.extend(collect_files_in_path(worktree, &entry.path)); + } else if entry.is_file() { + files.push(entry.path.clone()); + } + } + + files +} diff --git a/crates/assistant2/src/context_picker/file_context_picker.rs b/crates/assistant2/src/context_picker/file_context_picker.rs index c8258246e5..66aacf05c1 100644 --- a/crates/assistant2/src/context_picker/file_context_picker.rs +++ b/crates/assistant2/src/context_picker/file_context_picker.rs @@ -316,7 +316,10 @@ impl PickerDelegate for FileContextPickerDelegate { } } -fn codeblock_fence_for_path(path: Option<&Path>, row_range: Option>) -> String { +pub(crate) fn codeblock_fence_for_path( + path: Option<&Path>, + row_range: Option>, +) -> String { let mut text = String::new(); write!(text, "```").unwrap();