diff --git a/crates/agent_ui/src/acp/message_editor.rs b/crates/agent_ui/src/acp/message_editor.rs index 856cc4d0d47d1e1d618c0056c771dfabe3c0bda4..c8b8d45c730c09bc831b83f83392bb3fc8089ca2 100644 --- a/crates/agent_ui/src/acp/message_editor.rs +++ b/crates/agent_ui/src/acp/message_editor.rs @@ -463,6 +463,97 @@ impl MessageEditor { }) } + fn confirm_mention_for_directory( + &mut self, + abs_path: PathBuf, + cx: &mut Context, + ) -> Task> { + fn collect_files_in_path(worktree: &Worktree, path: &Path) -> Vec<(Arc, PathBuf)> { + 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(), worktree.full_path(&entry.path))); + } + } + + files + } + + let Some(project_path) = self + .project + .read(cx) + .project_path_for_absolute_path(&abs_path, cx) + else { + return Task::ready(Err(anyhow!("project path not found"))); + }; + let Some(entry) = self.project.read(cx).entry_for_path(&project_path, cx) else { + return Task::ready(Err(anyhow!("project entry not found"))); + }; + let directory_path = entry.path.clone(); + let worktree_id = project_path.worktree_id; + let Some(worktree) = self.project.read(cx).worktree_for_id(worktree_id, cx) else { + return Task::ready(Err(anyhow!("worktree not found"))); + }; + let project = self.project.clone(); + cx.spawn(async move |_, cx| { + let file_paths = worktree.read_with(cx, |worktree, _cx| { + collect_files_in_path(worktree, &directory_path) + })?; + let descendants_future = cx.update(|cx| { + join_all(file_paths.into_iter().map(|(worktree_path, full_path)| { + let rel_path = worktree_path + .strip_prefix(&directory_path) + .log_err() + .map_or_else(|| worktree_path.clone(), |rel_path| rel_path.into()); + + let open_task = project.update(cx, |project, cx| { + project.buffer_store().update(cx, |buffer_store, cx| { + let project_path = ProjectPath { + worktree_id, + path: worktree_path, + }; + buffer_store.open_buffer(project_path, cx) + }) + }); + + cx.spawn(async move |cx| { + let buffer = open_task.await.log_err()?; + let buffer_content = outline::get_buffer_content_or_outline( + buffer.clone(), + Some(&full_path), + &cx, + ) + .await + .ok()?; + + Some((rel_path, full_path, buffer_content.text, buffer)) + }) + })) + })?; + + let contents = cx + .background_spawn(async move { + let (contents, tracked_buffers) = descendants_future + .await + .into_iter() + .flatten() + .map(|(rel_path, full_path, rope, buffer)| { + ((rel_path, full_path, rope), buffer) + }) + .unzip(); + Mention::Text { + content: render_directory_contents(contents), + tracked_buffers, + } + }) + .await; + anyhow::Ok(contents) + }) + } + fn confirm_mention_for_fetch( &mut self, url: url::Url, diff --git a/crates/agent_ui/src/context.rs b/crates/agent_ui/src/context.rs index 2a1ff4a1d9d3e0bb6c8b128cf7f944e9ed3ff657..ff1864fba6d187e30f464e9f5c20292d06d3ea52 100644 --- a/crates/agent_ui/src/context.rs +++ b/crates/agent_ui/src/context.rs @@ -287,7 +287,7 @@ impl DirectoryContextHandle { let open_task = project.update(cx, |project, cx| { project.buffer_store().update(cx, |buffer_store, cx| { let project_path = ProjectPath { worktree_id, path }; - buffer_store.open_buffer(project_path, cx) + buffer_store.open_buffer(project_path, None, cx) }) }); diff --git a/crates/fs/src/encodings.rs b/crates/fs/src/encodings.rs index 780da0cadc54746a45775f113faf7835e14fd446..cbf3dece65e1fee80a146d36dac4219158922998 100644 --- a/crates/fs/src/encodings.rs +++ b/crates/fs/src/encodings.rs @@ -82,7 +82,7 @@ impl EncodingWrapper { } else { // If there were decoding errors, return an error. Err(anyhow::anyhow!( - "The file contains invalid bytes for the specified encoding: {}. This usually menas that the file is not a regular text file, or is encoded in a different encoding. Continuing to open it may result in data loss if saved.", + "The file contains invalid bytes for the specified encoding: {}.\nThis usually means that the file is not a regular text file, or is encoded in a different encoding.\nContinuing to open it may result in data loss if saved.", self.0.name() )) } diff --git a/crates/project/src/buffer_store.rs b/crates/project/src/buffer_store.rs index 04cf75b92e7dcaaf257894ee450cc71862db1405..d1a71fc4bc9ccc0e30eb9dbbdae8cba88037d4ee 100644 --- a/crates/project/src/buffer_store.rs +++ b/crates/project/src/buffer_store.rs @@ -9,6 +9,7 @@ use client::Client; use collections::{HashMap, HashSet, hash_map}; use fs::Fs; use futures::{Future, FutureExt as _, StreamExt, channel::oneshot, future::Shared}; +use fs::{Fs, encodings::EncodingWrapper}; use gpui::{ App, AppContext as _, AsyncApp, Context, Entity, EventEmitter, Subscription, Task, WeakEntity, }; @@ -631,11 +632,13 @@ impl LocalBufferStore { &self, path: Arc, worktree: Entity, + encoding: Option, cx: &mut Context, ) -> Task>> { let load_buffer = worktree.update(cx, |worktree, cx| { - let load_file = worktree.load_file(path.as_ref(), None, cx); + let load_file = worktree.load_file(path.as_ref(), encoding, cx); let reservation = cx.reserve_entity(); + let buffer_id = BufferId::from(reservation.entity_id().as_non_zero_u64()); let path = path.clone(); cx.spawn(async move |_, cx| { @@ -655,11 +658,7 @@ impl LocalBufferStore { cx.spawn(async move |this, cx| { let buffer = match load_buffer.await { - Ok(buffer) => { - // Reload the buffer to trigger UTF-16 detection - buffer.update(cx, |buffer, cx| buffer.reload(cx))?.await?; - Ok(buffer) - } + Ok(buffer) => Ok(buffer), Err(error) if is_not_found_error(&error) => cx.new(|cx| { let buffer_id = BufferId::from(cx.entity_id().as_non_zero_u64()); let text_buffer = text::Buffer::new( @@ -834,6 +833,7 @@ impl BufferStore { pub fn open_buffer( &mut self, project_path: ProjectPath, + encoding: Option, cx: &mut Context, ) -> Task>> { if let Some(buffer) = self.get_by_path(&project_path) { @@ -857,7 +857,7 @@ impl BufferStore { return Task::ready(Err(anyhow!("no such worktree"))); }; let load_buffer = match &self.state { - BufferStoreState::Local(this) => this.open_buffer(path, worktree, cx), + BufferStoreState::Local(this) => this.open_buffer(path, worktree, encoding, cx), BufferStoreState::Remote(this) => this.open_buffer(path, worktree, cx), }; diff --git a/crates/project/src/debugger/breakpoint_store.rs b/crates/project/src/debugger/breakpoint_store.rs index 42663ab9852a5dc2e9850d20dd20940c6723d03c..2791d6bfe1f322321cfd4b28104004bedc50a972 100644 --- a/crates/project/src/debugger/breakpoint_store.rs +++ b/crates/project/src/debugger/breakpoint_store.rs @@ -796,7 +796,7 @@ impl BreakpointStore { worktree_id: worktree.read(cx).id(), path: relative_path, }; - this.open_buffer(path, cx) + this.open_buffer(path, None, cx) })? .await; let Ok(buffer) = buffer else { diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 762070796f068fb01b19522b4a506eb693b9bd63..734fac8ce7a56924adce2fdd2dd5358fd245dbbf 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -8336,7 +8336,7 @@ impl LspStore { lsp_store .update(cx, |lsp_store, cx| { lsp_store.buffer_store().update(cx, |buffer_store, cx| { - buffer_store.open_buffer(project_path, cx) + buffer_store.open_buffer(project_path, None, cx) }) })? .await diff --git a/crates/project/src/lsp_store/rust_analyzer_ext.rs b/crates/project/src/lsp_store/rust_analyzer_ext.rs index 4d5f134e5f1682d53df3a0ab3f55a4b3676518f8..a8a6b06aafe1dffb8780f9423d146158b6d86bb5 100644 --- a/crates/project/src/lsp_store/rust_analyzer_ext.rs +++ b/crates/project/src/lsp_store/rust_analyzer_ext.rs @@ -91,7 +91,7 @@ pub fn cancel_flycheck( let buffer = buffer_path.map(|buffer_path| { project.update(cx, |project, cx| { project.buffer_store().update(cx, |buffer_store, cx| { - buffer_store.open_buffer(buffer_path, cx) + buffer_store.open_buffer(buffer_path, None, cx) }) }) }); @@ -140,7 +140,7 @@ pub fn run_flycheck( let buffer = buffer_path.map(|buffer_path| { project.update(cx, |project, cx| { project.buffer_store().update(cx, |buffer_store, cx| { - buffer_store.open_buffer(buffer_path, cx) + buffer_store.open_buffer(buffer_path, None, cx) }) }) }); @@ -198,7 +198,7 @@ pub fn clear_flycheck( let buffer = buffer_path.map(|buffer_path| { project.update(cx, |project, cx| { project.buffer_store().update(cx, |buffer_store, cx| { - buffer_store.open_buffer(buffer_path, cx) + buffer_store.open_buffer(buffer_path, None, cx) }) }) }); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 044c1501064bdb195c01f829548cc2acba99c8fa..04a3c5680dd9f6af036f46562b2736804edeec8d 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -28,6 +28,7 @@ use buffer_diff::BufferDiff; use context_server_store::ContextServerStore; use encoding_rs::Encoding; pub use environment::ProjectEnvironmentEvent; +use fs::encodings::EncodingWrapper; use git::repository::get_git_committer; use git_store::{Repository, RepositoryId}; pub mod search_history; @@ -2717,7 +2718,11 @@ impl Project { } self.buffer_store.update(cx, |buffer_store, cx| { - buffer_store.open_buffer(path.into(), cx) + buffer_store.open_buffer( + path.into(), + Some(EncodingWrapper::new(self.encoding.lock().as_ref().unwrap())), + cx, + ) }) } diff --git a/crates/remote_server/src/headless_project.rs b/crates/remote_server/src/headless_project.rs index 5d50853601b3949835a350559d48ef755419c93d..da02713a5f4ea14e29f52cf8f2258839aaf7705e 100644 --- a/crates/remote_server/src/headless_project.rs +++ b/crates/remote_server/src/headless_project.rs @@ -506,7 +506,14 @@ impl HeadlessProject { let (buffer_store, buffer) = this.update(&mut cx, |this, cx| { let buffer_store = this.buffer_store.clone(); let buffer = this.buffer_store.update(cx, |buffer_store, cx| { - buffer_store.open_buffer(ProjectPath { worktree_id, path }, cx) + buffer_store.open_buffer( + ProjectPath { + worktree_id, + path: Arc::::from_proto(message.payload.path), + }, + None, + cx, + ) }); anyhow::Ok((buffer_store, buffer)) })??; @@ -597,6 +604,7 @@ impl HeadlessProject { worktree_id: worktree.read(cx).id(), path: path, }, + None, cx, ) }); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d39864abdb140955b6029f0d320eccc5c623f6fb..79d446f85c31272dedb535efdc6995b2a2dd7f49 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -3416,6 +3416,7 @@ impl Workspace { window: &mut Window, cx: &mut Context, ) -> Task>> { + println!("{:?}", *self.encoding.lock().unwrap()); cx.spawn_in(window, async move |workspace, cx| { let open_paths_task_result = workspace .update_in(cx, |workspace, window, cx| { diff --git a/crates/worktree/src/worktree.rs b/crates/worktree/src/worktree.rs index b8837950f1c80a985bc28d2a0d7c325eea27c876..7be34dd9a2650f276ed805b90b6cd0ebaa9b8b9b 100644 --- a/crates/worktree/src/worktree.rs +++ b/crates/worktree/src/worktree.rs @@ -710,7 +710,7 @@ impl Worktree { pub fn load_file( &self, path: &Path, - encoding: Option>>, + encoding: Option, cx: &Context, ) -> Task> { match self { @@ -1324,7 +1324,7 @@ impl LocalWorktree { fn load_file( &self, path: &Path, - encoding: Option>>, + encoding: Option, cx: &Context, ) -> Task> { let path = Arc::from(path); @@ -1353,7 +1353,7 @@ impl LocalWorktree { .load_with_encoding( &abs_path, if let Some(encoding) = encoding { - EncodingWrapper::new(*encoding.lock().unwrap()) + encoding } else { EncodingWrapper::new(encoding_rs::UTF_8) },